基于Qt技术的可移植UI设计
—— 基于Qt的电视机UI实现技术
1 UI软件架构
基于Qt来实现一套电视机用户界面(User Interface),首先需要选择一个软件框架,在Qt中,Main Window 为创建应用程序的UI提供了一个框架。QMainWindow类用于管理UI页面。
及其相关类共同完成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可以是多种类型: ? Qt提供的标准窗口部件,比如QWidget、QTextEdit等; ? 用户自定义的窗口部件;
? 布局管理器组织起来的多个widgets;
? 分裂器—QSplitter。QSplitter作为一个容器可以容纳多个窗口部件,此时中央部件是
一个包容多个窗口部件的容器;
? 多文档区部件—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、本项目中,由QMainWindow作为基类,派生出MainWindow类,除去基本的继承QMdiSubWindow三个类作为基类,派生出满足需求的类加以应用。
自QMainWindow的属性外,在MainWindow的构造函数中,加入了对主窗口几何位置、尺寸的设定;加入了主窗口背景属性的设定。最后将由QMdiArea类派生来的WindowHandler类的对象设置为Central Widget。
本项目中,由QMdiArea作为基类,派生出WindowHandler类,用以实现菜单页面
的管理。WindowHandler类中添加了用于页面之间跳转的槽函数slot_GotoScreen(int curSID, int dstSID, int param); 添加了注册各子菜单页面的函数 RegisterWindow(int nKey);
并给予每页菜单一个独一无二的ID号,将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所示:
QMdiSubWindowQMainWindowQMdiAreaMdiSubWindowMainWindow WindowHandler
BaseMenuMainMenuSetupHandler 图1-2-1 软件框架相关类间关系
1.3 Qt Graphics View 框架
本项目设置菜单实现动画切换效果,用到了Qt Graphics View 框架。该框架提供了一个用于管理大量自定义的二维图形item的平面,还提供了一个用于显示、缩放、旋转这些二维图形item的视图控件。结合本项目,与该框架相关的三个类分别为QGraphicsScene、QGraphicsView、QGraphicsProxyWidget。
QGraphicsScene类是诸多item的容器。需要动作的item首先被加入到QGraphicsView类是一个视口,用于观察QGraphicsScene及其中的各个item。可以QGraphicsScene中,归QGraphicsScene统一管理。
通过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相关的类之间的关系如下图所示:
QWidgetSetupSubItemPicturePictureAdvanceSound…...图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所示: