Qt高级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(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 #include #include #include #include #include #include #include #include #include /** * @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(obj); if (pWidget) { m_titleLabel->setText(pWidget->windowTitle()); return true; } } //设置图标 case QEvent::WindowIconChange: { QWidget *pWidget = qobject_cast(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(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 #include #include #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 #include #include #include #include #include /** * @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
点击领取Qt学习资料+视频教程~「链接」
main.cpp文件:#include #include "CommonHelper.h" #include "QWindowBase.h" #include #include #include #include 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、自定义窗体基类结果展示
运行结果: