0

私はwxPythonとtwistedのパースペクティブブローカーを学んでいます。私はそれらを一緒に使用してチャットクライアントを作成するように割り当てられています(私はすでにサーバーとコンソールベースのクライアントを作成しました)。

これが私を困惑させていることです。PBには、コールバックなどを備えた独自の「フロー」があり、wxpythonのイベント駆動型フローと直感的に一致しません。2つを連携させるには、どのようなプログラム構造を使用する必要がありますか?

プログラムのツイストpbクライアント部分を使用して、サーバーからローカルメソッドに情報を取得して保存し、wxpython guiが特定のイベントに応答して呼び出すことができ、最初にオンラインユーザーのリストを設定しようとしました。およびグループ。シーケンスで問題が発生していると思います。両方が同時に開始されるため、wxコードが必要な変数を呼び出す前に必要な変数を保存していません。フレーム作成などに時間遅延を挿入すると役立つかもしれませんが、解決策があるとしても、それは不器用な解決策のように感じます。

別のアプローチは、サーバー参照をwxPythonフレーム(およびサブパネル/ノートブック)に直接渡すことです。コールバックには別のクラスが必要であり、wxには同じクラスの情報が必要であるため、ここで問題が発生しています...そしておそらくそれらを同じ型に強制する方法がありますが、これも非常に不器用です(さらに私はまだそれを機能させることができていません。

この問題に対処するリソースはありますか?標準的なアプローチ?

これらが私のアプローチの問題を明らかにするかもしれない場合に備えて...

これが私のサーバーコードです:http://pastebin.com/84fmhsRV GUIクライアントコード:http://pastebin.com/UimXe4RY

ご協力ありがとうございました。

4

2 に答える 2

0

おそらく、Twisted と wxPython に関するこれらのページの両方を見てみたいと思うでしょう:

トピックに関するレシピも見つけました。wiki リンクには、簡単なチャット プログラムが既に作成されています。

于 2012-07-05T14:28:20.020 に答える
0

ここでのパーティーには本当に遅れましたが、将来の読者に役立つアドバイスを提供できます。

これが難しい理由は、2 つのイベント ループを連携させようとしているからです。Twisted リアクターと wxWidgets ループがあります。ループをメッシュするには 2 つの方法があります

  1. Twisted イベントと wx イベントを 1 つのループに結合するように設計された Twisted の特別なケースのリアクターを使用します。Twisted はこれを念頭に置いて設計されているため、この目的のためにカスタム リアクターを作成することはそれほど難しくありません。
  2. Twisted リアクターと wx イベント ループを別のスレッドで実行します。この場合、実行時間を各イベント ループに委任するためにオペレーティング システムに依存しています。

実は今日、これらの戦略の両方が Twisted と PyQt で機能するようになりました。Qt と wxWidgets はそれほど違いはないのでおそらく最小限の労力で私のソリューションを適応させることができると思います。ここでは Perspective Broker を使用していないことに注意してください。これを機能させる方法を理解すれば、Perspective Broker レイヤーを追加するのは非常に簡単になります。

最初に、pyqt4reactor に依存する方法 #1 を使用して私のソリューションを説明します。完全な作業コードは次のとおりです (interwebz のさまざまな非公式の場所にある pyqt4reactor が必要です)。

特別な原子炉を持つチャット クライアント

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

ChatProtocolとそのヘルパー クラスを調べてみましょうEmitter

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 Emitter(QtCore.QObject):

    signal = QtCore.pyqtSignal(str)

プロトコル自体は実にシンプルです。呼び出す.sendと、そのトランスポートにデータが書き込まれます。

データ受信はもう少し複雑です。Twisted コードが Qt イベント ループに着信チャットを通知するようにするために、単一のシグナルを発することができる QObject である Emitter を Protocol に付与します。Qt のメイン ウィンドウで、チャット ウィンドウにデータを投稿するようにこのシグナルを接続します。このフックアップは、接続を確立するときに発生します。調べてみましょう:

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)

クライアント ファクトリに TCP 接続を行うように指示します。これにより、結果のプロトコルを引数として呼び出される deferred が得られます。コールバック関数onConnectedには、そのプロトコルのエミッターのシグナルを に接続する役割がありますonNewData。これは、プロトコルのエミッターが呼び出されるたびに発生するたびdataReceivedに、データが Qt シグナル/スロット システムに伝達され、outputBox に表示されることを意味します。残りの関数は多かれ少なかれ意味を成すはずです。

まだ私と一緒に?もしそうなら、スレッドでこれを行う方法を示します。これが完全な作業コードです

スレッドを持つチャット クライアント

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_())

QThread で実行されるリアクター以外の興味深い違いは、コードの Twisted 部分でコールバックを接続する方法にあります。特に、ヘルパー関数を使用しますcallFromMain

    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)

Twisted スレッドで呼び出したい関数、関数の結果が利用可能になったときに発行したい Qt シグナル、および関数の追加の引数を提供します。リアクターは私たちの関数を呼び出し、提供されたシグナルを発する結果の deferred にコールバックをアタッチします。

これが誰かに役立つことを願っています:)

誰かが単純化されているのを見たら、私に知らせてください。

于 2013-09-23T06:50:27.090 に答える