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

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

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

無線通信

Python 3.x

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

Tkinter

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

Q&A

解決済

1回答

2411閲覧

nfcをTkinterで利用したい

person

総合スコア223

NFC

無線通信

Python 3.x

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

Tkinter

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

1グッド

1クリップ

投稿2020/02/28 00:36

編集2020/03/18 07:22

tkinterのテキストボックスにICのIDを入力したい。

ICリーダはRC-S380を使用。

ICは1回読めればいいです。もう1回かざしなおしても読み取らなくていいです。

自分なりに考えて下のようになりました。

上手く動作しないので、どこをどう直せばよいか教えてください。
(ICを読み込ませずにウィンドウを閉じようとすると、閉じるまでに時間がかかりそれまではフリーズ状態)

Python

1import nfc 2import tkinter as tk 3import binascii 4import threading 5 6def push_clear(): 7 e_id.delete(0, tk.END) 8 9def push_enter(): 10 print(e_id.get()) 11 e_id.delete(0, tk.END) 12 13def ic_read(): 14 global clf # on_close() でも扱うことがあるため 15 clf = nfc.ContactlessFrontend("usb") # 接続 16 try: 17 clf.connect( rdwr={"on-connect": connected} ) # 認識 18 finally: 19 clf.close() # 切断 20 21def connected(tag): 22 idm = binascii.hexlify(tag.idm) # <class 'byte'> 23 idm = idm.decode() # <class 'str'> 24 e_id.insert(tk.END, idm) 25 return True 26 27def on_closing(): 28 global clf 29 clf.close() # ICを読み込まずにウィンドウを閉じようとするときに、これがないとフリーズではなく応答なしになってしまう 30 thread_nfc.join() 31 win.destroy() 32 33if __name__ == "__main__": 34 thread_nfc = threading.Thread(target=ic_read) 35 thread_nfc.start() 36 37 win = tk.Tk() 38 39 e_id = tk.Entry(win) 40 e_id.grid() 41 42 b_clr = tk.Button(win, text="CLEAR", command=push_clear) 43 b_clr.grid() 44 45 b_ent = tk.Button(win, text="ENTER", command=push_enter) 46 b_ent.grid() 47 48 win.protocol("WM_DELETE_WINDOW", on_closing) 49 win.mainloop()

修正(ソースの間違い)

Python

1# main.py 2 3import tkinter as tk 4 5import input_ic 6 7 8def push_enter(): 9 print(e_id.get()) 10 e_id.delete(0, tk.END) 11 12 13def push_entry(e, e_id): 14 input_ic.input_ic(e, e_id) 15 16 17if __name__ == "__main__": 18 win = tk.Tk() 19 20 e_id = tk.Entry(win) 21 e_id.grid() 22 23 b_etr = tk.Button(win, text="ENTER", command=push_enter) 24 b_etr.grid() 25 26 e_id.bind("<Button-1>", lambda e:push_entry(e, e_id)) 27 28 win.mainloop()

Python

1# input_ic.py 2 3import nfc 4import tkinter as tk 5import binascii 6import threading 7 8 9def input_ic(e, input_widget): 10 11 def push_close(): 12 on_closing() 13 14 def push_clear(): 15 e_id.delete(0, tk.END) 16 17 def push_enter(): 18 input_widget.insert(tk.END, e_id.get()) 19 on_closing() 20 21 def connected(tag): 22 idm = binascii.hexlify(tag.idm) # <class 'byte'> 23 idm = idm.decode() # <class 'str'> 24 e_id.insert(tk.END, idm) 25 return True # これがないとICを1回かざしたときに複数回認識してしまう(whileループ時) 26 27 def ic_read(): 28 global clf 29 clf = nfc.ContactlessFrontend("usb") # 接続 30 try: 31 clf.connect( rdwr={"on-connect": connected} ) # 認識 32 finally: 33 clf.close() # 切断 34 35 def on_closing(): 36 global clf 37 clf.close() # これがないと応答なしになってしまう 38 thread_nfc.join() 39 sub_win.destroy() 40 41 sub_win = tk.Toplevel() 42 43 thread_nfc = threading.Thread(target=ic_read) 44 thread_nfc.start() 45 46 e_id = tk.Entry(sub_win) 47 e_id.insert(tk.END, input_widget.get()) 48 e_id.grid() 49 50 b_cls = tk.Button(sub_win, text="CLOSE", command=push_close) 51 b_cls.grid() 52 53 b_clr = tk.Button(sub_win, text="CLEAR", command=push_clear) 54 b_clr.grid() 55 56 b_etr = tk.Button(sub_win, text="ENTER", command=push_enter) 57 b_etr.grid() 58 59 sub_win.protocol("WM_DELETE_WINDOW", on_closing)
退会済みユーザー👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

残念ながら、私はICリーダを所有していないため確認できないのですが。。

不具合の原因は

Python

1clf.connect( rdwr={"on-connect": connected} ) # 認識

の関数がブロック関数のため、一度 ICを読み込ませるまでこの関数を抜けずにコードをブロックしてしまっているためです。。
そのため、ウィンドウを閉じたときにin_read()関数が終了せずに、thread_nfc の完了をずっと待っている状態となっているのかと思います。

ここ
https://readthedocs.org/projects/nfcpy/downloads/pdf/develop/
にドキュメントがありましたので確認してみたところ、83ページのAPIリファレンスの connect()の項目に

The calling thread is blocked until a single activation and deactivation has completed or a callback function supplied as the keyword argument terminate returns a true value.(この関数は完了するか、terminate引数に渡されたコールバック関数がtrue値を返すまでブロックします:超意訳)

とあるので、この terminateパラメータを使って

Python

1 2stop_flag=False 3clf.connect( rdwr={"on-connect": connected}, terminate=lambda: stop_flag ) # 認識

のようにして、

on_closing関数の中で、

Python

1def on_closing(): 2 global stop_flag 3 stop_flag = True 4 thread_nfc.join() 5 win.destroy()

のようにすると良いのではないでしょうか。

投稿2020/02/28 04:55

magichan

総合スコア15898

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

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

person

2020/02/28 10:51

回答ありがとうございます。 残念ながら、回答してくれたように変更しましたが応答なしになってしまいます。タスクマネージャーでも応答なしの表示あり。 質問のソースコードに書いたclf.close()を書かないとやはり応答なしに陥ってしまうようです。 (どういう原理で応答なしとフリーズという違う現象になるのかはわかりませんが、自分的にはウィンドウを閉じるときにclfも強制終了しとけばいいんじゃないって感じで書きました。) def on_closing(): global clf clf.close() # ICを読み込まずにウィンドウを閉じようとするときに、これがないとフリーズではなく応答なしになってしまう thread_nfc.join() win.destroy() もしかしたらtkinterとnfcは相性が悪いのかもしれませんね・・・。
magichan

2020/02/28 10:59

回答にも書きましたが残念ながら、私はICリーダを所有していないため確認できないので。。 私ならまず、terminate引数に渡した関数の戻り値をTrueに変えたタイミングで、問題なく connect関数から抜け出るか(connect関数の次の行にprint文を仕込んで、それが表示されるか)の確認を行います。 この関数から抜けでさえするとあとはなんとでもなるので。。
dodox86

2020/02/29 20:30 編集

magichanさんご提示の修正方法で試してみましたが、正しく終了しました。 (1) 起動して、Suica読み出し、IDm表示、ウィンドウクローズボタン押下、1秒以下で終了する。 (2) 起動して、すぐにウィンドウクローズボタン押下、1秒以下で終了する。 質問者さんの場合で動かないのはもしかすると、グローバル変数であるべきstop_flagの宣言位置がまずいのかもしれません。(<質問者さんが全体でどのように修正されたか分からないので、あくまで推測です)宣言と初期化の stop_flag = False は、clf.connect...の行の直前ではなくソースコードの冒頭、import の後などで宣言、定義してみてください。 あるいはもしかすると動作環境に依存するかもしれません。私の環境は以下です。※でもたぶん、コードの違いのせいかな、とは思います。 Windows10(64ビット), python 3.8.0(32ビット), libusb 1.0.23, RC-S380
person

2020/03/02 13:21

あ、できました。たしかに約1秒以内に終了はしますね。 ほんの少し終了するまでにラグがあるのは気になりますが、しょうがないんでしょうね。 ちなみにこの方法ならwhileを使ってICカードのかざし直しの認識もできるのかもしれませんね。
dodox86

2020/03/02 15:01

> ほんの少し終了するまでにラグがあるのは気になりますが、しょうがない おそらくですが、connectの中で1秒くらいの読み取りを繰り返しているのだと思います。これは仕方ないですね。
person

2020/03/17 11:05 編集

申し訳ないのですが、ソースが間違っていました。 追記形式で訂正しました。 2つのソースファイルを用意して、main.pyを実行してください。
person

2020/03/18 04:39

訂正したソースでは、ご指摘いただいたようにソースを変えると2回目以降ICは読み取ってくれませんでした。 訂正したソースはポップアップを開くと1回だけ入力ができ、再入力したい場合はポップアップを開きなおせばよいというものです(あまり完全とは言えませんが)。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問