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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

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

Tkinter

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

Q&A

解決済

2回答

3869閲覧

Tkinterで別スレッドの終了方法

GOYOSHI

総合スコア18

Python 3.x

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

Tkinter

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

0グッド

0クリップ

投稿2019/07/19 01:50

いつもお世話になります。
PLCとラズパイをつないでデータを書き込み・取得するプログラムの準備中です。
今回はラズパイに画面を設け、バーコードリーダーで品番を読み込み該当するパラメーターをDBより取得します。
そのパラメーターをPLCに書き込みご後、ラズパイでPLCから動作結果のデータを取得します。
準備として『Tkinter with Serial』(https://robotic-controls.com/learn/python-guis/tkinter-serial)などを参考にさせていただき
GUI画面に時計を表示するものを作ってみました。この時計表示の部分をPLCからデータを取得する動作にするつもりです。
とりあえず時計表示のソースは以下となります。

import tkinter as tk import time import datetime as dt import threading #make a TkInter Window root = tk.Tk() root.title("Reading Clock") # make a scrollbar scrollbar = tk.Scrollbar(root) scrollbar.pack(side=tk.RIGHT, fill="y") # make a text box to put the time log = tk.Text(root, width=30, height=30, takefocus=0) log.pack() # attach text box to scrollbar log.config(yscrollcommand=scrollbar.set) scrollbar.config(command=log.yview) def subthread(): thread_2 = threading.Thread(target=readTime) thread_2.setDaemon(True) thread_2.start() def readTime(): while True: # 現在の時刻を取得 dNow = dt.datetime.today() # 書き込み用日付の文字列を作成する nowStr = dNow.strftime("%H:%M:%S") log.insert('insert', nowStr+'\n') time.sleep(1) def on_closing(): root.destroy() # GUIクローズ処理 root.protocol("WM_DELETE_WINDOW", on_closing) # 画面表示後の処理 root.after(100, subthread) root.mainloop()

『Tikinter with serial』ではafter()を繰り返すことで実現しているようでしたが時計表示で試したところ時計が表示されず、希望した動作にはならなかったので別スレッドを立てる方法にしてみたところ時計が画面上で表示・更新されましたので希望する動作となりました。
また、『insert』はカーソル位置に挿入するということでしたので表示のボックス内でクリックするとその位置から追加されていくのでTikinter側もちゃんと反応していると思います。

そこで問題なのは止めるときの処理なんですがスレッドを開始する際に『thread_2.setDaemon(True)』としてメイン(この場合Tkinter側)が止まった場合には強制的に止めますとし、『root.protocol("WM_DELETE_WINDOW", on_closing)』で呼び出しています。
これでWindowの『X』で閉じることは閉じるのですが以下のようなエラーが表示されます。

>>> Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner self.run() File "/usr/lib/python3.5/threading.py", line 862, in run self._target(*self._args, **self._kwargs) File "/home/pi/PyPrg/tkseri.py", line 33, in readTime log.insert('insert', nowStr+'\n') File "/usr/lib/python3.5/tkinter/__init__.py", line 3130, in insert self.tk.call((self._w, 'insert', index, chars) + args) _tkinter.TclError: invalid command name ".1966440848"

詳しないのですが時計表示部の『While True』を止めていない状態で終了してしまうためだと思います。
いろいろ考えてみましたが良い解決方法が見つかりません。単純にエラーを出さなくするでも良いとは思いますがよろしくお願いいたします。
また、『readTime』でエラーが発生した場合の対処方法も頂けましたら助かります。
よろしくお願いいたします。

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

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

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

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

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

t_obara

2019/07/19 02:48

> 『While True』を止めていない状態で終了してしまうため を解決する手段が思いつかないので教えてくれという質問でしょうか? Trueの代わりにフラグを用意し、終了時にそのフラグを変化させ、joinでスレッド終了待ちをするとか
guest

回答2

0

ベストアンサー

スレッド終了させる変数を用意して、スレッド終了を待ち合わせる手もあります。

python

1import tkinter as tk 2import time 3import datetime as dt 4import threading 5 6#make a TkInter Window 7root = tk.Tk() 8root.title("Reading Clock") 9 10# make a scrollbar 11scrollbar = tk.Scrollbar(root) 12scrollbar.pack(side=tk.RIGHT, fill="y") 13 14# make a text box to put the time 15log = tk.Text(root, width=30, height=30, takefocus=0) 16log.pack() 17 18# attach text box to scrollbar 19log.config(yscrollcommand=scrollbar.set) 20scrollbar.config(command=log.yview) 21 22# サブスレッド動作フラグ 23running = True 24 25def readTime(): 26 while running: 27 # 現在の時刻を取得 28 dNow = dt.datetime.today() 29 # 書き込み用日付の文字列を作成する 30 nowStr = dNow.strftime("%H:%M:%S") 31 log.insert('insert', nowStr+'\n') 32 time.sleep(1) 33 34def on_closing(): 35 root.destroy() 36 37# GUIクローズ処理 38root.protocol("WM_DELETE_WINDOW", on_closing) 39# 画面表示後の処理 40thread_2 = threading.Thread(target=readTime) 41thread_2.setDaemon(True) 42root.after(100, thread_2.start) 43 44root.mainloop() 45 46# サブスレッド終了処理 47running = False 48thread_2.join()

投稿2020/02/12 19:47

shiracamus

総合スコア5406

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

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

GOYOSHI

2020/03/09 04:28

ありがとうございました。
guest

0

以下のように

  • スレッドセーフなqueueでスレッド間通信する
  • tkinter(GUI)へのアクセスはメインスレッドに任せる

とよいかと思います。

Python

1import tkinter as tk 2import time 3import datetime as dt 4import threading 5import queue 6 7class Application(tk.Frame): 8 def __init__(self, master=None): 9 super().__init__(master) 10 root.title("Reading Clock") 11 self.pack() 12 13 self.log = tk.Text(root, width=30, height=30, takefocus=0) 14 self.log.pack() 15 16 root.protocol("WM_DELETE_WINDOW", self.on_closing) 17 18 self.q_m = queue.Queue() 19 self.q_s = queue.Queue() 20 root.after(100, self.main_loop) 21 self.subthread() 22 23 def main_loop(self): 24 print('main_loop') 25 try: 26 q = self.q_m.get(False) # 反応を良くするため非同期で取得 27 print(q) 28 if q == 'stop': 29 root.destroy() 30 return 31 self.log.insert('insert', q+'\n') 32 except queue.Empty: 33 pass 34 35 root.after(100, self.main_loop) 36 37 def on_closing(self): 38 self.q_s.put('stop') # 終了指示 39 self.thread_2.join() # 終了待ち 40 41 def subthread(self): 42 self.thread_2 = threading.Thread(target=self.readTime) 43 self.thread_2.setDaemon(True) 44 self.thread_2.start() 45 46 def readTime(self): 47 for i in range(10): 48 try: 49 q = self.q_s.get(False) # 非同期で取得 50 if q == 'stop': 51 break 52 except queue.Empty: 53 pass 54 55 self.q_m.put(dt.datetime.today().strftime("%H:%M:%S")) 56 time.sleep(1) 57 58 self.q_m.put('stop') # メインを終了させる 59 60root = tk.Tk() 61app = Application(master=root) 62app.mainloop()

投稿2019/07/21 04:49

can110

総合スコア38256

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問