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

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

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

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

Tkinter

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

Python

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

Q&A

解決済

3回答

19276閲覧

プログラムを実行した際の応答なしを無くしたい

calsonic

総合スコア13

Python 3.x

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

Tkinter

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

Python

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

0グッド

1クリップ

投稿2019/01/14 10:45

前提・実現したいこと

Modbusで通信を行いPLCより連番コード、判定結果1、判定結果2、・・・総判定結果数を受け取り
受け取ったデータを別のPCから送られてきた画像ファイルと紐付け、画像ファイルの名前を連番コード+判定結果.bmpとしています

プログラム経験0から1週間ほどでいろんなところを継接ぎで追加していっているので通常ではありえないような書き方を
していると思いますが何卒お願いいたします

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

プログラムを実行後にGUIの画面を動かしたりすると応答なしの表示が出て固まってしまいます
実際のデータの受け取りとリストの表示などは少し遅いのですが行われています

エラーメッセージ

該当のソースコード

Python

1import serial 2import tkinter as tk 3import tkinter.ttk as ttk 4import glob 5import os 6import re 7import time 8 9 10 11#変数とか 12port = ["COM0", "COM1", "COM2", "COM3", "COM4", "COM5"] 13kai = ["", "OK", "NG"] 14comp = ["未完了", "完了"] 15complete = b"\x00" 16 17root = tk.Tk() 18root.title('試作') 19root.geometry("800x600") 20 21tree = ttk.Treeview(root) 22tree.pack(fill="x", padx=20, pady=20) 23 24 25def start(): 26 #完了信号待ち 27 while True: 28 global complete 29 if complete == b"\x00": 30 btn_click() 31 elif complete == b"\x01": 32 print("作業完了信号受信") 33 file_operation() 34 break 35 36def btn_click(): 37 #シリアル通信部分 38 global complete 39 global BC 40 global k1, k2, k3, k4, k5, k6, k7 41 client = serial.Serial(port[5], 9600, timeout=0.5, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE) 42 size = 128 43 print(client.name) 44 #S:1,F:4,Addr:3000,Data:100 45 commando = b"\x01\x04\x30\x00\x00\x64\xfe\xe1" 46 client.write(commando) 47 result1 = client.readline() 48 print(result1.hex()) 49 complete = result1[4:5] 50 BC = result1[5:7] 51 k1 = result1[7:9] 52 k2 = result1[9:11] 53 k3 = result1[11:13] 54 k4 = result1[13:15] 55 k5 = result1[15:17] 56 k6 = result1[17:19] 57 k7 = result1[19:21] 58 #k101あたりに送られてくるデータ数を入れる 59 client.close() 60 61def file_operation(): 62 global filename 63 global BC 64 global kai 65 global complete 66 path = 'c:/test/' 67 glob.glob(os.path.join(path, '*.txt')) 68 os.chdir(path) 69 filename = glob.glob('*.txt') 70 #ここらへんでif文入れて送られてきたデータ数に合わせて出力する 71 print(filename[0]) 72 print(filename[1]) 73 print(filename[2]) 74 75 time_str = time.strftime('%Y%m%d') 76 os.rename(filename[0], BC.hex() + "-1-" + kai[int.from_bytes(k1, 'big')] + time_str + '.txt') 77 os.rename(filename[1], BC.hex() + "-2-" + kai[int.from_bytes(k2, 'big')] + time_str + '.txt') 78 os.rename(filename[2], BC.hex() + "-3-" + kai[int.from_bytes(k3, 'big')] + time_str + '.txt') 79 tree.insert("", "end", values=(BC.hex(), kai[int.from_bytes(k1, 'big')], filename[0], BC.hex() + "-1-" + kai[int.from_bytes(k1, 'big')] + time_str + '.txt')) 80 tree.insert("", "end", values=(BC.hex(), kai[int.from_bytes(k2, 'big')], filename[1], BC.hex() + "-2-" + kai[int.from_bytes(k2, 'big')] + time_str + '.txt')) 81 tree.insert("", "end", values=(BC.hex(), kai[int.from_bytes(k3, 'big')], filename[2], BC.hex() + "-3-" + kai[int.from_bytes(k3, 'big')] + time_str + '.txt')) 82 83 client = serial.Serial(port[5], 9600, timeout=0.5, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE) 84 size = 128 85 #作業完了信号をOFFにする 86 commando1 = b"\x01\x06\x30\x00\x00\x00\x86\xca" 87 client.write(commando1) 88 client.close() 89 complete = b"\x00" 90 91 92btn = tk.Button(root,text='取得スタート',width=10,bg="gray", command=start) 93btn.pack(fill='x', padx=30) 94 95 96tree["columns"] = (1, 2, 3, 4) 97tree["show"] = "headings" 98tree.column(1, anchor='center', width=75) 99tree.column(2, anchor='center', width=75) 100tree.column(3, anchor='center', width=120) 101tree.column(4, anchor='center', width=120) 102 103tree.heading(1, text="連番") 104tree.heading(2, text="判定") 105tree.heading(3, text="変更前ファイル名") 106tree.heading(4, text="変更後ファイル名") 107 108tree.pack(side=tk.TOP, fill=tk.BOTH, expand=1) 109 110root.mainloop()

試したこと

シリアル通信のタイムアウトを2秒など長めにしても変化がありませんでした

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

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

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

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

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

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

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

guest

回答3

0

GUIアプリケーションに共通のルールなのですが、画面を制御しているスレッドで(大まかに言えば)数十ミリ秒以上時間がかかる処理をやってはいけません。それが長くなればなるほど画面の反応が悪くなり秒オーダーに達するともはや使い物にならないといっても過言ではないです。

利用者の操作に応じて時間のかかる処理(btn_click)をする際に、それを別のスレッドで実行するのが常套手段です。

https://docs.python.org/ja/3.7/library/threading.html

プログラム経験0からとのことでそもそもスレッドって何?という疑問を持たれるかも知れませんが、それはここで詳しい説明(※1)はできませんのでご自身で調べて把握につとめてみてください。並列処理は曖昧な知識で取り組むと大変難しいバグにつながるのでしっかり把握したうえで(※2)取り組むことをお勧めします。

※1: ごく簡単な説明
通常はただ一人のインタープリタさんがせっせと順番にプログラムを実行しているのを、もう一りのインタープリタさんが助けにきて二人で並行してプログラムの異なる部分を同時に実行しているという感じです。この実行を担っている人がスレッドというふうに捉えると当たらずとも遠からずかな・・・

※2: 例えば
スレッド間のデータのやりとりは今現在のように単に複数のグローバル変数ではまずいと思います。並行して実行していることからいいかげんなタイミングで参照・設定をしてしまうと矛盾した状況になりかねません。そこで「スレッド間での情報のやりとり」の配慮がひつようです。「python スレッド間 変数」みたいなキーワードで調べると参考になる記事がヒットすると思います。

投稿2019/01/14 11:09

KSwordOfHaste

総合スコア18392

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

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

calsonic

2019/01/15 10:01

回答ありがとうございます ご指摘の通り欲しいプログラム例だけをつまみ食いしている状態でしたのでスレッドに関しては存在すら知らない状態でした。 チャットアプリ等を例にとって考えてみると然り、と理解することができました。 ただスレッドでの変数についてはまだよく理解できていないので調べながらもっと詰めていこうと思います。 ありがとうございました
KSwordOfHaste

2019/01/15 12:49

経験0からこんなに早くプログラムが書けてしまうんですね。早い人は早いんだなぁと思いました。並列動作は「同じプログラムなのに複数回実行したときの結果が異なる」のが厄介な点です。単純なプログラムと違って、動かしながら問題を直していくというアプローチが使えません。「何が問題でどうしなければならないか」を理論的に把握するのが肝心といえましょう。そこを正しく把握できさえすれば正解にぐっと近づけると思います。
guest

0

KSwordOfHasteさんの回答に完全に同意なのですが、update_idletasksを呼び出してみるという手もあります。
この関数を呼び出すと、Tkの画面更新処理などが内部で呼ばれます。
具体的にはwhile True:ループ内の最後にroot.update_idletasks()を追加します。

でもまあ、動作未検証ですし、はなはだ場当たり的な対応方法ですね…

投稿2019/01/14 11:21

can110

総合スコア38234

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

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

KSwordOfHaste

2019/01/14 11:44

update_idletask...それは場合によってはかなり単純で簡単な解決法になりそうですね。 参考になりました!
can110

2019/01/14 11:53

今回のソースでは効果があるか不透明ですが、手法としてはいたってシンプルですね。
KSwordOfHaste

2019/01/14 12:49

pySerialのリファレンスをチラ見したレベルですが、timeoutを駆使すれば長い時間スレッドをブロックせずにread/writeできそうなので、update_idletaskを使う余地もあるように思いました。 実際やった経験がないのではっきりわかりませんが...
can110

2019/01/14 12:58

pySerialで読み取りのtimeout使ったことありますが、質問者様のコードでも0.5秒指定しているようです。 ただreadline()でもタイムアウト効いたかどうか? 今見直したら私はざっと以下のような読込コードを書いてました。 recv = ser.read(1) n = ser.inWaiting() if n: recv = recv + ser.read(n) もうよく覚えてませんが…
KSwordOfHaste

2019/01/14 13:09

自分のイメージはcan110さんのコードのように1byteずつのリードを10msぐらいのtimeoutでループしながら改行まで読みこむみたいなものでした... でも、やったことない自分があまり想像でコメントしないほうがよさそうですね。失礼しました。
calsonic

2019/01/15 10:09

回答ありがとうございます 上記のコードを試してみましたがstart()を実行すると同じく応答なしが出てしまいました。 英文が多くてあまり理解できていないないのですが、この[update_idletasks]は他の処理をとりあえず置いといて再描写を行う、という認識で良いのでしょうか? 理解不足ですみません
can110

2019/01/15 10:22

はい。その認識でよいです。
guest

0

ベストアンサー

とりあえず、こんな感じでやればstartが即座に呼び出し元に返ってくるようにはなるはずです。

python

1import threading 2 3# ... 4 5def start(): 6 th = threading.Thread(target=start_i) 7 th.start() 8 9def start_i(): 10 #完了信号待ち 11 while True: 12 global complete 13 if complete == b"\x00": 14 btn_click() 15 elif complete == b"\x01": 16 print("作業完了信号受信") 17 file_operation() 18 break 19 20# ...

でも同時に複数走ると困るなー・・・という希望があると思うので(上のだと処理中にボタン押すとそうなります)、そうならないような工夫をします。

python

1import threading 2 3# ... 4 5lock = threading.Lock() 6 7def start(): 8 if lock.acquire(timeout=0): 9 th = threading.Thread(target=start_i) 10 th.start() 11 else: 12 # すでに処理が走っていたときの処理を書いてください(適当に画面にメッセージを出すとか。面倒くさければ単にpassでもいいが) 13 14def start_i(): 15 #完了信号待ち 16 while True: 17 global complete 18 if complete == b"\x00": 19 btn_click() 20 elif complete == b"\x01": 21 print("作業完了信号受信") 22 file_operation() 23 break 24 lock.release() 25 26# ...

雑だけど、これくらいで最低限動くかな?

17.1. threading — スレッドベースの並列処理 — Python 3.6.5 ドキュメント

投稿2019/01/14 11:30

編集2019/01/14 11:33
hayataka2049

総合スコア30933

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

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

calsonic

2019/01/15 10:16

具体的な例まで示して頂きありがとうございます このコードを試してみたところ、プログラムを実行してからウィンドウを動かしても応答なしが出なくなりました。 ズバリ同時に走られると困るところだったので大変参考になりました。 一歩前進できたのでまた先に進んでいきたいと思います
hayataka2049

2019/01/15 10:27 編集

KSwordOfHasteさんが仰っているように、並列処理は曖昧な知識で取り組むと大変難しいバグにつながります。コピペでちゃんと動いたのは(書いた私にとって)嬉しいことですが、「仕様を変えたいけどどうしたら良い?」とか次に聞かれたとき答える保証はありません。 「python スレッド」みたいなキーワードで検索して出てくる記事を読んだりし、自分で理解するよう努めることが望ましいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問