QWidget及其子類窗體組件的標題欄受操做系統的控制,即標題欄的界面風格與操做系統的主題風格相同,工程實踐中須要開發者自行定義,達到美化應用程序界面的目的。app
自定義標題欄須要完成功能以下:
(1)自定義標題欄須要包含最小化按鈕、最大化按鈕、關閉按鈕、標題標籤、圖標標籤等圖形元素。
(2)標題欄的拖拽。
(3)鼠標雙擊標題欄實現窗體的最大化、最小化。less
自定義標題欄的界面佈局以下:ide
窗體的拖拽平移過程以下圖:
當鼠標在窗體的標題欄按下並移動時,窗體會按照鼠標移動的軌跡進行平移。所以,窗體每次移動都是在當前位置按照鼠標移動的矢量進行移動。標題欄拖拽功能的實現須要實現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; } }
鼠標雙擊事件處理函數mouseDoubleClickEvent實現以下:ui
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event) { m_maximizeButton->click(); }
最大化、最小化、關閉按鈕的槽函數以下:this
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(); } } }
自定義窗體基類的功能以下:
(1)自定義標題欄。
(2)增長內容組件,內容組件內部的界面佈局徹底由具體的用戶決定。操作系統
TitleBar.h文件:3d
#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
工程目錄結構:
運行結果: