我正在为开源客户端/服务器4X策略游戏Thousand Parsec构建一个Qt客户端。这是Google Summer of Code项目。然而,我陷入了死胡同。基本上,客户端通过C ++协议层与服务器连接,以促进客户端/服务器通信。该协议的文档可用here。
现在我的问题是该协议要求您在客户端中创建虚拟EventLoop类(link)的子类。在同一链接上有一个用于控制台客户端的SimpleEventLoop示例。我很难弄清楚如何设计我自己的事件循环子类来处理协议的事件,同时挂钩到Qt应用程序。我的研究让我相信QAbstractEventDispatcher是我想要使用的Qt类,但文档似乎相当渺茫,我不确定如何去做这个。
是否有其他人有将外部事件循环与Qt应用程序相关联的经验?我也在Qt页面上找到了这个example,但它没有太大帮助 - 或者至少我没有真正理解它。
谢谢!
答案 0 :(得分:33)
我最近没有做过太多的Qt开发,但是如果我没记错的话,你可以在你自己的事件循环中调用QApplication::processEvents()
(而不是通过QApplication::exec()
启动Qt主循环)
编辑:我利用周日早晨的机会来试驾/学习PyQt(Qt的Python绑定)并拼凑出一个概念验证代码如下。使用基于QApplication::exec()
的自定义事件循环替换对QApplication::processEvents()
的调用似乎可以正常工作。
我也很快查看了simpleeventloop.cpp
和tpclient-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超时。
虽然不是一个漂亮的解决方案。