2009-06-26 7 views
29

Estoy construyendo un cliente de Qt para el juego de estrategia de cliente/servidor de código abierto 4X Thousand Parsec. Este es un proyecto Google Summer of Code. Sin embargo estoy atrapado en un callejón sin salida. Básicamente, el cliente interactúa con el servidor a través de una capa de protocolo C++ que facilita la comunicación cliente/servidor. La documentación del protocolo está disponible here.Peinar un bucle de evento externo con Qt's

Ahora mi problema es que el protocolo requiere que cree una subclase de la clase virtual de EventLoop (link) en su cliente. Hay un ejemplo SimpleEventLoop utilizado para clientes de consola en el mismo enlace. Estoy teniendo dificultades para descubrir cómo puedo diseñar mi propia subclase de bucle de eventos que maneja los eventos del protocolo mientras se engancha en la aplicación Qt al mismo tiempo. Mi investigación me ha llevado a creer que QAbstractEventDispatcher es la clase Qt que quiero usar, pero la documentación parece bastante delgada y no estoy exactamente seguro de cómo voy a hacer esto.

¿Alguien más tiene experiencia enlazando bucles de eventos externos con una aplicación Qt? También encontré este example en la página de Qt pero no fue muy útil, o al menos no lo entendí.

Gracias!

Respuesta

30

no he hecho demasiado desarrollo Qt hace poco, pero si no recuerdo mal, puede llamar QApplication::processEvents() dentro de su propio bucle de eventos (en lugar de empezar el bucle principal de Qt a través QApplication::exec())

Editar: Yo aproveché la oportunidad de una lenta mañana de domingo para probar/aprender algo sobre PyQt (enlaces de Python para Qt) y compilé un código de prueba de concepto a continuación. Reemplazar la llamada a QApplication::exec() con un bucle de evento personalizado basado en QApplication::processEvents()parece funcionar.

También he buscado rápidamente simpleeventloop.cpp y tpclient-cpptext main.cpp. Por lo que parece, debería estar bien simplemente agregar QApplication::processEvents() en algún lugar del lazo principal de SimpleEventLoop::runEventLoop(). Para añadirlo al bucle principal, probablemente reemplace el intervalo tv para la función select() en lines 106 through 117 con

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

y cambiar la firma en line 89 a void SimpleEventLoop::runEventLoop(QApplication *app). Debería estar bien agregar las cosas de Qt habituales a su implementación del cliente (su reemplazo de tpclient-cpptext main.cpp)

Parece un truco, sin embargo. Probablemente comenzaría con algo como esto para empezar. Creo que su idea de ajustar TPSocket y el temporizador dentro de los conceptos de Qt para enviarlos con el QAbstractEventDispatcher al QEventLoop es la mejor solución a largo plazo. Entonces debería ser suficiente que su runEventLoop() simplemente llame al QApplication::exec(). Pero nunca antes había usado QAbstractEventDispatcher, así que tome mis comentarios por lo que son.

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

Si bien mi objetivo es utilizar AbstractEventDispatcher para integrar correctamente eventloop, soy demasiado principiante en Qt para llevarlo a cabo rápidamente. Estoy tratando de hacer esto a un lado mientras obtengo una solución funcional. Estoy intentando hackear la clase SimpleEventLoop para que pueda devolver su estado "en ejecución" que luego se puede comprobar desde la aplicación principal con una instrucción while similar a su ejemplo. Te dejaré saber cómo funciona. Lo que más me preocupa es si las llamadas de señal de Boost se manejarán correctamente desde la aplicación Qt y de qué forma. – mhilmi

+0

@Gimpyfuzznut: agregué un truco de muestra para simpleeventloop.cpp. Es probable que el truco sugerido sea más complicado, porque la implementación actual de SimpleEventLoop :: runEventLoop() implementa un bucle de evento estándar (consulte, por ejemplo, http://stackoverflow.com/questions/658403/how-would-you-implement-a- basic-event-loop/658495 # 658495 para obtener una explicación) y, por lo tanto, nunca regresa a menos que una señal o un temporizador invoquen SimpleEventLoop :: endEventLoop() como parte del bucle de evento en ejecución. – stephan

2

Probablemente codifique los bucles de eventos como hilos separados. Puede manejar los eventos de la biblioteca en una clase y hacer que genere señales que luego serán manejadas por Qt eventloop siempre que lo desee (llame a QApplication :: processEvents() si es necesario en operaciones largas). El único truco para esto es asegurarse de que su bucle de evento externo sea un Q_OBJECT para que sepa cómo emitir las señales que le interesan.

Hay otros problemas de hilos, como nunca (nunca) pintar en un hilo que no es el hilo principal de QT.

+8

"ahora tienes dos problemas" :-) – Kos

1

La documentación de Qt dice:

Para que su aplicación realiza el procesamiento de inactividad (es decir, la ejecución de una función especial cuando no hay eventos pendientes), use un QTimer con 0 tiempo de espera.

No es una solución bonita.

Cuestiones relacionadas