<p>我在这里的聚会真的迟到了,但我可以为将来的读者提供一些有用的建议。在</p>
<p>这很难实现的原因是您试图让两个事件循环协同工作。你有扭曲的反应器和wxWidgets循环。有两种方法可以网格化循环</p>
<ol>
<li>在Twisted中使用一个特殊情况下的reactor,它被设计成将Twisted和wx事件合并成一个循环。Twisted设计时就考虑到了这一点,因此,为达到这个目的,酿造一个定制的反应器并不难。在</li>
<li>在不同的线程中运行Twisted reactor和wx事件循环。在这种情况下,您依赖于操作系统将执行时间委托给每个事件循环。在</li>
</ol>
<p>实际上,我今天刚刚完成了Twisted和PyQt这两种策略的工作。Qt和wxWidgets并没有什么不同,所以我认为您可以用最少的努力来调整我的解决方案。注意,这里我没有使用透视代理。一旦您理解了我是如何做到这一点的,添加透视代理层将非常容易。在</p>
<p>首先,我用方法1描述了我的解决方案,它依赖于PYQT4反应器。这是完整的工作代码(您需要pyqt4reactor,它可以在interwebz上的各种非正式地方找到)</p>
<p><strong>使用特殊反应器的聊天客户端</strong></p>
<pre><code>import sys
import PyQt4.QtGui as QtGui
import PyQt4.QtCore as QtCore
import PyQt4.uic as uic
import twisted.internet.defer as defer
import twisted.internet.protocol as protocol
import qt4reactor
import constants as C
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = uic.loadUi('ui.ui')
self.ui.sendButton.clicked.connect(self.sendMessage)
self.ui.inputBox.returnPressed.connect(self.sendMessage)
self.ui.connectButton.clicked.connect(self.getNetworkConnection)
self.ui.show()
def getNetworkConnection(self):
#This line should probably not be inside getNetworkConnection
factory = protocol.ClientCreator(reactor, ChatProtocol)
d = factory.connectTCP(C.HOST, C.PORT)
def onConnected(p):
self.cxn = p
p.emitter.signal.connect(self.onNewData)
self.ui.connectButton.setEnabled(False)
d.addCallback(onConnected)
def onNewData(self, data):
self.ui.outputBox.append(data)
def sendMessage(self):
message = str(self.ui.inputBox.text())
self.ui.inputBox.clear()
self.cxn.send(message)
class Emitter(QtCore.QObject):
signal = QtCore.pyqtSignal(str)
def __init__(self):
QtCore.QObject.__init__(self)
class ChatProtocol(protocol.Protocol):
def __init__(self):
self.emitter = Emitter()
def dataReceived(self, data):
self.emitter.signal.emit(data)
def send(self, data):
self.transport.write(data)
class ChatFactory(protocol.ClientFactory):
protocol = ChatProtocol
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
qt4reactor.install()
from twisted.internet import reactor
mainWindow = MainWindow()
reactor.run()
</code></pre>
<p>让我们检查一下<code>ChatProtocol</code>及其助手类<code>Emitter</code>:</p>
^{pr2}$
<p>协议本身非常简单。当您调用<code>.send</code>时,它通过传输写入数据。在</p>
<p>数据接收稍微复杂一些。为了让Twisted代码通知Qt事件循环传入的聊天,我们赋予协议一个发射器,它是一个可以发出单个信号的qo对象。在主Qt窗口中,我们将这个信号连接起来,以便它将数据发送到聊天窗口。这种连接发生在我们建立连接时。让我们来看看:</p>
<pre><code>class MainWindow(QtGui.QMainWindow):
<snip>
def getNetworkConnection(self):
#This line should probably not be inside getNetworkConnection
factory = protocol.ClientCreator(reactor, ChatProtocol)
d = factory.connectTCP(C.HOST, C.PORT)
def onConnected(p):
self.cxn = p
p.emitter.signal.connect(self.onNewData)
self.ui.connectButton.setEnabled(False)
d.addCallback(onConnected)
</code></pre>
<p>我们告诉客户机工厂进行TCP连接。这给出了一个deferred,它将以产生的协议作为参数来调用。我们的回调函数<code>onConnected</code>负责将该协议的发射器信号连接到<code>onNewData</code>。这意味着每当协议的发射器发射时,即调用<code>dataReceived</code>时,数据将传播到Qt信号/时隙系统并显示在outputBox中。其余的函数应该或多或少有意义。在</p>
<p>还和我在一起?如果你是,我现在将演示如何使用线程来实现这一点。这是完整的工作代码</p>
<p><strong>使用线程聊天客户端</strong></p>
<pre><code>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
import constants as C
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = uic.loadUi('ui.ui')
self.ui.sendButton.clicked.connect(self.sendMessage)
self.ui.inputBox.returnPressed.connect(self.sendMessage)
self.ui.connectButton.clicked.connect(self.getNetworkConnection)
self.ui.show()
self.networkThread = NetworkThread()
self.networkThread.start()
self.connect(self.networkThread,
self.networkThread.sigConnected,
self.onConnected)
def getNetworkConnection(self):
#This line should probably not be inside getNetworkConnection
factory = protocol.ClientCreator(reactor, ChatProtocol)
self.networkThread.callFromMain(factory.connectTCP,
self.networkThread.sigConnected,
C.HOST, C.PORT)
def onConnected(self, p):
self.cxn = p
p.emitter.signal.connect(self.onNewData)
self.ui.connectButton.setEnabled(False)
def onNewData(self, data):
self.ui.outputBox.append(data)
def sendMessage(self):
message = str(self.ui.inputBox.text())
self.networkThread.callFromMain(self.cxn.send, None, message)
self.ui.inputBox.clear()
class NetworkThread(QtCore.QThread):
"""Run the twisted reactor in its own thread"""
def __init__(self):
QtCore.QThread.__init__(self)
self.sigConnected = QtCore.SIGNAL("sigConnected")
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 Emitter(QtCore.QObject):
#Not sure why I specified a name here...
signal = QtCore.pyqtSignal(str, name='newData')
class ChatProtocol(protocol.Protocol):
def __init__(self):
self.emitter = Emitter()
def dataReceived(self, data):
self.emitter.signal.emit(data)
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_())
</code></pre>
<p>除了在QThread中运行reactor之外,这里有趣的区别在于我们在代码的扭曲部分中连接回调的方式。我们特别使用了一个辅助函数<code>callFromMain</code></p>
<pre><code> 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)
</code></pre>
<p>我们提供了一个我们希望在Twisted线程中调用的函数、当函数的结果可用时我们希望发出的Qt信号,以及函数的额外参数。reactor调用我们的函数,并将回调附加到结果deferred,它将发出我们提供的信号。在</p>
<p>我希望这对某人有帮助:)</p>
<p>如果有人看到简化,请告诉我。在</p>