Sillver7sea
@Sillver7sea

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

占いを表示するプログラム

 初めてここで質問させて頂きます。調べ回っても分かりませんでした。どうか、求めている結果をどうしたら得られるのかを教えてください。よろしくお願いいたします。
 Python3とTkinterを使用しています。VScodeで動かしています。何か不備があれば、できるだけ訂正や追記等を行いますので、お教えください。よろしくお願いいたします。

やりたいこと
 占い結果に対応するタロットカードを表示し、占い結果削除と共に画像を削除し、そのまま新しい占い結果を表示できるようにしたいです。

困っていること
 Aのコードでは、画像が表示されるが画像の削除が行えません。占い結果の削除は行えています。ただし、そのまま占い結果を削除しても新しい結果は表示されません。
 Bのコードでは、画像が表示されないが占い結果の削除と新しい結果の表示が出来ます。何故、画像が表示されないのかが分かりません。また、普通にターミナルで起動すると前述の通り画像が表示されませんが、何故かデバックモードで動かすと画像が表示されます。その時、占い結果をクリアする際にカードは消えませんが、再度OKボタンを押すと対応する占い結果と共にきちんと表示されます。

回答で求めていること
 Aのコードでのアプローチが正しいのか、それともBのコードでのアプローチが正しいのかを知りたいです。どちらかのアプローチが正しい場合は、そこからどうしたら求めている結果へと到れるのかの解説を知りたいです。
 もし、どちらのアプローチも正しくないのであれば、何か参考になりそうなサイトなどを教えて頂きたいです。
 Bのコードでは何故画像が表示されないのかの解説が知りたいです。また、何故デバックモードではきちんと動いたのか解説をお願いいたします。

A、B共通部分


#諸々インポート
import random
import tkinter as tk
from tkinter import messagebox
from t import torots
from PIL import Image,ImageTk
from cardlist import torotsimages

#ウィンドウ設定
main_win = tk.Tk()
main_win.title('イリアちゃん起動中')
main_win.geometry('1050x650')

#ウィンドウにメッセージ表示
label = tk.Label(main_win,text = '初めまして。私は占いをするチャットボット、イリアです。貴方の名前を教えてください。\n繰り返し、占いを行う場合は占いクリアボタンを押してから再度OKボタンを押してください。')
label.grid(row = 0,column = 0,sticky='w', padx = 100)

Aのコード


#グローバル関数化したselect
global select
select = random.randint(0,43)

#各フィールドから値を取得
def on_register():
    name = entry_name.get()
    genre = genre_var.get()
#全てのフィールドが入力されているか確認
    if not (name and genre):
        messagebox.showwarning('警告','選択されていない項目があります')
    else:
        pass

#取得したものをもとに、占いを表示
    label_kekka = tk.Label(main_win,text ='\r'+ name + torots[genre][select])
    label_kekka.grid(row = 7,column = 0,sticky='w',padx = 150)

#OK無効化
    clear_btn = tk.Button(main_win,text = '占いクリア',command = lambda:delete_result(label_kekka))
    clear_btn.grid(row = 6,column = 0,sticky='w', padx = 400)

#画像の読み込みとリサイズ
img = Image.open(torotsimages[select])
img = img.resize((300,450))
tk_img = ImageTk.PhotoImage(img)
#ラベルに画像を設定して配置
label_card = tk.Label(main_win,image = tk_img)
label_card.grid(row = 8,column = 0,sticky='w', padx = 600)

#占い結果をクリア
def delete_result(target):
    target.destroy()
#OK有効化
    button_register["state"] = "active"

Bのコード


#各フィールドから値を取得
def on_register():
    name = entry_name.get()
    genre = genre_var.get()
    select = random.randint(0,43)
#全てのフィールドが入力されているか確認
    if not (name and genre):
        messagebox.showwarning('警告','選択されていない項目があります')
    else:
        pass

#画像の読み込みとリサイズ
    img = Image.open(torotsimages[select])
    img = img.resize((300,450))
    tk_img = ImageTk.PhotoImage(img)
#ラベルに画像を設定して配置
    label_card = tk.Label(main_win,image = tk_img)
    label_card.grid(row = 8,column = 0,sticky='w', padx = 600)

#取得したものをもとに、占いを表示
    label_kekka = tk.Label(main_win,text ='\r'+ name + torots[genre][select])
    label_kekka.grid(row = 7,column = 0,sticky='w',padx = 150)

#OK無効化
    clear_btn = tk.Button(main_win,text = '占いクリア',command = lambda:delete_result(label_kekka))
    clear_btn.grid(row = 6,column = 0,sticky='w', padx = 400)

#占い結果をクリア
def delete_result(target):
    target.destroy()
#OK有効化
    button_register["state"] = "active"

A、B共通部分


#各フィールドを作る
#名前
label_name = tk.Label(main_win,text = '名前')
label_name.grid(row = 2,column = 0,sticky='w', padx = 200)
entry_name = tk.Entry(main_win)
entry_name.grid(row = 2,column = 0,sticky='w', padx = 250)

#占いジャンル
label_genre = tk.Label(main_win,text = '占いジャンル')
label_genre.grid(row = 3,column = 0,sticky='w', padx = 200)

#占い種別のラジオボタンを作成
genre_var = tk.StringVar()
genre_var.set(None)
radio_renai = tk.Radiobutton(main_win,text = '恋愛',variable = genre_var,value = '恋愛')
radio_renai.grid(row = 3,column = 0,sticky='w', padx = 290)
radio_kenkou = tk.Radiobutton(main_win,text = '健康',variable = genre_var,value = '健康')
radio_kenkou.grid(row = 3,column = 0,sticky='w', padx = 380)
radio_sigoto = tk.Radiobutton(main_win,text = '仕事',variable = genre_var,value = '仕事')
radio_sigoto.grid(row = 3,column = 0,sticky='w', padx = 460)

#OKボタンを作る
button_register = tk.Button(main_win,text = 'OK',command = on_register)
button_register.grid(row = 6,column = 0,sticky='w', padx = 300)

#諸々表示
main_win.mainloop()
2

1Answer

GUI部品は最初にすべて並べておいて、イベントに応じて内容を変更するのが楽ですよ。

# 諸々インポート
import random
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
from t import torots
from cardlist import torotsimages


def on_register():
    # 各フィールドから値を取得
    name = entry_name.get()
    genre = genre_var.get()
    # 全てのフィールドが入力されているか確認
    if not (name and genre):
        messagebox.showwarning('警告', '選択されていない項目があります')
        return

    # 乱数で占う
    select = random.randint(0, 43)
    label_kekka['text'] = f'\r{name}{torots[genre][select]}'

    # 画像の読み込みとリサイズ
    img = Image.open(torotsimages[select])
    img = img.resize((300, 450))
    tk_img = ImageTk.PhotoImage(img)
    label_card['image'] = tk_img
    label_card.image = tk_img
    button_register['state'] = 'disabled'
    clear_btn['state'] = 'active'


def delete_result():
    # 占い結果をクリア
    label_kekka['text'] = ''
    label_card['image'] = None
    label_card.image = None
    button_register['state'] = 'active'
    clear_btn['state'] = 'disabled'


# ウィンドウ設定
main_win = tk.Tk()
main_win.title('イリアちゃん起動中')
main_win.geometry('1050x650')

# ウィンドウにメッセージ表示
label = tk.Label(main_win, text='初めまして。私は占いをするチャットボット、イリアです。貴方の名前を教えてください。\n繰り返し、占いを行う場合は占いクリアボタンを押してから再度OKボタンを押してください。')
label.grid(row=0, column=0, sticky='w', padx=100)

# 画像ラベルを配置
label_card = tk.Label(main_win)
label_card.grid(row=8, column=0, sticky='w', padx=600)

# 名前
label_name = tk.Label(main_win, text='名前')
label_name.grid(row=2, column=0, sticky='w', padx=200)
entry_name = tk.Entry(main_win)
entry_name.grid(row=2, column=0, sticky='w', padx=250)

# 占いジャンル
label_genre = tk.Label(main_win, text='占いジャンル')
label_genre.grid(row=3, column=0, sticky='w', padx=200)

# 占い種別のラジオボタンを作成
genre_var = tk.StringVar()
genre_var.set(None)
radio_renai = tk.Radiobutton(main_win, text='恋愛', variable=genre_var, value='恋愛')
radio_renai.grid(row=3, column=0, sticky='w', padx=290)
radio_kenkou = tk.Radiobutton(main_win, text='健康', variable=genre_var, value='健康')
radio_kenkou.grid(row=3, column=0, sticky='w', padx=380)
radio_sigoto = tk.Radiobutton(main_win, text='仕事', variable=genre_var, value='仕事')
radio_sigoto.grid(row=3, column=0, sticky='w', padx=460)

# OKボタンを作る
button_register = tk.Button(main_win, text='OK', command=on_register)
button_register.grid(row=6, column=0, sticky='w', padx=300)

# 占い表示
label_kekka = tk.Label(main_win)
label_kekka.grid(row=7, column=0, sticky='w', padx=150)

# clearボタン
clear_btn = tk.Button(main_win, text='占いクリア', command=delete_result)
clear_btn['state'] = 'disabled'
clear_btn.grid(row=6, column=0, sticky='w', padx=400)

# 諸々表示
main_win.mainloop()
1Like

Comments

  1. @Sillver7sea

    Questioner

    ご回答ありがとうございます!まだまだ勉強不足なので、送って頂いたコードだと何故私がやりたかった事が出来て、自分が作ったものだと出来ないのかを見比べて調べて考えて理解したいと思います。本当にありがとうございます!!

  2. 元のコードでは、ボタンをクリックするたびにLabelとButtonを生成していて、どんどん増えて重なって表示されます。
    クリア処理でLabelをdestroyしていますが、Buttonもdestroyする必要があります。
    なので、GUI部品の生成は最初に終わらせておいて、ボタンクリックでは内容変更するのが簡単です。

    それと、tk_img = ImageTk.PhotoImage(img) で画像データをtk_img変数に代入していますが、イベント処理が終わるとtk_img変数がどこからも参照されなくなり画像データが破棄されてしまいます。
    私のコードでは label_card.image = tk_img でオブジェクトに代入して画像データが破棄されないようにしています。代わりに global tk_img でグローバル変数にしても破棄されなくなります。

  3. @Sillver7sea

    Questioner

    再びのコメントを頂きまして、誠にありがとうございます!
    申し訳ありません、根本の部分がまだ理解出来て居ないので、コメント頂きまして、更に疑問に思ったことを書かせて頂きます。

    ①Labelは前にクリックする度に作られていると言われたことがあったのですが、Buttonもなんですか?それは、なぜなのでしょう?私はてっきり、ボタンの生成は1度のみだと思っていました。そして、ボタンをdestroyすしてもテキストのように何も無い状態にならずに、ボタンの表示があるのは何故ですか?
    ②表示順が変わるとありましたが、何度かやってみたときに、新規で出てきたものもきちんと対応する結果と画像が出ていました。これは何故でしょうか?

    質問ばかりで、本当に申し訳ありません。

  4. 前のコメントを修正したので確認お願いします。

    ①Labelは前にクリックする度に作られていると言われたことがあったのですが、Buttonもなんですか?それは、なぜなのでしょう?

    #各フィールドから値を取得
    def on_register():
        name = entry_name.get()
        genre = genre_var.get()
    #全てのフィールドが入力されているか確認
        if not (name and genre):
            messagebox.showwarning('警告','選択されていない項目があります')
        else:
            pass
    
    #取得したものをもとに、占いを表示
        label_kekka = tk.Label(main_win,text ='\r'+ name + torots[genre][select])
        label_kekka.grid(row = 7,column = 0,sticky='w',padx = 150)
    
    #OK無効化
        clear_btn = tk.Button(main_win,text = '占いクリア',command = lambda:delete_result(label_kekka))
        clear_btn.grid(row = 6,column = 0,sticky='w', padx = 400)
    

    で、クリックされたときのon_register関数の中で tk.Label(...) でラベルを作り、tk.Button(...) でボタンも作っています。

    ボタンをdestroyすしてもテキストのように何も無い状態にならずに、ボタンの表示があるのは何故ですか?

    以下の簡略化したコードではボタンも消えましたよ。

    import tkinter as tk
    
    
    def on_register():
        global label_kekka
        label_kekka = tk.Label(main_win)
        label_kekka.grid(row=7, column=0, sticky='w', padx=150)
        label_kekka['text'] = 'message'
        global clear_btn
        clear_btn = tk.Button(main_win, text='占いクリア', command=delete_result)
        clear_btn.grid(row=6, column=0, sticky='w', padx=400)
    
    
    def delete_result():
        label_kekka.destroy()
        clear_btn.destroy()
    
    
    main_win = tk.Tk()
    main_win.title('イリアちゃん起動中')
    main_win.geometry('1050x650')
    
    button_register = tk.Button(main_win, text='OK', command=on_register)
    button_register.grid(row=6, column=0, sticky='w', padx=300)
    
    main_win.mainloop()
    

    ②表示順が変わるとありましたが、何度かやってみたときに、新規で出てきたものもきちんと対応する結果と画像が出ていました。これは何故でしょうか?

    基本的には最後に生成したGUI部品が一番上に表示されます。
    デバッガで止めるとtkinterの表示更新も止まるので、思った画像が表示されないこともあるかと思います。

  5. @Sillver7sea

    Questioner

    返信が遅くなってしまい、申し訳ありませんでした。理解するのに時間がかかってしまいまして。丁寧に教えて下さりありがとうございます!

Your answer might help someone💌