簡述
通過上節內容,我們實現了自定義窗體的移動,但是我們缺少一個標題欄來顯示窗體的圖標、標題,以及控制窗體最小化、最大化、關閉的按鈕。
自定義標題欄后,所有的控件我們都可以定制,比如:在標題欄中添加換膚、設置按鈕以及其他控件。
效果
自定義標題欄
實現
title_bar.h
#ifndef TITLE_BAR
#define TITLE_BAR
#include <QWidget>
class QLabel;
class QPushButton;
class TitleBar : public QWidget
{
Q_OBJECT
public:
explicit TitleBar(QWidget *parent = 0);
~TitleBar();
protected:
// 雙擊標題欄進行界面的最大化/還原
virtual void mouseDoubleClickEvent(QMouseEvent *event);
// 進行鼠界面的拖動
virtual void mousePressEvent(QMouseEvent *event);
// 設置界面標題與圖標
virtual bool eventFilter(QObject *obj, QEvent *event);
private slots:
// 進行最小化、最大化/還原、關閉操作
void onClicked();
private:
// 最大化/還原
void updateMaximize();
private:
QLabel *m_pIconLabel;
QLabel *m_pTitleLabel;
QPushButton *m_pMinimizeButton;
QPushButton *m_pMaximizeButton;
QPushButton *m_pCloseButton;
};
#endif // TITLE_BAR
title_bar.cpp
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QEvent>
#include <QMouseEvent>
#include <QApplication>
#include "title_bar.h"
#ifdef Q_OS_WIN
#pragma comment(lib, "user32.lib")
#include <qt_windows.h>
#endif
TitleBar::TitleBar(QWidget *parent)
: QWidget(parent)
{
setFixedHeight(30);
m_pIconLabel = new QLabel(this);
m_pTitleLabel = new QLabel(this);
m_pMinimizeButton = new QPushButton(this);
m_pMaximizeButton = new QPushButton(this);
m_pCloseButton = new QPushButton(this);
m_pIconLabel->setFixedSize(20, 20);
m_pIconLabel->setScaledContents(true);
m_pTitleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_pMinimizeButton->setFixedSize(27, 22);
m_pMaximizeButton->setFixedSize(27, 22);
m_pCloseButton->setFixedSize(27, 22);
m_pTitleLabel->setObjectName("whiteLabel");
m_pMinimizeButton->setObjectName("minimizeButton");
m_pMaximizeButton->setObjectName("maximizeButton");
m_pCloseButton->setObjectName("closeButton");
m_pMinimizeButton->setToolTip("Minimize");
m_pMaximizeButton->setToolTip("Maximize");
m_pCloseButton->setToolTip("Close");
QHBoxLayout *pLayout = new QHBoxLayout(this);
pLayout->addWidget(m_pIconLabel);
pLayout->addSpacing(5);
pLayout->addWidget(m_pTitleLabel);
pLayout->addWidget(m_pMinimizeButton);
pLayout->addWidget(m_pMaximizeButton);
pLayout->addWidget(m_pCloseButton);
pLayout->setSpacing(0);
pLayout->setContentsMargins(5, 0, 5, 0);
setLayout(pLayout);
connect(m_pMinimizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
connect(m_pMaximizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
connect(m_pCloseButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
}
TitleBar::~TitleBar()
{
}
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_UNUSED(event);
emit m_pMaximizeButton->clicked();
}
void TitleBar::mousePressEvent(QMouseEvent *event)
{
#ifdef Q_OS_WIN
if (ReleaseCapture())
{
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
}
event->ignore();
#else
#endif
}
bool TitleBar::eventFilter(QObject *obj, QEvent *event)
{
switch (event->type())
{
case QEvent::WindowTitleChange:
{
QWidget *pWidget = qobject_cast<QWidget *>(obj);
if (pWidget)
{
m_pTitleLabel->setText(pWidget->windowTitle());
return true;
}
}
case QEvent::WindowIconChange:
{
QWidget *pWidget = qobject_cast<QWidget *>(obj);
if (pWidget)
{
QIcon icon = pWidget->windowIcon();
m_pIconLabel->setPixmap(icon.pixmap(m_pIconLabel->size()));
return true;
}
}
case QEvent::WindowStateChange:
case QEvent::Resize:
updateMaximize();
return true;
}
return QWidget::eventFilter(obj, event);
}
void TitleBar::onClicked()
{
QPushButton *pButton = qobject_cast<QPushButton *>(sender());
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
if (pButton == m_pMinimizeButton)
{
pWindow->showMinimized();
}
else if (pButton == m_pMaximizeButton)
{
pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();
}
else if (pButton == m_pCloseButton)
{
pWindow->close();
}
}
}
void TitleBar::updateMaximize()
{
QWidget *pWindow = this->window();
if (pWindow->isTopLevel())
{
bool bMaximize = pWindow->isMaximized();
if (bMaximize)
{
m_pMaximizeButton->setToolTip(tr("Restore"));
m_pMaximizeButton->setProperty("maximizeProperty", "restore");
}
else
{
m_pMaximizeButton->setProperty("maximizeProperty", "maximize");
m_pMaximizeButton->setToolTip(tr("Maximize"));
}
m_pMaximizeButton->setStyle(QApplication::style());
}
}
接口說明
- mousePressEvent
之前,我們將界面移動的事件寫在主界面里面,這會有一個問題,一般情況下,是界面隨着標題欄的移動而移動,而並非界面中的所有位置都可以進行拖動,所以我們將事件寫在標題欄中比較合理。
- mouseDoubleClickEvent
雙擊標題欄會進行窗體的最大化/還原,所以我們需要重寫此事件進行控制。
-
eventFilter
-
事件過濾器,這里被監聽的窗體為標題欄所在的窗體,所以當窗體標題、圖標等信息發生改變時,標題欄也應該隨之改變。
-
最好不要通過直接調用接口的形式來操作對應的行為,比如:TitleBar中定義一個public函數來專門修改標題與圖標,這樣會造成不必要的麻煩,因為Qt本身就是基於事件的,所以此處采用過濾器的方式。
-
-
updateMaximize
因為窗體大小發生變化的時候,最大化的圖標、提示應該對應的發生變化,所以在eventFilter中事件觸發時調用。
使用方式
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setWindowFlags(Qt::FramelessWindowHint | windowFlags());
TitleBar *pTitleBar = new TitleBar(this);
installEventFilter(pTitleBar);
resize(400, 300);
setWindowTitle("Custom Window");
setWindowIcon(QIcon(":/Images/logo"));
QPalette pal(palette());
pal.setColor(QPalette::Background, QColor(50, 50, 50));
setAutoFillBackground(true);
setPalette(pal);
QVBoxLayout *pLayout = new QVBoxLayout();
pLayout->addWidget(pTitleBar);
pLayout->addStretch();
pLayout->setSpacing(0);
pLayout->setContentsMargins(0, 0, 0, 0);
setLayout(pLayout);
}
注意
installEventFilter必須在setWindowTitle、setWindowIcon之前調用,因為必須先安裝事件過濾器,相應事件觸發時,才會進入標題欄的eventFilter事件中。
原文作者:一去丶二三里
作者博客:去作者博客空間