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

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

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

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

Tkinter

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

Q&A

解決済

1回答

1510閲覧

Python3 Tkinter geometryを使っても表示位置を指定できない

person

総合スコア224

Python 3.x

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

Tkinter

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

0グッド

0クリップ

投稿2020/08/26 10:52

編集2020/08/27 16:58

過去にnfcをTkinterで利用したい
という質問をしました。
これに関係する質問です。

ICカードをかざした時にポップアップ画面を表示したいです。

そこで、下のようにソースコードを書いたのですが、Raspberry Piで動作させた時に、ポップアップ画面の表示位置が指定位置と異なることがあります。(ウィンドウの大きさは指定通りっぽい・・・)

nfcを使わずにポップアップを実装した時はgeometryの指定位置にちゃんと表示されていたので、個人的にはnfcが原因だと思っています。
Windowsではnfcを使っても指定位置に表示されるっぽいので、この現象はRaspberry Piでのみ確認しています。

実際のところどうなのでしょうか?

また、これの対策はできますか?

(もっとも、この質問nfcをTkinterで利用したいが根本的に解決したわけではないので、nfc実装のコードに問題がある可能性はあります・・・)

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 sub_win.destroy() 14 15 def push_clear(): 16 e_id.delete(0, tk.END) 17 18 def push_enter(): 19 input_widget.insert(tk.END, e_id.get()) 20 on_closing() 21 sub_win.destroy() 22 23 def connected(tag): 24 idm = binascii.hexlify(tag.idm) # <class 'byte'> 25 idm = idm.decode() # <class 'str'> 26 e_id.insert(tk.END, idm) 27 # ---------- 今回の実装部分 ここから---------- 28 toplevel = tk.Toplevel() 29 toplevel.geometry("100x100+100+100") # ラズパイだとgeometryの位置指定が効かない? 30 # ---------- 今回の実装部分 ここまで---------- 31 return True # これがないとICを1回かざしたときに複数回認識してしまう 32 33 # カードをかざす度に認識させる 34 def ic_read(): 35 global clf 36 clf = nfc.ContactlessFrontend('usb') 37 while clf.connect(rdwr={ 38 'on-connect': connected, 39 }): 40 pass 41 42 43 def on_closing(): 44 global clf 45 clf.close() 46 thread_nfc.join() 47 48 sub_win = tk.Toplevel() 49 50 thread_nfc = threading.Thread(target=ic_read) 51 thread_nfc.start() 52 53 e_id = tk.Entry(sub_win) 54 e_id.insert(tk.END, input_widget.get()) 55 e_id.grid() 56 57 b_cls = tk.Button(sub_win, text="CLOSE", command=push_close) 58 b_cls.grid() 59 60 b_clr = tk.Button(sub_win, text="CLEAR", command=push_clear) 61 b_clr.grid() 62 63 b_etr = tk.Button(sub_win, text="ENTER", command=push_enter) 64 b_etr.grid() 65

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

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

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

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

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

guest

回答1

0

ベストアンサー

環境がないので試せないのですが、気になる点:

  • Toplevel を 別スレッドで呼び出している。--> スレッドセーフな操作ではない

GUI の操作はイベントループを持つスレッド(通常はメインスレッド)
でのみ行うようにします。
別スレッドから同時アクセスが発生すると、
環境やタイミング次第では、おかしな挙動をすることがあります。

検証方法: 以下の2つのコードを試してみてください。

  • 上は別スレッド上でウィンドウを作っているコードです。
  • 下はメインスレッドで ウィンドウを作る関数を呼び出しています。

どちらもwindowsでは変わりありませんが、
repl.it で上のコードのダイアログが別の位置に表示されるのを確認しました。

nfc についても解らないので、解決の手掛かりになるかはわかりませんが、
「ダイアログの位置問題」に直接影響がなかったとしても、
スレッドセーフでない操作は、大きな不確定要素となるので、
スレッドの使い方は修正した方が良いです。

もし、影響が無かったとしたら、
nfc なし(補足: スレッドは使って)で問題が発生するかどうかを試してみてください。


python

1#!/usr/bin/env python3.8 2 3import tkinter as tk 4from threading import Thread 5import logging 6 7def thread_main(root): 8 logging.info("thread_main started") 9 logging.info("Open Dialog") 10 win = tk.Toplevel() 11 win.geometry("100x100+100+100") 12 13def main(): 14 root = tk.Tk() 15 16 def start(): 17 logging.info("Button clicked") 18 thread = Thread(target=thread_main, args=(root,), daemon=True) 19 thread.start() 20 21 tk.Button(root, text="Open", command=start).pack() 22 root.mainloop() 23 24if __name__ == '__main__': 25 logging.basicConfig(level=logging.INFO, format="[%(threadName)s] %(message)s") 26 main()

python

1#!/usr/bin/env python3.8 2 3import tkinter as tk 4from threading import Thread 5import logging 6 7def open_dialog(root): 8 logging.info("Open Dialog") 9 win = tk.Toplevel(root) 10 win.geometry("100x100+100+100") 11 12def thread_main(root): 13 logging.info("thread_main started") 14 root.after_idle(open_dialog, root) 15 16def main(): 17 root = tk.Tk() 18 19 def start(): 20 logging.info("Button clicked") 21 thread = Thread(target=thread_main, args=(root,), daemon=True) 22 thread.start() 23 24 tk.Button(root, text="Open", command=start).pack() 25 root.mainloop() 26 27if __name__ == '__main__': 28 logging.basicConfig(level=logging.INFO, format="[%(threadName)s] %(message)s") 29 main()

投稿2020/08/30 05:58

編集2020/08/30 13:55
teamikl

総合スコア8760

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

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

person

2020/09/01 02:12

> Toplevel を 別スレッドで呼び出している。 nfcの入力待ちについてはループさせないと、カードをかざす度に読み込ませることができない(?)ので結果的にスレッド内で呼び出す形になってしまっています。 そのためテキストボックスをクリックしたら読み込ませる用の別ウィンドウを表示してスレッドを回して入力待ち状態にして、そのウィンドウを閉じたらjoin()するようにしています。 カードをかざした('on-connect')時に読み込ませた内容が一定の条件を満たしたときにtoplevelでメッセージ表示するには、スレッド以外では可能でしょうか?
teamikl

2020/09/01 05:45

カードの読み出しについては、サブスレッドで大丈夫です。 そこから、「別ウィンドウを開く」GUI関連の処理のみを (mainloopを処理している)メインスレッドで行う必要があります。 「カードの読み出し」と「別ウィンドウの表示」を分離してください。 回答で提示した下のコードのように、 after_idle でダイアログを開く関数を呼び出します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問