我在python中将我的脚趾浸入网络和多线程中。我已经浏览了concurrent.futures和SocketServer上的文档,并尝试在我正在使用的示例中使用这些文档。文档中的示例似乎已经足够前进,但我正在努力将它们应用到我正在研究的示例中。
示例如下。 2个GUI应用程序,一个通过用户交互向另一个发送信息,另一个显示此信息。
到目前为止,我已经启动并运行了2个应用程序。应用程序A使服务器在单独的线程中运行。应用程序B能够连接到服务器并发送所需信息。
此时我似乎无法找到一种很好的方法来获取应用程序A的GUI中显示的信息。我可以想到几种hacky方法,但我对最好/最pythonic的方式感兴趣。这似乎是一个常见的问题,因此必须有一个共同的模式在这里使用。所以我的问题是。
示例代码如下
服务器窗口
import SocketServer
import concurrent.futures
import sys
from PyQt4 import QtGui
HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
class MyRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
print('...connected from:', self.client_address)
data = self.rfile.readline().strip()
print('Data from client %s' % data)
class ServerWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setGeometry(1500, 100, 500, 500)
self._control = QtGui.QWidget()
self.setCentralWidget(self._control)
l = QtGui.QVBoxLayout(self._control)
t = QtGui.QTextEdit()
l.addWidget(t)
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
self.startServerThread()
self.show()
def startServerThread(self):
self.executor.submit(self.startServer)
# How to get information from the thread while it is still running?
def startServer(self):
print('starting server')
tcpServ = SocketServer.TCPServer(ADDR, MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()
# How to get information from the client (custom request handler)
# back to the GUI in a thread safe manner?
def launch():
app = QtGui.QApplication(sys.argv)
ex = ServerWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
launch()
客户端窗口
import socket
import sys
import functools
from PyQt4 import QtGui
HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
class ClientWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setGeometry(1500, 650, 500, 500)
self._control = QtGui.QWidget()
self.setCentralWidget(self._control)
l = QtGui.QVBoxLayout(self._control)
for i in range(5):
name = 'test %d' % i
b = QtGui.QPushButton(name)
l.addWidget(b)
b.pressed.connect(functools.partial(self.onButtonClick, name))
self.show()
def onButtonClick(self, buttonName):
print('connecting to server')
tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpCliSock.connect(ADDR)
print('Sending name %s' % buttonName)
tcpCliSock.send(buttonName)
tcpCliSock.close()
def launch():
app = QtGui.QApplication(sys.argv)
ex = ClientWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
launch()
答案 0 :(得分:2)
因此,我所知道的将数据从线程传输到应用程序的主GUI线程的几种方法之一就是将数据放在python Queue
中。这个Queue
由QThread
(支持Qt信号和插槽的Qt线程实现)读取。 QThread
对queue.get()
进行了阻止调用。当Queue
方法将数据放入handle()
时,QThread
取消阻塞,从队列中读取数据,并向GUI线程发出线程安全信号。作为演示,我将数据添加到QTextEdit
。
所以基本上你需要一个python线程和Qt GUI之间的中介,它通常通过信号/插槽进行交互。 QThread执行此任务,将线程安全的Queue
对象与线程安全的qt信号发射相链接。
这实际上遵循了我给here的类似答案,但是这里有一个套接字而不是将数据放入队列的自定义线程。
您可能也对this SO帖子感兴趣,这解释了为什么我用来制作QThread
并连接信号等的一些代码行按照它们的顺序编写是!
P.S。当应用程序窗口关闭时,我添加了一行来关闭套接字服务器(套接字服务器用于在后台继续运行)
服务器窗口代码
import SocketServer
import concurrent.futures
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from Queue import Queue
HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
# create a global queue object that both the handle() method and the QThread (see later in the code) can access
queue = Queue()
class MyRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
print('...connected from:', self.client_address)
data = self.rfile.readline().strip()
print('Data from client %s' % data)
queue.put(data)
class ServerWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setGeometry(1500, 100, 500, 500)
self._control = QtGui.QWidget()
self.setCentralWidget(self._control)
l = QtGui.QVBoxLayout(self._control)
self.t = QtGui.QTextEdit()
l.addWidget(self.t)
self.executor = futures.ThreadPoolExecutor(max_workers=1)
self.startServerThread()
self.show()
@QtCore.pyqtSlot(str)
def receive_data(self, data):
self.t.moveCursor(QtGui.QTextCursor.End)
self.t.insertPlainText( data )
def startServerThread(self):
self.executor.submit(self.startServer)
# How to get information from the thread while it is still running?
def startServer(self):
print('starting server')
self.tcpServ = SocketServer.TCPServer(ADDR, MyRequestHandler)
print('waiting for connection...')
self.tcpServ.serve_forever()
# How to get information from the client (custom request handler)
# back to the GUI in a thread safe manner?
# This class runs in a QThread and listens on the end of a queue and emits a signal to the GUI
class MyReceiver(QtCore.QObject):
mysignal = QtCore.pyqtSignal(str)
def __init__(self,queue,*args,**kwargs):
QtCore.QObject.__init__(self,*args,**kwargs)
self.queue = queue
@QtCore.pyqtSlot()
def run(self):
while True:
text = self.queue.get()
self.mysignal.emit(text)
def launch():
app = QtGui.QApplication(sys.argv)
ex = ServerWindow()
# Create thread that will listen on the other end of the queue, and send the text to the textedit in our application
thread = QtCore.QThread()
my_receiver = MyReceiver(queue)
my_receiver.mysignal.connect(ex.receive_data)
my_receiver.moveToThread(thread)
thread.started.connect(my_receiver.run)
thread.start()
ret_code = app.exec_()
ex.tcpServ.shutdown()
sys.exit(ret_code)
if __name__ == '__main__':
launch()
答案 1 :(得分:0)
在服务器运行时,如何从自定义请求处理程序获取信息到服务器。
如何在线程运行时从线程获取信息到主应用程序?
这些是我对它应该如何运作的想法。
class MyRequestHandler(SocketServer.StreamRequestHandler):
@property
def application_window(self):
# you would override setup() for this, usually.
return self.server.application_window
def handle(self):
print(self.application_window)
# ...
def startServer(self):
print('starting server')
tcpServ = SocketServer.TCPServer(ADDR, MyRequestHandler)
tcpServ.application_window = self # !!!!!!!!!!!!!!!!!!!!!!!!! added
print('waiting for connection...', self)
tcpServ.serve_forever()
也许你需要调整一些东西。顺便提一下,有sharing information between server and client的技术。