這篇博文主要講解360安全衛(wèi)士標(biāo)題欄的創(chuàng)建。關(guān)于標(biāo)題欄,我想大家應(yīng)該都非常熟悉了,其主要包括窗口關(guān)閉、最大化/還原、最小化等按鈕;但是標(biāo)題欄的這些按鈕都是非常有特色的。
在我寫這篇博文之前,我就已經(jīng)完成了類似360安全衛(wèi)士標(biāo)題欄的創(chuàng)建代碼,在開始寫代碼時,我仔細(xì)想了想360安全衛(wèi)士主界面標(biāo)題欄的構(gòu)建方法,它是自繪的還是貼圖的?所以我特意在360論壇查了查它皮膚制作的方法,并在它的安裝目錄下的找到了這些按鈕的特定圖片(皮膚文件解壓),即由貼圖來做的。
既然知道了構(gòu)建方法,那么就用代碼實(shí)現(xiàn)即可,我實(shí)現(xiàn)的效果圖如下(初始界面是這樣,標(biāo)題欄的其他效果和360安全衛(wèi)士的標(biāo)題欄效果一樣):
標(biāo)題欄也是自定義的部件(繼承于QWidget),在這個自定義的部件里,你想實(shí)現(xiàn)啥功能都可以。
一、部件構(gòu)建
部件構(gòu)建當(dāng)然是創(chuàng)建子部件,設(shè)置子部件樣式,給標(biāo)題欄設(shè)置布局管理等,這些也都是很基礎(chǔ)的。
標(biāo)題欄由三個QLabel和五個QToolButton組成,這五個QToolButton即為標(biāo)題欄最右邊的五個功能按鈕,首先當(dāng)然是創(chuàng)建這些子部件了。
//創(chuàng)建子部件 void TitleBar::CreateWidget() { //圖像標(biāo)簽--logo m_pLabelIcon = new QLabel(this); QPixmap objPixmap(":/image/360AboutLogo.png"); m_pLabelIcon->setPixmap(objPixmap.scaled(TITLE_H,TITLE_H)); //文本標(biāo)簽--標(biāo)題 m_pLabelTitle = new QLabel(this); m_pLabelTitle->setText(QString("360 Safe Guard V8.5")); //文本標(biāo)簽--樣式版本 m_pLabelVersion = new QLabel(this); m_pLabelVersion->setText(QString("Use Class Style")); //設(shè)置鼠標(biāo)形狀 m_pLabelVersion->setCursor(Qt::PointingHandCursor); //按鈕--更換皮膚 m_pBtnSkin = new QToolButton(this); //設(shè)置初始圖片 SetBtnIcon(m_pBtnSkin,eBtnStateDefault,true); //按鈕--菜單 m_pBtnMenu = new QToolButton(this); SetBtnIcon(m_pBtnMenu,eBtnStateDefault,true); //按鈕--最小化 m_pBtnMin = new QToolButton(this); SetBtnIcon(m_pBtnMin,eBtnStateDefault,true); //按鈕--最大化/還原 m_pBtnMax = new QToolButton(this); SetBtnIcon(m_pBtnMax,eBtnStateDefault,true); //按鈕--關(guān)閉 m_pBtnClose = new QToolButton(this); SetBtnIcon(m_pBtnClose,eBtnStateDefault,true); //獲得子部件 const QObjectList &objList = children(); for(int nIndex=0; nIndex<objList.count();++nIndex) { //設(shè)置子部件的MouseTracking屬性 ((QWidget*)(objList.at(nIndex)))->setMouseTracking(true); //如果是QToolButton部件 if(0==qstrcmp(objList.at(nIndex)->metaObject()->className(),"QToolButton")) { //連接pressed信號為slot_btnpress connect(((QToolButton*)(objList.at(nIndex))),SIGNAL(pressed()),this,SLOT(slot_btnpress())); //連接clicked信號為slot_btnclick connect(((QToolButton*)(objList.at(nIndex))),SIGNAL(clicked()),this,SLOT(slot_btnclick())); //設(shè)置頂部間距 ((QToolButton*)(objList.at(nIndex)))->setContentsMargins(0,VALUE_DIS,0,0); } } }
子部件創(chuàng)建完之后,就要設(shè)置這些子部件的基本樣式了,我使用qss樣式表對其進(jìn)行樣式設(shè)置,當(dāng)然還有其他方法。
View Code
//設(shè)置子部件樣式(qss) void TitleBar::SetWidgetStyle() { //設(shè)置標(biāo)簽的文本顏色,大小等以及按鈕的邊框 setStyleSheet("QLabel{color:#CCCCCC;font-size:12px;font-weight:bold;}QToolButton{border:0px;}"); //設(shè)置左邊距 m_pLabelTitle->setStyleSheet("margin-left:6px;"); //設(shè)置右邊距以及鼠標(biāo)移上去時的文本顏色 m_pLabelVersion->setStyleSheet("QLabel{margin-right:10px;}QLabel:hover{color:#00AA00;}"); }
最后就是創(chuàng)建布局管理器,把這些子部件加入到布局管理器中,我使用水平布局管理器,如下代碼所示:
View Code
//創(chuàng)建設(shè)置布局 void TitleBar::CreateLayout() { //水平布局 m_pLayout = new QHBoxLayout(this); //添加部件 m_pLayout->addWidget(m_pLabelIcon); m_pLayout->addWidget(m_pLabelTitle); //添加伸縮項(xiàng) m_pLayout->addStretch(1); //添加部件 m_pLayout->addWidget(m_pLabelVersion); m_pLayout->addWidget(m_pBtnSkin); m_pLayout->addWidget(m_pBtnMenu); m_pLayout->addWidget(m_pBtnMin); m_pLayout->addWidget(m_pBtnMax); m_pLayout->addWidget(m_pBtnClose); //設(shè)置Margin m_pLayout->setContentsMargins(0,0,VALUE_DIS,0); //設(shè)置部件之間的space m_pLayout->setSpacing(0); setLayout(m_pLayout); }
在這節(jié)中,設(shè)置按鈕圖片的函數(shù)為SetBtnIcon函數(shù),該函數(shù)的原型如下所示:
View Code
void SetBtnIcon(QToolButton *pBtn,eBtnMoustState state,bool bInit=false);
其中pBtn代表被設(shè)置圖片的按鈕,而eBtnMoustState是一個枚舉值,代表該按鈕當(dāng)前的狀態(tài),枚舉定義如下所示:
View Code
//枚舉,按鈕狀態(tài) enum eBtnMoustState{ eBtnStateNone,//無效 eBtnStateDefault,//默認(rèn)值(如按鈕初始顯示) eBtnStateHover,//鼠標(biāo)移到按鈕上狀態(tài) eBtnStatePress//鼠標(biāo)按下按鈕時狀態(tài) };
而bInit表示是否是初始化設(shè)置,因?yàn)樵赟etBtnIcon函數(shù)里需要獲得主界面最大化標(biāo)志值,而這時候主界面窗口構(gòu)造函數(shù)還沒完成,同時在SetBtnIcon函數(shù)里又需要獲得主界面窗口對象,因此會矛盾,所以使用了bInit標(biāo)志值進(jìn)行區(qū)分。
SetBtnIcon函數(shù)的定義如下代碼所示:
View Code
//設(shè)置按鈕不同狀態(tài)下的圖標(biāo) void TitleBar::SetBtnIcon(QToolButton *pBtn,eBtnMoustState state,bool bInit/*=false*/) { //獲得圖片路徑 QString strImagePath = GetBtnImagePath(pBtn,bInit); //創(chuàng)建QPixmap對象 QPixmap objPixmap(strImagePath); //得到圖像寬和高 int nPixWidth = objPixmap.width(); int nPixHeight = objPixmap.height(); //如果狀態(tài)不是無效值 if(state!=eBtnStateNone) { /*設(shè)置按鈕圖片 按鈕的圖片是連續(xù)在一起的,如前1/4部分表示默認(rèn)狀態(tài)下的圖片部分,接后的1/4部分表示鼠標(biāo)移到按鈕狀態(tài)下的圖片部分 */ pBtn->setIcon(objPixmap.copy((nPixWidth/4)*(state-1),0,nPixWidth/4,nPixHeight)); //設(shè)置按鈕圖片大小 pBtn->setIconSize(QSize(nPixWidth/4,nPixHeight)); } }
View Code
//獲得圖片路徑(固定值) const QString TitleBar::GetBtnImagePath(QToolButton *pBtn,bool bInit/*=false*/) { QString strImagePath; //皮膚按鈕 if(m_pBtnSkin==pBtn) { strImagePath = ":/image/SkinButtom.png"; } //菜單按鈕 if(m_pBtnMenu==pBtn) { strImagePath = ":/image/title_bar_menu.png"; } //最小化 if(m_pBtnMin==pBtn) { strImagePath = ":/image/sys_button_min.png"; } //最大化/還原按鈕,所以包括最大化和還原兩張圖片 if(m_pBtnMax==pBtn) { //如果是初始設(shè)置或者主界面的最大化標(biāo)志不為真(其中MainWindow::Instance()使用單例設(shè)計(jì)模式) if(bInit==true || MainWindow::Instance()->GetMaxWin()==false) { //最大化按鈕圖片路徑 strImagePath = ":/image/sys_button_max.png"; } else { //還原按鈕圖片路徑 strImagePath = ":/image/sys_button_restore.png"; } } //關(guān)閉按鈕 if(m_pBtnClose==pBtn) { strImagePath = ":/image/sys_button_close.png"; } return strImagePath; }
二、設(shè)置按鈕其他效果
各位在使用360安全衛(wèi)士的時候,把鼠標(biāo)移到關(guān)閉按鈕上或者使用鼠標(biāo)按下關(guān)閉按鈕,其呈現(xiàn)不同的圖片以示區(qū)分,當(dāng)然其他按鈕也一樣。那么是不是也和工具欄按鈕一樣,子類化一個按鈕了?不需要。使用事件過濾器,在標(biāo)題欄部件中進(jìn)行事件判斷和目標(biāo)判斷即可。
首先是創(chuàng)建事件過濾器,代碼如下所示:
View Code
//創(chuàng)建事件過濾器 void TitleBar::CreateEventFiter() { m_pBtnSkin->installEventFilter(this); m_pBtnMenu->installEventFilter(this); m_pBtnMin->installEventFilter(this); m_pBtnMax->installEventFilter(this); m_pBtnClose->installEventFilter(this); }
然后在標(biāo)題欄部件中重寫eventFilter函數(shù)即可,代碼如下:
View Code
//事件過濾 bool TitleBar::eventFilter(QObject *obj, QEvent *event) { //按鈕狀態(tài) eBtnMoustState eState = eBtnStateNone; //判斷事件類型--QEvent::Enter if (event->type() == QEvent::Enter) { eState = eBtnStateHover; } //判斷事件類型--QEvent::Leave if (event->type() == QEvent::Leave) { eState = eBtnStateDefault; } //判斷事件類型--QEvent::MouseButtonPress if (event->type() == QEvent::MouseButtonPress && ((QMouseEvent*)(event))->button()== Qt::LeftButton) { eState = eBtnStatePress; } //判斷目標(biāo) if(m_pBtnSkin==obj || m_pBtnMenu==obj || m_pBtnMin==obj || m_pBtnMax==obj || m_pBtnClose==obj) { //如果狀態(tài)有效 if(eState != eBtnStateNone) { //根據(jù)狀態(tài)設(shè)置按鈕圖標(biāo) SetBtnIcon((QToolButton *)obj,eState); return false; } } return QWidget::eventFilter(obj,event); }
即根據(jù)事件類型設(shè)置按鈕狀態(tài);最后在各個按鈕的click槽函數(shù)中實(shí)現(xiàn)相應(yīng)功能即可,如窗口關(guān)閉,最大化等。
View Code
//槽函數(shù)--slot_btnclick void TitleBar::slot_btnclick() { QToolButton *pBtn = (QToolButton*)(sender()); if(pBtn==m_pBtnMin) { emit signal_min(); } if(pBtn==m_pBtnMax) { emit signal_maxrestore(); } if(pBtn==m_pBtnClose) { emit signal_close(); } }
上述代碼實(shí)現(xiàn)是發(fā)送自定義信號;狀態(tài)欄部分由于很簡單就不描述了。