Qt實現自定義窗體
Qt中已經為我們封裝了很好用的視窗風格,但在實際開發過程當中,要麼需求要麼UI拋過來的介面要求總是平添許多工作量。今天得空便來記錄下我在專案中實現的一個需求:扁平化介面風格。雖然可能還會在未來的使用過程中暴露出一些問題,但目前還是可以滿足基本使用需求的。
進入正題,首先我們必須建立一個基於QDialog的帶介面的類,然後對Qt生成的窗體進行改造,再在程式碼中應用訊號槽機制實現類似於原生窗體的事件互動。下面是實現程式碼:
YMDialog.h
#ifndef YMDIALOG_H
#define YMDIALOG_H
#include <QtWidgets/QDialog>
#include <QMouseEvent>
#include <QPoint>
#include "ui_YMDialog.h"
#define PADDING 2
enum Direction { UP=0, DOWN=1, LEFT, RIGHT, LEFTTOP, LEFTBOTTOM, RIGHTBOTTOM, RIGHTTOP, NONE };
//無邊框視窗
class YMDialog : public QDialog
{
Q_OBJECT
public:
YMDialog(QWidget *parent = 0);
~YMDialog();
void mousePressEvent(QMouseEvent * e);
void mouseReleaseEvent(QMouseEvent * e);
void mouseMoveEvent(QMouseEvent * e);
void region(const QPoint &cursorGlobalPoint);
public slots:
void minButtonSlot();
void maxButtonSlot();
void closeButtonSlot();
private:
Ui::YMDialogClass ui;
QPoint move_point; //移動的距離
bool mouse_press; //滑鼠按下
bool isNormal;
QSize nomalSize;
QSize maxSize;
QPoint nomalPoint;
QPoint maxPoint;
bool isLeftPressDown; // 判斷左鍵是否按下
QPoint dragPosition; // 視窗移動拖動時需要記住的點
Direction dir; // 視窗大小改變時,記錄改變方向
};
#endif // YMDIALOG_H
YMDialog.cpp
#include "YMDialog.h"
#include <QToolButton>
#include <QLabel>
#include <QAction>
#include <QDesktopWidget>
YMDialog::YMDialog(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
nomalSize.setWidth(600);
nomalSize.setHeight(400);
isNormal = true;
QDesktopWidget* detk = QApplication::desktop();
nomalPoint.setX((detk->width() - this->width())/2);
nomalPoint.setY((detk->height() - this->height())/2);
maxSize.setWidth(detk->width());
maxSize.setHeight(detk->height());
maxPoint.setX(detk->availableGeometry().x());
maxPoint.setY(detk->availableGeometry().y());
dir = NONE;
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
setMouseTracking(true);
ui.widget->setMouseTracking(true);
//自定義背景1
//QLabel * background = new QLabel(this);
//background->setStyleSheet("background-color:rgb(120, 120, 255)");
//background->setGeometry(0,0,this->width(),this->height());
//background->setMouseTracking(true);
//background->setPixmap(QPixmap(":/YMDialog/Resources/background.png"));
//background->setGeometry(0,0,this->width(),this->height());
//設定圖片充滿整個label
//background->setScaledContents(true);
//獲取最大化、最小化、關閉按鈕圖示
QPixmap minPix = style()->standardPixmap(QStyle::SP_TitleBarMinButton);
QPixmap maxPix = style()->standardPixmap(QStyle::SP_TitleBarMaxButton);
QPixmap closePix = style()->standardPixmap(QStyle::SP_TitleBarCloseButton);
//為按鈕設定圖示
ui.minbtn->setIcon(minPix);
ui.maxbtn->setIcon(maxPix);
ui.closebtn->setIcon(closePix);
int width = this->width(); //獲取介面的寬度
//設定按鈕在視窗上的位置
ui.minbtn->setGeometry(width-67,5,20,20);
ui.maxbtn->setGeometry(width-46,5,20,20);
ui.closebtn->setGeometry(width-25,5,20,20);
//設定按鈕的提示資訊
ui.minbtn->setToolTip(QStringLiteral("最小化"));
ui.maxbtn->setToolTip(QStringLiteral("最大化"));
ui.closebtn->setToolTip(QStringLiteral("關閉"));
//設定按鈕樣式
//ui.minbtn->setStyleSheet("background-color:transparent");
//ui.maxbtn->setStyleSheet("background-color:transparent");
//ui.closebtn->setStyleSheet("background-color:transparent");
ui.minbtn->setStyleSheet("QToolButton{background-color:transparent;}"
"QToolButton:hover{border: 0px;background-color: rgb(141, 141, 141);}"
"QToolButton:pressed{background-color: rgb(86, 86, 86);}");
ui.maxbtn->setStyleSheet("QToolButton{background-color:transparent;}"
"QToolButton:hover{border: 0px;background-color: rgb(141, 141, 141);}"
"QToolButton:pressed{background-color: rgb(86, 86, 86);}");
ui.closebtn->setStyleSheet("QToolButton{background-color:transparent;}"
"QToolButton:hover{border: 0px;background-color: rgb(141, 141, 141);}"
"QToolButton:pressed{background-color: rgb(86, 86, 86);}");
//this->setStyleSheet("background-color: rgb(95, 143, 143);");
connect(ui.minbtn,SIGNAL(clicked()),this, SLOT(minButtonSlot()));
connect(ui.maxbtn,SIGNAL(clicked()),this, SLOT(maxButtonSlot()));
connect(ui.closebtn,SIGNAL(clicked()),this, SLOT(closeButtonSlot()));
mouse_press = false;
//QFile file(":/YMDialog/qss/coffee.qss");
//file.open(QFile::ReadOnly);
//QString styleSheet = QString::fromLatin1(file.readAll());
//qApp->setStyleSheet(styleSheet);
}
YMDialog::~YMDialog()
{
}
void YMDialog::mousePressEvent( QMouseEvent * e )
{
if (e->button() == Qt::LeftButton)
{
mouse_press = true;
move_point = e->pos();
if(dir != NONE)
{
this->mouseGrabber();
} else
{
dragPosition = e->globalPos() - this->frameGeometry().topLeft();
}
}
}
void YMDialog::mouseReleaseEvent( QMouseEvent * e )
{
mouse_press = false;
if(dir != NONE)
{
this->releaseMouse();
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
void YMDialog::mouseMoveEvent( QMouseEvent * e )
{
QPoint gloPoint = e->globalPos();
QRect rect = this->rect();
QPoint tl = mapToGlobal(rect.topLeft());
QPoint rb = mapToGlobal(rect.bottomRight());
if(!mouse_press)
{
this->region(gloPoint);
} else
{
if(dir != NONE)
{
QRect rMove(tl, rb);
switch(dir)
{
case LEFT:
if(rb.x() - gloPoint.x() <= this->minimumWidth())
rMove.setX(tl.x());
else
rMove.setX(gloPoint.x());
break;
case RIGHT:
rMove.setWidth(gloPoint.x() - tl.x());
break;
case UP:
if(rb.y() - gloPoint.y() <= this->minimumHeight())
rMove.setY(tl.y());
else
rMove.setY(gloPoint.y());
break;
case DOWN:
rMove.setHeight(gloPoint.y() - tl.y());
break;
case LEFTTOP:
if(rb.x() - gloPoint.x() <= this->minimumWidth())
rMove.setX(tl.x());
else
rMove.setX(gloPoint.x());
if(rb.y() - gloPoint.y() <= this->minimumHeight())
rMove.setY(tl.y());
else
rMove.setY(gloPoint.y());
break;
case RIGHTTOP:
rMove.setWidth(gloPoint.x() - tl.x());
rMove.setY(gloPoint.y());
break;
case LEFTBOTTOM:
rMove.setX(gloPoint.x());
rMove.setHeight(gloPoint.y() - tl.y());
break;
case RIGHTBOTTOM:
rMove.setWidth(gloPoint.x() - tl.x());
rMove.setHeight(gloPoint.y() - tl.y());
break;
default:
break;
}
this->setGeometry(rMove);
} else
{
QPoint move_pos = e->globalPos();
this->move(move_pos - move_point);
}
}
QDialog::mouseMoveEvent(e);
}
void YMDialog::minButtonSlot()
{
this->showMinimized();
}
void YMDialog::maxButtonSlot()
{
if(isNormal)
{
//this->showMaximized();
this->setGeometry(maxPoint.x(),maxPoint.y(),maxSize.width(),maxSize.height());
isNormal = false;
}
else
{
this->setGeometry(nomalPoint.x(),nomalPoint.y(),nomalSize.width(),nomalSize.height());
isNormal = true;
}
}
void YMDialog::closeButtonSlot()
{
this->close();
}
void YMDialog::region(const QPoint &cursorGlobalPoint)
{
// 獲取窗體在螢幕上的位置區域,tl為topleft點,rb為rightbottom點
QRect rect = this->rect();
QPoint tl = mapToGlobal(rect.topLeft());
QPoint rb = mapToGlobal(rect.bottomRight());
int x = cursorGlobalPoint.x();
int y = cursorGlobalPoint.y();
if(tl.x() + PADDING >= x && tl.x() <= x && tl.y() + PADDING >= y && tl.y() <= y)
{
// 左上角
dir = LEFTTOP;
this->setCursor(QCursor(Qt::SizeFDiagCursor)); // 設定滑鼠形狀
} else if(x >= rb.x() - PADDING && x <= rb.x() && y >= rb.y() - PADDING && y <= rb.y())
{
// 右下角
dir = RIGHTBOTTOM;
this->setCursor(QCursor(Qt::SizeFDiagCursor));
} else if(x <= tl.x() + PADDING && x >= tl.x() && y >= rb.y() - PADDING && y <= rb.y())
{
//左下角
dir = LEFTBOTTOM;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
} else if(x <= rb.x() && x >= rb.x() - PADDING && y >= tl.y() && y <= tl.y() + PADDING)
{
// 右上角
dir = RIGHTTOP;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
} else if(x <= tl.x() + PADDING && x >= tl.x())
{
// 左邊
dir = LEFT;
this->setCursor(QCursor(Qt::SizeHorCursor));
} else if( x <= rb.x() && x >= rb.x() - PADDING)
{
// 右邊
dir = RIGHT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}else if(y >= tl.y() && y <= tl.y() + PADDING)
{
// 上邊
dir = UP;
this->setCursor(QCursor(Qt::SizeVerCursor));
} else if(y <= rb.y() && y >= rb.y() - PADDING)
{
// 下邊
dir = DOWN;
this->setCursor(QCursor(Qt::SizeVerCursor));
}else
{
// 預設
dir = NONE;
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
介面ui檔案
/********************************************************************************
** Form generated from reading UI file 'YMDialog.ui'
**
** Created by: Qt User Interface Compiler version 5.5.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_YMDIALOG_H
#define UI_YMDIALOG_H
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_YMDialogClass
{
public:
QVBoxLayout *verticalLayout;
QWidget *widget_2;
QHBoxLayout *horizontalLayout_2;
QSpacerItem *horizontalSpacer;
QToolButton *minbtn;
QToolButton *maxbtn;
QToolButton *closebtn;
QWidget *widget;
void setupUi(QDialog *YMDialogClass)
{
if (YMDialogClass->objectName().isEmpty())
YMDialogClass->setObjectName(QStringLiteral("YMDialogClass"));
YMDialogClass->resize(390, 600);
YMDialogClass->setStyleSheet(QStringLiteral(""));
verticalLayout = new QVBoxLayout(YMDialogClass);
verticalLayout->setSpacing(0);
verticalLayout->setContentsMargins(11, 11, 11, 11);
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
verticalLayout->setContentsMargins(0, 0, 0, 0);
widget_2 = new QWidget(YMDialogClass);
widget_2->setObjectName(QStringLiteral("widget_2"));
widget_2->setMaximumSize(QSize(16777215, 25));
widget_2->setStyleSheet(QStringLiteral("background-color: rgb(255, 255, 255);"));
horizontalLayout_2 = new QHBoxLayout(widget_2);
horizontalLayout_2->setSpacing(0);
horizontalLayout_2->setContentsMargins(11, 11, 11, 11);
horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
horizontalLayout_2->setContentsMargins(0, 0, 0, 0);
horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout_2->addItem(horizontalSpacer);
minbtn = new QToolButton(widget_2);
minbtn->setObjectName(QStringLiteral("minbtn"));
horizontalLayout_2->addWidget(minbtn);
maxbtn = new QToolButton(widget_2);
maxbtn->setObjectName(QStringLiteral("maxbtn"));
horizontalLayout_2->addWidget(maxbtn);
closebtn = new QToolButton(widget_2);
closebtn->setObjectName(QStringLiteral("closebtn"));
horizontalLayout_2->addWidget(closebtn);
verticalLayout->addWidget(widget_2);
widget = new QWidget(YMDialogClass);
widget->setObjectName(QStringLiteral("widget"));
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(widget->sizePolicy().hasHeightForWidth());
widget->setSizePolicy(sizePolicy);
widget->setStyleSheet(QStringLiteral("background-color: rgb(255, 255, 255);"));
verticalLayout->addWidget(widget);
retranslateUi(YMDialogClass);
QMetaObject::connectSlotsByName(YMDialogClass);
} // setupUi
void retranslateUi(QDialog *YMDialogClass)
{
YMDialogClass->setWindowTitle(QApplication::translate("YMDialogClass", "YMDialog", 0));
minbtn->setText(QString());
maxbtn->setText(QString());
closebtn->setText(QString());
} // retranslateUi
};
namespace Ui {
class YMDialogClass: public Ui_YMDialogClass {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_YMDIALOG_H
注意:承載實際功能區最底層的widget需要設定setMouseTracking(true),否則將跟蹤不到滑鼠的拖動事件。