之所以写这篇文章,是因为之前做了一些关于修改QtDemo的工作,而且之前的备忘里承诺过,虽然不一定很多人记得或者看过,但说到就要做到,哪怕说给自己听的事情。
不多扯了直接进入主题,qtdmemo想必大部分接触过Qt的都见过,他是一个用Qt实现的看起来非常炫的说明文档,里面列了很多基本的QT GUI程序的样例图,还可以点击运行样例,或者弹出代码说明文档。我主要看中的是它飞进飞出的文字想过还有动态的图标列表。当然如果一个正常的说明文档用CHM的是最好的,但如果你追求效果一定希望做一个和QTDemo一样的,本文就是简单的介绍一下该程序源码的结构,然后提供一些我已经完成的修改工作的总结和源码,希望对看的同志们有一定的帮助。
本文的目标是帮助你修改qtdemo,去除其中你不想要的动画和图片,然后扩展功能,实现多级目录(原程序只支持3级),去除QHelpEngine的依赖,实现按需载入自定义的说明文字和图示。(文末附带部分修改的源代码)
只要你安装了QT4,那么你一定有这个工程的源码,就在QT安装路径下,如:
C:\Qt\4.5.0\demos\qtdemo 如果你已经配置了QT ,那么在QT
Assistant下面就有qtdemo的可执行文件,就可以看看效果了。
现在简单的说明下源码的文件分类和基本作用,由于每个CPP文件都对应一个H文件,所以我就去除后缀,只列出名字:
Colors//主要是一些配置信息,比如是否显示动画效果,还有字体大小之类的
Demoitem//带Item名字的基类
Demoitemanimation
Demoscene
Dockitem
Guide
Guideline
Guidecircle
Headingitem
Imageitem
Itemcircleanimation
Letteritem
Score//动画效果实现的类,文字飞入飞出
Scanitem
Textbutton//定义显示在左边的按钮的一些属性,如果需要修改按钮的外观或者位置,在这里修改
以上的文件,除了需要了解Colors里面的配置信息外,其他部分均可直接跳过。如果需要修改,则可以根据目标,还有名字去查看。因为我发现他们和我要达到的目标无联系,故没有深入研究这些细节部分的内容。下面的类才是工作的重点,将会一一细讲。
Mainwindow
这个是主窗体类,其实就是整个程序UI的核心部分,所以前面说的去除不想要的两个图片,或者修改背景图(我觉得背景可以不修改,修改的话需要调整Button大小,增加工作量),或者程序图标,都在这个类中,只要根据资源图片的名字,查找代码里面出现这些名字的位置,很容易发现和去掉的,这里就不麻烦大家,我列出在mainwindow.h的下面有两个ImageItem*的对象,就是图标,去掉即可。其他部分,相信只要熟悉GUI编程的,很容易定制,就不多说了。
Menumanager
这个是核心部分,因为这里管理这整个程序MENU的结构以及相应的按钮和部件的初始化。涉及的操作有读取XML配置文件,生成显示按钮的列表,根据每个按钮所处位置的不同,定义不一样的值这个值会导致点击每个按钮时候的响应事件不同。同时menumanager是一个单例模式的典型应用(请看改类开头的注释)
首先看BUTTON_TYPE的定义,明显看出它仅仅支持三个层次,所以只定义到Menu2,第一件要做的事情就是扩展这个定义,一定要保持连续性,这样方便处理。改成这样:
enum BUTTON_TYPE {ROOT, MENU1, MENU2, MENU3, MENU4, MENU5, MENU6, QUIT, FULLSCREEN, UP, DOWN, BACK};
然后是XML的读取,是readXmlDocument在menumanager初始化的时候就读入了,然后后面会根据读入的内容遍历节点生成按钮的列表(即目录结构)。要扩展目录结构,首先要扩展XML文档的定义,根据我的定义,层次节点的命名最好统一,虽然不统一其实不影响Menu的生成,但会造成录入时候的混乱。每一个节点要包含filename和name属性,image属性可有可无(不一定每个部分都需要图示)。定义完XML扩展规则后就可以修改对XML的处理过程了,因为原始只处理到第三层(包括ROOT)。首先看按钮生产的函数,有三种,一个是createRootMenu生成根目录,createSubMenu生成非叶子(即目录节点),createLeafMenu生成叶子节点。需要做修改就是createSubMenu因为原来仅仅支持三层,Root-Menu-Leaf 每个Menu没有层次的区别和概念,但我们的需求是扩展Menu支持多层,那么Menu就需要有层次的概念了,所以加入一个类型为BUTTON_TYPE的参数区分Menu的层次。之所以先提这些是因为XML和这几个函数很有关系,在menumanager::init函数里负责初始化工作,这里对XML的节点进行遍历,载入节点的属性和信息(readInfoAboutExample函数的工作),然后根据节点的层次不同生成不同的按钮。同时根据XML节点Name的属性对按钮进行命名,按钮显示的内容默认就是name的值,我这里采用的是广度优先的遍历方法遍历所有XML节点,依次生成列表,代码如下:
typedef struct
{
BUTTON_TYPE type;
QDomElement element;
}NodeRecode;
// Create first level menu:
QDomElement rootElement = this->contentsDoc->documentElement();
this->createRootMenu(rootElement);
QDomNode node;
NodeRecode recode;
recode.type = MENU1;
QList<NodeRecode> nodeList;
node = rootElement.firstChild();
while(!node.isNull())//将根节点的子节点放入队列,一遍遍历
{
recode.element = node.toElement();
nodeList.push_back(recode);
node =node.nextSibling();
}
//遍历所有节点
while(!nodeList.isEmpty())
{
recode= nodeList.takeFirst();//队列中取出一个节点
if(recode.element.attribute("name")==NULL)//无名字节点剔除不做任何处理,包括子节点
{
continue;
}
this->readInfoAboutExample(recode.element);
if( info[recode.element.attribute("name")]["isLeaf"] != "true")//非叶子
{
recode.type = BUTTON_TYPE((int)recode.type + 1);// BUTTON_TYPE中的MENU必须按顺序摆放
this->createSubMenu(recode.element,recode.type);//创建目录按钮
node = recode.element.firstChild();
while(!node.isNull())
{
recode.element = node.toElement();
nodeList.push_back(recode);
node = node.nextSibling();
}
}
else//叶子
{
this->createLeafMenu(recode.element);//创建叶子按钮
}
}
其他一些边角的按钮我就不细说了,修改方法也很简单,找到也很简单,根据名字搜索即可找到按钮生成的代码了。
这个类另一个重点在于,既然扩展了层次的数量,那么原来的BACK按钮将不适用了(之前的处理方式是,不管你点到什么程度点BACK一律回到ROOT目录)所以需要修改以支持多级目录,我的方法是采用一个结构记录回退层次以及按钮的ID,处理逻辑是,每次点击按钮,就做适当的记录,然后点Back后,弹出最近的那个记录,然后恢复到那个点(ItemSelect就是按钮点击事件的处理逻辑,参数是按钮的BUTTON_TYPE和名字)
这个是回退记录的结构和处理的代码,以及包含了层次节点按钮事件处理的代码,以及BACK按钮事件的代码:
回退结构定义:
//后进先出队列记录 返回位置
typedef struct
{
BUTTON_TYPE type;
QString ID;
}BackPoint;
QList<BackPoint> backList;//记录回退地址
void setBackList( BackPoint );
层次按钮事件处理部分代码(itemSelected的一个Case):
注:以下的所有this->score都是处理文字飞入飞出的,可以无视,保留之即可。
case MENU1:
type=MENU1;
goto here;
case MENU2:
type=MENU2;
goto here;
case MENU3:
type=MENU3;
goto here;
case MENU4:
type=MENU4;
goto here;
case MENU5:
type=MENU5;
goto here;
case MENU6://如果需要添加层次 依上添加即可BUTTON_TYPE中 MENU N 必须按序排列否则会出错
type=MENU6;
goto here;
here:
if(info[menuName]["isLeaf"] == "true")//叶子节点处理方法
{
//对于Back按钮的操作,叶子节点不记录会跳路径
this->score->queueMovie(this->currentInfo + " -out", Score::NEW_ANIMATION_ONLY);
this->score->queueMovie(this->currentInfo + " -buttons -out", Score::NEW_ANIMATION_ONLY);
// book-keeping:
this->currentMenuCode = type;
this->currentInfo = menuName;
// in / shake:
this->score->queueMovie("upndown -shake");
this->score->queueMovie("back -shake");
this->score->queueMovie(this->currentMenu + " -shake");
this->score->queueMovie(this->currentInfo, Score::NEW_ANIMATION_ONLY);
this->score->queueMovie(this->currentInfo + " -buttons", Score::NEW_ANIMATION_ONLY);
if (!Colors::noTicker){
this->score->queueMovie("ticker -out", Score::NEW_ANIMATION_ONLY);
this->window->switchTimerOnOff(false);
}
}
else//非叶子节点(即目录节点)的操作
{
//目录节点需要记录回跳路径
this->score->queueMovie(this->currentMenu + " -out", Score::FROM_START, Score::LOCK_ITEMS);
this->score->queueMovie(this->currentMenuButtons + " -out", Score::FROM_START, Score::LOCK_ITEMS);
this->score->queueMovie(this->currentInfo + " -out");
// book-keeping:
this->currentMenuCode = type;
this->currentCategory = menuName;
tmp.type = type;
tmp.ID = menuName;
this->setBackList(tmp);
this->currentMenu = menuName + " -menu1";
this->currentInfo = menuName + " -info";
// in:
this->score->queueMovie("upndown -shake");
this->score->queueMovie("back -in");
this->score->queueMovie(this->currentMenu, Score::FROM_START, Score::UNLOCK_ITEMS);
this->score->queueMovie(this->currentInfo);
if (!Colors::noTicker)
this->ticker->useGuideTt();
}
break;
BACK按钮处理代码(ItemSelected的一个Case)
case BACK:{
// out:
this->score->queueMovie(this->currentInfo + " -out", Score::NEW_ANIMATION_ONLY);
this->score->queueMovie(this->currentInfo + " -buttons -out", Score::NEW_ANIMATION_ONLY);
// book-keeping:
this->currentMenuCode --;
this->currentMenuButtons = this->currentCategory + " -buttons";
this->currentInfo = this->currentCategory + " -info";
// in / shake:
this->score->queueMovie("upndown -shake");
this->score->queueMovie(this->currentMenu + " -shake");
this->score->queueMovie(this->currentInfo, Score::NEW_ANIMATION_ONLY);
this->score->queueMovie(this->currentInfo + " -buttons", Score::NEW_ANIMATION_ONLY);
if (!Colors::noTicker)
{
this->ticker->doIntroTransitions = false;
// this->tickerInAnim->startDelay = 500;
this->score->queueMovie("ticker", Score::NEW_ANIMATION_ONLY);
this->window->switchTimerOnOff(true);
}
if(this->backList.size() >=1 )//如果链表中节点多于两个
{
this->backList.removeLast();//剔除当前节点
}
if(this->backList.isEmpty() )//无节点,则跳到跟节点
{
itemSelected(ROOT,Colors::rootMenuName);
}
else//还有节点剩余,即为需要跳到的点
{
BackPoint point=this->backList.takeLast();
itemSelected(point.type,point.ID);
}
break; }
上面这些基本把该做的大部分事情都弄了,剩下的问题就是如何显示每个部分的文字和图示了,由于qtdemo涉及到qhelpEngine我们其实不需要使用的,所以要去掉它,方法是,屏蔽头文件,然后慢慢找出依赖的语句,一句句删干净就好了。很暴力,但也很简单。当然删完,就要自己把要显示的数据呈现出来了。以下两个类做的事情差不多,都是读取文字和图片,然后显示,但作用的位置不一样,Exmpleconten作用于叶子节点,Menucontent作用于目录节点。所以合并起来说。
Examplecontent
Memucontent
只有几个函数比较中要,分别是loadDescription(载入文字描述,显示出来),getImage(读取图片),getDescription(读取文字描述),createConten(设置显示的方式布局和字体之类的)。
我们采用的方法比较简单,就是从文件中读取跟按钮名字同名的文件,文字就从
./data/(按钮名).txt文件读入,图片优先从XML节点image属性指定的路径读入,如果没有再从./image/(按钮名).png读入。然后显示。所以比较简单,仅仅支持单一图片,所以如果你需要进一步扩展可能需要对程序了解更多才行。代码如下:
Main 不用理会…
这个程序的使用十分简单,仅需根据你要编辑的内容结构,生成一个
./xml/example.xml 文件,然后在./data/ 下根据按钮名字写好描述性的文字,规则如前所述。执行程序即可,扩展起来也很方便,但其缺点是,节点名字不能重合,除非他们使用相同的描述文字或者图示。不让后面的节点信息会覆盖前面的,我的处理方法是,给节点加” ”空格以区分以及尽量不让节点重名。(这部分内容需要参考MenuManager::readInfoAboutExample方法,他根据节点名从XML中读入属性,所以如何实现可以重名的结构我也做过尝试但失败了)
如果你没有耐心看以上部分,我也整理了个可用的代码,提供大家学习交流。
分享到:
相关推荐
### Qt事件机制浅析 #### 一、什么是事件? 在Qt框架中,事件是指系统或应用程序内部发生的某种“动作”,这种“动作”需要被特定的对象所感知并作出响应。这里的“动作”是一种广义上的概念,既包括用户交互操作...
### QT信号与槽机制浅析 #### 一、引言 在现代图形用户界面(GUI)设计与开发领域,Qt框架以其强大的跨平台能力和丰富的库功能备受开发者青睐。Qt中的信号与槽机制作为其核心特性之一,极大地简化了GUI程序中各组件...
浅析电力系统感性无功补偿 浅析电力系统感性无功补偿
浅析网络安全技术.pdf浅析网络安全技术.pdf浅析网络安全技术.pdf
浅析现代物流管理趋势与发展策略.doc
WDM PON设备浅析
2021年浅析数字化工厂五大核心系统.pdf
浅析人工智能体系建设.docx
全生命周期BOM管理浅析.pdf
STUN和TURN技术浅析
浅析美国市场经济模式.docx
浅析智能家居无线组网模式.pdf
微服务架构浅析V436页.ppt
浅析两种安全运维管理模式 (2).docx浅析两种安全运维管理模式 (2).docx浅析两种安全运维管理模式 (2).docx浅析两种安全运维管理模式 (2).docx浅析两种安全运维管理模式 (2).docx浅析两种安全运维管理模式 (2).docx...
浅析路由器相关浅析路浅析路由器相关技术由器相关技术技术浅析路由器相关技术
浅析金融经济管理中信息化的应用及创新.pdf浅析金融经济管理中信息化的应用及创新.pdf浅析金融经济管理中信息化的应用及创新.pdf浅析金融经济管理中信息化的应用及创新.pdf浅析金融经济管理中信息化的应用及创新.pdf...
浅析微信小程序在图书馆阅读推广的应用.pdf浅析微信小程序在图书馆阅读推广的应用.pdf浅析微信小程序在图书馆阅读推广的应用.pdf浅析微信小程序在图书馆阅读推广的应用.pdf浅析微信小程序在图书馆阅读推广的应用.pdf...
浅析微信小程序在图书馆阅读推广的应用.docx浅析微信小程序在图书馆阅读推广的应用.docx浅析微信小程序在图书馆阅读推广的应用.docx浅析微信小程序在图书馆阅读推广的应用.docx浅析微信小程序在图书馆阅读推广的应用...