1. 程式人生 > >Qt實現自定義窗體

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),否則將跟蹤不到滑鼠的拖動事件。