将外部事件循环与 Qt 结合
我正在为开源客户端/服务器 4X 策略游戏 Thousand Parsec 构建 Qt 客户端.这是一个 Google Summer of Code 项目.然而,我陷入了死胡同.基本上,客户端通过促进客户端/服务器通信的 C++ 协议层与服务器接口.该协议的文档可在此处获得.
现在我的问题是协议要求您创建虚拟 EventLoop 类的子类 (link) 在您的客户端中.有一个示例 SimpleEventLoop 用于同一链接上的控制台客户端.我很难弄清楚如何设计我自己的事件循环子类来处理协议的事件,同时连接到 Qt 应用程序.我的研究让我相信 QAbstractEventDispatcher 是我想使用的 Qt 类但文档似乎很薄,我不确定我将如何做这件事.
有没有其他人有将外部事件循环与 Qt 应用程序联系起来的经验?我还在 Qt 页面上找到了这个 example 但是这不是很有帮助 - 或者至少我没有真正理解它.
谢谢!
解决方案我最近没有做太多的 Qt 开发,但是如果我没记错的话,可以调用 QApplication::processEvents()
在您自己的事件循环中(而不是通过 QApplication::exec()
)
我利用周日上午缓慢的机会试驾/了解了一些关于PyQt(Qt 的 Python 绑定)并在下面拼凑了一个概念验证代码.使用基于 QApplication::processEvents()
的自定义事件循环替换对 QApplication::exec()
的调用似乎可行.>
我也快速查看了simpleeventloop.cpp
和 tpclient-cpptext main.cpp
/a>.从它的外观来看,只需在 SimpleEventLoop::runEventLoop()
的主循环中的某处添加 QApplication::processEvents()
应该没问题.要将其添加到主循环中,我可能会替换 select() 函数的 tv
间隔/gitweb/gitweb.cgi?p=libtpproto-cpp.git;a=blob;f=tpproto/simpleeventloop.cpp;hb=ebfc08b322c552d73d34a368cca0623782f8d3f8#l106" rel="nofollines">code>r1117
与
tv.tv_sec = 0;tv.tv_usec = 10000;//每 0.01 秒运行一次 processEvents()app->processEvents();
并更改 行 89
到 void SimpleEventLoop::runEventLoop(QApplication *app)
.将您常用的 Qt 内容添加到您的客户端实现中应该没问题(您替换 tpclient-cpptext main.cpp
)
不过看起来像黑客.我可能会从这样的事情开始.我认为你的想法包装 TPSocket
和 Qt 各自概念中的计时器,以便使用 QAbstractEventDispatcher
到 QEventLoop
是更好的长期解决方案.然后,您的 runEventLoop()
只需调用 QApplication::exec()
就足够了.但我以前从未使用过 QAbstractEventDispatcher
,所以请采纳我的意见.
导入系统导入时间从 PyQt4 导入 QtGui从 PyQt4 导入 QtCore# 全局变量用作通知我的快速而肮脏的方式# MainWindow 已经退出的主事件循环APP_RUNNING = 假类 SampleMainWindow(QtGui.QMainWindow):def __init__(self, parent=None):QtGui.QMainWindow.__init__(self)全球APP_RUNNINGAPP_RUNNING = 真# 主窗口self.setGeometry(300, 300, 250, 150)self.setWindowTitle('测试')self.statusBar().showMessage('Ready')# 退出动作(假设退出图标来自# http://upload.wikimedia.org/wikipedia/commons/b/bc/Exit.png# 保存为 Exit.png 与此文件在同一文件夹中)exitAction = QtGui.QAction(QtGui.QIcon('Exit.png'),'出口',自己)exitAction.setShortcut('Ctrl+Q')exitAction.setStatusTip('退出应用程序')self.connect(exitAction),QtCore.SIGNAL('触发()'),QtCore.SLOT('close()'))# 主菜单菜单栏 = self.menuBar()fileMenu = menubar.addMenu('&File')fileMenu.addAction(exitAction)# 工具栏self.toolbar = self.addToolBar('退出')self.toolbar.addAction(exitAction)# 文本编辑器textEdit = QtGui.QTextEdit()self.setCentralWidget(textEdit)#工具提示textEdit.setToolTip('输入一些文字')QtGui.QToolTip.setFont(QtGui.QFont('English', 12))def closeEvent(self, event):回复 = QtGui.QMessageBox.question(self,'信息',你确定吗?",QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)如果回复 == QtGui.QMessageBox.Yes:事件.接受()全球APP_RUNNINGAPP_RUNNING = 假别的:事件.忽略()# 主程序app = QtGui.QApplication(sys.argv)testWindow = SampleMainWindow()testWindow.show()# 运行自定义事件循环而不是 app.exec_()当 APP_RUNNING 时:app.processEvents()# sleep 以防止我的伟大的"事件循环消耗 100% cpu时间.睡眠(0.01)
I'm building a Qt client for the open-source client/server 4X strategy game Thousand Parsec. This a Google Summer of Code project. I'm however stuck at a dead end. Basically, the client interfaces with the server via a C++ protocol layer that facilitates client/server communication. The protocol's documentation is available here.
Now my problem is that the protocol requires you to create a subclass of the virtual EventLoop class (link) in your client. There is an example SimpleEventLoop used for console clients on the same link. I'm having difficulty figuring out how I can design my own event loop subclass that handles the protocol's events while hooking into the Qt application at the same time. My research has lead me to believe QAbstractEventDispatcher is the Qt class I want to use but the documentation seems pretty slim and I'm not exactly sure how I would go about doing this.
Does anyone else have experience linking external event loops with a Qt application? I've also found this example on the Qt page but it wasn't very helpful - or at least I didn't really understand it.
Thanks!
解决方案I haven't done too much Qt development recently, but if I remember correctly, you can call QApplication::processEvents()
within your own event loop (instead of starting the Qt main loop through QApplication::exec()
)
Edit: I have used the opportunity of a slow Sunday morning to test-drive / learn something about PyQt (Python bindings for Qt) and cobbled together a proof-of-concept code below. Replacing the call to QApplication::exec()
with a custom event loop based on QApplication::processEvents()
seems to work.
I have also quickly looked at simpleeventloop.cpp
and tpclient-cpptext main.cpp
. From the looks of it, it shoud be fine to just add QApplication::processEvents()
somewhere in the main loop of SimpleEventLoop::runEventLoop()
. To add it to the main loop, I would probably replace the tv
interval for the select()
function in lines 106
through 117
with
tv.tv_sec = 0;
tv.tv_usec = 10000; // run processEvents() every 0.01 seconds
app->processEvents();
and change the signature in line 89
to void SimpleEventLoop::runEventLoop(QApplication *app)
. It should than be fine to add your usual Qt stuff to your implementation of the client (your replacement of tpclient-cpptext main.cpp
)
Looks like a hack, though. I would probably start with something like this to get started. I think that your idea of wrapping TPSocket
and the timer within Qt's respective concepts in order to forward them with the QAbstractEventDispatcher
to the QEventLoop
is the better long-term solution. It should then be sufficient that your runEventLoop()
simply calls QApplication::exec()
. But I have never used QAbstractEventDispatcher
before, so take my comments for what they are.
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)
相关文章