[size=x-small;]? ? <span>之所以写这篇文章,是因为之前做了一些关于修改[/size]<span lang="EN-US">QtDemo</span><span>的工作,而且之前的备忘里承诺过,虽然不一定很多人记得或者看过,但说到就要做到,哪怕说给自己听的事情。</span></span>
?
[size=x-small;]
[/size]
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]?[/size]</span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]?[/size]</span>
<p class="MsoNormal">[size=x-small;]<span lang="EN-US">C:\Qt\4.5.0\demos\qtdemo [/size]<span>如果你已经配置了</span><span lang="EN-US">QT </span><span>,那么在</span><span lang="EN-US">QT
Assistant</span><span>下面就有</span><span lang="EN-US">qtdemo</span><span>的可执行文件,就可以看看效果了。</span></span>
<p class="MsoNormal">[size=x-small;]<span>? ?现在简单的说明下源码的文件分类和基本作用,由于每个[/size]<span lang="EN-US">CPP</span><span>文件都对应一个</span><span lang="EN-US">H</span><span>文件,所以我就去除后缀,只列出名字:</span></span>
<p class="MsoNormal">[size=x-small;]<span lang="EN-US">Colors//[/size]<span>主要是一些配置信息,比如是否显示动画效果,还有字体大小之类的</span></span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Demoitemanimation[/size]</span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Dockitem[/size]</span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Guideline[/size]</span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Headingitem[/size]</span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Itemcircleanimation[/size]</span>
<p class="MsoNormal">[size=x-small;]<span lang="EN-US">Score//[/size]<span>动画效果实现的类,文字飞入飞出</span></span>
<p class="MsoNormal">[size=x-small;]<span lang="EN-US">Textbutton//[/size]<span>定义显示在左边的按钮的一些属性,如果需要修改按钮的外观或者位置,在这里修改</span></span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]?[/size]</span>
<p class="MsoNormal" style="text-align: left;" align="left">[size=x-small;]<span>这个是主窗体类,其实就是整个程序[/size]<span lang="EN-US">UI</span><span>的核心部分,所以前面说的去除不想要的两个图片,或者修改背景图(我觉得背景可以不修改,修改的话需要调整</span><span lang="EN-US">Button</span><span>大小,增加工作量),或者程序图标,都在这个类中,只要根据资源图片的名字,查找代码里面出现这些名字的位置,很容易发现和去掉的,这里就不麻烦大家,我列出在</span><span lang="EN-US">mainwindow.h</span><span>的下面有两个</span><span lang="EN-US">ImageItem*</span><span>的对象,就是图标,去掉即可。其他部分,相信只要熟悉</span><span lang="EN-US">GUI</span><span>编程的,很容易定制,就不多说了。</span></span>
<p class="MsoNormal" style="text-align: left;" align="left"><span lang="EN-US">[size=x-small;]Menumanager[/size]</span>
<p class="MsoNormal" style="text-align: left;" align="left">[size=x-small;]<span>首先看[/size]<span lang="EN-US">BUTTON_TYPE</span><span>的定义,明显看出它仅仅支持三个层次,所以只定义到</span><span lang="EN-US">Menu2</span><span>,第一件要做的事情就是扩展这个定义,一定要保持连续性,这样方便处理。改成这样:</span></span>
<p class="MsoNormal" style="text-align: left;" align="left">?
enum BUTTON_TYPE {ROOT, MENU1, MENU2, MENU3, MENU4, MENU5, MENU6, QUIT, FULLSCREEN, UP, DOWN, BACK};
?
?
<p class="MsoNormal" style="text-align: left;" align="left"><span lang="EN-US">[size=x-small;]?[/size]</span>
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);//创建叶子按钮
}
}
?
?
<p class="MsoNormal" style="text-align: left;" align="left"><span>[size=x-small;]其他一些边角的按钮我就不细说了,修改方法也很简单,找到也很简单,根据名字搜索即可找到按钮生成的代码了。[/size]</span>
<p class="MsoNormal" style="text-align: left;" align="left">[size=x-small;]<span>这个类另一个重点在于,既然扩展了层次的数量,那么原来的[/size]<span lang="EN-US">BACK</span><span>按钮将不适用了(之前的处理方式是,不管你点到什么程度点</span><span lang="EN-US">BACK</span><span>一律回到</span><span lang="EN-US">ROOT</span><span>目录)所以需要修改以支持多级目录,我的方法是采用一个结构记录回退层次以及按钮的</span><span lang="EN-US">ID</span><span>,处理逻辑是,每次点击按钮,就做适当的记录,然后点</span><span lang="EN-US">Back</span><span>后,弹出最近的那个记录,然后恢复到那个点(</span><span lang="EN-US">ItemSelect</span><span>就是按钮点击事件的处理逻辑,参数是按钮的</span><span lang="EN-US">BUTTON_TYPE</span><span>和名字)</span></span>
<p class="MsoNormal" style="text-align: left;" align="left">?
<p class="MsoNormal" style="text-align: left;" align="left">?
//后进先出队列记录 返回位置
typedef struct
{
BUTTON_TYPE type;
QString ID;
}BackPoint;
QList<BackPoint> backList;//记录回退地址
void setBackList( BackPoint );
?
<p class="MsoNormal" style="text-align: left;" align="left">层次按钮事件处理部分代码(itemSelected的一个Case):
<p class="MsoNormal" style="text-align: left;" align="left">?
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;
?
?
<p class="MsoNormal" style="text-align: left;" align="left">?
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; }
?
<p class="MsoNormal" style="text-align: left;" align="left">[size=x-small;]<span>上面这些基本把该做的大部分事情都弄了,剩下的问题就是如何显示每个部分的文字和图示了,由于[/size]<span lang="EN-US">qtdemo</span><span>涉及到</span><span lang="EN-US">qhelpEngine</span><span>我们其实不需要使用的,所以要去掉它,方法是,屏蔽头文件,然后慢慢找出依赖的语句,一句句删干净就好了。很暴力,但也很简单。当然删完,就要自己把要显示的数据呈现出来了。以下两个类做的事情差不多,都是读取文字和图片,然后显示,但作用的位置不一样,</span><span lang="EN-US">Exmpleconten</span><span>作用于叶子节点,</span><span lang="EN-US">Menucontent</span><span>作用于目录节点。所以合并起来说。</span></span>
<p class="MsoNormal"><span lang="EN-US">[size=x-small;]Memucontent[/size]</span>
<p class="MsoNormal">[size=x-small;]<span>我们采用的方法比较简单,就是从文件中读取跟按钮名字同名的文件,文字就从[/size]
<span lang="EN-US">./data/</span><span>(按钮名)</span><span lang="EN-US">.txt</span><span>文件读入,图片优先从</span><span lang="EN-US">XML</span><span>节点</span><span lang="EN-US">image</span><span>属性指定的路径读入,如果没有再从</span><span lang="EN-US">./image/</span><span>(按钮名)</span><span lang="EN-US">.png</span><span>读入。然后显示。所以比较简单,仅仅支持单一图片,所以如果你需要进一步扩展可能需要对程序了解更多才行。代码如下:</span></span>
<p class="MsoNormal">[size=x-small;]<span lang="EN-US">Main <span>?[/size]</span><span>不用理会</span><span lang="EN-US">…</span></span>
<p class="MsoNormal">[size=x-small;]<span>这个程序的使用十分简单,仅需根据你要编辑的内容结构,生成一个[/size]
<span lang="EN-US">./xml/example.xml </span><span>文件,然后在</span><span lang="EN-US">./data/ </span><span>下根据按钮名字写好描述性的文字,规则如前所述。执行程序即可,扩展起来也很方便,但其缺点是,节点名字不能重合,除非他们使用相同的描述文字或者图示。不让后面的节点信息会覆盖前面的,我的处理方法是,给节点加</span><span lang="EN-US">” ”</span><span>空格以区分以及尽量不让节点重名。(这部分内容需要参考</span><span lang="EN-US">MenuManager::readInfoAboutExample</span><span>方法,他根据节点名从</span><span lang="EN-US">XML</span><span>中读入属性,所以如何实现可以重名的结构我也做过尝试但失败了)</span></span>
<p class="MsoNormal">如果你没有耐心看以上部分,我也整理了个可用的代码,提供大家学习交流。