一、Qt自定义标题栏简介
QWidget及其子类窗体组件的标题栏受操作系统的控制,即标题栏的界面风格与操作系统的主题风格相同,工程实践中需要开发者自行定义,达到美化应用程序界面的目的。
二、Qt自定义标题栏实现
1、自定义标题栏的功能
自定义标题栏需要完成功能如下:
(1)自定义标题栏需要包含最小化按钮、最大化按钮、关闭按钮、标题标签、图标标签等图形元素。
(2)标题栏的拖拽。
(3)鼠标双击标题栏实现窗体的最大化、最小化。
2、自定义标题栏的界面布局
自定义标题栏的界面布局如下:
3、标题栏拖拽功能的实现
窗体的拖拽平移过程如下图:
当鼠标在窗体的标题栏按下并移动时,窗体会按照鼠标移动的轨迹进行平移。因此,窗体每次移动都是在当前位置按照鼠标移动的矢量进行移动。标题栏拖拽功能的实现需要实现mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件处理函数。
MouseEvent中的globalPos()函数返回的是相对屏幕的位置坐标,而pos()则是返回鼠标在当前控件(即捕获该鼠标事件的控件)中的位置。
QWidget窗体的geometry().topLeft()则返回的是当前窗体的左上角在屏幕中的位置。
startPos=event->globalPos();//鼠标的全局初始位置,按下时记住curWindowPos=geometry().topleft();//窗体的全局位置,移动时endPos=event->globalPos();//鼠标按下发生移动之后的位置,移动时move(curWindowPos+(startPos-endPos));//根据矢量移动方向是初始位置减去末位置,移动时startPos=endPos;//将初始位置记为上次末位置,然后执行直到释放拖拽,移动时
实现代码如下:
voidTitleBar::mousePressEvent(QMouseEvent*event){//鼠标左键按下事件if(event->button()==Qt::LeftButton){//记录鼠标左键状态m_leftButtonPressed =true;//记录鼠标在屏幕中的位置m_start =event->globalPos();}}voidTitleBar::mouseMoveEvent(QMouseEvent*event){//持续按住才做对应事件if(m_leftButtonPressed){//将父窗体移动到父窗体原来的位置加上鼠标移动的位置:event->globalPos()-m_startparentWidget()->move(parentWidget()->geometry().topLeft()+event->globalPos()-m_start);//将鼠标在屏幕中的位置替换为新的位置m_start =event->globalPos();}}voidTitleBar::mouseReleaseEvent(QMouseEvent*event){//鼠标左键释放if(event->button()==Qt::LeftButton){//记录鼠标状态m_leftButtonPressed =false;}}
4、标题栏双击实现最大化、最小化
鼠标双击事件处理函数mouseDoubleClickEvent实现如下:
voidTitleBar::mouseDoubleClickEvent(QMouseEvent*event){m_maximizeButton->click();}
最大化、最小化、关闭按钮的槽函数如下:
voidTitleBar::onClicked(){QPushButton*pButton=qobject_cast<QPushButton*>(sender());QWidget*pWindow=this->window();if(pWindow->isTopLevel()){if(pButton==m_minimizeButton){pWindow->showMinimized();}elseif(pButton==m_maximizeButton){pWindow->isMaximized()?pWindow->showNormal():pWindow->showMaximized();}elseif(pButton==m_closeButton){pWindow->close();}}}
三、Qt自定义窗体基类示例
1、自定义窗体基类的功能
自定义窗体基类的功能如下:
(1)自定义标题栏。
(2)增加内容组件,内容组件内部的界面布局完全由具体的用户决定。
2、自定义窗体基类的实现
TitleBar.h文件:
#ifndefTITLEBAR_H#defineTITLEBAR_H#include<QWidget>#include<QPushButton>#include<QLabel>#include<QHBoxLayout>#include<QEvent>#include<QMouseEvent>#include<QApplication>#include<QPoint>#include<QPixmap>#include<QString>/***@brief标题栏界面组件*@author*/classTitleBar:publicQWidget{Q_OBJECTpublic:explicitTitleBar(QWidget*parent=NULL);/***@brief设置标题栏标题*@paramtitle,参数,设置的标题*/voidsetWindowTitle(constQString&title);/***@brief设置标题栏的图标*@paramiconPath,参数,图标的路径*/voidSetTitleBarIcon(constQString&iconPath);protected:/***@brief鼠标双击事件处理函数*@paramevent,参数,事件*@note双击标题栏进行界面的最大化/还原*/virtualvoidmouseDoubleClickEvent(QMouseEvent*event);/***@brief鼠标按下事件处理函数*@paramevent,参数,事件*@note按下鼠标左键*/virtualvoidmousePressEvent(QMouseEvent*event);/***@brief鼠标移动事件处理函数*@paramevent,参数,事件*@note移动鼠标*/virtualvoidmouseMoveEvent(QMouseEvent*event);/***@brief鼠标释放事件处理函数*@paramevent,参数,事件*@note释放鼠标*/virtualvoidmouseReleaseEvent(QMouseEvent*event);/***@brief事件过滤处理器*@paramobj,参数*@paramevent,参数,事件*@return成功返回true,失败返回false*@note设置标题、图标*/virtualbooleventFilter(QObject*obj,QEvent*event);/***@brief最大化/还原*/voidupdateMaximize();protectedslots:/***@brief最小化、最大化/还原、关闭按钮点击时响应的槽函数*/voidonClicked();private:QLabel*m_iconLabel;QLabel*m_titleLabel;QPushButton*m_minimizeButton;QPushButton*m_maximizeButton;QPushButton*m_closeButton;QPointm_start;//起始点QPointm_end;//结束点boolm_leftButtonPressed;//鼠标左键按下标记};#endif//TITLEBAR_H
TitleBar.cpp文件:
#include"TitleBar.h"TitleBar::TitleBar(QWidget*parent):QWidget(parent){setFixedHeight(30);setWindowFlags(Qt::FramelessWindowHint);m_iconLabel =newQLabel(this);m_iconLabel->setFixedSize(20,20);m_iconLabel->setScaledContents(true);m_titleLabel =newQLabel(this);m_titleLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);m_minimizeButton =newQPushButton(this);m_minimizeButton->setFixedSize(27,22);m_minimizeButton->setObjectName("minimizeButton");m_maximizeButton =newQPushButton(this);m_maximizeButton->setFixedSize(27,22);m_maximizeButton->setObjectName("maximizeButton");m_closeButton =newQPushButton(this);m_closeButton->setFixedSize(27,22);m_closeButton->setObjectName("closeButton");QHBoxLayout*layout=newQHBoxLayout;layout->addWidget(m_iconLabel);layout->addStretch(1);layout->addWidget(m_titleLabel);layout->addStretch(1);layout->addWidget(m_minimizeButton);layout->addWidget(m_maximizeButton);layout->addWidget(m_closeButton);setLayout(layout);setProperty("titleBar",true);setObjectName("titleBar");connect(m_minimizeButton,SIGNAL(clicked(bool)),this,SLOT(onClicked()));connect(m_maximizeButton,SIGNAL(clicked(bool)),this,SLOT(onClicked()));connect(m_closeButton,SIGNAL(clicked(bool)),this,SLOT(onClicked()));}voidTitleBar::setWindowTitle(constQString&title){m_titleLabel->setAlignment(Qt::AlignCenter);m_titleLabel->setText(title);}voidTitleBar::SetTitleBarIcon(constQString&iconPath){QPixmapmap(iconPath);m_iconLabel->setPixmap(map);}voidTitleBar::mouseDoubleClickEvent(QMouseEvent*event){m_maximizeButton->click();}voidTitleBar::mousePressEvent(QMouseEvent*event){//鼠标左键按下事件if(event->button()==Qt::LeftButton){//记录鼠标左键状态m_leftButtonPressed =true;//记录鼠标在屏幕中的位置m_start =event->globalPos();}}voidTitleBar::mouseMoveEvent(QMouseEvent*event){//持续按住才做对应事件if(m_leftButtonPressed){//将父窗体移动到父窗体原来的位置加上鼠标移动的位置:event->globalPos()-m_startparentWidget()->move(parentWidget()->geometry().topLeft()+event->globalPos()-m_start);//将鼠标在屏幕中的位置替换为新的位置m_start =event->globalPos();}}voidTitleBar::mouseReleaseEvent(QMouseEvent*event){//鼠标左键释放if(event->button()==Qt::LeftButton){//记录鼠标状态m_leftButtonPressed =false;}}boolTitleBar::eventFilter(QObject*obj,QEvent*event){switch(event->type()){//设置标题caseQEvent::WindowTitleChange:{QWidget*pWidget=qobject_cast<QWidget*>(obj);if(pWidget){m_titleLabel->setText(pWidget->windowTitle());returntrue;}}//设置图标caseQEvent::WindowIconChange:{QWidget*pWidget=qobject_cast<QWidget*>(obj);if(pWidget){QIconicon=pWidget->windowIcon();m_iconLabel->setPixmap(icon.pixmap(m_iconLabel->size()));returntrue;}}//窗口状态变化、窗口大小变化caseQEvent::WindowStateChange:caseQEvent::Resize:updateMaximize();returntrue;}returnQWidget::eventFilter(obj,event);}voidTitleBar::updateMaximize(){QWidget*pWindow=this->window();if(pWindow->isTopLevel()){boolbMaximize=pWindow->isMaximized();if(bMaximize){m_maximizeButton->setToolTip(tr("Restore"));m_maximizeButton->setProperty("maximizeProperty","restore");}else{m_maximizeButton->setProperty("maximizeProperty","maximize");m_maximizeButton->setToolTip(tr("Maximize"));}m_maximizeButton->setStyle(QApplication::style());}}voidTitleBar::onClicked(){QPushButton*pButton=qobject_cast<QPushButton*>(sender());QWidget*pWindow=this->window();if(pWindow->isTopLevel()){if(pButton==m_minimizeButton){pWindow->showMinimized();}elseif(pButton==m_maximizeButton){pWindow->isMaximized()?pWindow->showNormal():pWindow->showMaximized();}elseif(pButton==m_closeButton){pWindow->close();}}}
QWindowBase.h文件:
#ifndefQWINDOWBASE_H#defineQWINDOWBASE_H#include<QFrame>#include<QWidget>#include<QVBoxLayout>#include"TitleBar.h"/***@brief界面组件基类*@noteQWindowBase界面组件主要用作顶层窗口,对于非顶层窗口的界面组件使用QWidget。*/classQWindowBase:publicQFrame{Q_OBJECTpublic:QWindowBase(QFrame*parent=NULL);/***@brief设置标题*@paramtitle,输入参数,标题内容*/voidsetWindowTitle(constQString&title);/***@brief设置标题栏的图标*@paramiconPath,输入参数,图标资源路径*/voidSetTitleBarIcon(constQString&iconPath);/***@brief获取内容组件对象指针*@return返回QWidget**/QWidget*contentWidget();/***@brief设置标题栏高度*@paramh,输入参数,标题栏高度*/voidsetWindowTitleHeight(inth);private:QWidget*m_contentWidget;//内容组件TitleBar*m_titleBar;//标题栏QVBoxLayout*m_layout;//布局管理器};#endif//QWINDOWBASE_H
QWindowBase.cpp文件:
#include"QWindowBase.h"QWindowBase::QWindowBase(QFrame*parent):QFrame(parent){setWindowFlags(windowFlags()|Qt::FramelessWindowHint);m_titleBar =newTitleBar(this);m_contentWidget =newQWidget(this);m_contentWidget->setObjectName("Contents");m_layout =newQVBoxLayout;m_layout->addWidget(m_titleBar);m_layout->addWidget(m_contentWidget);m_layout->setSpacing(0);m_layout->setContentsMargins(0,0,0,0);setLayout(m_layout);}voidQWindowBase::setWindowTitle(constQString&title){m_titleBar->setWindowTitle(title);}voidQWindowBase::SetTitleBarIcon(constQString&iconPath){m_titleBar->SetTitleBarIcon(iconPath);}QWidget*QWindowBase::contentWidget(){returnm_contentWidget;}voidQWindowBase::setWindowTitleHeight(inth){m_titleBar->setFixedHeight(h);}
CommonHelper.h文件:
#ifndefCOMMONHELPER_H#defineCOMMONHELPER_H#include<QString>#include<QFile>#include<QApplication>#include<QDebug>#include<QColor>#include<QPalette>/***@brief通用功能辅助类*/classCommonHelper{public:/***@brief为应用程序设置QSS样式表*@paramfilepath,输入参数,QSS文件路径*/staticvoidsetStyleSheet(constQString&filepath){//加载样式文件QFileqss(filepath);if(qss.open(QFile::ReadOnly)){QStringstylesheet=QLatin1String(qss.readAll());QStringpaletteColor=stylesheet.mid(20,7);qApp->setPalette(QPalette(QColor(paletteColor)));qApp->setStyleSheet(stylesheet);}}};#endif//COMMONHELPER_H
main.cpp文件:
#include<QApplication>#include"CommonHelper.h"#include"QWindowBase.h"#include<QPushButton>#include<QVBoxLayout>#include<QHBoxLayout>#include<QTreeView>intmain(intargc,char*argv[]){QApplicationa(argc,argv);QWindowBasew;w.setWindowTitle("WidgetBase");QPushButton*button1=newQPushButton("OK");QHBoxLayout*hLayout1=newQHBoxLayout;hLayout1->addStretch(1);hLayout1->addWidget(button1);QVBoxLayout*layout=newQVBoxLayout;QTreeView*treeView=newQTreeView;layout->addWidget(treeView);layout->addLayout(hLayout1);layout->addStretch(1);w.contentWidget()->setLayout(layout);w.setWindowTitleHeight(40);w.show();CommonHelper::setStyleSheet("://qss/lightblue.qss");returna.exec();}
工程文件:
QT+=coreguigreaterThan(QT_MAJOR_VERSION,4):QT+=widgetsTARGET=TitleBarDemoTEMPLATE=app#Thefollowingdefinemakesyourcompileremitwarningsifyouuse#anyfeatureofQtwhichhasbeenmarkedasdeprecated(theexactwarnings#dependonyourcompiler).Pleaseconsultthedocumentationofthe#deprecatedAPIinordertoknowhowtoportyourcodeawayfromit.DEFINES+=QT_DEPRECATED_WARNINGS#YoucanalsomakeyourcodefailtocompileifyouusedeprecatedAPIs.#Inordertodoso,uncommentthefollowingline.#YoucanalsoselecttodisabledeprecatedAPIsonlyuptoacertainversionofQt.#DEFINES+=QT_DISABLE_DEPRECATED_BEFORE=0x060000#disablesalltheAPIsdeprecatedbeforeQt6.0.0SOURCES+=\main.cpp\TitleBar.cpp\QWindowBase.cppHEADERS+=\TitleBar.h\CommonHelper.h\QWindowBase.hRESOURCES+=\TitileBarDemo.qrc
工程目录结构:
lightblue.qss:
QPalette{background:#EAF7FF;}*{outline:0px;color:#386487;}QWidget#Contents{background:#C0DEF6;}QWidget .QLabel{min-height:30px;background:transparent;}QWidget[titleBar="true"] .QPushButton#minimizeButton{border-style:none;border:0px solid #C0DEF6;image:url(:/images/Min.png);}QWidget[titleBar="true"] .QPushButton#maximizeButton{border-style:none;border:0px solid #C0DEF6;image:url(:/images/Max.png);}QWidget[titleBar="true"] .QPushButton#closeButton{border-style:none;border:0px solid #C0DEF6;image:url(:/images/Close.png);}
3、自定义窗体基类结果展示
运行结果: