2009-06-26 11 views
29

Je construis un client Qt pour le jeu de stratégie 4X open-source client/serveur Thousand Parsec. C'est un projet Google Summer of Code. Je suis cependant coincé dans une impasse. Fondamentalement, le client s'interface avec le serveur via une couche de protocole C++ qui facilite la communication client/serveur. La documentation du protocole est disponible here.Combinaison d'une boucle d'événement externe avec Qt's

Maintenant, mon problème est que le protocole vous oblige à créer une sous-classe de la classe virtuelle EventLoop (link) dans votre client. Il y a un exemple SimpleEventLoop utilisé pour les clients console sur le même lien. J'ai du mal à comprendre comment je peux concevoir ma propre sous-classe de boucle d'événements qui gère les événements du protocole tout en accrochant dans l'application Qt en même temps. Ma recherche m'a amené à croire que la classe Qt que je veux utiliser est QAbstractEventDispatcher mais la documentation semble assez mince et je ne suis pas vraiment sûr de la façon dont j'irais faire ça.

Est-ce que quelqu'un d'autre a de l'expérience en reliant des boucles d'événements externes à une application Qt? J'ai aussi trouvé ce example sur la page Qt mais ce n'était pas très utile - ou du moins je ne l'ai pas vraiment compris.

Merci!

Répondre

30

Je n'ai pas fait trop de développement Qt récemment, mais si je me souviens bien, vous pouvez appeler QApplication::processEvents() au sein de votre propre boucle d'événements (au lieu de commencer la boucle principale Qt par QApplication::exec())

Edit: I J'ai utilisé l'opportunité d'un dimanche matin pour tester/apprendre quelque chose sur PyQt (bindings Python pour Qt) et bricoler ensemble un code de preuve de concept ci-dessous. Remplacement de l'appel à QApplication::exec() avec une boucle d'événement personnalisé basé sur QApplication::processEvents()semble pour fonctionner. J'ai également regardé rapidement simpleeventloop.cpp et tpclient-cpptext main.cpp. D'après les apparences, il serait bon d'ajouter QApplication::processEvents() quelque part dans la boucle principale de SimpleEventLoop::runEventLoop(). Pour ajouter à la boucle principale, je remplacerais probablement l'intervalle tv pour la fonction select() en lines 106 through 117 avec

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

et changer la signature line 89 à void SimpleEventLoop::runEventLoop(QApplication *app). Il devrait être bien d'ajouter vos trucs Qt habituels à votre implémentation du client (votre remplacement de tpclient-cpptext main.cpp)

Cela ressemble à un hack, cependant. Je commencerais probablement avec quelque chose comme ça pour commencer. Je pense que votre idée d'emballage TPSocket et le minuteur dans les concepts respectifs de Qt afin de les transmettre avec le QAbstractEventDispatcher au QEventLoop est la meilleure solution à long terme. Il devrait alors être suffisant que votre runEventLoop() appelle simplement QApplication::exec(). Mais je n'ai jamais utilisé QAbstractEventDispatcher auparavant, alors prenez mes commentaires pour ce qu'ils sont.

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

Alors que mon objectif est d'utiliser le AbstractEventDispatcher pour intégrer correctement le eventloop, je suis trop débutant à Qt pour le décrocher rapidement. J'essaie de faire cela sur le côté tout en obtenant une solution de travail en place. J'essaie de pirater la classe SimpleEventLoop afin qu'elle puisse retourner son statut "en cours d'exécution" qui peut ensuite être vérifié depuis l'application principale avec une instruction while similaire à votre exemple. Je vous ferai savoir comment cela fonctionne. La chose qui m'inquiète est de savoir si et comment les rappels du signal Boost seront traités correctement depuis l'application Qt. – mhilmi

+0

@Gimpyfuzznut: ajout d'un exemple de hack pour simpleeventloop.cpp. Votre hack suggéré est probablement plus compliqué, car l'implémentation actuelle de SimpleEventLoop :: runEventLoop() implémente une boucle d'événement standard (voir par exemple http://stackoverflow.com/questions/658403/how-would-you-implement-a- basic-event-loop/658495 # 658495 pour une explication), et ne retourne donc jamais, sauf si un signal ou un temporisateur appelle SimpleEventLoop :: endEventLoop() dans le cadre de la boucle d'événement en cours d'exécution. – stephan

2

Je coderais probablement les boucles d'événements pour être des threads séparés. Vous pouvez gérer les événements de la bibliothèque dans une classe et lui faire générer des signaux qui seront ensuite traités par le Qt eventloop principal quand vous le souhaitez (appelez QApplication :: processEvents() si nécessaire dans les opérations longues). La seule astuce consiste à s'assurer que votre boucle d'événement externe est un Q_OBJECT afin qu'il sache comment émettre les signaux qui vous intéressent.

Il y a d'autres problèmes de fil, comme jamais (jamais) la peinture dans un fil qui n'est pas le principal fil de l'intervalle QT.

+8

"maintenant vous avez deux problèmes" :-) – Kos

1

La documentation Qt dit:

Pour rendre votre application effectuer un traitement au ralenti (à savoir l'exécution d'une fonction spéciale à chaque fois qu'il n'y a pas d'événements en attente), utilisez un QTimer avec 0 délai d'attente.

Pas une solution assez bien.