在python运行时将数据从GUI传递给线程
Posted
技术标签:
【中文标题】在python运行时将数据从GUI传递给线程【英文标题】:Pass data to thread from GUI on runtime in python 【发布时间】:2019-06-20 09:10:17 【问题描述】:我正在尝试构建一个小型 python 应用程序,其中一个简单的网络服务器在后台运行,您可以使用 GUI 发送不同的消息。
我正在使用 PyQt5 和 Python3.6,我已经设法将数据从工作线程传递到 GUI,但我不知道如何反过来做。
这是我的代码框架:
MainWindow:
class Ui_MainWindow(object):
def __init__(self):
super().__init__()
self.input = True
# 1 - create Worker and Thread inside the Form
self.obj = WebServer.WebServer(self.input) # no parent!
self.thread = QtCore.QThread() # no parent!
# 2 - Connect Worker`s Signals to Form method slots to post data.
self.obj.dataReady.connect(self.onDataReady)
# 3 - Move the Worker object to the Thread object
self.obj.moveToThread(self.thread)
# 4 - Connect Worker Signals to the Thread slots
self.obj.finished.connect(self.thread.quit)
# 5 - Connect Thread started signal to Worker operational slot method
self.thread.started.connect(self.obj.startServer)
# 6 - Start the thread
self.thread.start()
# 7 - Start the form
self.setupUi()
def setupUi(self):
# Set up the GUI
#...
self.MainWindow.show()
def onDataReady(self, data):
# Data received from working thread
# Do stuff...
def on_click_set2(self):
self.input = not self.input
print(self.input)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Ui_MainWindow()
sys.exit(app.exec_())
WebServer:
class WebServer(QObject):
finished = pyqtSignal()
dataReady = pyqtSignal(dict)
def __init__(self, input):
super().__init__()
self.input = input
@pyqtSlot()
def startServer(self): # A slot takes no params
# self.loop = asyncio.get_event_loop()
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
coro = asyncio.start_server(self.handle_update, '192.168.2.1', 8888, loop=self.loop)
self.server = self.loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on '.format(self.server.sockets[0].getsockname()))
try:
self.loop.run_forever()
except KeyboardInterrupt:
pass
self.finished.emit()
async def handle_update(self, reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f'Received: message from addr')
if self.input:
print("Input is True")
else:
print("Input is False")
reply = self.input
print(f'Send: reply')
writer.write(str(reply).encode())
await writer.drain()
print("Close the client socket")
writer.close()
self.dataReady.emit(reply)
例如,我想将输入传递给线程,如果我确实像上面那样(显然)输入总是保持初始值,并且当我点击 GUI 上的按钮时不会在线程中改变。
我该怎么做,只要我点击按钮,输入的值就会更新(因此在运行时将值从 GUI 传递给线程)?我假设类似于从线程传递到 GUI,因此从 GUI 发出信号并在胎面上连接到它,但我不知道如何从工作线程中找到对 GUI 的引用。
那么关于如何做到这一点有什么建议吗?当然,欢迎任何其他有关应用程序/后台服务器解决方案的代码或方法的输入!提前感谢您的帮助!
更新:
也许我不清楚我的问题是什么,所以在这里:
如何在 GUI 线程并行运行时将值从 GUI 线程发送到工作线程?(如果上面的代码对您有意义,则将其用作示例,否则一般示例将不胜感激)
【问题讨论】:
显示了所有的GUI代码但无法分析问题出在哪里 代码的问题是input
变量的值在创建时传递给线程,但在运行时没有更新。但我更新了我的帖子,强调我的问题。
【参考方案1】:
WebServer 不必存在于另一个线程中,只需要将事件循环在另一个线程中执行。在这种情况下,WebServer 通过队列与服务器线程交换数据。虽然 QObjects 不是线程安全的,但信号是这样的,从 QObject 所在的线程以外的线程发出信号没有问题
import asyncio
import threading
import queue
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class WebServer(QtCore.QObject):
dataReady = QtCore.pyqtSignal(object)
def startServer(self):
self.m_loop = asyncio.new_event_loop()
self.m_queue = queue.Queue()
asyncio.set_event_loop(self.m_loop)
coro = asyncio.start_server(
self.handle_update, "127.0.0.1", 10000, loop=self.m_loop
)
self.server = self.m_loop.run_until_complete(coro)
print("Serving on ".format(self.server.sockets[0].getsockname()))
threading.Thread(target=self.m_loop.run_forever, daemon=True).start()
@QtCore.pyqtSlot(object)
def setData(self, data):
if hasattr(self, "m_queue"):
self.m_queue.put(data)
def stop(self):
if hasattr(self, "m_loop"):
self.m_loop.stop()
async def handle_update(self, reader, writer):
reply = ""
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info("peername")
print(f"Received: message from addr")
if not self.m_queue.empty():
data = self.m_queue.get(block=False)
reply = data
print(f"Send: reply")
writer.write(str(reply).encode())
await writer.drain()
print("Close the client socket")
writer.close()
self.dataReady.emit(reply)
class Widget(QtWidgets.QWidget):
dataChanged = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
self.m_lineedit = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Send", clicked=self.onClicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.m_lineedit)
lay.addWidget(button)
self.m_web = WebServer()
self.m_web.startServer()
self.dataChanged.connect(self.m_web.setData)
@QtCore.pyqtSlot()
def onClicked(self):
text = self.m_lineedit.text()
self.dataChanged.emit(text)
@QtCore.pyqtSlot(object)
def onDataReady(self, data):
print(data)
def closeEvent(self, event):
self.m_web.stop()
super().closeEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
【讨论】:
以上是关于在python运行时将数据从GUI传递给线程的主要内容,如果未能解决你的问题,请参考以下文章