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

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

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

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

Python

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

Q&A

解決済

1回答

1940閲覧

pyqtgrafとtkinterのウィンドウを二つ同時に表示

grintea

総合スコア15

Tkinter

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

Python

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

0グッド

0クリップ

投稿2020/12/10 05:16

前提・実現したいこと

pyqtgrafとtkinterのウィンドウを二つ同時に表示することは可能ですか?
可能であるならその方法を教えていただけると幸いです。

ちなみに、pyqtgrafは機器から得られたデータをリアルタイムでグラフ化、ファイル格納するプログラムで、tkinterは命令を表示するプログラムです。

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

コマンドプロンプトで二つのファイルを同時に実行しようとすると下記のようなエラーが出ます。

sys:1: RuntimeWarning: Visible window deleted. To prevent this, store a reference to the window object.

試したこと

  • コマンドプロンプトによるファイル同時実行

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

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

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

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

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

guest

回答1

0

ベストアンサー

イベントループを別スレッドもしくは、別プロセスで実行することになります。
イベントループを独自に組めれば、双方を同一スレッド上で動かすことは可能です。

python

1 2def tk_event_loop(): # root.mainloop() を想定 3 while True: 4 print("tk") 5 6def qt_event_loop(): # app.exec_() を想定 7 while True: 8 print("qt") 9 10# 問題点: GUIライブラリはイベントループを持ちますが 11# 2つのループを同一スレッド上で実行は出来ない 12 13# 双方のイベントループを動かす独自のイベントループを実装する 14def my_event_loop(): 15 while True: 16 print("tk") # root.update() ... tk のイベント処理 17 print("qt") # app.processEvents() ... qt のイベント処理

同時にイベントループを稼働する方法は、終了時に同期を取る方法が難しい為、
別プロセスを検討した方が良さそうです。


python

1def tk_main(shared): 2 import tkinter as tk 3 from tkinter import ttk 4 from functools import partial 5 6 main_quit = partial(shared.queue.put, None) 7 8 root = tk.Tk() 9 button = ttk.Button(root, text="Quit") 10 button.config(command=main_quit) 11 button.pack() 12 13 root.protocol("WM_DELETE_WINDOW", main_quit) 14 15 root.mainloop() 16 17 18def qt_main(shared): 19 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton 20 21 app = QApplication(shared.argv) 22 win = QMainWindow() 23 button = QPushButton("Quit", win) 24 button.clicked.connect(win.close) 25 win.show() 26 27 app.aboutToQuit.connect(lambda: shared.queue.put(None)) 28 app.exec_() 29 30 31def main(): 32 import sys 33 from types import SimpleNamespace 34 from multiprocessing import Process, Queue 35 36 shared = SimpleNamespace( 37 argv = sys.argv, 38 queue = Queue(), 39 ) 40 procTk = Process(target=tk_main, args=(shared,), daemon=True) 41 procQt = Process(target=qt_main, args=(shared,), daemon=True) 42 43 procTk.start() 44 procQt.start() 45 46 for item in iter(shared.queue.get, None): 47 queue.task_done() 48 49 50if __name__ == '__main__': 51 main() 52 53

tkinter と pyqt のウィンドウを同時に開くサンプル
このコードでは合計3つのプロセスを使いますが、最少は qt と tk の2つでも良いです。
終了時の処理を解りやすくするために3つにしました。

メインのプロセスではキューへのメッセージを待ち、
None が入れられるとプログラムを終了します。

qt <-> tk 間の連携には「プロセス間通信」を調べて見て下さい、
qt 側で読み取る Queue や tk 側で読み取る Queue を増やし、
Queue を通じてメッセージのやり取りをすることになります。

注意点: 終了時に tk/qt 側で何か処理を行う場合は、
daemon=False にして join メソッドでプロセスの終了を待ちます。
その場合、安全に各GUIのイベントループを終了する仕組みが必要になります。


qt/tk のイベントループを結合する方法

独自のイベントループを使う為、終了時のイベント処理は未対応。

デメリット:

  • Qt の aboutToQuit シグナル等は期待通りに処理されない等、他への影響があります。
  • 片方の遅延がもう片方に影響する。(tk のイベント処理が遅れると qt も遅くなる → 動作が重く)

別プロセスにした方が良いと思いますが、2つ以上のイベントループを使いたい場合、
このようなアプローチにすることも有ります。(例: asyncio + GUI 等)

python

1import sys 2import time 3from functools import partial 4from types import SimpleNamespace 5 6import tkinter as tk 7from PyQt5.QtCore import pyqtSignal 8from PyQt5.QtWidgets import QApplication, QMainWindow 9 10def my_quit(app, root, shared): 11 shared.stop_request = True 12 13def my_event_loop(app, root, shared): 14 while not shared.stop_request: 15 root.update() 16 app.processEvents() 17 time.sleep(0.1) 18 app.quit() 19 root.destroy() 20 21def main(): 22 app = QApplication(sys.argv) 23 root = tk.Tk() 24 shared = SimpleNamespace( 25 stop_request = False, 26 ) 27 cleanup = partial(my_quit, app, root, shared) 28 29 # tk widgets 30 root.protocol("WM_DELETE_WINDOW", cleanup) 31 button = tk.Button(root, text="OK") 32 button.pack() 33 34 # qt widgets 35 class MyMainWindow(QMainWindow): 36 closed = pyqtSignal() 37 def closeEvent(self, event): 38 self.closed.emit() 39 event.accept() 40 win = MyMainWindow() 41 win.closed.connect(cleanup) 42 win.show() 43 44 # XXX: app.aboutToQuit does not work 45 46 my_event_loop(app, root, shared) 47 48 49if __name__ == '__main__': 50 main()

投稿2020/12/10 06:31

編集2020/12/10 08:52
teamikl

総合スコア8760

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

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

grintea

2020/12/10 06:55

また、回答していただきありがとうございます。 別プロセスでの実行となると、また別のファイルを作成してdefでループを回すという形ですか?
teamikl

2020/12/10 07:01

ファイルは同じでも良いけど、 multiprocessing モジュールを使う場合は if __name__ == "__main__": で 別プロセスで実行する部分とそうでない部分の区別は必要になりますね。 勿論、長くなる場合はファイルを別けた方が良いです。 > def でループを? tkinter のメインループ root.mainloop() と qt のメインループ app.exec_() を別々のプロセスで実行します
grintea

2020/12/10 07:20

本当に無知ですみません。 別プロセスで実行する部分って言うのは、前回送っていただいた、「start_timer」のところですか? >def start_timer(queue): > import time > for num in [3, 2, 1]: > queue.put(("label-set-text", str(num))) > time.sleep(1) > queue.put(("label-set-text", "")) > queue.put(("label-bind-key", None))
teamikl

2020/12/10 07:30

Process(target=...) に渡す関数が別プロセスで実行されます。 前回の続きであれば、start_timer 内に qt のコードを書くことになりますね。 どちらの方法を採用するかによりますが、 - 別プロセスで動かす → プロセス間通信、Queue の使い方 - 同一プロセスで qt/tk 両方を実行 → イベントループ、イベント駆動プログラミング の前知識が必要になってきます。 場合によっては、tk -> qt に移植する方が簡単な事も。(プログラムの規模次第) ※ 逆の qt -> tk 移植は、必要なウィジェットが足りないことがあります。
grintea

2020/12/10 07:56

また、挑戦してみます! わからない点についてまたお伺いすることになると思いますが、よろしくお願いします。
teamikl

2020/12/10 08:57 編集

以前試したことがあったので、コード発掘してきました。>回答に掲載 どちらの方法でも、終了時の処理は工夫する必要があります。
grintea

2020/12/10 09:47

ありがとうございます! まだ、コード書くのに手間取っていますが、もうしばらくかんばってみます。 今回もまたお世話になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問