【问题标题】:Combing an External Event Loop with Qt's将外部事件循环与 Qt 相结合
【发布时间】:2009-06-26 21:21:28
【问题描述】:

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

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

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

谢谢!

【问题讨论】:

    标签: c++ qt events


    【解决方案1】:

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

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

    我还快速查看了simpleeventloop.cpptpclient-cpptext main.cpp。从外观上看,在SimpleEventLoop::runEventLoop() 的主循环中的某处添加QApplication::processEvents() 应该没问题。要将其添加到主循环中,我可能会将lines 106 through 117select() 函数的tv 间隔替换为

    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

    不过,看起来像个 hack。我可能会从这样的事情开始。我认为您将TPSocket 和计时器包装在Qt 各自概念中以便将它们与QAbstractEventDispatcher 一起转发到QEventLoop 的想法是更好的长期解决方案。那么您的runEventLoop() 只需调用QApplication::exec() 就足够了。但我之前从未使用过QAbstractEventDispatcher,所以请以我的 cmets 为准。

    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)
    

    【讨论】:

    • 虽然我的目标是使用 AbstractEventDispatcher 正确集成事件循环,但我只是 Qt 的初学者,无法快速完成。我正在尝试一边做这件事,一边只是获得一个可行的解决方案。我正在尝试破解 SimpleEventLoop 类,以便它可以返回其“运行”状态,然后可以使用类似于您的示例的 while 语句从主应用程序中检查该状态。我会让你知道它是如何工作的。我担心的是是否以及如何从 Qt 应用程序内部正确处理 Boost 信号回调。
    • @Gimpyfuzznut:为 simpleeventloop.cpp 添加了一个示例 hack。您建议的破解很可能更复杂,因为 SimpleEventLoop::runEventLoop() 的当前实现实现了标准事件循环(参见例如stackoverflow.com/questions/658403/… 以获得解释),因此除非信号或计时器调用 SimpleEventLoop::endEventLoop 否则永远不会返回() 作为正在运行的事件循环的一部分。
    【解决方案2】:

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

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

    【讨论】:

    • "现在你有两个问题" :-)
    【解决方案3】:

    Qt 文档说:

    要让您的应用程序执行空闲处理(即在没有未决事件时执行特殊功能),请使用具有 0 超时的 QTimer。

    虽然不是一个很好的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多