Peignage un Événement Externe Boucle avec Qt
je construis un client Qt pour le jeu de stratégie open-source client / server 4X Mille Parsecs. Ce un Google Summer of Code du projet. Je suis toutefois retrouver dans une impasse. Fondamentalement, le client se connecte avec le serveur via une couche de protocole C++ qui facilite la communication client/serveur. La documentation du protocole est disponible ici.
maintenant mon problème est que le protocole vous demande de créer une sous-classe de la classe EventLoop virtuelle (lien) 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 se connectant à L'application Qt en même temps. Mes recherches m'a conduit à croire QAbstractEventDispatcher est la classe Qt je veux utiliser, mais la documentation semble assez mince et je ne sais pas exactement comment je pourrais faire.
quelqu'un d'autre a-t-il de l'expérience dans la liaison de boucles d'événements externes avec une application Qt? J'ai aussi trouvé ce exemple sur la page Qt mais ça n'a pas été très utile - ou du moins je ne l'ai pas vraiment compris.
Merci!
3 réponses
je n'ai pas fait trop de développement Qt récemment, mais si je me souviens correctement, vous pouvez appeler QApplication::processEvents()
dans votre propre boucle d'événements (au lieu de lancer la boucle principale Qt par QApplication::exec()
)
Edit: j'ai profité d'un dimanche matin lent pour tester / apprendre quelque chose sur PyQt (Python bindings pour Qt) et a assemblé un code de validation de principe ci-dessous. De remplacer l'appel à QApplication::exec()
avec une boucle d'événement personnalisée basée sur QApplication::processEvents()
semble au travail.
j'ai également regardé simpleeventloop.cpp
et tpclient-cpptext main.cpp
. Apparemment, il devrait être bien tout simplement à ajouter QApplication::processEvents()
quelque part dans la boucle principale de SimpleEventLoop::runEventLoop()
. Pour l'ajouter à la boucle principale, je serais probablement remplacer le tv
intervalle pour le select()
function lignes 106
117
tv.tv_sec = 0;
tv.tv_usec = 10000; // run processEvents() every 0.01 seconds
app->processEvents();
et changer la signature en ligne 89
void SimpleEventLoop::runEventLoop(QApplication *app)
. Il ne devrait pas être plus mal d'ajouter vos trucs Qt habituels à votre implémentation du client (votre remplacement de tpclient-cpptext main.cpp
)
Ressemble à un hack. Je commencerais probablement par quelque chose comme ça pour commencer. Je pense que votre idée de l'emballage TPSocket
et le minuteur dans les concepts respectifs de Qt afin de les transmettre avec le QAbstractEventDispatcher
QEventLoop
est le mieux solution à long terme. Il devrait alors être suffisant que votre runEventLoop()
simplement des appels QApplication::exec()
. Mais je n'ai jamais utilisé QAbstractEventDispatcher
avant, donc 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)
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 manipulés par L'eventloop Qt principal quand vous le voulez (appelez QApplication::processEvents() si nécessaire dans les opérations longues). Le seul truc à faire est de s'assurer que votre boucle d'événement externe est un Q_OBJECT pour qu'il sache émettre les signaux auxquels vous tenez.
il y a d'autres problèmes de fil, tels que jamais (jamais) peindre dans un fil qui n'est pas le fil QT principal.
La documentation de Qt dit:
pour que votre application effectue un traitement inactif (c'est-à-dire 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 timeout.
Pas une jolie solution.