如何让两个程序相互通信

时间:2014-04-21 00:13:52

标签: python multithreading networking pyqt

我在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()

2 个答案:

答案 0 :(得分:2)

因此,我所知道的将数据从线程传输到应用程序的主GUI线程的几种方法之一就是将数据放在python Queue中。这个QueueQThread(支持Qt信号和插槽的Qt线程实现)读取。 QThreadqueue.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的技术。