Skip to content

Commit 4280d3e

Browse files
committed
在线程中操作UI
1 parent 5ae47a6 commit 4280d3e

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

QMetaObject/CallInThread.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Created on 2023/02/23
5+
@author: Irony
6+
@site: https://pyqt.site https://github.com/PyQt5
7+
@email: 892768447@qq.com
8+
@file: CallInThread.py
9+
@description:
10+
"""
11+
import time
12+
from datetime import datetime
13+
from threading import Thread
14+
15+
from PyQt5.QtCore import (Q_ARG, Q_RETURN_ARG, QMetaObject, Qt, QThread,
16+
pyqtSignal, pyqtSlot)
17+
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QTextBrowser, QWidget
18+
19+
20+
class ThreadQt(QThread):
21+
22+
def __init__(self, textBrowser, *args, **kwargs):
23+
super(ThreadQt, self).__init__(*args, **kwargs)
24+
self._textBrowser = textBrowser
25+
26+
def stop(self):
27+
self.requestInterruption()
28+
29+
def run(self):
30+
while not self.isInterruptionRequested():
31+
text = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
32+
# 通过`invokeMethod`直接调用对应的槽函数
33+
# 1. 获取函数`isReadOnly1`返回值, self.parent() 是Window窗口对象
34+
# NOTE:注意这里获取返回值要用 `Qt.DirectConnection` 方式
35+
retValue = QMetaObject.invokeMethod(self.parent(), 'isReadOnly1',
36+
Qt.DirectConnection,
37+
Q_RETURN_ARG(bool))
38+
# 2. 通过`invokeMethod`队列调用对应控件槽函数`append`
39+
argValue = Q_ARG(str, text + ' readOnly: ' + str(retValue))
40+
QMetaObject.invokeMethod(self._textBrowser, 'append',
41+
Qt.QueuedConnection, argValue)
42+
self.sleep(1)
43+
44+
45+
class ThreadPy(Thread):
46+
47+
def __init__(self, textBrowser, parent, *args, **kwargs):
48+
super(ThreadPy, self).__init__(*args, **kwargs)
49+
self._running = True
50+
self._textBrowser = textBrowser
51+
self._parent = parent
52+
53+
def stop(self):
54+
self._running = False
55+
56+
def run(self):
57+
while self._running:
58+
text = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
59+
# 通过`invokeMethod`队列调用对应控件信号,`self._parent`是Window窗口对象
60+
QMetaObject.invokeMethod(self._parent, 'appendText',
61+
Qt.QueuedConnection,
62+
Q_ARG(str, text + ' from Signal'))
63+
# 通过`invokeMethod`队列调用对应控件槽函数`append`
64+
QMetaObject.invokeMethod(self._textBrowser, 'append',
65+
Qt.QueuedConnection,
66+
Q_ARG(str, text + ' to Slot'))
67+
time.sleep(1)
68+
69+
70+
class Window(QWidget):
71+
72+
# 更新信号
73+
appendText = pyqtSignal(str)
74+
75+
def __init__(self, *args, **kwargs):
76+
super(Window, self).__init__(*args, **kwargs)
77+
layout = QHBoxLayout(self)
78+
self.textBrowser1 = QTextBrowser(self)
79+
self.textBrowser2 = QTextBrowser(self)
80+
layout.addWidget(self.textBrowser1)
81+
layout.addWidget(self.textBrowser2)
82+
83+
self.appendText.connect(self.textBrowser2.append)
84+
85+
# Qt线程
86+
self.thread1 = ThreadQt(self.textBrowser1, self)
87+
self.thread1.start()
88+
89+
# PY线程
90+
self.thread2 = ThreadPy(self.textBrowser2, self)
91+
self.thread2.start()
92+
93+
@pyqtSlot(result=bool)
94+
def isReadOnly1(self):
95+
# 线程中直接调用该槽函数获取UI中的内容
96+
return self.textBrowser1.isReadOnly()
97+
98+
def closeEvent(self, event):
99+
self.thread1.stop()
100+
self.thread2.stop()
101+
self.thread1.wait()
102+
self.thread2.join()
103+
super(Window, self).closeEvent(event)
104+
105+
106+
if __name__ == '__main__':
107+
import cgitb
108+
import sys
109+
110+
cgitb.enable(format='text')
111+
app = QApplication(sys.argv)
112+
w = Window()
113+
w.show()
114+
sys.exit(app.exec_())

QMetaObject/README.en.md

Whitespace-only changes.

QMetaObject/README.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# QMetaObject
2+
3+
- 目录
4+
- [在线程中操作UI](#1在线程中操作UI)
5+
6+
## 1、在线程中操作UI
7+
[运行 CallInThread.py](CallInThread.py)
8+
9+
如果想在`QThread`或者`threading.Thread`中不通过信号直接操作UI,则可以使用`QMetaObject.invokeMethod`调用。
10+
11+
该函数一般有常用的几种调用方法:
12+
13+
1. 直接调用槽函数:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.QueuedConnection)`
14+
2. 直接调用信号:`QMetaObject.invokeMethod(uiobj, 'signal_method', Qt.QueuedConnection)`
15+
3. 调用信号或槽函数并传递参数:`QMetaObject.invokeMethod(uiobj, 'method', Qt.QueuedConnection, Q_ARG(str, 'text'))`
16+
4. 调用槽函数得到返回值:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.DirectConnection)`
17+
5. 调用带参数的槽函数得到返回值:`QMetaObject.invokeMethod(uiobj, 'slot_method', Qt.DirectConnection, Q_ARG(bool, False))`
18+
19+
这里需要注意:
20+
21+
1. 调用函数都是异步队列方式,需要使用`Qt.QueuedConnection`
22+
2. 而要得到返回值则必须使用同步方式, 即`Qt.DirectConnection`
23+
24+
![CallInThread](ScreenShot/CallInThread.png)
13.9 KB
Loading

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@
259259
- [最小化到系统托盘](QSystemTrayIcon/MinimizeToTray.py)
260260
- [QSystemTrayIcon](QSystemTrayIcon)
261261
- [系统托盘闪烁](QSystemTrayIcon/TrayNotify.py)
262+
- [QMetaObject](QMetaObject)
263+
- [在线程中操作UI](QMetaObject/CallInThread.py)
262264

263265
- [Demo](Demo)
264266
- [重启窗口Widget](Demo/RestartWindow.py)

0 commit comments

Comments
 (0)