一退疫、簡(jiǎn)述
在Qt開(kāi)發(fā)中讼撒,有時(shí)候要使用自定義窗口的樣式浑厚,這樣既可以實(shí)現(xiàn)跨平臺(tái)樣式的統(tǒng)一,也往往也比默認(rèn)的要美觀根盒。
二钳幅、原理和方法
在實(shí)際開(kāi)發(fā)中,可以采用如下方法把原窗口的邊框去除炎滞。
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
然后創(chuàng)建一個(gè)BaseWindow類以及一個(gè)BaseTitleBar類敢艰,前者用于封裝一個(gè)窗口,可以繼承QWidget/QMainWindow/QDialog等册赛,后者是其標(biāo)題欄钠导,并添上相關(guān)信號(hào)和槽震嫉。
三、相關(guān)代碼
---BaseTitleBar.h
---
#ifndef BASETITLEBAR_H
#define BASETITLEBAR_H
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QTimer>
enum ButtonType
{
MIN_BUTTON = 0, // 最小化和關(guān)閉按鈕;
MIN_MAX_BUTTON , // 最小化牡属、最大化和關(guān)閉按鈕;
ONLY_CLOSE_BUTTON // 只有關(guān)閉按鈕;
};
class BaseTitleBar : public QWidget
{
Q_OBJECT
public:
BaseTitleBar(QWidget *parent = NULL);
~BaseTitleBar();
// 設(shè)置標(biāo)題欄背景色;
void setBackgroundColor(int r, int g, int b);
// 設(shè)置標(biāo)題欄圖標(biāo);
void setTitleIcon(QString filePath);
// 設(shè)置標(biāo)題內(nèi)容;
void setTitleContent(QString titleContent);
// 設(shè)置標(biāo)題欄長(zhǎng)度;
void setTitleWidth(int width);
// 設(shè)置標(biāo)題欄上按鈕類型;
void setButtonType(ButtonType buttonType);
// 設(shè)置標(biāo)題欄中的標(biāo)題是否會(huì)滾動(dòng);具體可以看效果;
void setTitleRoll();
// 保存/獲取 最大化前窗口的位置及大小;
void saveRestoreInfo(const QPoint point, const QSize size);
void getRestoreInfo(QPoint& point, QSize& size);
private:
void paintEvent(QPaintEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
// 初始化控件;
void initControl();
// 信號(hào)槽的綁定;
void initConnections();
// 加載樣式文件;
void loadStyleSheet(const QString &sheetName);
signals:
// 按鈕觸發(fā)的信號(hào);
void signalButtonMinClicked();
void signalButtonRestoreClicked();
void signalButtonMaxClicked();
void signalButtonCloseClicked();
private slots:
// 按鈕觸發(fā)的槽;
void onButtonMinClicked();
void onButtonRestoreClicked();
void onButtonMaxClicked();
void onButtonCloseClicked();
void onRollTitle();
private:
QLabel* m_pIcon; // 標(biāo)題欄圖標(biāo);
QLabel* m_pTitleContent; // 標(biāo)題欄內(nèi)容;
QPushButton* m_pButtonMin; // 最小化按鈕;
QPushButton* m_pButtonRestore; // 最大化還原按鈕;
QPushButton* m_pButtonMax; // 最大化按鈕;
QPushButton* m_pButtonClose; // 關(guān)閉按鈕;
// 標(biāo)題欄背景色;
int m_colorR;
int m_colorG;
int m_colorB;
// 最大化票堵,最小化變量;
QPoint m_restorePos;
QSize m_restoreSize;
// 移動(dòng)窗口的變量;
bool m_isPressed;
QPoint m_startMovePos;
// 標(biāo)題欄跑馬燈效果時(shí)鐘;
QTimer m_titleRollTimer;
// 標(biāo)題欄內(nèi)容;
QString m_titleContent;
// 按鈕類型;
ButtonType m_buttonType;
};
#endif // BASETITLEBAR_H
---BaseTitleBar.cpp
---
#include "BaseTitleBar.h"
#include <QHBoxLayout>
#include <QPainter>
#include <QFile>
#include <QMouseEvent>
#include <QDebug>
#define BUTTON_HEIGHT 30 // 按鈕高度;
#define BUTTON_WIDTH 30 // 按鈕寬度;
#define TITLE_HEIGHT 30 // 標(biāo)題欄高度;
BaseTitleBar::BaseTitleBar(QWidget *parent)
: QWidget(parent)
, m_colorR(153)
, m_colorG(153)
, m_colorB(153)
, m_isPressed(false)
, m_buttonType(MIN_MAX_BUTTON)
{
// 初始化;
initControl();
initConnections();
loadStyleSheet("MyTitle");
}
BaseTitleBar::~BaseTitleBar()
{
}
// 初始化控件;
void BaseTitleBar::initControl()
{
m_pIcon = new QLabel;
m_pTitleContent = new QLabel;
m_pButtonMin = new QPushButton;
m_pButtonRestore = new QPushButton;
m_pButtonMax = new QPushButton;
m_pButtonClose = new QPushButton;
m_pButtonMin->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
m_pButtonRestore->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
m_pButtonMax->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
m_pButtonClose->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
m_pTitleContent->setObjectName("TitleContent");
m_pButtonMin->setObjectName("ButtonMin");
m_pButtonRestore->setObjectName("ButtonRestore");
m_pButtonMax->setObjectName("ButtonMax");
m_pButtonClose->setObjectName("ButtonClose");
QHBoxLayout* mylayout = new QHBoxLayout(this);
mylayout->addWidget(m_pIcon);
mylayout->addWidget(m_pTitleContent);
mylayout->addWidget(m_pButtonMin);
mylayout->addWidget(m_pButtonRestore);
mylayout->addWidget(m_pButtonMax);
mylayout->addWidget(m_pButtonClose);
mylayout->setContentsMargins(5, 0, 0, 0);
mylayout->setSpacing(0);
m_pTitleContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
this->setFixedHeight(TITLE_HEIGHT);
this->setWindowFlags(Qt::FramelessWindowHint);
}
// 信號(hào)槽的綁定;
void BaseTitleBar::initConnections()
{
connect(m_pButtonMin, SIGNAL(clicked()), this, SLOT(onButtonMinClicked()));
connect(m_pButtonRestore, SIGNAL(clicked()), this, SLOT(onButtonRestoreClicked()));
connect(m_pButtonMax, SIGNAL(clicked()), this, SLOT(onButtonMaxClicked()));
connect(m_pButtonClose, SIGNAL(clicked()), this, SLOT(onButtonCloseClicked()));
}
// 設(shè)置標(biāo)題欄背景色,在paintEvent事件中進(jìn)行繪制標(biāo)題欄背景色;
//在構(gòu)造函數(shù)中給了默認(rèn)值,可以外部設(shè)置顏色值改變標(biāo)題欄背景色;
void BaseTitleBar::setBackgroundColor(int r, int g, int b)
{
m_colorR = r;
m_colorG = g;
m_colorB = b;
// 重新繪制(調(diào)用paintEvent事件);
update();
}
// 設(shè)置標(biāo)題欄圖標(biāo);
void BaseTitleBar::setTitleIcon(QString filePath)
{
QPixmap titleIcon(filePath);
m_pIcon->setPixmap(titleIcon.scaled(25 , 25));
}
// 設(shè)置標(biāo)題內(nèi)容;
void BaseTitleBar::setTitleContent(QString titleContent)
{
m_pTitleContent->setText(titleContent);
m_titleContent = titleContent;
}
// 設(shè)置標(biāo)題欄長(zhǎng)度;
void BaseTitleBar::setTitleWidth(int width)
{
this->setFixedWidth(width);
}
// 設(shè)置標(biāo)題欄上按鈕類型;
// 由于不同窗口標(biāo)題欄上的按鈕都不一樣逮栅,所以可以自定義標(biāo)題欄中的按鈕;
// 這里提供了四個(gè)按鈕悴势,分別為最小化、還原措伐、最大化特纤、關(guān)閉按鈕,如果需要其他按鈕可自行添加設(shè)置;
void BaseTitleBar::setButtonType(ButtonType buttonType)
{
m_buttonType = buttonType;
switch (buttonType)
{
case MIN_BUTTON:
{
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(false);
}
break;
case MIN_MAX_BUTTON:
{
m_pButtonRestore->setVisible(false);
}
break;
case ONLY_CLOSE_BUTTON:
{
m_pButtonMin->setVisible(false);
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(false);
}
break;
default:
break;
}
}
// 設(shè)置標(biāo)題欄中的標(biāo)題是否會(huì)自動(dòng)滾動(dòng)侥加,跑馬燈的效果;
// 一般情況下標(biāo)題欄中的標(biāo)題內(nèi)容是不滾動(dòng)的捧存,但是既然自定義就看自己需要嘛,想怎么設(shè)計(jì)就怎么搞O(∩_∩)O担败!
void BaseTitleBar::setTitleRoll()
{
connect(&m_titleRollTimer, SIGNAL(timeout()), this, SLOT(onRollTitle()));
m_titleRollTimer.start(200);
}
// 保存窗口最大化前窗口的位置以及大小;
void BaseTitleBar::saveRestoreInfo(const QPoint point, const QSize size)
{
m_restorePos = point;
m_restoreSize = size;
}
// 獲取窗口最大化前窗口的位置以及大小;
void BaseTitleBar::getRestoreInfo(QPoint& point, QSize& size)
{
point = m_restorePos;
size = m_restoreSize;
}
// 繪制標(biāo)題欄背景色;
void BaseTitleBar::paintEvent(QPaintEvent *event)
{
//設(shè)置背景色;
QPainter painter(this);
QPainterPath pathBack;
pathBack.setFillRule(Qt::WindingFill);
pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
painter.fillPath(pathBack, QBrush(QColor(m_colorR, m_colorG, m_colorB)));
// 當(dāng)窗口最大化或者還原后昔穴,窗口長(zhǎng)度變了,標(biāo)題欄的長(zhǎng)度應(yīng)當(dāng)一起改變;
if (this->width() != this->parentWidget()->width())
{
this->setFixedWidth(this->parentWidget()->width());
}
QWidget::paintEvent(event);
}
// 雙擊響應(yīng)事件提前,主要是實(shí)現(xiàn)雙擊標(biāo)題欄進(jìn)行最大化和最小化操作;
void BaseTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
// 只有存在最大化傻咖、還原按鈕時(shí)雙擊才有效;
if (m_buttonType == MIN_MAX_BUTTON)
{
// 通過(guò)最大化按鈕的狀態(tài)判斷當(dāng)前窗口是處于最大化還是原始大小狀態(tài);
// 或者通過(guò)單獨(dú)設(shè)置變量來(lái)表示當(dāng)前窗口狀態(tài);
if (m_pButtonMax->isVisible())
{
onButtonMaxClicked();
}
else
{
onButtonRestoreClicked();
}
}
return QWidget::mouseDoubleClickEvent(event);
}
// 以下通過(guò)mousePressEvent、mouseMoveEvent岖研、mouseReleaseEvent三個(gè)事件實(shí)現(xiàn)了鼠標(biāo)拖動(dòng)標(biāo)題欄移動(dòng)窗口的效果;
void BaseTitleBar::mousePressEvent(QMouseEvent *event)
{
if (m_buttonType == MIN_MAX_BUTTON)
{
// 在窗口最大化時(shí)禁止拖動(dòng)窗口;
if (m_pButtonMax->isVisible())
{
m_isPressed = true;
m_startMovePos = event->globalPos();
}
}
else
{
m_isPressed = true;
m_startMovePos = event->globalPos();
}
return QWidget::mousePressEvent(event);
}
void BaseTitleBar::mouseMoveEvent(QMouseEvent *event)
{
if (m_isPressed)
{
QPoint movePoint = event->globalPos() - m_startMovePos;
QPoint widgetPos = this->parentWidget()->pos();
m_startMovePos = event->globalPos();
this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
}
return QWidget::mouseMoveEvent(event);
}
void BaseTitleBar::mouseReleaseEvent(QMouseEvent *event)
{
m_isPressed = false;
return QWidget::mouseReleaseEvent(event);
}
// 加載本地樣式文件;
// 可以將樣式直接寫在文件中卿操,程序運(yùn)行時(shí)直接加載進(jìn)來(lái);
void BaseTitleBar::loadStyleSheet(const QString &sheetName)
{
QFile file(":/" + sheetName + ".css");
file.open(QFile::ReadOnly);
if (file.isOpen())
{
QString styleSheet = this->styleSheet();
styleSheet += QLatin1String(file.readAll());
this->setStyleSheet(styleSheet);
}
}
// 以下為按鈕操作響應(yīng)的槽;
void BaseTitleBar::onButtonMinClicked()
{
emit signalButtonMinClicked();
}
void BaseTitleBar::onButtonRestoreClicked()
{
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(true);
emit signalButtonRestoreClicked();
}
void BaseTitleBar::onButtonMaxClicked()
{
m_pButtonMax->setVisible(false);
m_pButtonRestore->setVisible(true);
emit signalButtonMaxClicked();
}
void BaseTitleBar::onButtonCloseClicked()
{
emit signalButtonCloseClicked();
}
// 該方法主要是讓標(biāo)題欄中的標(biāo)題顯示為滾動(dòng)的效果;
void BaseTitleBar::onRollTitle()
{
static int nPos = 0;
QString titleContent = m_titleContent;
// 當(dāng)截取的位置比字符串長(zhǎng)時(shí),從頭開(kāi)始;
if (nPos > titleContent.length())
nPos = 0;
m_pTitleContent->setText(titleContent.mid(nPos));
nPos++;
}
---BaseWindow.h
---
#ifndef BASEWINDOW_H
#define BASEWINDOW_H
#include <QWidget>
#include <QMainWindow>
#include "BaseTitleBar.h"
class BaseWindow : public QWidget
{
Q_OBJECT
public:
BaseWindow(QWidget *parent = 0);
~BaseWindow();
private:
void initTitleBar();
void paintEvent(QPaintEvent *event);
private slots:
void onButtonMinClicked();
void onButtonRestoreClicked();
void onButtonMaxClicked();
void onButtonCloseClicked();
protected:
BaseTitleBar* m_titleBar;
};
#endif // BASEWINDOW_H
---BaseWindow.cpp
---
#include "BaseWindow.h"
#include <QDesktopWidget>
#include <QApplication>
#include <QPainter>
#include <QFile>
BaseWindow::BaseWindow(QWidget *parent)
: QWidget(parent)
{
// FramelessWindowHint屬性設(shè)置窗口去除邊框;
// WindowMinimizeButtonHint 屬性設(shè)置在窗口最小化時(shí)孙援,點(diǎn)擊任務(wù)欄窗口可以顯示出原窗口;
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
// 設(shè)置窗口背景透明;
setAttribute(Qt::WA_TranslucentBackground);
// 初始化標(biāo)題欄;
initTitleBar();
}
BaseWindow::~BaseWindow()
{
}
void BaseWindow::initTitleBar()
{
m_titleBar = new BaseTitleBar(this);
m_titleBar->move(0, 0);
connect(m_titleBar, SIGNAL(signalButtonMinClicked()), this, SLOT(onButtonMinClicked()));
connect(m_titleBar, SIGNAL(signalButtonRestoreClicked()), this, SLOT(onButtonRestoreClicked()));
connect(m_titleBar, SIGNAL(signalButtonMaxClicked()), this, SLOT(onButtonMaxClicked()));
connect(m_titleBar, SIGNAL(signalButtonCloseClicked()), this, SLOT(onButtonCloseClicked()));
}
void BaseWindow::paintEvent(QPaintEvent* event)
{
// Q_UNUSED(event);
// QStyleOption opt;
// opt.init(this);
// QPainter painter(this);
// style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
//設(shè)置背景色;
QPainter painter(this);
QPainterPath pathBack;
pathBack.setFillRule(Qt::WindingFill);
pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
painter.fillPath(pathBack, QBrush(QColor(215, 221, 228)));
return QWidget::paintEvent(event);
}
void BaseWindow::loadStyleSheet(const QString &sheetName)
{
QFile file(":/Resources/" + sheetName + ".css");
file.open(QFile::ReadOnly);
if (file.isOpen())
{
QString styleSheet = this->styleSheet();
styleSheet += QLatin1String(file.readAll());
this->setStyleSheet(styleSheet);
}
}
void BaseWindow::onButtonMinClicked()
{
if (Qt::Tool == (windowFlags() & Qt::Tool))
{
hide(); //設(shè)置了Qt::Tool 如果調(diào)用showMinimized()則窗口就銷毀了害淤??拓售?
}
else
{
showMinimized();
}
}
void BaseWindow::onButtonRestoreClicked()
{
QPoint windowPos;
QSize windowSize;
m_titleBar->getRestoreInfo(windowPos, windowSize);
this->setGeometry(QRect(windowPos, windowSize));
}
void BaseWindow::onButtonMaxClicked()
{
m_titleBar->saveRestoreInfo(this->pos(), QSize(this->width(), this->height()));
QRect desktopRect = QApplication::desktop()->availableGeometry();
QRect FactRect = QRect(desktopRect.x() - 3, desktopRect.y() - 3, desktopRect.width() + 6, desktopRect.height() + 6);
setGeometry(FactRect);
}
void BaseWindow::onButtonCloseClicked()
{
close();
}
四窥摄、應(yīng)用代碼
創(chuàng)建一個(gè)自定義窗口類CustomMainWindow,繼承自BaseWindow(帶Ui文件)础淤。
---CustomMainWindow.h
---
#ifndef CUSTOMMAINWINDOW_H
#define CUSTOMMAINWINDOW_H
#include <QWidget>
#include "BaseWindow.h"
namespace Ui {
class CustomMainWindow;
}
class CustomMainWindow : public BaseWindow
{
Q_OBJECT
public:
explicit CustomMainWindow(QWidget *parent = 0);
~CustomMainWindow();
private:
void initTitleBar();
Ui::CustomMainWindow *ui;
};
#endif // CUSTOMMAINWINDOW_H
---CustomMainWindow.cpp
---
#include "CustomMainWindow.h"
#include "ui_CustomMainWindow.h"
CustomMainWindow::CustomMainWindow(QWidget *parent) :
BaseWindow(parent),
ui(new Ui::CustomMainWindow)
{
initTitleBar();
ui->setupUi(this);
}
CustomMainWindow::~CustomMainWindow()
{
delete ui;
}
void CustomMainWindow::initTitleBar()
{
// 設(shè)置標(biāo)題欄跑馬燈效果崭放,可以不設(shè)置;
m_titleBar->setTitleRoll();
m_titleBar->setBackgroundColor(56,70,85);
m_titleBar->setTitleIcon(":/Resources/titleicon.png");
m_titleBar->setTitleContent(QStringLiteral("自定義窗口-歡迎來(lái)到我的窗口"));
m_titleBar->setButtonType(MIN_MAX_BUTTON);
m_titleBar->setTitleWidth(this->width());
}
需要注意的是,ui文件要將其上方留出一定空白給標(biāo)題欄鸽凶。
---main.cpp
---
#include "CustomMainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomMainWindow w;
w.show();
return a.exec();
}
附上項(xiàng)目源碼币砂。
參考資料:
[1].Qt 之 自定義窗口標(biāo)題欄