一、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;// 将初始位置記為上次末位置,然後執行直到釋放拖拽,移動時
實作代碼如下:
void TitleBar::mousePressEvent(QMouseEvent *event)
{
// 滑鼠左鍵按下事件
if (event->button() == Qt::LeftButton)
{
// 記錄滑鼠左鍵狀态
m_leftButtonPressed = true;
//記錄滑鼠在螢幕中的位置
m_start = event->globalPos();
}
}
void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
// 持續按住才做對應事件
if(m_leftButtonPressed)
{
//将父窗體移動到父窗體原來的位置加上滑鼠移動的位置:event->globalPos()-m_start
parentWidget()->move(parentWidget()->geometry().topLeft() +
event->globalPos() - m_start);
//将滑鼠在螢幕中的位置替換為新的位置
m_start = event->globalPos();
}
}
void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
// 滑鼠左鍵釋放
if (event->button() == Qt::LeftButton)
{
// 記錄滑鼠狀态
m_leftButtonPressed = false;
}
}
4、标題欄輕按兩下實作最大化、最小化
滑鼠輕按兩下事件處理函數mouseDoubleClickEvent實作如下:
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
m_maximizeButton->click();
}
最大化、最小化、關閉按鈕的槽函數如下:
void TitleBar::onClicked()
{
QPushButton *pButton = qobject_cast<QPushButton *>(sender());
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
if (pButton == m_minimizeButton)
{
pWindow->showMinimized();
}
else if (pButton == m_maximizeButton)
{
pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();
}
else if (pButton == m_closeButton)
{
pWindow->close();
}
}
}
三、Qt自定義窗體基類示例
1、自定義窗體基類的功能
自定義窗體基類的功能如下:
(1)自定義标題欄。
(2)增加内容元件,内容元件内部的界面布局完全由具體的使用者決定。
2、自定義窗體基類的實作
TitleBar.h檔案:
#ifndef TITLEBAR_H
#define TITLEBAR_H
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QPoint>
#include <QPixmap>
#include <QString>
/**
* @brief 标題欄界面元件
* @author
*/
class TitleBar : public QWidget
{
Q_OBJECT
public:
explicit TitleBar(QWidget *parent = NULL);
/**
* @brief 設定标題欄标題
* @param title,參數,設定的标題
*/
void setWindowTitle(const QString& title);
/**
* @brief 設定标題欄的圖示
* @param iconPath,參數,圖示的路徑
*/
void SetTitleBarIcon(const QString& iconPath);
protected:
/**
* @brief 滑鼠輕按兩下事件處理函數
* @param event,參數,事件
* @note 輕按兩下标題欄進行界面的最大化/還原
*/
virtual void mouseDoubleClickEvent(QMouseEvent *event);
/**
* @brief 滑鼠按下事件處理函數
* @param event,參數,事件
* @note 按下滑鼠左鍵
*/
virtual void mousePressEvent(QMouseEvent *event);
/**
* @brief 滑鼠移動事件處理函數
* @param event,參數,事件
* @note 移動滑鼠
*/
virtual void mouseMoveEvent(QMouseEvent *event);
/**
* @brief 滑鼠釋放事件處理函數
* @param event,參數,事件
* @note 釋放滑鼠
*/
virtual void mouseReleaseEvent(QMouseEvent *event);
/**
* @brief 事件過濾處理器
* @param obj,參數
* @param event,參數,事件
* @return 成功傳回true,失敗傳回false
* @note 設定标題、圖示
*/
virtual bool eventFilter(QObject *obj, QEvent *event);
/**
* @brief 最大化/還原
*/
void updateMaximize();
protected slots:
/**
* @brief 最小化、最大化/還原、關閉按鈕點選時響應的槽函數
*/
void onClicked();
private:
QLabel* m_iconLabel;
QLabel* m_titleLabel;
QPushButton* m_minimizeButton;
QPushButton* m_maximizeButton;
QPushButton* m_closeButton;
QPoint m_start;//起始點
QPoint m_end;//結束點
bool m_leftButtonPressed;//滑鼠左鍵按下标記
};
#endif // TITLEBAR_H
TitleBar.cpp檔案:
#include "TitleBar.h"
TitleBar::TitleBar(QWidget *parent) : QWidget(parent)
{
setFixedHeight(30);
setWindowFlags(Qt::FramelessWindowHint);
m_iconLabel = new QLabel(this);
m_iconLabel->setFixedSize(20, 20);
m_iconLabel->setScaledContents(true);
m_titleLabel = new QLabel(this);
m_titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_minimizeButton = new QPushButton(this);
m_minimizeButton->setFixedSize(27, 22);
m_minimizeButton->setObjectName("minimizeButton");
m_maximizeButton = new QPushButton(this);
m_maximizeButton->setFixedSize(27, 22);
m_maximizeButton->setObjectName("maximizeButton");
m_closeButton = new QPushButton(this);
m_closeButton->setFixedSize(27, 22);
m_closeButton->setObjectName("closeButton");
QHBoxLayout* layout = new QHBoxLayout;
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()));
}
void TitleBar::setWindowTitle(const QString &title)
{
m_titleLabel->setAlignment(Qt::AlignCenter);
m_titleLabel->setText(title);
}
void TitleBar::SetTitleBarIcon(const QString &iconPath)
{
QPixmap map(iconPath);
m_iconLabel->setPixmap(map);
}
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
m_maximizeButton->click();
}
void TitleBar::mousePressEvent(QMouseEvent *event)
{
// 滑鼠左鍵按下事件
if (event->button() == Qt::LeftButton)
{
// 記錄滑鼠左鍵狀态
m_leftButtonPressed = true;
//記錄滑鼠在螢幕中的位置
m_start = event->globalPos();
}
}
void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
// 持續按住才做對應事件
if(m_leftButtonPressed)
{
//将父窗體移動到父窗體原來的位置加上滑鼠移動的位置:event->globalPos()-m_start
parentWidget()->move(parentWidget()->geometry().topLeft() +
event->globalPos() - m_start);
//将滑鼠在螢幕中的位置替換為新的位置
m_start = event->globalPos();
}
}
void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
// 滑鼠左鍵釋放
if (event->button() == Qt::LeftButton)
{
// 記錄滑鼠狀态
m_leftButtonPressed = false;
}
}
bool TitleBar::eventFilter(QObject *obj, QEvent *event)
{
switch(event->type())
{
//設定标題
case QEvent::WindowTitleChange:
{
QWidget *pWidget = qobject_cast<QWidget *>(obj);
if (pWidget)
{
m_titleLabel->setText(pWidget->windowTitle());
return true;
}
}
//設定圖示
case QEvent::WindowIconChange:
{
QWidget *pWidget = qobject_cast<QWidget *>(obj);
if (pWidget)
{
QIcon icon = pWidget->windowIcon();
m_iconLabel->setPixmap(icon.pixmap(m_iconLabel->size()));
return true;
}
}
// 視窗狀态變化、視窗大小變化
case QEvent::WindowStateChange:
case QEvent::Resize:
updateMaximize();
return true;
}
return QWidget::eventFilter(obj, event);
}
void TitleBar::updateMaximize()
{
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
bool bMaximize = 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());
}
}
void TitleBar::onClicked()
{
QPushButton *pButton = qobject_cast<QPushButton *>(sender());
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
if (pButton == m_minimizeButton)
{
pWindow->showMinimized();
}
else if (pButton == m_maximizeButton)
{
pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();
}
else if (pButton == m_closeButton)
{
pWindow->close();
}
}
}
QWindowBase.h檔案:
#ifndef QWINDOWBASE_H
#define QWINDOWBASE_H
#include <QFrame>
#include <QWidget>
#include <QVBoxLayout>
#include "TitleBar.h"
/**
* @brief 界面元件基類
* @note QWindowBase界面元件主要用作頂層視窗,對于非頂層視窗的界面元件使用QWidget。
*/
class QWindowBase : public QFrame
{
Q_OBJECT
public:
QWindowBase(QFrame* parent = NULL);
/**
* @brief 設定标題
* @param title,輸入參數,标題内容
*/
void setWindowTitle(const QString& title);
/**
* @brief 設定标題欄的圖示
* @param iconPath,輸入參數,圖示資源路徑
*/
void SetTitleBarIcon(const QString& iconPath);
/**
* @brief 擷取内容元件對象指針
* @return 傳回QWidget*
*/
QWidget* contentWidget();
/**
* @brief 設定标題欄高度
* @param h,輸入參數,标題欄高度
*/
void setWindowTitleHeight(int h);
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 = new TitleBar(this);
m_contentWidget = new QWidget(this);
m_contentWidget->setObjectName("Contents");
m_layout = new QVBoxLayout;
m_layout->addWidget(m_titleBar);
m_layout->addWidget(m_contentWidget);
m_layout->setSpacing(0);
m_layout->setContentsMargins(0, 0, 0, 0);
setLayout(m_layout);
}
void QWindowBase::setWindowTitle(const QString &title)
{
m_titleBar->setWindowTitle(title);
}
void QWindowBase::SetTitleBarIcon(const QString &iconPath)
{
m_titleBar->SetTitleBarIcon(iconPath);
}
QWidget *QWindowBase::contentWidget()
{
return m_contentWidget;
}
void QWindowBase::setWindowTitleHeight(int h)
{
m_titleBar->setFixedHeight(h);
}
CommonHelper.h檔案:
#ifndef COMMONHELPER_H
#define COMMONHELPER_H
#include <QString>
#include <QFile>
#include <QApplication>
#include <QDebug>
#include <QColor>
#include <QPalette>
/**
* @brief 通用功能輔助類
*/
class CommonHelper
{
public:
/**
* @brief 為應用程式設定QSS樣式表
* @param filepath,輸入參數,QSS檔案路徑
*/
static void setStyleSheet(const QString& filepath)
{
//加載樣式檔案
QFile qss(filepath);
if(qss.open(QFile::ReadOnly))
{
QString stylesheet = QLatin1String(qss.readAll());
QString paletteColor = 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>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWindowBase w;
w.setWindowTitle("WidgetBase");
QPushButton* button1 = new QPushButton("OK");
QHBoxLayout* hLayout1 = new QHBoxLayout;
hLayout1->addStretch(1);
hLayout1->addWidget(button1);
QVBoxLayout* layout = new QVBoxLayout;
QTreeView* treeView = new QTreeView;
layout->addWidget(treeView);
layout->addLayout(hLayout1);
layout->addStretch(1);
w.contentWidget()->setLayout(layout);
w.setWindowTitleHeight(40);
w.show();
CommonHelper::setStyleSheet("://qss/lightblue.qss");
return a.exec();
}
工程檔案:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = TitleBarDemo
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
TitleBar.cpp \
QWindowBase.cpp
HEADERS += \
TitleBar.h \
CommonHelper.h \
QWindowBase.h
RESOURCES += \
TitileBarDemo.qrc
工程目錄結構:
3、自定義窗體基類結果展示
運作結果:
源碼:http://down.51cto.com/data/2457159