基于qt的电视机ui实现技术.doc_第1页
基于qt的电视机ui实现技术.doc_第2页
基于qt的电视机ui实现技术.doc_第3页
基于qt的电视机ui实现技术.doc_第4页
基于qt的电视机ui实现技术.doc_第5页
已阅读5页,还剩57页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

基于Qt技术的可移植UI设计 基于Qt的电视机UI实现技术1 UI软件架构基于Qt来实现一套电视机用户界面(User Interface),首先需要选择一个软件框架,用于管理UI页面。在Qt中,Main Window 为创建应用程序的UI提供了一个框架。QMainWindow类及其相关类共同完成Main Window中的页面管理。本项目选择了Qt的Main Window 框架作为实现电视机UI的软件架构。下面介绍一下Qt的Main Window 框架在实际项目中的应用。1.1 Qt的Main Window 框架Qt的Main Window 框架,以QMainWindow类作为程序主窗口。QMainWindow类拥有自己的布局,如图1-1-1所示:图1-1-1 主窗口布局根据电视机UI的特点,用户只能通过遥控器及本机按键来操作UI,因此只需要一个中心区域显示交互内容就足够了。对于QMainWindow布局中的Menu Bar、Toolbars、Dock Widgets、Status Bar这几个部分是我们所不需要的。我们只需要关注Central Widget即可。QMainWindow的Central Widget可以是多种类型:l Qt提供的标准窗口部件,比如QWidget、QTextEdit等;l 用户自定义的窗口部件;l 布局管理器组织起来的多个widgets;l 分裂器QSplitter。QSplitter作为一个容器可以容纳多个窗口部件,此时中央部件是一个包容多个窗口部件的容器;l 多文档区部件QMdiArea。如果应用程序使用MDI,则Central Widget将被一个QMdiArea部件占据。每个MDI窗口都是这个QMdiArea部件的一个子部件。应用程序选择哪种类型作为Central Widget,需要由具体需求决定。电视机UI通常具有主菜单、一级子菜单、二级子菜单、快捷菜单、信息提示菜单等多个菜单显示页面。本项目将每个菜单页面视作一个文档(Document),各级菜单之间的切换,采用多文档界面(Multiple Document Interface)模式进行管理,因此选择QMdiArea作为主窗口的Central Widget。通过调用QMainWindow的setCentralWidget()方法来设置Central Widget。QMdiArea提供了一个管理/显示多文档界面的区域。它通常作为应用程序多文档界面主窗口的Central Widget,实现对子窗口的管理、绘制和排布。QMdiArea具有独特的多文档子窗口类QMdiSubWindow,它在多文档区部件内表现为一个顶层窗口,可以关闭、最小化和最大化,具有独立的窗口标题。QMdiSubWindow具有自己的布局管理器,该布局管理器管理窗口标题栏和放置窗口部件的中心区域。多文档子窗口QMdiSubWindow和多文档区部件QMdiArea共同实现应用程序的多文档功能。通常通过调用函数QMdiArea:addSubWindow()为一个多文档部件添加一个多文档窗口,并返回该多文档子窗口的指针。总结:一个基于MDI的Qt Main Window 框架,由QMainWindow、QMdiArea、QMdiSubWindow三个核心类构成。其中,QMainWindow类主要提供了一个应用程序的主窗口,在主窗口中提供了Central Widget区域,用于页面管理。QMdiArea类被设置成Central Widget,实行具体的子窗口管理任务;在QMdiArea中加入的每个子窗口都是QMdiSubWindow类或其派生类的对象;该类负责管理菜单页面的创建、销毁、显示、隐藏等等。需要注意的是,新菜单创建、老菜单销毁过程中,要防止内存泄露;此外,如何高效快速完成菜单间的切换是非常重要的。QMdiSubWindow类是具体的每个子窗口,或者称为子菜单页面的基类;实际的电视机主菜单、图像子菜单、声音子菜单等等的实现,均由该类派生。1.2 Qt的Main Window 框架的实际应用实际应用中,将根据具体的UI方案需求,以QMainWindow、QMdiArea、QMdiSubWindow三个类作为基类,派生出满足需求的类加以应用。本项目中,由QMainWindow作为基类,派生出MainWindow类,除去基本的继承自QMainWindow的属性外,在MainWindow的构造函数中,加入了对主窗口几何位置、尺寸的设定;加入了主窗口背景属性的设定。最后将由QMdiArea类派生来的WindowHandler类的对象设置为Central Widget。本项目中,由QMdiArea作为基类,派生出WindowHandler类,用以实现菜单页面的管理。WindowHandler类中添加了用于页面之间跳转的槽函数slot_GotoScreen(int curSID, int dstSID, int param);添加了注册各子菜单页面的函数RegisterWindow(int nKey);并给予每页菜单一个独一无二的ID号,将ID号与指向该页面类型的指针函数以对的形式存放于QMap m_mapSc中,来动态的创建、销毁指定ID号的子菜单。本项目中,由QMdiSubWindow作为基类,派生出MdiSubWindow类。在MdiSubWindow中添加了用于页面跳转的信号void sig_goto_screen(int curSID, int dstSID, int param);添加了公共参数用于接收前一页菜单传递过来的参数int m_PrevWindow_Param;在构造函数中增加了设置窗口标志的语句setWindowFlags(Qt:FramelessWindowHint);在具体菜单页面实现时,跟菜单、主菜单、设置菜单在MdiSubWindow的基础上派生出了BaseMenu、MainMenu、SetupHandler类。软件框架相关的类之间的关系如图1-2-1所示: 图1-2-1 软件框架相关类间关系1.3 Qt Graphics View 框架本项目设置菜单实现动画切换效果,用到了Qt Graphics View 框架。该框架提供了一个用于管理大量自定义的二维图形item的平面,还提供了一个用于显示、缩放、旋转这些二维图形item的视图控件。结合本项目,与该框架相关的三个类分别为QGraphicsScene、QGraphicsView、QGraphicsProxyWidget。QGraphicsScene类是诸多item的容器。需要动作的item首先被加入到QGraphicsScene中,归QGraphicsScene统一管理。QGraphicsView类是一个视口,用于观察QGraphicsScene及其中的各个item。可以通过QGraphicsView来改变被其观察的QGraphicsScene的坐标轴,从而实现场景的动态效果。QGraphicsProxyWidget是本项目用到的类。通常向QGraphicsScene这个容器中加入的需要产生动画效果的item,都是通过QGraphicsItem及其派生类产生的对象。这些item通常可以是图片、文字、路径、几何形状等简单的动画元素。由于本项目实现的UI中,设置菜单中需要产生动画效果的item是一个由图像、文字以及各种widget组合而成的一个复杂widget,所以QGraphicsItem及其派生类产生的对象无法满足要求。在此用到了QGraphicsScene中的一个添加widget作为item的函数:QGraphicsProxyWidget * QGraphicsScene:addWidget ( QWidget * widget, Qt:WindowFlags wFlags = 0 )这样,由Picture、Sound等类产生的含有图像、文字、控件的对象便可以顺利加入到场景中,并对其进行旋转缩放等操作。1.4 “设置”菜单中的subIem架构“设置”菜单对应的类名为SetupHandler。在该菜单下,包含“图像”、“声音”、“菜单”、“网络”、“时间”、“频道”设置子项。每个子项并未独立为一页菜单,而是以subItem的形式,嵌入到SetupHandler类所构建的场景中,由场景管理每个subItem。由于在SetupHandler类中,需要将按键响应传递到每个subItem内部的调整函数中,这就要求在SetupHandler类中,可以调用subItem的方法。每个subItem的实现都是一个类,但是在SetupHandler中,每个subItem是动态生成的,这就希望有一个类型的指针可以指向各个有所区别的subItem类的对象。基于上述两点考虑,在实现每个具体的subItem之前,构建了一个SetupSubItem类,在该类中实现了一个公有的currentItem变量用于描述subItem中当前选中项;实现了两个字体对象并在构造函数中初始化,用于subItem中选中与未选中项的字号区别;实现了两个虚函数,用于满足在SetupHandler类中可以通过SetupSubItem型的指针调用具体的subItem对象的按键响应函数。与subItem相关的类之间的关系如下图所示:图1-4-1 SubItem相关类间关系每个subItem的实现非常类似,根据UI设计方案,在实现之前,将subItem划分几个部分,最后用QFram组装到了一起。下面以Picture类为例,分析一下subItem的架构。Picture子项的UI设计外观如图1-4-2所示:图1-4-2 Picture子项的UI设计外观将Picture子项的实现分为4部分:A. 背景框与上下键选择提示图标,由QLabel及QPixmap组合实现;B. 左侧文字列表由一个QListWidget实现; C. 中部选项列表由一个QListWidget实现;D. 右侧图示列表由QFrame、QGroupBox包裹QSlider及QRadioButton实现。划分后的控件示意图如图1-4-3所示:图1-4-3 控件划分示意图上图中的D区域,使用了两个QFrame,外侧较大的QFrame目的是将所有的QGroupBox装到一起,在响应上下键时做整体移动;内存较小的QFrame目的是遮罩,只显示出4组QGroupBox。B,C区域使用的QListWidget类有一个很好的属性,当设置当前的item(QListWidget:setCurrentItem)时,QListWidget会自动调整列表的位置,并且只要预先设置了QListWidget的几何属性,便决定了QListWidget的可见区域。2 内存管理2.1 Qt内存管理规则C+语言没有提供自动的内存管理。通常,应用程序的编写者必须自己处理内存分配与回收的问题。Qt可以接管一部分内存管理的工作。从QObject类派生而来的类的一些对象,可以由一个树形结构来管理。这些对象可能会产生子对象。如果一个对象被delete掉了,那么Qt会自动delete掉它所有的子对象,并且这些子对象会依序delete掉它们的子孙。也可以这样理解,如果一个对象树的根对象消失了,则Qt自动删除整棵树。Qt这种自动管理内存的功能,可以使编程人员从追踪对象的子对象及释放它们所占用的内存空间的工作中解脱出来。但是,为了使这种自动内存管理的功能起作用,所有的子对象(以及子对象的子对象)必须在堆中分配内存(即通过使用操作符new来产生子对象)。用new操作符创建对象会返回一个指向堆的指针。在每一个Qt对象中,都有一个链表,这个链表保存有它所有子对象的指针。当创建一个新的Qt对象的时候,如果把另外一个Qt对象指定为这个对象的父对象, 那么父对象就会在它的子对象链表中加入这个子对象的指针。另外,对于任意一个Qt对象而言,在其生命周期的任何时候,都还可以通过setParent函数 重新设置它的父对象。当一个父对象在被delete的时候,它会自动的把它所有的子对象全部delete。当一个子对象在delete的时候,会把它自己 从它的父对象的子对象链表中删除。对象的父子关系大大简化了内存管理,并且降低了内存泄露的风险。对于使用new创建的对象,我们只需要delete那些没有父对象的便可以了。父对象、子对象及父对象的子对象列表关系如图2-1-1所示:图2-1-1 父对象、父对象的子对象列表及子对象间的关系有以下几种情况,在使用new操作符创建一个对象时,不需要显式地指定父对象,但可以通过其它方式让其具有父对象,形成父子关系:情况1:布局管理器对其所管理的对象,可以统一指定父对象。例如:#include #include #include int main(int argc, char *argv)QApplication a(argc, argv);QWidget window;QVBoxLayout* mainLayout = new QVBoxLayout(&window);/指定布局管理/器的父对象,同时该布局管理器内的所有部件的父对象,也被指定QLabel* label1 = new QLabel(One);/创建时未指定父对象QLabel* label2 = new QLabel(Two);/创建时未指定父对象mainLayout-addWidget(label1);/加入布局管理器mainLayout-addWidget(label2); /加入布局管理器window.show();return a.exec();以上代码中,在用new创建两个QLabel对象时,并没有指定其父对象。但是通过布局管理器的addWidget()被加入到布局管理器之后,它们的父对象就成了window,因为布局管理器创建时指定了自己的父对象为window。它们之间的父子关系如图2-1-2所示:图2-1-2 父子关系情况2:将一个对象设置为一个QMainWindow对象的Cnetral Widget时,QMainWindow接管该对象的指针,并在合适的时间将其删除。例如:#include #include #include int main(int argc, char *argv)QApplication a(argc, argv);QMainWindow mainWindow;QLabel *label = new QLabel(Central Widget);/未指定父对象mainWindow.setCentralWidget(label);/label对象被mainWindow“收养”mainWindow.show();return a.exec();在以上代码中,用new创建label对象时,没有指定父对象。通过调用setCentralWidget将label设置为mainWindow的Central Widget之后,label对象被mainWindow“收养”,即mainWindow成为label的父对象。情况3:将一个QGraphicsItem对象加入到一个QGraphicsScene对象中之后,场景对象成为item对象的父对象。?2.2 动态创建与删除在菜单页面之间切换时,伴随着老菜单的删除(或隐藏),新菜单的创建(或显示)的内存操作动作。Qt是基于C+语言的。内存的分配回收采用new与delete操作符完成。如何动态的创建或删除指定类型的页面对象,有一定的技巧性。为了在不同菜单之间加以区分,通常会给每一个菜单页分配一个ID号。typedef enum ID_MENU=0, ID_NETWORK, ID_PICTURE, ID_SOUND, ID_CHANNEL, ID_TIME, /add new item here ID_PICTUREADVANCE, ID_SETUPITEM_MAXE_ID_SETUPITEM;在程序中执行菜单切换时,希望给定一个ID号,便能创建一个相应类型的菜单对象,或者给定一个ID号,便能删除一个相应类型的菜单对象。最简单的方法可以用switch-case结构实现。但是代码冗长,效率不高。一下提供了一种实现技巧:定义两个带模板类的函数:/用于创建指定类型的页面对象,在堆中分配内存templateMdiSubWindow *allocSc() return new _T();/用于删除指定类型的页面对象,回收堆中已分配的内存templatevoid delSc(MdiSubWindow *ptr) delete static_cast(ptr);/定义两个函数指针,去指向分配内存的函数typedef MdiSubWindow* (*fnAlloc)();typedef void (*fnDelete)(MdiSubWindow*);/定义一个结构体,将创建、删除函数指针封装在一个结构中struct SFunc fnAlloc pfnAlloc; fnDelete pfnDelete;在类的声明中加入private:QMap m_mapSc;/用于存储 ID-函数指针 对public:template int RegisterWindow(int nKey);/向m_mapSc中添加一个ID-函数指针 对protected:void RegisterWindows();/将所有的ID-函数指针 对加入m_mapScint CreateSubWindow(int nKey);/动态创建一个指定页面int DeleteSubWindow(int nKey);/动态删除一个指定页面在类的实现中,实现注册函数,完成一张map表:templateint WindowHandler:RegisterWindow(int nKey) SFunc *funcP=new SFunc; funcP-pfnAlloc=allocSc;/将函数指针指向分配内存的函数 funcP-pfnDelete=delSc;/将函数指针指向释放内存的函数 if(m_mapSc.contains(nKey) return 1; else m_mapSc.insert(nKey, funcP);/将ID-函数指针 对 插入m_mapSc return 0; void WindowHandler:RegisterWindows() RegisterWindow(SID_Base); RegisterWindow(SID_MainMenu);RegisterWindow(SID_SetupHandler);动态创建与删除的函数:int WindowHandler:CreateSubWindow(int nKey) if(nKey = SID_Max) return 1; else m_pCurSubWin = m_mapScnKey-pfnAlloc();/分配内存 connect(m_pCurSubWin, SIGNAL(sig_goto_screen(int, int, int), this, SLOT(slot_GotoScreen(int, int, int);/动态关联信号-槽 return 0; Int WindowHandler:DeleteSubWindow(int nKey) if(nKey = SID_Max) return 1; else m_mapScnKey-pfnDelete(m_pCurSubWin);/释放内存 return 0; 本项目实现中,有两个地方用到了这种动态创建与删除的技巧。一处是在WindowHandler类中,实现BaseMenu、MainMenu、SetupHandler之间的切换;一处是在SetupHandler类中,实现Picture、PictureAdvance、Sound等subItem之间的切换。3 控件美化在电视机UI界面中,通常会用到一些控件进行信息描述,比如亮度调节时的滑块调节控件,自动搜台时的进度控件等。Qt提供了许多现成控件可供使用,并且每种控件都有一个setStyleSheet()方法,可以设置控件的各种样式。掌握了setStyleSheet()的使用方法,就很容易实现UI设计方案中外观精美的控件。Qt Style Sheets 功能非常强大,利用它可以定制各种控件的外观。Style sheets可通过QApplication:setStyleSheet()应用到整个应用程序中,也可以通过QWidget:setStyleSheet()应用于某个特定的控件。若有多个style sheets在不同的层级上应用,Qt会将这些应用叠加起来。Qt Style Sheets的概念、术语、语法受HTML的层叠样式表(CCS:Cascading Style Sheets)启发甚深,其使用的术语、语法几乎与HTML CSS相同。要熟练的使用Qt Style Sheets,对CSS语法要有一定的了解。3.1 CSS中的若干概念Qt控件在设置Style Sheets时,需要清楚每种属性的语法,下面就最常用到的几个CSS概念加以介绍。3.1.1 CSS中的Box model当使用style sheets时,每个控件都被当作一个具有四个同中心矩形的box来对待,这四个矩形分别是:margin矩形、border矩形、padding矩形及content矩形。四个同心矩形可由图3-1-1表示如下:图3-1-1 box的矩形概念每个box都有一个content 区域(文字,图像等),周围可选择的padding,border,以及margin区域。每个区域的尺寸有下面定义的属性加以指定。图3-1-1显示了这些区域之间的依赖关系。margin, border,padding,这三者可以分成top,right,bottom,left四个段(例如,图3-1-2中,“LM”代表left margin,“RP”代表right padding,而“TB”代表top border,等等)。这四个区域(content, padding, border, margin)的周界被称作edge,所以每个box有4个edge:content edge(或者称为inner edge):content edge 环绕在box的width与height定义的矩形的周围,通常由元素的渲染内容决定。四个content edges定义出了box的content box。图3-1-2 Box model区域padding edge:padding edge 围绕在box padding的四周。如果padding的width为0,则padding edge与content edge相同。四个padding edges定义了box的padding box。border edge:border edge围绕在box的border四周。如果border的width为0,则border edge与padding edge相同。四个border edges定义了box的border box。margin edge(或者称为outer edge):margin edge围绕在box margin的四周。如果margin的width为0,则margin edge与border edge相同。四个margin edges定义了box的margin box。margin、border-width、padding属性在缺省情况下均被置为0。此时,四个矩形将精确重合。3.1.2 background相关属性每个box都有一个背景层,这个背景层可能是全透明的(缺省值),可能填充了某种颜色,也可能填充了一幅或几幅图像。背景属性指明了需要使用哪种颜色(background-color),使用哪些图像(background-image),以及它们的尺寸,位置等等。可以用background-color属性指定box的背景色。背景色将绘制于任何背景图像之后。例如:QLabel background-color: red /* opaque red */QLabel background-color: #FF0000 /* opaque red */QLabel background-color: rgba(255, 0, 0, 75%) /* 75% opaque red */QLabel background-color: rgb(255, 0, 0) /* opaque red */QLabel background-color: rgb(100%, 0%, 0%) /* opaque red */QLabel background-color: hsv(60, 255, 255) /* opaque yellow */QLabel background-color: hsva(240, 255, 255, 75%) /* 75% blue */可以用background-image属性为控件指定一个背景。缺省情况下,background-image仅被绘制在border区域的内部。若想绘制在border区域以外,可以使用background-clip属性。使用background-repeat与background-origin来控制背景图像是否在空间上重复以及重复绘制的方向。一个background-image不会根据控件的尺寸来缩放自身的大小。若想让背景图像按照控件尺寸进行缩放,必须使用border-image属性。border-image属性提供了一个可变的背景,当指定了一个border-image时,便不需要再指定background-image了。若是同时指定了这两个属性,则border-image会将background-image覆盖。例如:QFrame background-color: rgba(0,255,0,100) /*设置背景色*/QFrame background-image: url(:/ball.png) /*设置背景图片*/QFrame background-repeat: repeat-x /*在x轴方向重复绘制背景*/*repeat-y在y轴方向重复绘制背景*/*no-repeat不重复绘制背景*/*默认情况下,x,y方向均会被重复绘制*/QFrame border: 1px solid #ff0000 /*设置border的宽度,线形,颜色*/综合设置后的结果图3-1-3所示:图3-1-3 style sheet设置效果3.2 利用style sheet设置QSlider控件Qt中默认的QSlider控件外观如图3-2-1所示:图3-2-1 QSlider控件默认外观QSlider控件由两部分组成:groove与handle。可以看成是由两个box叠加构成的。用style sheet来改变QSlider控件外观最直接的方法就是分别设置groove与handle两个box的background image。其中的handle部分的box,其高度是跟groove的box高度相关的。在设置handle box的尺寸时,只需设置其宽度。若需要调整handle box的高度,则可利用handle box的margin来调整。下面给出一段设置QSlider外观的参考代码,其效果如图3-2-2所示:QSlider *slider = new QSlider(parent);slider-setMaximum(100);/设置最大值slider-setMinimum(0); /设置最小值slider-setGeometry(10,60,350,35);/设置几何参数slider-setValue(45);/设置当前值slider-setOrientation(Qt:Horizontal);/设置控件方向,水平slider-setStyleSheet(QSlider:groove:horizontal n/*设置groove属性*/ border: 1px solid #00ff00;n height: 23px; n width: 326px; n background-image: url(:/bar.png);n background-repeat:none;n margin: 0px 0px 0px 0px;n n QSlider:handle:horizontal n/*设置handle属性*/ background-image: url(:/ball.png);n border: 1px solid #ff0000;n width: 16px;n margin: 1px 0px 1px 0px; n/*设置上下边距缩小height*/ background-repeat:none;n border-radius: 0px;n/*设置border的弯角属性*/ );图3-2-2 slider设置之后的效果还可以将QSlider控件分成4部分:groove、handle、add page、sub page。其中add page是控制handle右侧的groove外观;sub page是控制handle左侧的groove外观。下面给出一段示例代码,其效果如图3-2-3所示:QSlider *slider = new QSlider(Qt:Horizontal);slider-setRange(0, 100);/set default rangeslider-setStyleSheet(QSlider:groove:horizontal border-image: url(:/prgbar_bg_unsel.png); height:21px; margin:-7px 0px -9px 0px; left: 11px; right: 10px; QSlider:handle:horizontal border-radius: 5px; margin: -7px -10px -8px -10px; background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.34, fx:0.5, fy:0.5, stop:0.293785 rgba(255, 255, 255, 255), stop:0.379831 rgba(236, 236, 236, 180), stop:0.830508 rgba(255, 158, 158, 0); QSlider:sub-page:horizontalborder-top-left-radius: 3px; border-bottom-left-radius: 3px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D6D6D6, stop:1 #7B7B7B); QSlider:add-page:horizontal );图3-2-3 slider设置之后的效果在上段示例代码中,handle并未使用背景图片,而是用color pattern填充了background-color。sub page部分同样使用了color pattern进行颜色填充。型如qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #D6D6D6, stop:1 #7B7B7B);的颜色样式可以利用Qt Designer工具进行编辑,同时工具中也提供了多种固定的样式,可以在这些固定样式的基础上进行修改,也可以进行全新的创作。Qt Designer中的propertystyle sheetAdd Gradient background-color中的各种pattern对应的效果如图3-2-4所示:图3-2-4 Qt Designer中提供的固定样式4 事件处理机制所有Qt应用程序都是由事件驱动的,程序的每个动作都是由幕后某个事件所触发。应用程序在main函数中进行初始化,然后在main函数中由一个QApplication对象调用exec方法,这就启动了应用程序的事件循环。事件循环负责处理两个任务:A 事件循环负责处理来自系统的事件(比如重新绘制一个窗口区域)。事件循环将接收到的来自系统的事件转换成基于Qt的事件类型,转换后的事件被封装成以QEvent作为基类的派生类。B 事件循环负责处理Qt自身产生的事件(比如某个指定时间到而触发的定时事件)。这些事件也是基于QEvent的,被事件循环处理。每个QEvent都有一个类型。QEvent的子类可以包含所需的全部信息。比如,QMouseEvent处理按键及鼠标位置的信息。Qt通过QCoreApplication:postEvnets()将事件传递给特定的对象。这些接收事件的对象必须继承自QObject。这个方法的第一个参数是接收事件的对象,第二个参数是QEvent对象。postEvents()将事件传递给目标对象的event()方法。event()方法的任务是依据接收事件的对象的所属类的需求来决定处理或忽略接收到的事件。这个event()方法也被称作事件句柄(event handler)。如果一个事件不能被接收方立即处理,该事件将被放入一个队列然后延迟处理。根据Qt处理事件的机制,可以将事件处理由强到弱分成几个级别(强者先处理):给QApplication对象安装事件过滤器:一旦给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个eventFilter()。在Qt对象上安装事件过滤器:事件过滤器可以让对象A接收、处理对象B的事件。对于对象B的每个事件,先由A接收并处理(被A的eventFilter()过滤一遍),然后剩下还需处理的事件再传递给对象B去处理。重载event()函数:通过重载event()函数,可以在事件被特定的事件处理函数处理之前处理它。重载特定事件处理函数:比如重载keyPressEvent(), paintEvent(),timerEvent()等。4.1 事件过滤器的应用(eventFilter)在进行事件过滤之前,必须先安装事件过滤器。假设需要给对象B安装事件过滤器,对象B的事件先被A过滤一遍,再传到B。那么,首先要在对象A的构造函数中调用installEventFilter()函数来监视对象B的事件。安装过滤器之后,对象B完全放弃了它自己的事件,由对象A来决定是将本属于B的事件过滤掉还是将它们传递给B。在A中需要重载eventFilter()函数,其定义如下:bool QObject:eventFilter(QObject *watched, QEvent *e);参数watched允许对来自多个被监视的对象加以区分,参数e是被处理的事件。返回值告诉Qt的事件系统,该如何处理事件。如果返回false,事件e将被传递给被监视的对象;如果返回true,事件e将被过滤掉,这意味着该事件将不会传递到原本想到达的目标对象。event filter的常见应用场合:用来处理热键 比如一个界面上可以由用户热键来触发的多个按钮。 由于只有得到焦点的控件才能获得键盘的事件, 如果不用event filter就需要给每个button都加上键盘事件的处理, 还要在button里去访问兄弟button的指针, 逻辑非常混乱。 如果由主窗体做各个按钮的eventFilter, 则只需要在主窗体里去处理键盘事件就好, 而且主窗体可以很容易的访问到各个button的指针, 很方便。用来代替派生和重写虚函数 Qt里的键盘鼠标事件基本上都是以虚函数的方式来处理, 要想重写虚函数则必须派生一个子类, 这样的话如果只是一个简单的事件处理也去派生子类代价未免大了些, 这时候就值得用用eventFilter。 比如我的MDI界面想在每个子窗体关闭的时候做一些统一的操作, 一般的做法是处理子窗体的closeEvent。 但显然给每个子窗体都去派生个子类太不现实, 最好的方法是把mainwindow作为子窗体的eventFilter去处理CloseEvent事件。以下是一个应用事件过滤器的实例。该实例所实现的主要功能是,在一个含有QTextEdit对象与QTextBrowser对象的对话框中,向QTextEdit控件中输入字符,然后按“Return”键或“Enter”键,内容被追加到QTextBrowser对象中,同时清除QTextEdit对象中的内容。实例的运行效果如图4-1-1所示:4-1-1 eventFilter实例运行效果实现代码如下所示:/ chatwindow.h#ifndef CHATWINDOW_H#define CHATWINDOW_H#include class QTextBrowser;class QTextEdit;class QEvent;class ChatWindow : public QWidgetQ_OBJECTpublic:ChatWindow(QWidget *parent = 0);bool eventFilter(QObject *watched, QEvent *e);/事件过滤器void submitChatText();/负责发送textprivate:QTextBrowser *conversationView;/文本浏览控件QTextEdit *chatEdit;/文本编辑控件;#endif / CHATWINDOW_H/ chatwindow.cpp#include #include chatwindow.hChatWindow:ChatWindow(QWidget *parent): QWidget(parent)QVBoxLayout *lay = new QVBoxLayout(this);QSplitter *splitter = new QSplitter(Qt:Vertical, this);lay-addWidget(splitter);conversationView = new QTextBrowser;chatEdit = new QTextEdit;splitter-addWidget(conversationView);splitter-addWidget(chatEdit);chatEdit-installEventFilter(this);/给chatEdit对象安装事件过滤器setWin

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论