🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
openpyxl

openpyxlは、Excel2007以降のファイル(xlsx/xlsm/xltx/xltm)を読み書きするためのPythonライブラリです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2590閲覧

[Python3.x]Excelを列ごとにGUI上にLabel表示させたい

MM921

総合スコア14

openpyxl

openpyxlは、Excel2007以降のファイル(xlsx/xlsm/xltx/xltm)を読み書きするためのPythonライブラリです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/03/16 04:26

編集2021/03/16 04:56

前提・実現したいこと

在庫管理アプリを作ろうとしています。
一度完成はさせましたが,コードの修正が簡単になるように簡素化しようとしています。

表示させる予定の備品名と個数をExcelの表にまとめており,その表から列ごとラベルでpack()表示しようと試みましたが置きたい場所に縦表示することができません。(これまで一つずつ表示していた)
お力添えいただけると幸いです。
以下が理想図です。(一度完成させたものです)
イメージ説明

発生している問題

canvasを用いての画面遷移後,上記画像の備品名と備品個数のように表示させたいのですがpack()のanchorを用いて表示すると備品名の上に縦積みになってしまいます。
縦積みではなく,備品名と備品個数が並んで表示されるようにしたいです。(コード中のAall1とAall2のことです)

該当のコードとexcelファイル

# -*- coding: utf-8 -*- import tkinter try:import tkinter as tk except:import tkinter as tk from tkinter import messagebox import pandas as pd import openpyxl # ウィンドウの作成 window = tkinter.Tk() window.geometry("1358x750") window.title("Screen Transition") # 遷移前の画面の作成(画面1) canvas1 = tkinter.Canvas(bd=0, width=1366, height=768) canvas1.place(x=0, y=0) # キャンバス titleLabel1 = tk.Label(canvas1, text=" ※@で画面を閉じる ", font=('Helvetica', '13'), fg='gray8') titleLabel1.place(relx=0.85, rely=0.01) titleLabel2 = tkinter.Label(canvas1, text="こんにちは。前室在庫管理システムです。", font=('BIZ UDゴシック', '25', 'bold'), bg="misty rose", fg='firebrick4') # テキスト titleLabel2.place(relx=0.29, rely=0.25) titleLabel3 = tkinter.Label(canvas1, text="下記ボックスにカーソルを置いて,別紙の社員QRコードをかざしてログインしてください。", font=('BIZ UDゴシック', '15'), fg='gray4') # テキスト titleLabel3.place(relx=0.23, rely=0.35) titleLabel4 = tkinter.Label(canvas1, text="↓Name Here↓", font=('BIZ UDゴシック', '23'), fg='gray4') # テキスト titleLabel4.place(relx=0.42, rely=0.42) button = tkinter.Button(canvas1, text=" Go ", font=('BIZ UDゴシック', '15'), command=lambda: transition_button1(canvas1), fg='gray4') # 遷移ボタン button.place(relx=0.45, rely=0.53) # button実行結果 def transition_button1(widget): widget.place_forget() # canvas(widget)を隠す global entry1 # 遷移後の画面の作成(画面2) canvas2 = tkinter.Canvas(bd=0, width=1366, height=1500) # キャンバス canvas2.place(x=0, y=0) # Top Widget上に Scrollbar を生成して配置 bar = tk.Scrollbar(window, orient=tk.VERTICAL) bar.pack(side=tk.RIGHT, fill=tk.Y) # Scrollbarの設定 bar.config(command=canvas2.yview) # ScrollbarでCanvasを制御 # Canvas Widget をTopWidget上に配置 canvas2.config(yscrollcommand=bar.set) # Canvasのサイズ変更をScrollbarに通知 canvas2.config(scrollregion=(0, 0, 7000, 7000)) # スクロール範囲 canvas2.bind_all("<MouseWheel>", lambda eve: canvas2.yview_scroll(int(-eve.delta / 120), 'units')) canvas2.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Frame Widgetを 生成 frame = tk.Frame(canvas2) # Frame Widgetを Canvas Widget上に配置() canvas2.create_window((0, 0), window=frame, anchor=tk.NW, width=1366, height=7000) # ブックを取得 book = openpyxl.load_workbook(r'C:\Users\mn-manabe\PycharmProjects\sample2Project\一覧備品.xlsx') # シートを取得 sheet = book['Sheet1'] # A1からC7まで(すべて)のセルの値を「行ごと」に取得 print("--- すべての値(行ごと)を取得 ---") for row in sheet.iter_rows(min_row=1, max_col=1, max_row=sheet.max_row, values_only=True): print(row) row1 = '\n'.join(row) Aall1 = tkinter.Label(frame, text=row, font=('メイリオ', '15'), bg='pink', width=30, anchor="w") Aall1.pack(anchor='nw') for row in sheet.iter_rows(min_row=1, max_col=2, min_col=2, max_row=sheet.max_row, values_only=True): print(row) Aall2 = tkinter.Label(frame, text=row, font=('メイリオ', '15'), bg='pink', width=30, anchor="e") Aall2.pack(anchor='n') window.mainloop()

以下が表示させたい表です。
↓一覧部品.xlsx

備品名個数
a500
b500
c500
e500
f500
g500
### 試したこと
Aall1とAall2がExcelの表のように縦表示されたいためpack()のanchorとtopを使って自分なりに試してみましたがどうしても並んでくれませんでした…
また画面遷移はスクロールバーが使用可能なcanvasを使っています。
frameを用いてAall1とAall2を表示はできましたがスクロールができないため断念しました。

python初学者なため至らない点が多々あると思いますがご教示お願い致します。

※掲載ミスにより再掲

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

t_obara

2021/03/16 04:32

何がどううまくいかないか具体的にご提示されると回答がつきやすいと思いますよ。
MM921

2021/03/16 04:38

t_obaraさん,ご回答ありがとうございます。 投稿文一部消えていましたので文章を再度練って再掲したいと思います。 気づいていなかったためご指摘感謝いたします。
guest

回答1

0

ベストアンサー

grid を使いましょう。row/column を指定して任意の場所に配置します。
enumerate 関数を使うと連番付けを楽に記述できます。

python

1 2 # Header 3 for idx, val in enumerate(["備品名", "備品個数", "必要個数"]): 4 label = tk.Label(frame, text=val) 5 label.grid(row=0, column=idx) 6 7 # Body 8 data = [ ## テスト用仮データ sheet.iter_rows(...) 9 ["A", "295"], 10 ["B", "215"], 11 ] 12 for idx, row in enumerate(data, start=1): 13 label1 = tk.Label(frame, text=row[0]) 14 label2 = tk.Label(frame, text=row[1]) 15 entry = tk.Entry(frame) 16 17 label1.grid(row=idx, column=0) 18 label2.grid(row=idx, column=1) 19 entry.grid(row=idx, column=2) 20

pack でのレイアウトでは 各行の column 幅を一定に揃えることが出来ません。
自分で幅を計算するか固定幅にすることになります → レイアウトに任せることが出来ない、余分な手間に。

※ セル1つに1つのウィジェットを作るため、
この実装では、データのサイズがメモリに乗る規模であることが前提です。
大規模なデータを取り扱う場合は、省メモリ化を考慮した対策が必要です。

(キャンバスのサイズ7000x7000とあることから、
そのサイズのグリッドを埋める規模の数のウィジェットを作成すると、
パフォーマンスへの影響が懸念されます)

投稿2021/03/16 06:38

teamikl

総合スコア8738

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

MM921

2021/03/16 07:56

teamiklさん,ご回答ありがとうございます。 引用してみるとgridでrow/columnを指定するときれいに並びますね。 本文掲載不足で申し訳ありません。 実を申しますと記述している備品名等は仮で設定されたもので,実際はすべてのデータを合わせると約130個のデータがあります(増えるかもしれません)。 これから修正を簡単にするため今回Excelの表から列を抽出して一つのラベルにまとめるといった方法をとっていました。excelを修正したらそれに合わせてGUIも修正されるようにしてみたいと思っております。 teamiklさんがご提案いただいたコードのリスト(data)のようにexcel表から引用したデータをリストに入れてラベル表示することは可能なんでしょうか?長々と申し訳ありません。 しかしお時間があるときにみていただけたら幸いです。
teamikl

2021/03/16 08:34

実際のデータがわからなかったので、仮で定義しただけです。 リストにせずにイテレータのまま、for文で繰り返し処理で良いと思います。 data の部分は sheet.iter_rows(...) に置き換えて下さい。 カラム数が不定/動的に判別したい場合は row の内容を更に for 文で回すことになります for row_idx, row in enumerate(sheet.iter_rows(...), start=1):  for col_idx, value in enumerate(row):   label = tk.Label(frame, text=value)   label.grid(row=row_idx, column=col_idx)
MM921

2021/03/17 00:52

teamiklさん,お返事遅れて申し訳ありません。 実際のコードで上記を試してみたところ,Excel列がきちんとLabel表示ができていました! データ数が膨大な場合はfor文を用いるとコードも短くなるんですね。 とても勉強になりました,お忙しい中ありがとうございました????
teamikl

2021/03/17 01:45

データ数が膨大な場合は注意点があって データ数が膨大な場合は、for文を使うと時間がかかり その間の GUI が一時的にフリーズしてしまう問題があります。 (for文の実行に数秒かかるようであれば、ユーザーはGUIのラグを感じるようになります) 本来は「時間のかかる」ループは、GUIの反応を止めてしまう為 イベント処理内では行わない方が良いので、応急処置的な回避策として for (...): # rowループ  for (...): # columnループ   ...  window.update() # 適度にイベントループ処理を呼び出す # update を呼ばない場合は、関数の実行が終わるまで表示が反映されない。 数秒の待ち時間程度で許容できる範囲なら良いのですが、 きちんと対策するなら、設計から変更が必要になってきます。 残念ながら tkinter の標準では大規模データに対応したウィジェットは用意されてないため 独自に工夫して実装するか、外部ライブラリ(pandastable等) に頼ることになります。 参考: tkinterのtreeviewにて、大量のデータを表示させる方法 https://teratail.com/questions/323794 https://teratail.com/questions/323469 # 回答にて100万件のデータを表示するサンプル掲載
MM921

2021/03/19 04:26

teamiklさん,お返事ありがとうございます。 確かに一度完成させたコード(簡素化していないものです)を実行させたとき処理が遅かったです。 for文後に上記のwindow.update()をつけるようにしたいと思います。 度々申し訳ありませんが,追加の質問があります。teamiklさんのお時間があるときに見ていただけたら幸いです。 最初回答してくださった部分でentryを行数に合わせて複数設置されるところですが,.get()で一つずつentryから数値を取り出したいときどのようにしたら個々entryを指定できるのかわかりませんでした。(上からn番目のentryの.get()を求めたい) >for idx, row in enumerate(data, start=1): label1 = tk.Label(frame, text=row[0]) label2 = tk.Label(frame, text=row[1]) entry = tk.Entry(frame) #この部分です そもそも指定が可能なのか調べてもわかりませんでした…。
teamikl

2021/03/19 08:11 編集

上から n 番目の entry の参照は、幾つか方法があります。 A. 変数 entry をリストに保存しておく。  同一行内に複数の entry がある場合は 2次元配列や (row, column) のタプルをキーとした辞書等 B. widget 生成時に name= で名前を付ける。nametowidget で対象のウィジェットを取り出し。  名前を省略した場合は連番で付けられます。  名前をして参照可能ですが、レイアウトやコードの変更には弱くなります。名前の衝突には注意。  → 行番号を名前に含める事で、特定行の entry を指定可能になります。 C. grid 内容を問い合わせる方法  frame.grid_slaves(row=n, column=m)  但し、同じセル内に複数のウィジェットを重ねる事も可能なので、戻り値はリストです。 データの件数次第ですが、 行自体を一つのクラスとしてまとめ、リストに持たせるのが率直な実装です。 → リストのindex+1 が行数。
MM921

2021/04/05 01:08

teamiklさん,お返事ありがとうございます。大変遅れて申し訳ありません。 Aの方法を中心に調べていましたが,dictを用いてentryやspinboxを指定する方法が記述されておらず八方塞がり状態です... Aの方法はentry1,entry2...のように辞書に登録するということでしょうか?
teamikl

2021/04/05 04:29 編集

entry は一行に一つでしょうか?それとも複数ありますか? また、ウィジェットの数・規模により最適な方法は異なりますが、 率直な実装では ## 辞書を使う方法 外部で辞書を用意しておき entries = {} 配置する grid の (row,column) をキーにウィジェットを登録 entries[(row, column)] = entry ## リストを使う方法 (1行に複数のentry がある場合を想定) 外部でリストを用意しておき entries = [] ループ内でウィジェットを纏めて追加 entries.append([entery1, entry2, entry3]) n行目のウィジェットの参照は (※ indexは0開始) entry1, entry2, entry3 = entries[n-1]
MM921

2021/04/05 07:22

teamiklさん,お返事ありがとうございます。 entryをリストへ入れて,for文を用いたらentryが指定できるようになりました! 長々と質問をしてしまい申し訳ありませんでした。しかし,非常に勉強になりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問