扭曲的Qt怎么回来?

时间:2013-09-21 09:03:45

标签: pyqt pyqt4 twisted

我正在通过编写聊天客户端来练习PyQt + Twisted。这需要让两个事件循环发挥得很好。我想弄清楚如何在没有Twisted QTReactor的情况下做到这一点。我想要实现的模式是

  1. Qt正常运行,Twisted在另一个线程中运行(比如QThread)。
  2. Qt函数/方法使用callFromThread调用扭曲函数。
  3. 我们连接Twisted给我们的延迟,以便它们引起Qt信号的发射。通过这种方式,Twisted线程可以回调Qt线程。
  4. 我实际上已经让那部分工作了。解决方案的核心是这个功能

    def callFromMain(self, func, successSignal, *args):
        """Call an async I/O function with a Qt signal as it's callback
    
        func is the Twisted function you want to invoke. It probably returns a
        deferred. successSignal is the signal you want to emit once the asynchronous
        call finishes. This signal will be emitted with the result of func as an
        argument
        """
    
        def succeed(result):
            self.emit(successSignal, result)
    
        def wrapped():
            d = defer.maybeDeferred(func, *args)
            if successSignal is not None:
                d.addCallback(succeed)
    
        reactor.callFromThread(wrapped)
    

    问题在于,当我收到“未询问”数据时,我不知道该怎么做。例如,某人 else 可能会发送聊天消息。我需要能够在Twisted线程中检测到数据的接收,并以某种方式将该数据放入Qt线程中。这看起来很棘手,因为在这种情况下,我没有为程序的Twisted部分提供一个用作回调的信号。我怎么能这样做?

    下面请找到完整的示例代码来运行我几乎正常工作的聊天客户端和服务器。要使用这些程序,首先将服务器和客户端复制为.py文件,将ui规范复制为.ui文件。运行服务器,然后运行客户端。客户端窗口启动后,单击“连接”按钮。您应该在服务器上看到一条消息,指示新连接。然后尝试键入行编辑并单击“发送”按钮。您将看到数据进入服务器并返回,但它不会显示在客户端的QTextEdit框中。这是因为我无法弄清楚如何从ChatProtocol到程序的Qt部分获取数据。我们该怎么做?

    SERVER(asyncore)

    import asyncore
    import socket
    import constants as C
    
    HOST = 'localhost'
    PORT = 12344
    
    class ChatServer(asyncore.dispatcher):
        """Receive and forward chat messages
    
        When a new connection is made we spawn a dispatcher for that
        connection.
        """
        ADDRESS_FAMILY = socket.AF_INET
        SOCKET_TYPE = socket.SOCK_STREAM
        def __init__(self, host, port):
            self.map = {}
            self.address = (host,port)
            self.clients = []
            asyncore.dispatcher.__init__(self, map=self.map)
    
        def serve(self):
            """Bind to socket and start asynchronous loop"""
            self.create_socket(self.ADDRESS_FAMILY, self.SOCKET_TYPE)
            self.bind(self.address)
            print("ChatServer bound to %s %s"%self.address)
            self.listen(1)
            asyncore.loop(map=self.map)
    
        def writable(self):
            return False
    
        def readable(self):
            return True
    
        def newMessage(self, data, fromWho):
            """Put data in all clients' buffers"""
            print("new data: %s"%data)
            for client in self.clients:
                client.buffer = client.buffer + data
    
        def handle_accept(self):
            """Deal with newly accepted connection"""
            (connSock, clientAddress) = self.accept()
            print("New connection accepted from %s %s"%clientAddress)
            self.clients.append(ChatHandler(connSock, self.map, self))
    
    class ChatHandler(asyncore.dispatcher):
        def __init__(self, sock, map, server):
            self.server = server
            self.buffer = ''
            asyncore.dispatcher.__init__(self, sock, map)
    
        def writable(self):
            return len(self.buffer) > 0
    
        def readable(self):
            return True
    
        def handle_read(self):
            """Notify server of any new incoming data"""
            data = self.recv(4096)
            if data:
                self.server.newMessage(data, self)
    
        def handle_write(self):
            """send some amount of buffer"""
            sent = self.send(self.buffer)
            self.buffer = self.buffer[sent:]
    
    if __name__=='__main__':
        if HOST is None:
            HOST = raw_input('Host: ')
        if PORT is None:
            PORT = int(raw_input('Port: '))
        s = ChatServer(HOST, PORT)
        s.serve()
    

    客户(PyQt + Twisted)

    import sys
    
    import PyQt4.QtGui as QtGui
    import PyQt4.QtCore as QtCore
    import PyQt4.uic as uic
    
    import twisted.internet.reactor as reactor
    import twisted.internet.defer as defer
    import twisted.internet.protocol as protocol
    
    class MainWindow(QtGui.QMainWindow):
        def __init__(self):
            QtGui.QMainWindow.__init__(self)
            self.ui = uic.loadUi('ui.ui')
            self.networkThread = NetworkThread()
            self.networkThread.start()
    
            self.ui.sendButton.clicked.connect(self.sendMessage)
            self.ui.connectButton.clicked.connect(self.getNetworkConnection)
            #Make connections from network thread signals to our slots
            self.connect(self.networkThread,
                         self.networkThread.sigConnected,
                         self.onConnected)
            self.connect(self.networkThread,
                         self.networkThread.sigNewData,
                         self.onNewData)
            self.ui.show()
    
        def getNetworkConnection(self):
            factory = protocol.ClientCreator(reactor, ChatProtocol)
            self.networkThread.callFromMain(factory.connectTCP,
                                            self.networkThread.sigConnected,
                                            'localhost', 12344)
    
        def onConnected(self, p):
            print("Got a protocol!")
            self.cxn = p
    
        def onNewData(self, data):
            self.ui.outputBox.append('\r\n'+data)
    
        def sendMessage(self):
            message = str(self.ui.inputBox.text())
            self.networkThread.callFromMain(self.cxn.send, None, message)
    
    class NetworkThread(QtCore.QThread):
        """Run the twisted reactor in its own thread"""
        def __init__(self):
            QtCore.QThread.__init__(self)
            self.sigConnected = QtCore.SIGNAL("sigConnected")
            self.sigNewData = QtCore.SIGNAL("sigNewData")
    
        def run(self):
            reactor.run(installSignalHandlers=0)
    
        def callFromMain(self, func, successSignal, *args):
            """Call an async I/O function with a Qt signal as it's callback"""
    
            def succeed(result):
                self.emit(successSignal, result)
    
            def wrapped():
                d = defer.maybeDeferred(func, *args)
                if successSignal is not None:
                    d.addCallback(succeed)
    
            reactor.callFromThread(wrapped)
    
    class ChatProtocol(protocol.Protocol):
        def dataReceived(self, data):
            print("Got data: %s"%data)
            print("...but I don't know how to pass it to Qt :(")
    
        def send(self, data):
            self.transport.write(data)
    
    class ChatFactory(protocol.ClientFactory):
        protocol = ChatProtocol
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        ex = MainWindow()
        sys.exit(app.exec_())
    

    UI文件

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>600</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QVBoxLayout" name="verticalLayout_2">
        <item>
         <layout class="QVBoxLayout" name="verticalLayout">
          <item>
           <widget class="QTextEdit" name="outputBox"/>
          </item>
          <item>
           <widget class="QLineEdit" name="inputBox"/>
          </item>
          <item>
           <layout class="QHBoxLayout" name="horizontalLayout">
            <item>
             <widget class="QPushButton" name="sendButton">
              <property name="text">
               <string>send</string>
              </property>
             </widget>
            </item>
            <item>
             <spacer name="horizontalSpacer">
              <property name="orientation">
               <enum>Qt::Horizontal</enum>
              </property>
              <property name="sizeHint" stdset="0">
               <size>
                <width>40</width>
                <height>20</height>
               </size>
              </property>
             </spacer>
            </item>
            <item>
             <widget class="QPushButton" name="connectButton">
              <property name="text">
               <string>connect</string>
              </property>
             </widget>
            </item>
           </layout>
          </item>
         </layout>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>800</width>
         <height>21</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

0 个答案:

没有答案