近期,做項目用到無邊框窗體,令人蛋疼的是無邊框窗體大小的改變要像右邊框那樣,上下左右四周,而且要流暢。
網上也找了些代碼,發現居然還要連接到windows事件,這顯然不合常理,后來自己新建了demo,寫了一個小時,問題太多了,扔一邊先不管了。
今天需要改進UI界面,沒辦法了,重新整理了下思路,沒想到做出來了。下面來分享下實現的過程,也許是菜鳥專欄,高手勿噴~
1.首先大家要了解各九宮格的概念

一個窗體可以被划分為上、下、左、右、左上、左下、右上、右下、中間,除了中間部分,其他都需要寫程序處理。
在程序中定義Padding 為2,並同時定義枚舉類型。
#define PADDING 2
enum Direction { UP=0, DOWN=1, LEFT, RIGHT, LEFTTOP, LEFTBOTTOM, RIGHTBOTTOM, RIGHTTOP, NONE };
2.定義一個方法,參數為鼠標目前的全局位置。
void Dialog::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));
}
}
3.在對話框類中定義幾個私有成員變量
bool isLeftPressDown; // 判斷左鍵是否按下 QPoint dragPosition; // 窗口移動拖動時需要記住的點
Direction dir; // 窗口大小改變時,記錄改變方向
編寫對話框構造函數,初始化幾個變量並做一些其他工作。
isLeftPressDown = false;
this->dir = NONE;
this->setMinimumHeight(100);
this->setMinimumWidth(150);
this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowSystemMenuHint); // 設置成無邊框對話框
this->setMouseTracking(true); // 追蹤鼠標
this->setStyleSheet("QDialog{background:url(:/bg_main.png)}"); // 設置樣式背景色,可有可無
4.接着就要實現幾個重要的重載事件了
void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
實現過程如下:
void Dialog::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
isLeftPressDown = false;
if(dir != NONE) {
this->releaseMouse();
this->setCursor(QCursor(Qt::ArrowCursor));
}
}
}
void Dialog::mousePressEvent(QMouseEvent *event)
{
switch(event->button()) {
case Qt::LeftButton:
isLeftPressDown = true;
if(dir != NONE) {
this->mouseGrabber();
} else {
dragPosition = event->globalPos() - this->frameGeometry().topLeft();
}
break;
case Qt::RightButton:
this->close();
break;
default:
QDialog::mousePressEvent(event);
}
}
void Dialog::mouseMoveEvent(QMouseEvent *event)
{
QPoint gloPoint = event->globalPos();
QRect rect = this->rect();
QPoint tl = mapToGlobal(rect.topLeft());
QPoint rb = mapToGlobal(rect.bottomRight());
if(!isLeftPressDown) {
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 {
move(event->globalPos() - dragPosition);
event->accept();
}
}
QDialog::mouseMoveEvent(event);
}
到此為止,一個無邊框窗體拖動並且改變大小的功能就實現了,Run and debug it。
總結起來,這個算法其實並不復雜,就看有幾個點能不能想到:
1)窗體矩形區域要轉換成在屏幕上的區域,我采取的方式就是取TopLeft和RightBottom兩個點來確定這個區域。
2)鼠標移動要去全局的坐標。
3)region函數中判斷坐標區間,然后改變鼠標形狀,這塊很容易出錯,如果你一下子就寫出來,那我真的很佩服。
這個糾結的網上都沒有現成代碼的問題,終於在今天圓滿解決了。如需運行,請下載實例代碼:SizableNoFrame.rar
轉載請注明出處哦:http://www.cnblogs.com/xufeiyang/p/3313104.html
http://www.cnblogs.com/xufeiyang/p/3313104.html
下載:
http://download.csdn.net/detail/qq_33266987/9497160
http://www.qtcn.org/bbs/read-htm-tid-55986.html

