目錄
* 一、概述 <https://www.cnblogs.com/swarmbees/p/11415829.html#一概述>
* 二、效果展示 <https://www.cnblogs.com/swarmbees/p/11415829.html#二效果展示>
* 三、demo制作 <https://www.cnblogs.com/swarmbees/p/11415829.html#三demo制作>
* 1、設計窗體 <https://www.cnblogs.com/swarmbees/p/11415829.html#設計窗體>
* 2、雙擊放大 <https://www.cnblogs.com/swarmbees/p/11415829.html#雙擊放大>
* 四、拖拽 <https://www.cnblogs.com/swarmbees/p/11415829.html#四拖拽>
* 五、相關文章 <https://www.cnblogs.com/swarmbees/p/11415829.html#五相關文章>
原文鏈接:Qt無邊框窗體-最大化時支持拖拽還原 <https://www.cnblogs.com/swarmbees/p/11415829.html>
一、概述
用Qt進行開發(fā)界面時,既想要實現(xiàn)友好的用戶交互又想界面漂亮,那么自定義界面就必不可少。其中有一個操作就是是我們每一個Qter開發(fā)者都要會的,而且是經常進行的。
Qt::FramelessWindowHint這個屬性想必大家都使用過,有些同學可能對這個屬性很了解,也用的是爐火純青,今天我們也來說說這個屬性。
關于這個無邊框屬性網上也有一些文章,有些談論的是bug,當然了這是針對不同os而言,也有些是跟其他第三方庫混合使用時的問題??墒菃栴}歸問題,想要實現(xiàn)自定義的優(yōu)秀界面這個屬性也是必不可少的。
今天我們就來實現(xiàn)一個無邊框窗體最大化時,支持拖拽標題欄進行還原的功能。
無邊框窗體支持縮放、移動這些不屬于本篇文章的內容,本篇文章主要講解怎么實現(xiàn)最大化時拖拽標題欄進行還原窗體,本篇文章的代碼依賴于博主之前封裝的一個拖拽代理類。
二、效果展示
如效果圖所示,做了一個簡單的事例,雙擊標題欄窗體最大化,這個時候如果進行標題欄拖拽,當鼠標按下并移動一段距離時窗體恢復normal狀態(tài)。
恢復normal狀態(tài)下的窗體仍然支持放大和縮小,有接口可以設置。
三、demo制作
demo的制作過程還是比較簡單的,分為如下幾步
1、設計窗體
通過desinger設計器我們拖拽了一個大致窗體內容,為了更好的展示效果,標題欄加上了icon和背景色
2、雙擊放大
鼠標雙擊標題欄放大這個功能實現(xiàn)起來方法也比較多,這里博主選擇了代碼量最少并且實現(xiàn)起來最簡單的方式,直接把標題欄的事件循環(huán)安裝到了主窗體上。
ui.widget->installEventFilter(this);
接下來我們就需要重寫主窗口的eventFilter函數(shù)即可
bool DragWidget::eventFilter(QObject * watched, QEvent * event) { if (watched
== ui.widget) { if (event->type() == QEvent::MouseButtonDblClick) { if
(isMaximized()) { showNormal(); m_handler.setWidgetResizable(true);
m_handler.setWidgetMovable(true); } else { showMaximized();
m_handler.setWidgetResizable(false); m_handler.setWidgetMovable(false); } } }
return QWidget::eventFilter(watched, event); }
細心的同學就會發(fā)現(xiàn)代碼里有一個m_handler變量,這個類就是博主之前自己封裝的一個拖拽代理,通過接口可以設置被代理的窗體,并設置需要代理哪些行為。
本篇文章中所演示的事例代碼,我們代理了主窗口上標題欄部分的移動事件和整個窗體的縮放事件,設置代碼如下所示
m_handler.activateOn(this); m_handler.useLocalMoveabled(true);
m_handler.addLocalWidget(ui.widget); m_handler.setMaximumMove(true, true);
拖拽代理類內容比較多,本篇文章暫不講解。
四、拖拽
為了更好的理解本篇文章,這里需要把拖拽代理類的頭文件放出來,這樣更有利于大家理解。
接口都比較簡單,代碼中也有注釋,大家自行閱讀。
class WidgetResizeHandler : public QObject { public: explicit
WidgetResizeHandler(QObject* parent = 0); ~WidgetResizeHandler(); public: void
activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理 void
removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理 Qt::CursorShape
CursorShape(QWidget * widget); //窗口移動 default:true void setWidgetMovable(bool
movable); bool isWidgetMovable(); //大小可變 default:true void
setWidgetResizable(bool resizable); bool isWidgetResizable(); // 橡膠式窗口移動
default:false void useRubberBandOnMove(bool use); bool
isUsingRubberBandOnMove(); //橡膠式修改大小 default:false void
useRubberBandOnResize(bool use); bool isUsingRubberBandOnResisze(); void
setBorderWidth(int newBorderWidth); int borderWidth(); //局部可移動 void
useLocalMoveabled(bool use); void addLocalWidget(QWidget *); //最大化時支持拖拽
參數(shù)2表示是否可放大縮小 void setMaximumMove(bool move, bool resize = false); protected:
virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;
private: WidgetResizeHandlerImpl * d_ptr; };
值得注意的是最后一個setMaximumMove接口,他就是我們今天的豬腳-是否支持最大化時拖拽。當我們設置了這個接口后,窗體最大化時也就能進行拖拽,并還原到之前的normal狀態(tài)。
文章第三小節(jié)講解demo時,說過主窗體已經被代理拖拽類進行了事件代理,那么主窗體的所有事件首先都會傳遞給這個代理類,這里我們需要重點關注下鼠標按下時移動事件。
void WidgetData::handleMouseMoveEvent(QMouseEvent* event) { if
(mLeftButtonPressed) { if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges)
{ resizeWidget(event->globalPos()); } else if (d_ptr->mWidgetMovable) {
moveWidget(event->globalPos()); } else if (d_ptr->mMaxMovable) { if
(mWidget->isMaximized() && TryMoveWidget(event)) { d_ptr->mWidgetMovable =
true; //d_ptr->mWidgetResizable = true; } } } else if (d_ptr->mWidgetResizable)
{ updateCursorShape(event->globalPos()); } }
這段代碼包含有其他縮放窗體和正常移動的邏輯,最大化時支持移動的邏輯應該不難找木九十TryMoveWidget這個函數(shù),該函數(shù)中我們進行了充分的邏輯判斷,一旦觸發(fā)了窗體移動,那么我們把mWidgetMovable變量置為true,下一次鼠標按下移動事件就會觸發(fā)正常的拖拽邏輯。
仔細思考上邊一段話,其中有2個關鍵信息
* 觸發(fā)窗體移動,并還原到之前的normal狀態(tài)
* 進行了第一步后,需要把mWidgetMovable變量置為true,之后走正常的窗體移動流程
窗體移動
嘗試移動窗體,當鼠標當前位置距離鼠標按下時的距離大于20px時,進行窗體還原操作,并返回true,代表窗體已經被重置到normal態(tài)。
bool WidgetData::TryMoveWidget(QMouseEvent* event) { QPoint distance =
event->globalPos() - mDragPos; int length = distance.manhattanLength(); if
(length > 20) { QRect rect = mWidget->normalGeometry(); int desX = mDragPos.x()
* rect.width() / mWidget->geometry().width(); int desY = mDragPos.y();
rect.moveTopLeft(event->globalPos() - QPoint(desX, desY));
mWidget->showNormal(); mWidget->setGeometry(rect); mDragPos = QPoint(desX,
desY); mIsMaxMove = true; return true; } return false; }
上述代碼中的mIsMaxMove標識是為了在一次窗體還原操作后,釋放鼠標時可以正常的設置縮放標識而設。
有了上述代碼之后,窗體就能還原到最大化之前的大小,并且為之也移動到了鼠標相應的位置,關于這個新位置的計算這里需要說明下。
x坐標
x軸坐標使用了比例計算方式。窗體全屏時鼠標按下的位置在窗體上的位置在窗體還原后依然保持不變,這樣計算比較簡單而且不會出錯,保證窗體還原后,鼠標會一直在標題欄內。
如果需要優(yōu)化x軸坐標的計算方法,只需要重新計算上述代碼中的desX值即可。
y坐標
y軸坐標這里沒有做特殊處理。因為窗體還原時,標題欄的高度是沒有發(fā)生變化的,因此這里不需要做特殊處理。
講到這里,本篇文章的主要內容基本完成,關于代理拖拽類,不屬于本篇文章內容,因此就不做過多解釋。
五、相關文章
值得一看的優(yōu)秀文章:
* 財聯(lián)社-產品展示 <https://www.cnblogs.com/swarmbees/p/6707798.html>
* 廣聯(lián)達-產品展示 <https://www.cnblogs.com/swarmbees/p/10836505.html>
* Qt定制控件列表 <https://blog.csdn.net/qq_30392343/article/details/95527107>
* 牛逼哄哄的Qt庫 <https://blog.csdn.net/qq_30392343/article/details/95526527>
如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝?。?!
很重要--轉載聲明
*
本站文章無特別說明,皆為原創(chuàng),版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八
<https://www.cnblogs.com/swarmbees/> or Twowords
<https://www.jianshu.com/u/7673f8cfb4e6>
*
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利于轉載者的目的。
熱門工具 換一換