在 Qt 中处理平台相关的底层事件Word下载.docx
《在 Qt 中处理平台相关的底层事件Word下载.docx》由会员分享,可在线阅读,更多相关《在 Qt 中处理平台相关的底层事件Word下载.docx(15页珍藏版)》请在冰豆网上搜索。
![在 Qt 中处理平台相关的底层事件Word下载.docx](https://file1.bdocx.com/fileroot1/2023-2/6/aa58c7e5-0868-4b27-a886-ecac830f609a/aa58c7e5-0868-4b27-a886-ecac830f609a1.gif)
Q_WS_WIN
Windows
Q_WS_X11
XWindow
Q_WS_MAC
Mac
如果项目中调用了平台相关的APIs,那么利用条件编译,使得源程序不用修改,就可以适用于不同的平台。
对于XWindow平台,代码片断如清单1所示:
清单1.
#ifdefined(Q_WS_X11)
//与XWindow相关的nativeAPIs
#endif
对于Windows平台,代码片断如清单2所示:
清单2.
#ifdefined(Q_WS_WIN)
//与Windows相关的nativeAPIs
对于MAC平台,代码片断如清单3所示:
清单3.
#ifdefined(Q_WS_MAC)
//与Mac相关的nativeAPIs
2.Xlib与XEvent
Xlib是XWindow提供的API集合,Xlib之上是XtIntrinsicslibrary,Xt采用面向对象的设计方法,提供了大量的widgets。
在Xt之上则是各种各样的toolkits,最常用的是Motif。
Qt没有对Xt进行封装,而是直接建立在Xlib之上。
Xlib用一个叫"
XEvent"
的结构来保存从X服务器那里接收到的事件。
Xlib提供了大量的事件类型。
XEvent包括事件的类型,以及与事件相关的数据。
Qt也定义了一组事件类。
其中QEvent是基类。
其他具体事件类,例如QMouseEvent、QKeyEvent、QFocusEvent、QPaintEvent、QMoveEvent、QResizeEvent等都是从QEvent继承而来。
多数情况下,利用Qt的事件机制,能够满足我们的需求。
但是,在某些特定的情况,我们需要能够截获XWindow的底层事件,并对其进行处理。
本文介绍了两种截获XEvent的方法,一种是重载QApplication的x11EventFilter()方法;
另一种是重载QWidget的x11Event()方法。
文中提供的例子基于Qt4.3,并在FedoraCore6上调试通过。
3.QApplication:
:
x11EventFilter()如何工作
QApplication类管理Qt应用程序的控制流和主要设置。
它包含了主事件循环,对来自窗口系统的所有事件进行处理和调度。
它也处理应用程序的初始化和结束,并且提供对话管理。
QApplication中定义了针对XWindow平台的虚函数,如清单4所示:
清单4.
boolQApplication:
x11EventFilter(XEvent*)
创建QApplication的子类,并且重新实现函数x11EventFilter(),那么所有底层的XEvent会首先被函数x11EventFilter()截获。
如果希望在函数x11EventFilter()中针对某事件进行响应,那么在响应结束后返回TRUE,表示该事件不会被分派到QApplication的Qt主事件循环中。
如果函数返回FALSE,那么该事件会被继续分派到QApplication的Qt主事件循环中,由Qt包装成Qt事件。
此外,针对Windows平台,QApplication定义了类似的虚函数,如清单5所示:
清单5.
boolQCoreApplication:
winEventFilter(MSG*msg,long*result)
针对Mac平台,QApplication定义了类似的虚函数,如清单6所示:
清单6.
macEventFilter(EventHandlerCallRefcaller,EventRefevent)
下面通过一个例子说明,如何在Qt中处理XEvent。
该例子包括两个可执行程序,一个是xclient,另一个是qtx11filter。
xclient基于Xlib,向qtx11filter发送XClientMessageEvent。
qtx11filter截获所有的XEvent,但只对XClientMessageEvent进行处理,将其它所有类型的XEvent都分派到QApplication的Qt主事件循环中。
3.1xclient的主要流程和源代码
xclient的主要流程如下:
1.用户通过命令行输入16进制的windowID。
2.向该windowID所在的窗口发送XClientMessageEvent。
该事件所携带的数据是一个长度为4的字符串”2008”。
xclient的完整源代码xclient.c如清单7所示:
清单7.
#include<
stdio.h>
X11/X.h>
X11/Xlib.h>
charline[80];
unsignedlongwinID,mask;
XEventev;
Display*disp;
Statusstatus;
intmain(intargc,char*argv[]){
disp=XOpenDisplay(NULL);
if(!
disp){
perror("
UnabletoopenXdisplay"
);
exit
(1);
}
do{
printf("
Enteraline:
"
fgets(line,80,stdin);
if(line[0]!
='
q'
){
winID=strtol(line,NULL,16);
Youentered%d:
0x%x\n"
winID,winID);
ev.xclient.type=ClientMessage;
ev.xclient.window=winID;
ev.xclient.message_type=0;
ev.xclient.format=8;
ev.xclient.data.b[0]='
2'
;
ev.xclient.data.b[1]='
0'
ev.xclient.data.b[2]='
ev.xclient.data.b[3]='
8'
ev.xclient.data.b[4]='
\0'
mask=0l;
status=XSendEvent(disp,winID,False,mask,&
ev);
TheXSendEventreturned:
%d\n"
status);
XFlush(disp);
}while(line[0]!
}
在FedoraCore6中,利用如下命令编译xclient:
清单8.
gccxclient.c–oxclient–lX11
3.2qtx11filter的主要流程和源代码
qtx11filter的主要流程如下:
1.创建QApplication的子类App,并实现虚函数x11EventFilter。
注意使用宏定义Q_WS_X11,因为x11EventFilter只适用于XWindow。
2.在x11EventFilter中,判断所截获的XEvent,如果事件类型为ClientMessage,那么打印出该事件发送者的windowID,并且打印出该事件所携带的数据。
3.在main()函数中,创建类App的实例,并且通过调用app.exec(),启动Qt主事件循环。
qtx11filter的完整源代码qtx11filter.c如清单9所示:
清单9.
qapplication.h>
qpushbutton.h>
qdialog.h>
classApp:
publicQApplication{
public:
App(intargc,char**argv):
QApplication(argc,argv){}
#ifdefined(Q_WS_X11)
boolx11EventFilter(XEvent*xe){
switch(xe->
type){
caseClientMessage:
CaughtClientMessageXEventfromWindow%d\n"
xe->
xclient.window);
Receivemessage:
%s\n"
xclient.data.s);
returntrue;
returnfalse;
#endif
};
classDialog:
publicQDialog{
Dialog(QWidget*parent=0):
QDialog(parent){
QPushButton*done=newQPushButton("
Done"
this);
connect(done,SIGNAL(clicked()),qApp,SLOT(quit()));
intmain(intargc,char**argv){
Appapp(argc,argv);
Dialogdialog;
app.setMainWidget(&
dialog);
dialog.show();
returnapp.exec();
在FedoraCore6中,用以下命令编译qtx11filter:
清单10.
g++qtx11filter.c-L/usr/lib/qt-3.3/lib-I/usr/lib/qt-3.3/include
-lqt-mt–lX11-oqtx11filter
3.3演示xclient和qtx11filter
1.在终端启动qtx11filter
清单11.
./qtx11filter
弹出仅包含一个按钮的窗口,如图1所示:
图1.qtx11filter窗口
2.在终端启动xwininfo,如图2所示:
图2.xwininfo窗口
3.将光标移动到窗口qtx11filter,点击鼠标左键,xwininfo可以获取qtx11filter的windowID,如图3所示(截图中已经用红色方框标出)。
图3.获取qtx11filter的windowID
4.在终端启动xclient,并输入qtx11filter的windowID,如图4所示。
然后,xclient会向qtx11filter发送XClientMessageEvent,该事件携带一个四字节的字符串”2008”。
图4.输入qtx11filter的windowID
5.打开运行qtx11filter的终端,qtx11filter已经从windowID为39845889的窗口接收到一个XClientMessageEvent,并读取出该事件所携带的数据”2008”,如图5所示:
图5.qtx11filter处理XClientMessageEvent
4.QWidget:
x11Event()如何工作
QWidget是Qt所有用户界面对象的基类。
它从窗口系统接收鼠标、键盘和其它事件,并且在屏幕上绘制自己。
QWidget定义了针对XWindow平台的虚函数:
清单12.
boolQWidget:
x11Event(XEvent*)
创建QWidget或者其已有派生类的子类,并且重新实现函数x11Event(),那么所有发送到该Widget的XEvent首先被函数x11Event()截获。
如果希望在函数x11Event()中针对某事件进行响应,那么在响应结束后返回TRUE。
如果函数返回FALSE,那么该事件会被包装成Qt事件,发送给Widget。
QWidget:
x11Event(XEvent*)和QApplication:
x11EventFilter(XEvent*)的区别在于:
前者在XEvent发送到特定QtWidget之前,被该Widget截获;
后者在XEvent发送到整个程序的主事件循环之前,被QApplication截获。
5.Qt中其它与平台相关的APIs
在每个平台上,Qt都为QWidget提供了一个winId()函数,返回该QtWidget的windowID;
QWidget还提供了一个静态函数find(),该函数可以返回一个特定windowID所对应的QtWidget。
我们可以将获得的windowID传递给NativeAPI来执行平台特定的操作。
此外,针对XWindow,Qt提供了类X11Info,用以获取XWindow平台的信息。
例如,通过调用静态成员函数X11Info:
display()可以获取Qt程序与Xserver建立的连接句柄。
参考资料
∙Qt官方网站
∙Xorg官方网站
∙Qt兴趣邮件列表
∙Qt的信号与槽机制介绍