了解QT显示机制,最重要的就是要了解QT是如何管理窗体的显示区域的,这里有个重要的类:QRegion, 在QT中可以通过QRegion定义一个窗体的显示区域,也可以通过QRegion定义窗体的可修改区域,比如在QPainter()中通过QPainter::setClipRect设定一个区域,我们绘图则只能在这个区域,此区域外绘图都是无效的。通过QRegion可以作一系列的逻辑运算,如两个区域相加,相减等。QRegion定义的区域不一定是连续的,但一定是由封闭的区域组成的,我们常会碰到一个窗体的显示区域被其他窗体分割为几块的情况。QT对这些显示区域的管理,类似于对窗体的管理,也是通过服务器与客户端的方式。参照以前的说法Server表示为全局的Global
,客户端为本地得Local。那么WindowsServer管理一个全局的显示区域即所有的Top-Level widget显示区域。而其他的child windget 的管理则在每一个QT应用程序中由QWSRegionManager管理,Top_Level widget 显示区域也会加载在其中,这个不难理解,因为Server只是负责将窗体事件发送到客户端,具体处理还是由客户端来操作。具体的流程还是来看代码吧。
显示区域管理者QWSRegionManager的初始化
服务器:
通过调用openDisplay()。
客户端:
在QWSDisplayData类的构造函数中通过调用QWSDisplayData::init()完成。
考虑一个比较简单的情况,我们要显示的widget 是一个Top_Leverl widget。在调用Show()函数中,这个widget将通过showWindows()向服务器请求做三件事:(以下窗体是指在global windows statck 中的TOP_Level widget)
1:调用QWSDisplay::requestRegion向服务器请求窗体显示区域。
2:调用QWSDdisplay::setAltitude向服务器请求设置窗体的优先级。此优先级是指在windows statck中的位置,而不是指QWSWidow 中的窗体优先级属性。Windows statck 中的第一个窗体就是显示在LCD上最前面的窗体。
3:调用QWSDisplay::requestFocus 向服务器请求设置窗体为焦点窗体。焦点窗体能接收Key, Mouse 事件,但不是所有的焦点窗体都能接收Key,Mouse 事件,如果有窗体设置为GrabKey 或则GrabMouse 则Key, Mouse 事件将分别传递至此窗体。
下面将通过代码分析winddows Server对这三个请求的处理过程:
一: QWSDisplay::requestRegion的处理
void QWSServer::invokeRegion( QWSRegionCommand *cmd, QWSClient *client )
{
................
QRegion region;
region.setRects(cmd->rectangles, cmd->simpleData.nrectangles);
if ( !region.isEmpty() )
changingw->setNeedAck( TRUE );
bool isShow = !changingw->isVisible() && !region.isEmpty();
setWindowRegion( changingw, region ); //***设置窗体显示区域
syncRegions( changingw ); //***通知客户端 刷新显示区域
if ( isShow )
emit windowEvent( changingw, Show );
if ( !region.isEmpty() )
emit windowEvent( changingw, Geometry );
else
emit windowEvent( changingw, Hide );
if ( focusw == changingw && region.isEmpty() )
setFocus(changingw,FALSE);
.................
}
invokeRegion调用setWindowRegion设置窗体显示区域,调用syncRegions通知客户端 刷新显示区域,并产生一些窗体事件如:Show, Geometry,Hide 。
setWindowRegion函数的实现如下:
QRegion QWSServer::setWindowRegion( QWSWindow* changingw, QRegion r )
{
QRegion exposed;
if (changingw) {
changingw->requested_region = r;
r = r - serverRegion; //exposed不为空则有显示区域被释放
exposed = changingw->allocation() - r; //低等级窗体增加可见区域
} else {
exposed = serverRegion-r;
serverRegion = r;
}
QRegion extra_allocation;
int windex = -1;
bool deeper = changingw == 0;
for (uint i=0; i<windows.count(); i++) {
QWSWindow* w = windows.at(i);
if ( w == changingw ) {
windex = i;
extra_allocation = r - w->allocation(); //如果extra_allocation不为空
deeper = TRUE; //需要增加新的新的显示区域
} else if ( deeper ) {
w->removeAllocation(rgnMan, r);//低优先级窗体去掉被覆盖的区域
r -= w->allocation();//如果r为空 则更低优先级的窗体被完全覆盖
} else { //如果窗体是第一次调用Show 直接走这
//higher windows
r -= w->allocation();//如果r为空 则窗体被高优先级窗体完全覆盖
}
if ( r.isEmpty() ) { //窗体被完全覆盖
break; // Nothing left for deeper windows
}
}
...................
if ( changingw && !changingw->requested_region.isEmpty() )
changingw->addAllocation( rgnMan, extra_allocation & screenRegion );
//为changingw窗体增加新的可见区域 置modifed标志为TRUE
else if ( !disablePainting )
paintServerRegion();
exposeRegion( exposed, windex+1 );//增加低级窗体可见区域。
return exposed;
}
注:增加新的显示区域不一定是整个显示区域的面积增大了,而是显示区域的块变多了。 一个显示区域可能由多个不连续和连续的Region组成。
void QWSServer::exposeRegion( QRegion r, int start )
{
r &= screenRegion;
for (uint i=start; i<windows.count(); i++) {
if ( r.isEmpty() ) //可见区域为空
break; // Nothing left for deeper windows
QWSWindow* w = windows.at(i);
w->addAllocation( rgnMan, r ); //增加新的可见区域 置modifed标志为TRUE
r -= w->allocation(); //r 更低级窗体可见区域
}
dirtyBackground |= r; //得到需要刷新的背景区域 如果r为空 则新增区域为0
}
exposeRegion为低等级窗体增加可见区域。
syncRegions:此函数主要是向客户端发送RegionModified事件,真正的绘图也是由客户端来完成。 还是通过代码来分析:
void QWSServer::syncRegions( QWSWindow *active )
{
rgnMan->commit(); //拷贝数据到一段共享内存,服务器为读写权限,客户端为只读
notifyModified( active );//通过客户端显示区域已更改,客户端绘制相关区域
paintBackground( dirtyBackground );//绘制背景区域修改部分。
dirtyBackground = QRegion();
}
void QWSServer::notifyModified( QWSWindow *active )
{
// notify active window first
if ( active )
active->updateAllocation(); //首先通知active 窗体
// now the rest //通知所有modified标志为TRUE的窗体
for (uint i=0; i<windows.count(); i++) {
QWSWindow* w = windows.at(i);
w->updateAllocation();
}
}
void QWSWindow::updateAllocation()
{
if ( modified || needAck) {
c->sendRegionModifyEvent( id, exposed, needAck ); // 发送消息
exposed = QRegion(); //复位低级窗体新增显示区域
modified = FALSE; //modified为真表示窗体的显示区域被修改。
needAck = FALSE;
}
}
客户端对RegionModifyEvent的处理。
客户端接收到消息后会调用translateRegionModifiedEvent函数来进行处理
bool QETWidget::translateRegionModifiedEvent( const QWSRegionModifiedEvent *event )
{
QWSRegionManager *rgnMan = qt_fbdpy->regionManager();
if ( alloc_region_index < 0 ) {
alloc_region_index = rgnMan->find( winId() ); //从共享内存中得到region索引
if ( alloc_region_index < 0 ) {
return FALSE;
}
}
QWSDisplay::grab();
int revision = *rgnMan->revision( alloc_region_index );
if ( revision != alloc_region_revision ) {
alloc_region_revision = revision;
QRegion newRegion = rgnMan->region( alloc_region_index );//得到显示区域
QWSDisplay::ungrab();
alloc_region = newRegion;
// set children's allocated region dirty
................
} else {
QWSDisplay::ungrab();
}
if ( event->simpleData.nrectangles )
{ // alloc_region >= exposed
QRegion exposed; //需要刷新区域的大小
exposed.setRects( event->rectangles, event->simpleData.nrectangles );
QSize s( qt_screen->deviceWidth(), qt_screen->deviceHeight() );
exposed = qt_screen->mapFromDevice( exposed, s );
qwsUpdateActivePainters();
repaintDecoration( exposed, FALSE );//绘制窗体的一些修饰如边框,caption等
repaintHierarchy( exposed, FALSE ); //绘制窗体显示区域及子窗体通过发送
} //PaintEvent事件到各窗体
qws_regionRequest = FALSE;
return TRUE;
}
repaintHierarchy函数中所有需要刷新的子窗体都会收到Paint事件。在Paint事件中,开始绘图。显示中只刷新exposed这个区域而不是将分配的区域alloc_region 全部刷新一次,这样做可以提高效率。
二:QWSDdisplay::setAltitude 的处理
invokeSetAltitude(const QWSChangeAltitudeCommand *cmd,
QWSClient *client)
{
int winId = cmd->simpleData.windowid;
int alt = cmd->simpleData.altitude;
bool fixed = cmd->simpleData.fixed;
...................
QWSWindow* changingw = findWindow(winId, 0);
...................
changingw->setNeedAck( TRUE );
if ( fixed && alt >= 1) {
changingw->onTop = TRUE;
}
if ( alt < 0 )
lowerWindow( changingw, alt ); //窗体优先级下降
else
raiseWindow( changingw, alt ); // 提升窗体优先级
if ( !changingw->forClient(client) ) {
refresh();
}
}
invokeSetAltitude通过调用lowerWindow,raiseWindow来调整窗体的优先级,如果一个Widget被显示,即调用Show此时alt == 0; 如果alt == 1则此窗体应该为最上层,如果alt == 2则窗体位FULL-SCREEN即全屏显示的窗体,可以通过setWFlags(WStyle_StaysOnTop) 来设定这个属性。 优先级较高的窗体将被优先显示, 在没有显式通过SetRegionPriority命令来改变窗体优先级的话,在Windows Stack中窗体将按照后进的优先级较高为原则。
可以参考 insertPrioritizedWindow函数,在qt-embedded-free-3.3.6 可能没有这个函数,因为在这个版本中不存在窗体优先级,除了WStyle_StaysOnTop属性的窗体为第一级优先级外,其他窗体都按照后进的优先为原则。
三:QWSDisplay::requestFocus 的处理请参考invokeSetFocus函数。
分享到:
相关推荐
QT的显示机制,可以看看,比较不错 QT的显示机制,可以看看,比较不错 QT的显示机制,可以看看,比较不错
用Qt实现信号与槽机制,未使用ui实现窗口的变换和显示。
QT5.7开发,基于QSerialport、QChart实现串口数据的接收、发送,并动态显示数据。 采用信号与槽机制实现。
QT登陆验证机制,并显示数据库内容 代码详见源文件 注册验证为:验证用户名是否已存在、两次输入密码是否相同,满足则注册成功。 修改密码验证为:验证用户名是否存在,新密码是否与旧密码不同。 登陆验证为:验证...
代码功能:通过TCP网络通信实现图像数据的接收,进而将其进行绘制于UI上显示,实现实时视频的播放效果。代码框架利用了QT的多线程机制,解决图像显示的卡顿问题。
自己写的一个简单多线程,可以参考下,应该对多线程同步有用,与普通的重写run函数有不同,重点是理解QT信号的几个同步机制。
本书以Qt 5.9 LTS版本为开发平台,详细介绍了Qt C++开发应用程序的技术,包括Qt应用程序的基本架构、信号与槽工作机制、图形显示的Graphics/View架构、数据编辑和显示的Model/View架构、对话框和多窗口的设计与调用...
5 Qt消息机制和事件 50 5.1 事件 50 5.2 event() 52 5.3 事件过滤器 55 5.4 总结 59 5.5 不规则窗体 62 6 绘图和绘图设备 63 6.1 QPainter 63 6.2 绘图设备 65 6.2.1 QPixmap、QBitmap、QImage 66 6.2.2 QPicture ...
Qt 使用了自己定义的Locale机制,在编码支持和信息文件(Message File)的翻译上... Qt 的这种机制可以使 Qt 的同一 组件(QWidget)上同时显示不同编码的文本。 比如,Qt 的标签上可以同时使用中文简体 和中文繁体文本。
自己写的在线程内使用信号与槽机制,控制界面的显示。会一点点Qt的人都能看懂。
15:QT4与QT5均可编译通过 亲测无误 ">说明: 1:此示例只是用来显示视频流 并没有处理存储视频及回放视频功能 2:在打开项目后务必将构建里面的影子构建 Shadow build 取消 3:实时显示视频 视频响应速度比VLC QTAV等...
该书以Qt 5.9 LTS版本为开发平台,详细介绍了包括Qt应用程序的基本架构、信号与槽工作机制、 图形显示的Graphics/View架构、数据编辑和显示的Model/View架构、对话框和多窗口的设计与调用方法等技术;也介绍了常用...
\初级篇 第1章 Qt初步实践 卢传富 建立了第一个较简单的Qt应用程序,在GUI用户界面中显示一行中文。 2 \ 第2章 对话框 \——QDialog 卢传富介绍了Qt的对话框类QDialog,实现了一个自定义的登录对话框,举例说明了Qt...
在界面跳转方面,通过QT独有的信号和槽机制,进行设计,此项目主要在登录界面和消息交互界面设计了界面的跳转。至于客户端和服务端的搭建,是本项目主要需要实现的地方,通过ip地址和端口号,进行,各个客户端之间的...
\初级篇 第1章 Qt初步实践 卢传富 建立了第一个较简单的Qt应用程序,在GUI用户界面中显示一行中文。 2 \ 第2章 对话框 \——QDialog 卢传富介绍了Qt的对话框类QDialog,实现了一个自定义的登录对话框,举例说明了...
关于QT5自带绘图功能的一些精讲和实例 1,QT基础图形绘制 2,QT5双缓冲机制 3,QT5 SVG格式图片的显示
使用Qt Creator,用C++语言写了一个小实例,第一次用,工程名还是helloworld,但是具有了一个界面,实现显示框数据随slider控件变化的功能,对于初学者有助于理解Qt的机制。
1,在QT程序中,新建一件,再添加一个用于操作硬件的新方法,如controlMachine,建立按钮与con个属于你自己的类,如Widget用于显示。 2,在你的类中添加按钮控trolMachine函数之间的信号与槽机制(使用connect函数)...