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

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

ただいまの
回答率

87.37%

Tkinterを何度も使おうとするとエラーが出る

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 5,032

score 25

前提・実現したいこと

Pythonでいいえが押されるまで質問するシステムを作っています。
Tkinterを使い何度も分岐をする機能を実装中に以下のエラーメッセージが発生しました。

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

エラーメッセージ
return self.tk.call('wm', 'geometry', self._w, newGeometry)
_tkinter.TclError: can't invoke "wm" command: application has been destroyed

該当のソースコード

import tkinter
from tkinter import messagebox


# ウインドウ

root = tkinter.Tk()
root.withdraw()
messagebox.showinfo('', '開始!!!!')



def ajust_moving():
    # データ数入力
    root.deiconify()
    root.title(u"調整")
    root.geometry("250x120")
    Static1 = tkinter.Label(text=u'+か-を入れてください!')
    Static1.pack()
    EditBox1 = tkinter.Entry()
    EditBox1.insert(tkinter.END, '+')
    EditBox1.pack()

    Static2 = tkinter.Label(text=u'100単位で数字を入れてください')
    Static2.pack()
    EditBox2 = tkinter.Entry()
    EditBox2.insert(tkinter.END, '100')
    EditBox2.pack()

    aaa = tkinter.Button(root, text='確定', width='10', command=root.destroy)
    aaa.pack()
    value = EditBox1.get()
    nuaa = EditBox2.get()

    root.mainloop()

    anka = 'm:1' + value + 'p' + nuaa + '\r\n'
    # 文字列の結合
    # 相対移動文字列を作る
    # 関数内関数作ってもいいんじゃね?

    root.geometry("0x0")
    # ウィンドウのタイトルバーを消す(Windows用の設定)
    root.overrideredirect(1)


while True:

    OneMore_adjust = messagebox.askyesno('Adjusts_Onemore', 'もう一度調整しますか?')
    if OneMore_adjust:
        ajust_moving()

    else:
        break

root = tkinter.Tk()
root.withdraw()
messagebox.showinfo('', '完了!!')







#追記のソースコードここから




import tkinter
from tkinter import messagebox

# ウインドウ
root = tkinter.Tk()


# root.withdraw()


def on_start():
    messagebox.showinfo('', '開始!!!!')
    # 次にmainloop内から main_proc() 関数を呼ぶ
    root.after(0, main_proc)
    # 0秒後にmain_procという関数を呼ぶという意味


def main_proc():
    # ここで設定Windowを Toplevel Widget にて作成
    global top
    top = tkinter.Toplevel(root)
    top.deiconify()
    top.title(u"調整")
    top.geometry("250x120")
    # 以下に 設定画面を作成(省略)
    Static1 = tkinter.Label(top, text=u'+か-を入れてください!')
    Static1.pack()
    EditBox1 = tkinter.Entry(top)
    EditBox1.insert(tkinter.END, '+')
    EditBox1.pack()

    Static2 = tkinter.Label(top, text=u'100単位で数字を入れてください')
    Static2.pack()
    EditBox2 = tkinter.Entry(top)
    EditBox2.insert(tkinter.END, '100')
    EditBox2.pack()

    button = tkinter.Button(top, text='確定', width='10',command=on_closing())
    button.pack()

    sign = EditBox1.get()
    value = EditBox2.get()

    root.mainloop()

    anka = 'm:1' + sign + 'p' + value + '\r\n'

    # 文字列の結合
    # 相対移動文字列を作る
    # 関数内関数作ってもいいんじゃね?

    # ser.write(bytes(anka, 'UTF-8'))
    # ser.write(b'g:\r\n')

    print(anka)

    # Toplevel Widgetが閉じられるときは on_close()関数を呼ぶ



def on_closing():
    top.destroy()
    # 確認用ダイアログを出す
    onemore = messagebox.askyesno('Adjusts_Onemore', 'もう一度調整しますか?')
    if onemore:
        # 再度 mainloop内から main_proc() 関数を呼ぶ
        root.after(0, main_proc)
    else:
        # 完了ダイアログを出す
        messagebox.showinfo('', '完了!!')
        # rootを破棄(これでmainloopを抜けるはず)
        root.destroy()


# 最初にmainloop内から on_start() 関数を呼ぶ
root.after(0, on_start)
root.mainloop()

試したこと

while文にする前は自分が想像していた動作が実現できました。
あと、Tkinterは何度も実行する仕様ではないということも調べた結果わかりました。

教えていただきたいこと

最初と最後は、withdraw()で隠したいです。
関数を実行するときは、表示したいとおもっているため 、
deiconify()を使っています。

tkinterでタブを削除した後
While文内のif文を何度も実行してうまく挙動させるに葉どうしたらいいのか?
もしくは、while文を使わないでif文の挙動を何回もできるようにしたいです。
もしくはTkinterを使わないでも実装できるやり方を知りたいです。

解決コード

サブウィンドウを作成し、それを何度も破壊することでメインウィンドウを削除しないでもよくなった。
※回答欄から転載

import tkinter
from tkinter import messagebox

# ウインドウ
root = tkinter.Tk()
root.withdraw()

def on_start():
    messagebox.showinfo('', '開始!!!!')
    # 次にmainloop内から main_proc() 関数を呼ぶ
    root.after(0, main_proc)

def main_proc():
    # ここで設定Windowを Toplevel Widget にて作成
    global top
    top = tkinter.Toplevel(root)
    top.deiconify()
    top.title(u"調整")
    top.geometry("250x120")
    # 以下に 設定画面を作成(省略)

    # Toplevel Widgetが閉じられるときは on_close()関数を呼ぶ
    top.protocol("WM_DELETE_WINDOW", on_closing)

def on_closing():
    # Toplevel Widgetを破棄
    top.destroy()
    # 確認用ダイアログを出す
    onemore = messagebox.askyesno('Adjusts_Onemore', 'もう一度調整しますか?')
    if onemore:
        # 再度 mainloop内から main_proc() 関数を呼ぶ
        root.after(0, main_proc)
    else:
        # 完了ダイアログを出す
        messagebox.showinfo('', '完了!!')
        # rootを破棄(これでmainloopを抜けるはず)
        root.destroy()

# 最初にmainloop内から on_start() 関数を呼ぶ
root.after(0, on_start)
root.mainloop()

注意点

commandで関数を指定するときは、
command=on_closing()
ではなく、
command=on_closing
と記述する。
commandパラメータに渡すのは、「関数」を渡す。
括弧つきで記述すると、その場で関数が実行され、その戻り値がcommandパラメータに渡すという意味になってしまうので、注意が必要。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • t_obara

    2019/10/29 17:56

    どのような動きを期待しているのでしょうか。
    基本的なこととして、UIプログラムにおいて、処理の制御をwhileで行うべきではありません。ウィンドウの描画処理などのループがroot.mainloop で行う必要があり、whileが入ることでそれらを阻害するからです。

    キャンセル

  • YUMA-NAGAO

    2019/10/30 08:32

    何度も、while文内のif文を繰り返す動きを実装したいです。

    色々試行錯誤した結果、while文はいらなくなりました。
    他の処理で何度もwhile文内のif文を実行するからです。

    キャンセル

回答 1

checkベストアンサー

+1

ジャストアイデアです。

root = tkinter.Tk() を何度も生成・破棄を繰り返すのではなく、root は一度だけ生成しておいて(当然mainloopも一度だけ呼ぶ)、別ウィンドウ tkinter.Toplevel(root)の生成・破棄を繰り返すというのはどうでしょうか。

以下は簡単なテストコードです。

import tkinter
from tkinter import messagebox

# ウインドウ
root = tkinter.Tk()
root.withdraw()

def on_start():
    messagebox.showinfo('', '開始!!!!')
    # 次にmainloop内から main_proc() 関数を呼ぶ
    root.after(0, main_proc)

def main_proc():
    # ここで設定Windowを Toplevel Widget にて作成
    global top
    top = tkinter.Toplevel(root)
    top.deiconify()
    top.title(u"調整")
    top.geometry("250x120")
    # 以下に 設定画面を作成(省略)

    # Toplevel Widgetが閉じられるときは on_close()関数を呼ぶ
    top.protocol("WM_DELETE_WINDOW", on_closing)

def on_closing():
    # Toplevel Widgetを破棄
    top.destroy()
    # 確認用ダイアログを出す
    onemore = messagebox.askyesno('Adjusts_Onemore', 'もう一度調整しますか?')
    if onemore:
        # 再度 mainloop内から main_proc() 関数を呼ぶ
        root.after(0, main_proc)
    else:
        # 完了ダイアログを出す
        messagebox.showinfo('', '完了!!')
        # rootを破棄(これでmainloopを抜けるはず)
        root.destroy()

# 最初にmainloop内から on_start() 関数を呼ぶ
root.after(0, on_start)
root.mainloop()

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/30 11:41

    返答ありがとうございます。
    やってみたところ、新たな疑問が生まれました。
    aaaのボタンのcommandにon_closing()を入れてみたのですが、確定ボタンが表示されないで消えてしまいます。
    どうすれば、解決できるでしょうか?

    やってみたコードを追記しておきます。

    キャンセル

  • 2019/10/30 11:50

    もしかして
    command=on_closing()
    と記述してませんか? commandパラメータに渡すのは『関数』ですので
    command=on_closing
    のように記述します。(括弧をつけない)
    最初ように括弧付きで記述すると、その場で on_closing() 関数が実行され、その戻り値がcommandパラメータに渡すという意味になるので注意が必要です。

    キャンセル

  • 2019/10/30 13:15

    なるほど!!!
    ()括弧をつけると、その場で実行されるんですね!!!

    commandは関数の名前を与えるのは初めて知りました!
    ありがとうございました!!!

    こちらのコードを使うことで、該当部分を実装することが出来ました!

    ありがとうございました!!

    キャンセル

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

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る