Qt無邊框窗體-最大化時支持拖拽還原

来源:https://www.cnblogs.com/swarmbees/archive/2019/08/27/11415829.html
-Advertisement-
Play Games

[TOC] 原文鏈接: "Markdown模板" 一、概述 用Qt進行開發界面時,既想要實現友好的用戶交互又想界面漂亮,那麼自定義界面就必不可少。其中有一個操作就是是我們每一個Qter開發者都要會的,而且是經常進行的。 Qt::FramelessWindowHint這個屬性想必大家都使用過,有些同學 ...


目錄

原文鏈接:Qt無邊框窗體-最大化時支持拖拽還原

一、概述

用Qt進行開發界面時,既想要實現友好的用戶交互又想界面漂亮,那麼自定義界面就必不可少。其中有一個操作就是是我們每一個Qter開發者都要會的,而且是經常進行的。

Qt::FramelessWindowHint這個屬性想必大家都使用過,有些同學可能對這個屬性很瞭解,也用的是爐火純青,今天我們也來說說這個屬性。

關於這個無邊框屬性網上也有一些文章,有些談論的是bug,當然了這是針對不同os而言,也有些是跟其他第三方庫混合使用時的問題。可是問題歸問題,想要實現自定義的優秀界面這個屬性也是必不可少的。

今天我們就來實現一個無邊框窗體最大化時,支持拖拽標題欄進行還原的功能。

無邊框窗體支持縮放、移動這些不屬於本篇文章的內容,本篇文章主要講解怎麼實現最大化時拖拽標題欄進行還原窗體,本篇文章的代碼依賴於博主之前封裝的一個拖拽代理類。

二、效果展示

如效果圖所示,做了一個簡單的事例,雙擊標題欄窗體最大化,這個時候如果進行標題欄拖拽,當滑鼠按下並移動一段距離時窗體恢復normal狀態。

恢復normal狀態下的窗體仍然支持放大和縮小,有介面可以設置。

三、demo製作

demo的製作過程還是比較簡單的,分為如下幾步

1、設計窗體

通過desinger設計器我們拖拽了一個大致窗體內容,為了更好的展示效果,標題欄加上了icon和背景色

2、雙擊放大

滑鼠雙擊標題欄放大這個功能實現起來方法也比較多,這裡博主選擇了代碼量最少並且實現起來最簡單的方式,直接把標題欄的事件迴圈安裝到了主窗體上。

ui.widget->installEventFilter(this);

接下來我們就需要重寫主視窗的eventFilter函數即可

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);
}

細心的同學就會發現代碼里有一個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 *);

    //最大化時支持拖拽 參數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狀態。

文章第三小節講解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這個函數,該函數中我們進行了充分的邏輯判斷,一旦觸發了窗體移動,那麼我們把mWidgetMovable變數置為true,下一次滑鼠按下移動事件就會觸發正常的拖拽邏輯。

仔細思考上邊一段話,其中有2個關鍵信息

  1. 觸發窗體移動,並還原到之前的normal狀態
  2. 進行了第一步後,需要把mWidgetMovable變數置為true,之後走正常的窗體移動流程

窗體移動

嘗試移動窗體,當滑鼠當前位置距離滑鼠按下時的距離大於20px時,進行窗體還原操作,並返回true,代表窗體已經被重置到normal態。

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軸坐標使用了比例計算方式。窗體全屏時滑鼠按下的位置在窗體上的位置在窗體還原後依然保持不變,這樣計算比較簡單而且不會出錯,保證窗體還原後,滑鼠會一直在標題欄內。

如果需要優化x軸坐標的計算方法,只需要重新計算上述代碼中的desX值即可。

y坐標

y軸坐標這裡沒有做特殊處理。因為窗體還原時,標題欄的高度是沒有發生變化的,因此這裡不需要做特殊處理。

講到這裡,本篇文章的主要內容基本完成,關於代理拖拽類,不屬於本篇文章內容,因此就不做過多解釋。

五、相關文章

值得一看的優秀文章:

  1. 財聯社-產品展示
  2. 廣聯達-產品展示
  3. Qt定製控制項列表
  4. 牛逼哄哄的Qt庫





如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!














很重要--轉載聲明

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 自己評論的信息可以刪除,別人評論的信息不可刪除sql 實現: 主要實現的語句:c.person_id=? as flag 在前臺進行判斷。 f :不是自己評論的信息 t:是自己評論的信息 顯示的效果如下: ...
  • 前言:本人是一個初學者,也是第一次寫博客,敲鍵盤的時候還不知道發佈後是什麼效果,希望內容給其他初學的同學一點幫助,同時加深自己的理解。這篇隨筆講的是Page頁面的生命周期,在開發中是基礎中的基礎,很容易理解。 先給出直達官方的鏈接: 1、小程式頁面生命周期圖:https://developers.w ...
  • JS的原型、原型鏈一直是比較難理解的內容,不少初學者甚至有一定經驗的老鳥都不一定能完全說清楚,更多的"很可能"是一知半解,而這部分內容又是JS的核心內容,想要技術進階的話肯定不能對這個概念一知半解,碰到問題靠“猜”,卻不理解它的規則! prototype 只有函數有prototype屬性 Objec ...
  • 最近在Github上弄項目,需要搭建一個webpack開發環境。Emmm,是的,從0開始搭建一個項目確實不容易,光Webpack的坑就夠我踩一路的了。這不,剛搭建到“圖片打包”這裡,就遇到了麻煩。最後找到了問題的源頭,在mini-css-extract-plugin(抽離CSS代碼為一個CSC文件的 ...
  • 企業應用系統,如果系統之間的通信、集成與整合,尤其當面臨異構系統時,那麼需要分散式的調用與通信。系統中一般會有很多對實時性要求不高但零零碎碎且耗時的地方,比如發送簡訊,郵件提醒,記錄用戶操作日誌等,在用戶訪問量比較大的情況下,對系統壓力比較大。 面對這些問題,我們一般會將這些請求,放在消息隊列MQ中 ...
  • 1 題目描述 求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 ...
  • 文章瀏覽量統計,low的做法是:用戶每次瀏覽,前端會發送一個GET請求獲取一篇文章詳情時,會把這篇文章的瀏覽量 +1,存進資料庫里。 ...
  • 一、switch練習 註意:switch(int/String) 我們舉例,這裡傳入的是char類型,而實際上卻是'B',就是66 二、我們判斷一個學生成績的等級 三、源碼: d21_switch_exercise.java 地址:https://github.com/ruigege66/Java/ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...