用Qt组合外部事件循环

时间:2009-06-26 21:21:29

标签: c++ qt events

我正在为开源客户端/服务器4X策略游戏Thousand Parsec构建一个Qt客户端。这是Google Summer of Code项目。然而,我陷入了死胡同。基本上,客户端通过C ++协议层与服务器连接,以促进客户端/服务器通信。该协议的文档可用here

现在我的问题是该协议要求您在客户端中创建虚拟EventLoop类(link)的子类。在同一链接上有一个用于控制台客户端的SimpleEventLoop示例。我很难弄清楚如何设计我自己的事件循环子类来处理协议的事件,同时挂钩到Qt应用程序。我的研究让我相信QAbstractEventDispatcher是我想要使用的Qt类,但文档似乎相当渺茫,我不确定如何去做这个。

是否有其他人有将外部事件循环与Qt应用程序相关联的经验?我也在Qt页面上找到了这个example,但它没有太大帮助 - 或者至少我没有真正理解它。

谢谢!

3 个答案:

答案 0 :(得分:33)

我最近没有做过太多的Qt开发,但是如果我没记错的话,你可以在你自己的事件循环中调用QApplication::processEvents()(而不是通过QApplication::exec()启动Qt主循环)

编辑:我利用周日早晨的机会来试驾/学习PyQt(Qt的Python绑定)并拼凑出一个概念验证代码如下。使用基于QApplication::exec() 的自定义事件循环替换对QApplication::processEvents()的调用似乎可以正常工作。

我也很快查看了simpleeventloop.cpptpclient-cpptext main.cpp。从它的外观来看,只需在QApplication::processEvents()的主循环中的某处添加SimpleEventLoop::runEventLoop()即可。要将其添加到主循环中,我可能会用lines 106 through 117替换tv函数的select()间隔

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

并将line 89中的签名更改为void SimpleEventLoop::runEventLoop(QApplication *app)。将常用的Qt内容添加到客户端的实现(替换tpclient-cpptext main.cpp

应该没那么好

看起来像是黑客。我可能会从这样的事情开始。我认为您将TPSocket和计时器包装在Qt各自的概念中以便使用QAbstractEventDispatcher转发到QEventLoop的想法是更好的长期解决方案。您的runEventLoop()只需拨打QApplication::exec()即可。但我以前从未使用QAbstractEventDispatcher,所以请注意我们的意见。

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)

答案 1 :(得分:2)

我可能会将事件循环编码为单独的线程。您可以在类中处理库中的事件,并让它生成信号,然后在需要时由主Qt事件循环处理(如果需要在长操作中调用QApplication :: processEvents())。唯一的技巧是确保您的外部事件循环是Q_OBJECT,以便它知道如何发出您关心的信号。

还有其他线程问题,例如在一个不是主要QT线程的线程中永远(永远)绘制。

答案 2 :(得分:1)

Qt文档说:

  

要使应用程序执行空闲处理(即,在没有挂起事件时执行特殊功能),请使用QTimer,其中包含0超时。

虽然不是一个漂亮的解决方案。