質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
Tkinter

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

Python

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

Q&A

解決済

1回答

2982閲覧

tkinterを使ったクイズゲームで、画像をランダムに表示させたい

退会済みユーザー

退会済みユーザー

総合スコア0

Tkinter

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

Python

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

0グッド

1クリップ

投稿2020/01/26 15:14

前提・実現したいこと

クイズゲームを作成しています。
取り込んだcsvの要素をランダムに選び、同じ要素を答えさせるゲームはいったん完成したため
新たにランダムに植物の画像を表示し、その植物の名前を当てるゲームを考えています。
※csvは以下のようなイメージです。
[{'name': 'ススキ', 'family': 'イネ科', '画像': 'ine.jpg'},
{'name': 'コムギ', 'family': 'イネ科', '画像': 'komugi.jpg'},…

ラベルに画像を表示させる(まだ固定ファイル名)ことはなんとかできたのですが、
1.その画像をリサイズする処理がうまくいきません。
リサイズ処理のみ動かした場合、うまくいったプログラムを移植したのですが・・・
2.画像をランダムに選択するため、画像読み込みの部分を関数init_view(tki)内に入れたいのですが
テスト的に固定ファイル名を指定した状態でも、まともに表示できません。

特に2に関してはラベルの理解が甘いのだと思うのですが、調べてはみたものの
ラベルの扱いも画像表示の手法も人によってさまざまで、前に進まなくなってきました。

発生している問題・エラーメッセージ

1.について _tkinter.TclError: image specification must contain an odd number of elements

該当のソースコード

python3.8.0

1import random 2import csv 3import tkinter, tkinter.messagebox 4from tkinter import * 5from tkinter import ttk 6 7tki = None 8rdo = None 9b_options = None 10rdo_var = None 11answer = None 12 13def init_view(tki): 14 global rdo, b_options, rdo_var, answer 15 16 # 上記DictReaderで読み込んだ辞書リストから、問題用に、ひとつの辞書を呼び出す 17 p_plant = random.choice(rows) 18 print(p_plant) 19 p_name = p_plant["name"] 20 21 # 問題用の植物と同じ科(family)の植物をリストにして、 22 # その中から問題用の植物を削除。 23 # 残りからひとつを選ぶ(=その植物が答えになる) 24 25 q_list = [] 26 while len(q_list) < 2: 27 q_list = [x['name'] for x in rows if x['family'] == p_plant["family"]] 28 q_list.remove(p_plant["name"]) 29 answer = random.choice(q_list) 30 31 # 答えとは異なる科(family)の植物をリスト化し、そこから3つ選ぶ 32 b_list = [x['name'] for x in rows if x['family'] != p_plant["family"]] 33 b_options = random.sample(b_list , 3) 34 b_options.append(str(answer)) 35 random.shuffle(b_options) 36 37 # 配置済みの子ウィジェットが存在すれば、削除しておく 38 children = tki.winfo_children() 39 for child in children: 40 # print("type of widget is : " + str(type(child))) 41 if str(type(child)) == "<class 'tkinter.Radiobutton'>": 42 child.destroy() 43 elif str(type(child)) == "<class 'tkinter.Button'>": 44 child.destroy() 45 elif str(type(child)) == "<class 'tkinter.Label'>": 46 child.destroy() 47 48 # ラジオボタンのラベルをリスト化する 49 # rdo_txt = random.sample(b_options , k=4) 50 # ラジオボタンの状態 51 rdo_var = tkinter.IntVar() 52 53 # ラジオボタンを動的に作成して配置 54 for i in range(4): 55 rdo = tkinter.Radiobutton(tki, value=i, variable=rdo_var, text=b_options[i]) 56 rdo.place(x=50, y=80 + (i * 24)) 57 58 # ボタン作成 59 btn = tkinter.Button(tki, text='答え合わせ', command=btn_click) 60 btn.place(x=100, y=270) 61 62 63# 以前画像を使わない4択問題で使っていたコード 64# var = tkinter.StringVar() 65# var.set(p_name + "と同じ科の植物はどれでしょう") 66# label = tkinter.Label(tki, image=icon,textvariable=var, compound="top",width=100, ) 67# label.pack() 68 69# ボタンクリックイベント 70def btn_click(): 71 num = rdo_var.get() 72 73 if b_options[num] == str(answer): 74 tkinter.messagebox.showinfo('結果' , "正解!") 75 else: 76 tkinter.messagebox.showinfo("結果" , '不正解!正解は' + answer) 77 78 # 表示内容の再初期化 79 init_view(tki) 80 81with open('plant.csv', newline='') as csvfile: 82 reader = csv.DictReader(csvfile) 83 rows = [] 84 for row in reader: 85 rows.append(row) 86 87root = Tk() 88root.geometry('300x400') 89root.title('ラジオボタン') 90frame1 = ttk.Frame(root) 91frame1.grid() 92 93# ここが困っている部分です 94icon = PhotoImage(file="aburana_f.gif") 95label1 = ttk.Label(frame1 , image=icon , text="てきすと", compound="top") 96label1.grid(row=1,column=1) 97 98root.mainloop() 99 100

試したこと

1.について
img = Image.open("daikon_f.jpg") # イメージを開く
resize_image = img.resize((200, int(200 * img.size[1] / img.size[0]))) # 画像のリサイズ

2.について
icon = PhotoImage(file="aburana_f.gif")
label1 = ttk.Label(frame1 , image=icon , text="てきすと", compound="top")
label1.grid(row=1,column=1)

ボタン作成の下に入れ、多少いじりましたが、ウィンドウは出るものの、画像もボタンも何も表示されません。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

Labelに画像を動的に張り付ける方法ですが、以下のようにすることで可能となります。Labelに張り付けるのはPhotoImageでなければいけない為、Imageで加工した後、PhotoImageのコンストラクタに引数として渡して利用します。動的に画像の変更ができれば、ランダムに指定することも可能でしょう。
※既にインストールされていたのかもしれませんが、Pillow(PIL)が必要です。($ pip install pillow)

Python3

1from tkinter import ttk 2from PIL import Image, ImageTk 3 4img = Image.open("aburana_f.gif") 5resize_img = img.resize((200, int(200 * img.size[1] / img.size[0]))) 6icon = ImageTk.PhotoImage(resize_img) 7 8icon_label = ttk.Label(tki, compound="top") 9icon_label.place(x=10, y=240) 10 11# 動的に画像とテキストをセット 12icon_label.configure(image=icon, text="アイコンラベル") 13icon_label.image = icon

ここで、icon_label.configure(image=icon, text="アイコンラベル")に続けてicon_label.image = iconとするのは冗長で無意味に思えるのですが、こうしないと画像が表示できませんでした。

質問中にご提示のコードは既存のラジオボタンやボタンをplaceメソッドを用いて画面上の位置を決めているので、Labelも同様にplaceメソッドで配置するよう修正追加してみた例が以下です。画像の切り替えが動的にできるかどうか、交互に表示するデモ動作になっています。

Python3

1import random 2import csv 3import tkinter, tkinter.messagebox 4from tkinter import * 5from tkinter import ttk 6from PIL import Image, ImageTk 7 8#tki = None 9root = None 10rdo = None 11b_options = None 12rdo_var = None 13answer = None 14icon_label = None 15toggle = None 16 17def init_view(tki): 18 global rdo, b_options, rdo_var, answer, icon_label, toggle 19 20 # 上記DictReaderで読み込んだ辞書リストから、問題用に、ひとつの辞書を呼び出す 21 p_plant = random.choice(rows) 22 print(p_plant) 23 p_name = p_plant["name"] 24 25 # 問題用の植物と同じ科(family)の植物をリストにして、 26 # その中から問題用の植物を削除。 27 # 残りからひとつを選ぶ(=その植物が答えになる) 28 29 q_list = [] 30 while len(q_list) < 2: 31 q_list = [x['name'] for x in rows if x['family'] == p_plant["family"]] 32 q_list.remove(p_plant["name"]) 33 answer = random.choice(q_list) 34 35 # 答えとは異なる科(family)の植物をリスト化し、そこから3つ選ぶ 36 b_list = [x['name'] for x in rows if x['family'] != p_plant["family"]] 37 b_options = random.sample(b_list , 3) 38 b_options.append(str(answer)) 39 random.shuffle(b_options) 40 41 # 配置済みの子ウィジェットが存在すれば、削除しておく 42 children = tki.winfo_children() 43 for child in children: 44 print("type of widget is : " + str(type(child))) 45 if str(type(child)) == "<class 'tkinter.Radiobutton'>": 46 child.destroy() 47 elif str(type(child)) == "<class 'tkinter.Button'>": 48 child.destroy() 49 elif str(type(child)) == "<class 'tkinter.Label'>": 50 child.destroy() 51 52 # ラジオボタンのラベルをリスト化する 53 # rdo_txt = random.sample(b_options , k=4) 54 # ラジオボタンの状態 55 rdo_var = tkinter.IntVar() 56 57 # ラジオボタンを動的に作成して配置 58 for i in range(4): 59 rdo = tkinter.Radiobutton(tki, value=i, variable=rdo_var, text=b_options[i]) 60 rdo.place(x=50, y=80 + (i * 24)) 61 62 # ボタン作成 63 btn = tkinter.Button(tki, text='答え合わせ', command=btn_click) 64 #btn.place(x=100, y=270) 65 btn.place(x=100, y=200) 66 67 # img = Image.open("aburana_f.gif") 68 # デモ - 確認用に、画像を交互に表示 69 toggle = not toggle 70 fname = "aburana_f.gif" if toggle else "kikyo_f.gif" 71 img = Image.open(fname) 72 resize_img = img.resize((200, int(200 * img.size[1] / img.size[0]))) 73 icon = ImageTk.PhotoImage(resize_img) 74 label = tkinter.Label(tki, compound="top") 75 label.place(x=10, y=240) 76 77 # 動的に画像とテキストをセット 78 label.configure(image=icon, text="アイコンラベル: " + fname) 79 label.image = icon 80 81# 以前画像を使わない4択問題で使っていたコード 82# var = tkinter.StringVar() 83# var.set(p_name + "と同じ科の植物はどれでしょう") 84# label = tkinter.Label(tki, image=icon,textvariable=var, compound="top",width=100, ) 85# label.pack() 86 87# ボタンクリックイベント 88def btn_click(): 89 num = rdo_var.get() 90 91 if b_options[num] == str(answer): 92 tkinter.messagebox.showinfo('結果' , "正解!") 93 else: 94 tkinter.messagebox.showinfo("結果" , '不正解!正解は' + answer) 95 96 # 表示内容の再初期化 97 init_view(root) 98 99with open('plant.csv', newline='') as csvfile: 100 reader = csv.DictReader(csvfile) 101 rows = [] 102 for row in reader: 103 rows.append(row) 104 105root = Tk() 106root.geometry('300x400') 107root.title('ラジオボタン') 108init_view(root) 109 110root.mainloop() 111 112

Python 3.8 32ビット版 + Pillow 7.0.0で、Windows 10(64ビット)上にて確認しました。

投稿2020/01/28 07:41

編集2020/01/28 19:15
dodox86

総合スコア9183

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

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

dodox86

2020/01/28 07:51 編集

画像ファイルを安易に何回もopenしたりしてよく問題になるのがメモリリークやリソースリークの類ですが、動作させつつWindowsのタスクマネージャーで軽く確認したかぎりでは如実にリークしているような様子はありませんでした。(深くは追っていません) 一応、そんな心配もあるかもしれないと気に留めておくと良いです。使う画像ファイルが決まっているのであれば、生成したイメージを使いまわす方が良いでしょう。
退会済みユーザー

退会済みユーザー

2020/01/28 14:56

ありがとうございます!ラベルにはPhotoImageでないと画像を使えないのですね。 また動的処理への対処まで恐れ入ります・・・しかし、configureとtoggleは完全に初耳のため、初見で全体像が理解できませんでした。周辺情報を勉強しつつ、理解に努めます。解決済とさせていただくのに、少しお時間をいただければと思います。
dodox86

2020/01/28 17:28

ランダムに画像をLabelに表示させるとき、configure メソッドは必要だと思いますが、toggle変数は単に交互に画像を切り替え表示させるためのフラグ変数として使っているだけなので、あまり気にしないでください。 toggle = not toggle fname = "aburana_f.gif" if toggle else "kikyo_f.gif" のコード部分が分かりづらいということでしたら、 if toggle == True: toggle = False fname = "aburana_f.gif" else: toggle = True fname = "kikyo_f.gif" のような意味です。
dodox86

2020/01/28 19:17

見直してみると、Labelを生成する前段に「子ウィジェットを削除(destroy)する」ので、「ラベル未生成の場合のみ、生成する」のは判定自体が無駄で、少し不適切でした。RadioButtonやButtonの扱いと同じようになるよう、コードを修正させていただきました。
退会済みユーザー

退会済みユーザー

2020/01/29 14:49

さらなるコメント、また全体まで修正いただき恐れ入ります。全体像も大枠理解できたと思います。修正いただいたコードをもとに、画像をランダムに表示させる(正確にはランダムに選択したディクショナリから画像を表示する)こともできました。さらにラベルとウィジェットについて勉強し、表示を整えていきたいと思います。まことにありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問