2009-06-26 9 views
29

kämmen Ich baue einen Qt-Client für das Open-Source-Client/Server-4X-Strategiespiel Thousand Parsec. Dies ist ein Google Summer of Code-Projekt. Ich stecke jedoch in einer Sackgasse. Im Prinzip ist der Client über eine C++ - Protokollschicht mit dem Server verbunden, die die Client/Server-Kommunikation erleichtert. Die Dokumentation des Protokolls ist verfügbar here.Eine externe Ereignisschleife mit Qt

Jetzt ist mein Problem, dass das Protokoll erfordert, dass Sie eine Unterklasse der virtuellen EventLoop-Klasse (link) in Ihrem Client erstellen. Es gibt ein Beispiel für SimpleEventLoop, das für Konsolenclients auf demselben Link verwendet wird. Ich habe Schwierigkeiten herauszufinden, wie ich meine eigene Event-Loop-Unterklasse entwerfen kann, die die Ereignisse des Protokolls behandelt, während gleichzeitig in die Qt-Anwendung eingehakt wird. Meine Forschung hat mich dazu gebracht zu glauben, dass die Qt-Klasse, die ich verwenden möchte, QAbstractEventDispatcher ist, aber die Dokumentation scheint ziemlich schlank zu sein und ich bin mir nicht ganz sicher, wie ich das machen würde.

Hat noch jemand Erfahrung, externe Ereignisschleifen mit einer Qt-Anwendung zu verknüpfen? Ich habe auch diese example auf der Qt-Seite gefunden, aber es war nicht sehr hilfreich - oder zumindest habe ich es nicht wirklich verstanden.

Danke!

Antwort

30

Ich habe nicht zu viel Qt-Entwicklung vor kurzem getan, aber wenn ich mich richtig erinnere, können Sie QApplication::processEvents() innerhalb Ihrer eigenen Ereignisschleife aufrufen (statt der Qt-Hauptschleife durch QApplication::exec() des Startens)

Edit: I Ich nutze die Gelegenheit eines langsamen Sonntagmorgens, um etwas über PyQt (Python-Bindungen für Qt) zu testen/zu lernen, und habe unten einen Proof-of-Concept-Code zusammengeschustert. Das Ersetzen des Anrufs zu QApplication::exec() durch eine benutzerdefinierte Ereignisschleife basierend auf QApplication::processEvents()scheint zu funktionieren.

Ich habe auch schnell simpleeventloop.cpp und tpclient-cpptext main.cpp betrachtet. Von den Anblicken her sollte es in Ordnung sein, einfach QApplication::processEvents() irgendwo in der Hauptschleife von SimpleEventLoop::runEventLoop() hinzuzufügen. So fügen Sie sie in die Hauptschleife, würde ich wahrscheinlich das tv Intervall für die select() Funktion in lines 106 through 117 mit

tv.tv_sec = 0; 
tv.tv_usec = 10000; // run processEvents() every 0.01 seconds 
app->processEvents(); 

ersetzen und die Signatur in line 89 zu void SimpleEventLoop::runEventLoop(QApplication *app) ändern. Es sollte in Ordnung sein, Ihre üblichen Qt-Sachen zu Ihrer Implementierung des Klienten hinzuzufügen (Ihr Wiedereinbau von tpclient-cpptext main.cpp)

Sieht wie ein Hack, obwohl. Ich würde wahrscheinlich mit so etwas anfangen, um anzufangen. Ich denke, dass Ihre Idee, TPSocket und den Timer in Qt's jeweilige Konzepte zu wickeln, um sie mit der QAbstractEventDispatcher an die QEventLoop weiterzuleiten, die bessere langfristige Lösung ist. Es sollte dann ausreichen, dass Ihr runEventLoop() einfach QApplication::exec() anruft. Aber ich habe nie zuvor QAbstractEventDispatcher verwendet, also nehmen Sie meine Kommentare für das, was sie sind.

import sys 
import time 

from PyQt4 import QtGui 
from PyQt4 import QtCore 

# Global variable used as a quick and dirty way to notify my 
# main event loop that the MainWindow has been exited 
APP_RUNNING = False 

class SampleMainWindow(QtGui.QMainWindow): 
    def __init__(self, parent=None): 
     QtGui.QMainWindow.__init__(self) 
     global APP_RUNNING 
     APP_RUNNING = True 

     # main window 
     self.setGeometry(300, 300, 250, 150) 
     self.setWindowTitle('Test') 
     self.statusBar().showMessage('Ready') 

     # exit action (assumes that the exit icon from 
     # http://upload.wikimedia.org/wikipedia/commons/b/bc/Exit.png 
     # is saved as Exit.png in the same folder as this file) 
     exitAction = QtGui.QAction(QtGui.QIcon('Exit.png') 
            ,'Exit' 
            ,self) 
     exitAction.setShortcut('Ctrl+Q') 
     exitAction.setStatusTip('Exit application') 
     self.connect(exitAction 
        ,QtCore.SIGNAL('triggered()') 
        ,QtCore.SLOT('close()')) 

     # main menu 
     menubar = self.menuBar() 
     fileMenu = menubar.addMenu('&File') 
     fileMenu.addAction(exitAction) 

     # toolbar 
     self.toolbar = self.addToolBar('Exit') 
     self.toolbar.addAction(exitAction) 

     # text editor 
     textEdit = QtGui.QTextEdit() 
     self.setCentralWidget(textEdit) 

     #tool tip 
     textEdit.setToolTip('Enter some text') 
     QtGui.QToolTip.setFont(QtGui.QFont('English', 12)) 

    def closeEvent(self, event): 
     reply = QtGui.QMessageBox.question(self 
              ,'Message' 
              ,"Are you sure?" 
              ,QtGui.QMessageBox.Yes 
              ,QtGui.QMessageBox.No) 

     if reply == QtGui.QMessageBox.Yes: 
      event.accept() 
      global APP_RUNNING 
      APP_RUNNING = False 
     else: 
      event.ignore() 

# main program 
app = QtGui.QApplication(sys.argv) 
testWindow = SampleMainWindow() 
testWindow.show() 
# run custom event loop instead of app.exec_() 
while APP_RUNNING: 
    app.processEvents() 
    # sleep to prevent that my "great" event loop eats 100% cpu 
    time.sleep(0.01) 
+0

Während mein Ziel ist, den AbstractEventDispatcher zu verwenden, um die Eventloop richtig zu integrieren, bin ich einfach zu viel Anfänger bei Qt, um es schnell durchzuziehen. Ich versuche dies auf der Seite zu tun, während ich nur eine funktionierende Lösung einrichte. Ich versuche, SimpleEventLoop-Klasse zu hacken, so dass es seinen "laufenden" Status zurückgeben kann, der dann von der Haupt-App mit einer while-Anweisung wie in Ihrem Beispiel überprüft werden kann. Ich werde dich wissen lassen, wie es funktioniert. Die Sache, um die ich mir Sorgen mache, ist, ob und wie die Boost-Signalrückrufe von der Qt-Anwendung aus richtig gehandhabt werden. – mhilmi

+0

@Gimpyfuzznut: ein Beispiel Hack für simpleeventloop.cpp hinzugefügt. Ihr vorgeschlagener Hack ist wahrscheinlich komplizierter, da die aktuelle Implementierung von SimpleEventLoop :: runEventLoop() eine Standard-Event-Schleife implementiert (siehe zB http://stackoverflow.com/questions/658403/how-would-you-implement-a- basic-event-loop/658495 # 658495 für eine Erklärung) und kehrt daher nie zurück, wenn nicht ein Signal oder ein Timer SimpleEventLoop :: endEventLoop() als Teil der laufenden Ereignisschleife aufruft. – stephan

2

Ich würde wahrscheinlich die Ereignis-Schleifen als separate Threads codieren. Sie können die Ereignisse aus der Bibliothek in einer Klasse behandeln und Signale generieren, die dann von der Haupt-Qt-Eventloop wann immer Sie möchten verarbeitet werden (rufen Sie QApplication :: processEvents() auf, falls in langen Operationen erforderlich). Der einzige Trick dabei ist, sicherzustellen, dass Ihre externe Ereignisschleife ein Q_OBJECT ist, damit sie weiß, wie sie die Signale aussendet, die Ihnen wichtig sind.

Es gibt andere Thread-Probleme, wie nie (überhaupt) in einem Thread zu malen, der nicht der Haupt-QT-Thread ist.

+8

"Jetzt haben Sie zwei Probleme" :-) – Kos

1

Die Qt-Dokumentation sagt:

Um Ihre Anwendung ausführen Leerlauf Verarbeitung zu machen (das heißt eine spezielle Funktion ausführt, wenn es keine ausstehenden Ereignisse sind), eine QTimer mit 0 Timeout verwenden.

Keine schöne Lösung.

Verwandte Themen