https://qtutils.readthedocs.io/en/stable/invoke_in_main/
QtUtilsは、スレッドセーフな方法でQtオブジェクトにアクセスするための便利な関数を提供します。Qtは、すべてのGUIオブジェクトがMainThreadに存在し、これらのオブジェクトへのアクセスがMainThreadからのみ行われることを必要とします(Qtドキュメントを参照)。これは理解可能ですが、スレッド化が容易なPythonアプリケーションに大きな制限を課します。Qt信号、スロット、およびaを使用するソリューションはありますがQThread、これらには不要であると考えるかなりのボイラープレートコードが必要です。
Qtライブラリの一部でPythonスレッドを使用することが安全であるかどうかについては、いくつかの議論がありますが、最近これに挑戦しています。QtUtilsライブラリは、をインスタンス化し、PythonスレッドからQEvent呼び出すだけQCoreApplication.postEvent()です。基盤となるPythonスレッド実装が特定のプラットフォームの基盤となるQtスレッド実装と一致している限り、このライブラリの記述方法に問題はないようです。ライブラリに関する問題は確認されていませんが(Windows、OSX、Ubuntuで広範囲に使用されています)、すべてのプラットフォームが同じように動作するわけではありません。これが重要な場合は、PythonとQtのビルドの基盤となるスレッドの実装を確認することをお勧めします。
Qtイベントループを使用して、QtイベントをセカンダリスレッドからMainThreadにポストすることにより、MainThreadで任意のメソッドを実行します。QtUtilsはinmain、MainThreadで実行するメソッドへの参照を受け取り、その後メソッドに渡される引数が続くという、呼び出される関数を提供します。
from qtutils import inmain
# This is equivalent to calling my_func(arg1, arg2, foo=7, bar='baz') in the MainThread# The calling thread will wait for the result to be returned before continuingresult = inmain(my_func, arg1, arg2, foo=7, bar='baz')
inmainQtイベントループがメッセージを処理し、指定されたメソッドを実行して結果を返すまで、呼び出しスレッドをブロックする呼び出し。結果を待つのを待たない場合、または結果を待つ間に他の処理を実行したい場合は、QtUtilsがinmain_later関数を提供します。これはと同じように機能しinmainますが、Python Queueオブジェクトへの参照をすぐに返します。次の例に示すように、結果はいつでもこのキューから取得できます。
from qtutils import inmain_later, get_inmain_result
# This is equivalent to calling my_func(arg1, arg2, foo=7, bar='baz') in the MainThread# The calling thread will immediately continue execution, and the result of the function# will be placed in the queue once the Qt event loop has processed the requestqueue = inmain_later(my_func, arg1, arg2, foo=7, bar='baz')# You can get the result (or raise any caught exceptions) by calling# Note that any exception will have already been raised in the MainThreadresult = get_inmain_result(queue)
もちろん、これはQtメソッドやユーザー定義関数/メソッドと直接連動します。例えば:
from qtutils import inmain_later, get_inmain_result, inthread
def run_in_thread(a_line_edit, ignore=True):
# set the text of the line edit, and wait for it to be set before continuing
inmain(a_line_edit.setText, 'foobar')
# Get the text of the line edit, and wait for it to be returned
current_text = inmain(a_line_edit.text)
# queue up a call to deselect() and don't wait for a result to be returned
inmain_later(a_line_edit.deselect)
# request the text of the line edit, but don't wait for it to be returned
# However, this call is guaranteed to run AFTER the above inmain_later call
queue = inmain_later(a_line_edit.text)
# do some intensive calculations here
# now get the text
current_text = get_inmain_result(queue)
print(current_text)
# instantiate a QLineEdit# This object should only be accessed from the MainThreadmy_line_edit = QLineEdit()
# starts a Python thread (in daemon mode)# with target run_in_thread(my_line_edit, ignore=False)thread = inthread(run_in_thread,my_line_edit,ignore=False)
ご覧のとおり、Qtメソッドの直接呼び出しとスレッドセーフな方法での変更の違いは非常に簡単です。
a_line_edit.setText('foobar')inmain(a_line_edit.setText,'foobar')
また、呼び出し側のスレッドに関係なく、常に装飾されたメソッドがMainThreadで実行されるように、デコレーターも提供しています。これは、Pythonプロパティと組み合わせると特に便利です。
# This function will always run in the MainThread, regardless of which thread calls it.# The calling thread will block until the function is run in the MainThread, and the result returned.# If called from the MainThread, the function is executed immediately as if you had called a_function()@inmain_decorator(wait_for_return=True)def a_function(a_line_edit):
a_line_edit.setText('bar')
return a_line_edit.text()
# This function will always run in the MainThread, regardless of which thread calls it.# A call to this function will return immediately, and the function will be run at a# later time. A call to this function returns a python Queue.Queue() in which the result of# the decorated function will eventually be placed (or any exception raised)@inmain_decorator(wait_for_return=False)def another_function(a_line_edit):
a_line_edit.setText('baz')
QtUtilsは、Pythonスレッドをデーモンモードで起動するための便利な機能も提供します。 inthread(target_method, arg1, arg2, … kwarg1=False, kwargs2=7, …)
例外処理
通常、例外は呼び出しスレッドで発生します。ただし、inmain_later関連付けられたデコレータもMainThreadで例外を発生させます。これは、呼び出し側のスレッドから結果が読み取られるという保証がないためです。
MainThreadからのQtUtilsの使用
inmain、または関連付けられたデコレータを使用する場合、QtUtilsは指定されたメソッドをただちに実行するだけで、Qtイベントループをバイパスします。これにより、呼び出しコードがQtイベントループによって実行されているという明らかなデッドロックが回避され、Qtイベントループが次のイベントを実行するのを待っています(次のイベントを待ってブロックされているため、これは発生しません)呼び出しコード)。 inmain_laterMainThreadから使用した場合でも、Qtイベントループにイベントをポストします。これは、MainThreadから非同期で何かを実行する場合(たとえば、ラベルのテキストを非同期に更新する場合)に役立ちますが、デッドロックが発生する危険性があるため、このような呼び出しの結果を読み取らないようにすることをお勧めします。
スレッドでユーザー入力を待つ場合はどうなりますか?
スレッドがユーザー入力を待つようにしたい場合、これはあなたのためのライブラリではありません!QtソリューションのGUIからのユーザー入力をスレッドで待機する方法や、PythonソリューションのPythonスレッドイベントを確認することをお勧めします。
APIリファレンス
クラスqtutils.invoke_in_main.
CallEvent
(queue、exceptions_in_main、fn、* args、** kwargs)[ソース]
関数呼び出しのリクエストを含むイベント。
クラスqtutils.invoke_in_main.
Caller
[ソース]
CallEvent内に保持されている関数を呼び出すイベントハンドラー。
event
(self、QEvent) →bool[ソース]qtutils.invoke_in_main.
get_inmain_result
(キュー)[ソース]
の結果を処理しqtutils.invoke_in_main.inmain_later()ます。
この関数はによって返されたキューを受け取り、inmain_later結果が取得されるまでブロックします。MainThreadで関数の実行中に例外が発生した場合、ここで再び発生します(MainThreadでも発生します)。例外が発生しなかった場合、関数の実行結果が返されます。
パラメーター
queue –によって返されるPython Queueオブジェクトinmain_later
戻り値
の呼び出しで指定された関数を実行した結果 inmain_later
qtutils.invoke_in_main.
inmain
(fn、* args、** kwargs)[ソース]
メインスレッドで関数を実行します。完了するまで待って、戻り値を返します。
この関数は、カスタムQEventをQtイベントループのキューに入れます。このイベントは、指定さfnれた引数とキーワード引数を使用して、Python MainThreadで指定された関数を実行し、結果を呼び出し側スレッドに返します。
この関数はMainThreadから使用できますが、Qtイベントループをバイパスして、関数を直接呼び出すだけです。
パラメーター
-
fn – MainThreadで実行する関数またはメソッドへの参照。
-
* args – fnMainThreadから呼び出されたときに渡される引数。
-
** kwargs – fnMainThreadから呼び出されたときに渡されるキーワード引数
戻り値
実行結果 fn(*args, **kwargs)
qtutils.invoke_in_main.
inmain_decorator
(wait_for_return = True、exceptions_in_main = True)[ソース]
MainThreadで装飾されたスレッドの実行を強制するデコレータ。
このデコレータは、いずれかの装飾が施さ関数やメソッドをラップします qtutils.invoke_in_main.inmain()か qtutils.invoke_in_main.inmain_later()。
キーワード引数
-
wait_for_return – inmain(if True)またはinmain_later(if False)を使用するかどうかを指定します。
-
exceptions_in_main-メインスレッドで例外を発生させるかどうかを指定します。これは無視されます wait_for_return=True。これがそうである場合 False、明示的に使用しないと、例外が沈黙する可能性があります qtutils.invoke_in_main.get_inmain_result()。
戻り値
デコレータは、inmainor への適切な呼び出しでデコレートされた関数をラップした関数を返しますinmain_later(デコレータの動作に慣れていない場合は、Pythonのドキュメントを参照してください)。
デコレートされた関数を呼び出すと、結果はMainThread(if wait_for_return=True)で実行された関数の結果か、qtutils.invoke_in_main.get_inmain_result()後で使用するPythonキューのいずれかになります 。
qtutils.invoke_in_main.
inmain_later
(fn、* args、** kwargs)[ソース]
メインスレッドで関数の実行をキューに入れ、すぐに戻ります。
この関数は、カスタムQEventをQtイベントループのキューに入れます。このイベントは、指定さfnれた引数とキーワード引数を使用して、Python MainThreadで指定された関数を実行し、最終的にの実行結果を保持するPythonキューを返します fn。結果にアクセスするには、を使用しますqtutils.invoke_in_main.get_inmain_result()。
この関数はMainThreadから使用できますが、Qtイベントループをバイパスして、関数を直接呼び出すだけです。
パラメーター
-
fn – MainThreadで実行する関数またはメソッドへの参照。
-
* args – fnMainThreadから呼び出されたときに渡される引数。
-
** kwargs – fnMainThreadから呼び出されたときに渡されるキーワード引数
戻り値
最終的に結果を保持しますA Pythonのキュー ところを 。(fn(*args, **kwargs), exception)exception=[type,value,traceback]
qtutils.invoke_in_main.
inthread
(f、* args、** kwargs)[ソース]
Pythonスレッドを開始するための便利な関数。
この関数は、デーモンモードでPythonスレッドを起動し、実行中のスレッドオブジェクトへの参照を返します。
パラメーター
-
f – Pythonスレッドで実行されるターゲット関数への参照。
-
* args – f新しいスレッドで実行されるときに渡す引数。
-
** kwargs – f新しいスレッドで実行されるときに渡されるキーワード引数。
戻り値
(すでに実行中の)Pythonスレッドオブジェクトへの参照