了解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函数。

http://blog.csdn.net/songjinshi/article/details/6544582

QT显示机制(7篇相关文章)的更多相关文章

  1. 【Jmeter】他人总结篇链接(共八篇相关文章)

    [Jmeter]他人总结篇链接(共八篇相关文章) https://blog.csdn.net/mu_wind/article/category/9029006

  2. 修改进程占用内存SetProcessWorkingSetSize函数(多篇相关文章值得学习)

    物理内存和虚拟内存 物理内存,在应用中,自然是顾名思义,物理上,真实的插在板子上的内存是多大就是多大了.看机器配置的时候,看的就是这个物理内存. 如果执行的程序很大或很多,就会导致物理内存消耗殆尽.为 ...

  3. 两篇很好的EPG相关文章

    两篇很好的EPG相关文章 原文地址:http://blog.sina.com.cn/s/blog_53220cef0100pi8j.html 1 基于DVB-SI的数字有线电视机顶盒节目指南的设计实现 ...

  4. 第六篇 ANDROID窗口系统机制之显示机制

    第六篇 ANDROID窗口系统机制之显示机制 ANDROID的显示系统是整个框架中最复杂的系统之一,涉及包括窗口管理服务.VIEW视图系统.SurfaceFlinger本地服务.硬件加速等.窗口管理服 ...

  5. ZBLOG PHP调用相关文章列表以及上一篇下一篇文章代码

    如果是比较小的个人博客.专题类网站项目,老蒋还是比较喜欢使用ZBLOG PHP程序的,无论是轻便度还是易用性上比WordPress简单很多,虽然WP的功能很强大,比如强大的插件和主题丰富功能是当前最为 ...

  6. Qt事件处理机制

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...

  7. Qt 事件处理机制

    Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt ...

  8. MySQL优化篇系列文章(二)——MyISAM表锁与InnoDB锁问题

    我可以和面试官多聊几句吗?只是想... MySQL优化篇系列文章(基于MySQL8.0测试验证),上部分:优化SQL语句.数据库对象,MyISAM表锁和InnoDB锁问题. 面试官:咦,小伙子,又来啦 ...

  9. 【转】解读Qt 事件处理机制(上篇)

    [转自]:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...

随机推荐

  1. [LeetCode]题解(python):013-Roman to Integer

    题目来源: https://leetcode.com/problems/roman-to-integer/ 题意分析: 这道题目和上一题目相反,是将罗马数字转化成阿拉伯数字. 题目思路: 只要知道罗马 ...

  2. nginx下配置二级域名指向子目录

    今天终于把nginx的二级域名配置搞定了,哎之前在测试服务器上弄过一次,不过那个是在本地解析的hosts,把ip指向到域名上就ok,再在nginx.conf里改了下配置就好了,用同样的方法改了正式服务 ...

  3. hibernate 持久化对象的生命周期 2.1

    持久化对象的生命周期 瞬态(自由态) 表示对象在内存中存在,在数据库中没有数据相关,比如刚刚new出来的一个对象 持久态 持久态指的是持久化对象处于由Hibernate管理的状态,这种状态下持久化对象 ...

  4. android 破解九宫格

    将目录切换到D:/adb目录下,命令如下 敲入命令 adb shell 然后回车,可以见到如下结果 再敲入命令cd /data/system然后回车, 再执行 rm gesture.key 回车,搞定 ...

  5. Python3 官方文档翻译 - 4.7 函数定义

    4.7.1 默认函数定义 最常用的就是为一个或多个参数设定默认值,这让函数可以用比定义时更少的参数来调用,例如: def ask_ok(prompt, retries=4, complaint='Ye ...

  6. BZOJ 1618: [Usaco2008 Nov]Buying Hay 购买干草

    题目 1618: [Usaco2008 Nov]Buying Hay 购买干草 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 679  Solved:  ...

  7. Data Structure(2)

    在大体看过一遍<数据结构导论>的基础上完成了上一篇的博客,周五晚上通过上讲课,还是发现了一些问题的,主要体现在对第一章看的比较粗心,第一章的概论其实是对整本书的整体概况,这里没有多看上几遍 ...

  8. ios inHouse 公布应用

    一.明白几个概念 1.企业版IDP: 即iOS Development Enterprise Program.注意是$299/Year那种.并非$99/Year的那种 2.In House:是指企业内 ...

  9. android Graphics(三):区域(Range)

    前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家.有关界面开发的东东内 ...

  10. JS+CSS打造三级折叠菜单,自动收缩其它级 js

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...