Qt之無邊框視窗建立
為什麼要去邊框?
美觀
個人感覺系統自帶的邊框美觀上稍微欠缺一點,這也是好多軟體去掉邊框的原因吧。
自定義
去掉邊框後,就能自由在任何位置新增一個自己的邊框、標題欄之類的,甚至可以在上面新增一些工具選單。
如何去掉邊框?
Qt自帶的函式
使用下面這個函式就能去掉邊框:
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
//第一個引數是設定無邊框。第二個引數是允許工作列按鈕右鍵選單,第三個引數是允許最小化與還原。
使用第一個引數即可。
遇到問題
去掉邊框後,發現滑鼠也無法移動視窗,也無法自由調整大小。要說最小最大化、關閉的話,這個可以新增兩個按鈕來實現。
於是,我網上開始查詢一些資料、自己也思考了一下,大概如下解決:
關於移動視窗
這個自然是重寫滑鼠點選、移動事件了。
步驟一:滑鼠點選,記錄滑鼠點選時的位置。
步驟二:滑鼠移動,根據滑鼠當前的位置和起始位置,計算出滑鼠的位移,然後設定視窗的新位置。
關於自由調整大小
這個是比較麻煩的,網上找了好多資料,都沒有完美的實現方法。
網上比較多的是下面兩種做法:
一種是呼叫Windows API來實現,但是這樣就失去了跨平臺的意義。
一種是通過重寫滑鼠的region和move事件來實現,但是有些小BUG。
不完美的實現
通過第二種方法和我的除錯以及一些思考,提出以下做法:
條件一:首先,滑鼠region窗體時,而且距離邊框進入某個閾值PADDING時,此時被認為可以自動調整窗體大小。而且此效果優先於移動視窗的效果。
條件二:滿足第一步的情況下,按下滑鼠左鍵,並且移動滑鼠,將改變窗體的大小。不過根據滑鼠的位置會朝不同的方向改變。比如在窗體左邊界附近,那麼只會左側的窗體擴充套件或者收縮。
在前面兩個條件下,確實能滿足自由調整大小,但是會出現一個問題。在主窗體中,某些子元件在滑鼠進入時,主窗體會失去對滑鼠的檢測。可是在滑鼠點選的時候,主窗體又會再次獲得滑鼠的檢測。所以會出現,當滑鼠進入主窗體,啟用region效果,然後滑鼠進入子元件後,滑鼠位移了好長一段距離,主窗體的region效果仍然存在,此時如果滑鼠左擊,同時觸發第二個條件,然後窗體一下子大小就變了。以上是我進過反覆除錯得到的結論。
然後考慮到出現此BUG的最主要緣故是,子元件獲得滑鼠移動的檢測時,主窗體會丟失滑鼠的檢測。但是網上找了好多方法都沒有解決這個問題的,網上有些說設定mouseTracking可以解決,但是有些元件可以解決,有些元件無效。
於是我想了一個辦法:既然無法解決滑鼠的事情,就考慮重置region那個效果。當子元件獲得滑鼠檢測的時候,也就是mouseenter的時候,會發出一個mouseenter的訊號,然後父元件接收這個訊號的時候,觸發losemouse事件,重置region效果。這樣只需要對每個邊界處的子元件,編寫mouseenter事件就可以解決上面的BUG了。
具體程式碼:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
//PADDING用於設定改變視窗時滑鼠偵測的邊距
#define PADDING 4
enum Direction
{
UP,
DOWN,
LEFT,
RIGHT,
LEFTTOP,
LEFTBOTTOM,
RIGHTBOTTOM,
RIGHTTOP,
NONE
};
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
public slots:
void loseMouse();//當滑鼠焦點被子元件獲取時,需要還原滑鼠形狀和changeDir。
void setMaxSize();
private:
void region(const QPoint &cursorGlobalPoint);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
protected:
bool isMaxSize;
bool isLeftPressDown;//判斷左鍵是否按下
QPoint dragPosition;//視窗移動拖動時需要記住的點
Direction changeDir;//視窗大小改變時,記錄改變方向
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
resize(970, 700);
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
//第一個引數是設定無邊框。第二個引數是允許工作列按鈕右鍵選單,第三個引數是允許最小化與還原。
isLeftPressDown = false;
isMaxSize = false;
changeDir = NONE;
setMaximumWidth(QApplication::desktop()->width());
setMaximumHeight(QApplication::desktop()->height());
setMinimumHeight(570);
setMinimumWidth(985);
setMouseTracking(true);//追蹤滑鼠
//this->setStyleSheet("QDialog{background:url(:/bg_main.png)}");//設定樣式背景色,可有可無
this->setFocusPolicy(Qt::ClickFocus);//主視窗設定滑鼠點選焦點,新建歌單時有用
}
void MainWindow::setMaxSize()
{
if (this->isMaxSize)
{
this->isMaxSize = false;
this->showNormal();
}
else
{
this->isMaxSize = true;
this->showMaximized();
}
}
void MainWindow::region(const QPoint &cursorGlobalPoint)
{
if (isMaxSize)
return;
//獲取窗體在螢幕上的位置區域,tl為topleft點,rb為rightbottom點
QRect rect = this->rect();
QPoint topleft = mapToGlobal(rect.topLeft());
QPoint rightbottom = mapToGlobal(rect.bottomRight());
int x = cursorGlobalPoint.x();
int y = cursorGlobalPoint.y();
if(topleft.x()+PADDING >= x &&
topleft.x() <= x &&
topleft.y()+PADDING >= y &&
topleft.y() <= y)
{
// 左上角
changeDir = LEFTTOP;
this->setCursor(QCursor(Qt::SizeFDiagCursor));//設定滑鼠形狀
}
else if(x >= rightbottom.x()-PADDING &&
x <= rightbottom.x() &&
y >= rightbottom.y()-PADDING &&
y <= rightbottom.y())
{
// 右下角
changeDir = RIGHTBOTTOM;
this->setCursor(QCursor(Qt::SizeFDiagCursor));
}
else if(x <= topleft.x()+PADDING &&
x >= topleft.x() &&
y >= rightbottom.y()-PADDING &&
y <= rightbottom.y())
{
//左下角
changeDir = LEFTBOTTOM;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}
else if(x <= rightbottom.x() &&
x >= rightbottom.x()-PADDING &&
y >= topleft.y() &&
y <= topleft.y()+PADDING)
{
// 右上角
changeDir = RIGHTTOP;
this->setCursor(QCursor(Qt::SizeBDiagCursor));
}
else if(x <= topleft.x()+PADDING &&
x >= topleft.x())
{
// 左邊
changeDir = LEFT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}
else if(x <= rightbottom.x() &&
x >= rightbottom.x()-PADDING)
{
// 右邊
changeDir = RIGHT;
this->setCursor(QCursor(Qt::SizeHorCursor));
}
else if(y >= topleft.y() &&
y <= topleft.y()+PADDING)
{
// 上邊
changeDir = UP;
this->setCursor(QCursor(Qt::SizeVerCursor));
}
else if(y <= rightbottom.y() &&
y >= rightbottom.y()-PADDING)
{
// 下邊
changeDir = DOWN;
this->setCursor(QCursor(Qt::SizeVerCursor));
}
else
{
// 預設
changeDir = NONE;
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
void MainWindow::loseMouse()
{
changeDir = NONE;
this->setCursor(QCursor(Qt::ArrowCursor));
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
if (isMaxSize)
return;
if(event->button() == Qt::LeftButton)
{
isLeftPressDown = false;
if(changeDir != NONE)
{
this->releaseMouse();
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (isMaxSize)
return;
if (event->button() == Qt::LeftButton)
{
isLeftPressDown = true;
if(changeDir != NONE)
this->mouseGrabber();
else
dragPosition = event->globalPos() - this->frameGeometry().topLeft();
}
else
QWidget::mousePressEvent(event);
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (isMaxSize)
return;
QPoint globalPoint = event->globalPos();
QRect rect = this->rect();
QPoint topleft = mapToGlobal(rect.topLeft());
QPoint bottomright = mapToGlobal(rect.bottomRight());
if(!isLeftPressDown)
{
this->region(globalPoint);
}
else if(changeDir != NONE)
{
QRect rMove(topleft, bottomright);
switch(changeDir)
{
case LEFT:
if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
rMove.setX(topleft.x());
else
rMove.setX(globalPoint.x());
break;
case RIGHT:
rMove.setWidth(globalPoint.x() - topleft.x());
break;
case UP:
if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
rMove.setY(topleft.y());
else
rMove.setY(globalPoint.y());
break;
case DOWN:
rMove.setHeight(globalPoint.y() - topleft.y());
break;
case LEFTTOP:
if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
rMove.setX(topleft.x());
else
rMove.setX(globalPoint.x());
if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
rMove.setY(topleft.y());
else
rMove.setY(globalPoint.y());
break;
case RIGHTTOP:
rMove.setWidth(globalPoint.x() - topleft.x());
rMove.setY(globalPoint.y());
break;
case LEFTBOTTOM:
rMove.setX(globalPoint.x());
rMove.setHeight(globalPoint.y() - topleft.y());
break;
case RIGHTBOTTOM:
rMove.setWidth(globalPoint.x() - topleft.x());
rMove.setHeight(globalPoint.y() - topleft.y());
break;
default:
break;
}
this->setGeometry(rMove);
}
else
{
move(event->globalPos() - dragPosition);
//event->accept();
}
QWidget::mouseMoveEvent(event);
}