欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Qt跨平臺窗口選擇功能的實(shí)現(xiàn)過程

 更新時(shí)間:2022年12月09日 16:38:22   作者:mahuifa  
很多時(shí)候?yàn)榱朔奖丬浖氖褂?我們需要讓編寫的界面程序顯示在最上層,這時(shí)候就需要對窗口屬性進(jìn)行調(diào)整,下面這篇文章主要給大家介紹了關(guān)于Qt跨平臺窗口選擇功能的實(shí)現(xiàn)過程,需要的朋友可以參考下

1、概述

  • Qt版本:V5.12.5
  • 兼容系統(tǒng):
    • Windows:這里測試了Windows10,其它的版本沒有測試;
    • Linux:這里測試了ubuntu18.04、20.04,其它的沒有測試(ubuntu自帶的截圖軟件沒有這個(gè)功能);
    • Mac:等啥時(shí)候我有了Mac電腦再說。
  1. 我們在使用截圖軟件、錄屏軟件時(shí)常常有一個(gè)選項(xiàng),就是窗口截圖,當(dāng)我們鼠標(biāo)移動到窗口上時(shí),程序會自動識別到鼠標(biāo)位置的窗口,獲取窗口的大小、位置,這是怎么實(shí)現(xiàn)的呢;
  2. 這里就研究了一下,如果使用Qt的鼠標(biāo)事件、事件過濾器,一般鼠標(biāo)出了窗口范圍就不會觸發(fā)鼠標(biāo)事件了,想要獲取全局鼠標(biāo)事件只能使用系統(tǒng)API,Windows下就是使用user32、ubuntu下就是X11;
  3. 在這個(gè)示例中實(shí)現(xiàn)了自動獲取鼠標(biāo)所在坐標(biāo)窗口的位置、大小信息,并使用一個(gè)矩形窗口框選、覆蓋住所在窗口;
  4. 在windows實(shí)現(xiàn)的窗口選擇功能可精確識別系統(tǒng)任務(wù)欄、程序圖標(biāo)、文件資源管理器的各個(gè)窗口部件等(很多截屏軟件都沒這個(gè)功能)。

2、實(shí)現(xiàn)效果

Windows下實(shí)現(xiàn)效果

Linux下實(shí)現(xiàn)效果

3、實(shí)現(xiàn)原理

Windows

  1. 使用定時(shí)器每隔200ms獲取一次當(dāng)前鼠標(biāo)位置;
  2. 調(diào)用系統(tǒng)user32 API通過鼠標(biāo)位置查詢當(dāng)前位置的窗口句柄;
  3. 通過獲取的窗口句柄獲取窗口的位置、大小;
  4. 將當(dāng)前窗口覆蓋到鼠標(biāo)所在窗口上方(注意:當(dāng)前窗口需要設(shè)置鼠標(biāo)穿透,否則第2部獲取到的就是當(dāng)前窗口的句柄);

Linux

  1. 使用定時(shí)器每隔200ms獲取一次當(dāng)前鼠標(biāo)位置;
  2. 調(diào)用系統(tǒng)x11 API獲取當(dāng)前屏幕的所有窗口;
  3. 通過獲取的窗口句柄獲取窗口的位置、大小;
  4. 通過當(dāng)前窗口的句柄過濾掉獲取的所有窗口句柄中的當(dāng)前窗口(否則當(dāng)前窗口因?yàn)槭窃谧钌蠈樱看潍@取的都是當(dāng)前窗口的大?。?;
  5. 遍歷所有窗口的位置、大小,判斷包含鼠標(biāo)位置的窗口,并記錄位置、大小信息,由于遍歷是從最底層窗口到最頂層窗口,所以需要保存最后一個(gè)窗口的位置、大小信息;
  6. 將當(dāng)前窗口覆蓋到鼠標(biāo)所在窗口上方(注意:linux下不能設(shè)置鼠標(biāo)穿透,否則窗口出現(xiàn)顯示不全的問題);

4、關(guān)鍵代碼

由于使用到了系統(tǒng)API,所以pro文件中需要鏈接系統(tǒng)庫

win32 {
LIBS+= -luser32    # 使用WindowsAPI需要鏈接庫
}
unix:!macx{
LIBS += -lX11      # linux獲取窗口信息需要用到xlib
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void on_timeout();
private:
    QTimer m_timer;
};
#endif // WIDGET_H

NtpClient.cpp

#include "widget.h"
#include <QDebug>
#include <qgridlayout.h>

#if defined(Q_OS_WIN)
#include <Windows.h>
#include <windef.h>
#elif defined(Q_OS_LINUX)
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#elif defined(Q_OS_MAC)
#endif

#if defined(Q_OS_WIN)
static HHOOK g_hook = nullptr;
/**
 * @brief           處理鼠標(biāo)事件的回調(diào)函數(shù)
 * @param nCode
 * @param wParam
 * @param lParam
 * @return
 */
LRESULT CALLBACK CallBackProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    switch (wParam)
    {
    case WM_LBUTTONDOWN:   // 鼠標(biāo)左鍵按下
    {
        POINT pos;
        bool ret = GetCursorPos(&pos);
        if(ret)
        {
            qDebug() << pos.x <<" " << pos.y;
        }
        qDebug() << "鼠標(biāo)左鍵按下";
        break;
    }
    default:
        break;
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);   // 注意這一行一定不能少,否則會出大問題
}
#endif


Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowTitle(QString("Qt-框選鼠標(biāo)當(dāng)前位置窗口范圍 - V%1").arg(APP_VERSION));
#if defined(Q_OS_WIN)
    // linux下鼠標(biāo)穿透要放在后面兩行代碼的全前面,否則無效(但是鼠標(biāo)穿透了會導(dǎo)致一些奇怪的問題,如窗口顯示不全,所以這里不使用)
    // windows下如果不設(shè)置鼠標(biāo)穿透則只能捕獲到當(dāng)前窗口
    this->setAttribute(Qt::WA_TransparentForMouseEvents, true);
#endif
    this->setWindowFlags(Qt::FramelessWindowHint);                            // 去掉邊框、標(biāo)題欄
    this->setAttribute(Qt::WA_TranslucentBackground);                         // 背景透明
    this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint);     // 設(shè)置頂級窗口,防止遮擋

#if defined(Q_OS_WIN)
    // 由于windows不透明的窗體如果不設(shè)置設(shè)置鼠標(biāo)穿透WindowFromPoint只能捕捉到當(dāng)前窗體,而設(shè)置鼠標(biāo)穿透后想要獲取鼠標(biāo)事件只能通過鼠標(biāo)鉤子
    g_hook = SetWindowsHookExW(WH_MOUSE_LL, CallBackProc, GetModuleHandleW(nullptr), 0);  // 掛載全局鼠標(biāo)鉤子
    if (g_hook)
    {
        qDebug() << "鼠標(biāo)鉤子掛接成功,線程ID:" << GetCurrentThreadId();

    }
    else
    {
        qDebug() << "鼠標(biāo)鉤子掛接失敗:" << GetLastError();
    }
#endif

    // 在當(dāng)前窗口上增加一層QWidget,否則不會顯示邊框
    QGridLayout* gridLayout = new QGridLayout(this);
    gridLayout->setSpacing(0);
    gridLayout->setContentsMargins(0, 0, 0, 0);
    gridLayout->addWidget(new QWidget(), 0, 0, 1, 1);

    this->setStyleSheet(" background-color: rgba(58, 196, 255, 40); border: 2px solid rgba(58, 196, 255, 200);"); // 設(shè)置窗口邊框樣式 dashed虛線,solid 實(shí)線

    // 使用定時(shí)器定時(shí)獲取當(dāng)前鼠標(biāo)位置的窗口位置信息
    connect(&m_timer, &QTimer::timeout, this, &Widget::on_timeout);
    m_timer.start(200);

}

Widget::~Widget()
{
#if defined(Q_OS_WIN)
    if(g_hook)
    {
        bool ret = UnhookWindowsHookEx(g_hook);
        if(ret)
        {
            qDebug() << "卸載鼠標(biāo)鉤子。";
        }
    }
#endif
}

void Widget::on_timeout()
{
    QPoint point = QCursor::pos();  // 獲取鼠標(biāo)當(dāng)前位置
#if defined(Q_OS_WIN)
    POINT pos;
    pos.x = point.x();
    pos.y = point.y();

    HWND hwnd = nullptr;
    hwnd = WindowFromPoint(pos);   // 通過鼠標(biāo)位置獲取窗口句柄
    if(!hwnd) return;

    RECT lrect;
    bool ret = GetWindowRect(hwnd, &lrect);     //獲取窗口位置
    if(!ret) return;

    QRect rect;
    rect.setX(lrect.left);
    rect.setY(lrect.top);
    rect.setWidth(lrect.right - lrect.left);
    rect.setHeight(lrect.bottom - lrect.top);
    this->setGeometry(rect);         // 設(shè)置窗口邊框
#elif defined(Q_OS_LINUX)  // linux下使用x11獲取的窗口大小有可能不太準(zhǔn)確,例如瀏覽器的大小會偏小
    // 獲取根窗口
    Display* display = XOpenDisplay(nullptr);
    Window rootWindow = DefaultRootWindow(display);

    Window root_return, parent_return;
    Window * children = nullptr;
    unsigned int nchildren = 0;
    // 函數(shù)詳細(xì)說明見xlib文檔:https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
    // 該函數(shù)會返回父窗口的子窗口列表children,因?yàn)檫@里用的是當(dāng)前桌面的根窗口作為父窗口,所以會返回所有子窗口
    // 注意:窗口順序(z-order)為自底向上
    XQueryTree(display, rootWindow, &root_return, &parent_return, &children, &nchildren);

    QRect recte;                      // 保存鼠標(biāo)當(dāng)前所在窗口的范圍
    for(unsigned int i = 0; i < nchildren; ++i)
    {
        if(children[i] == this->winId()) continue;           // 由于當(dāng)前窗口一直在最頂層,所以這里要過濾掉當(dāng)前窗口,否則一直獲取到的就是當(dāng)前窗口大小
        XWindowAttributes attrs;
        XGetWindowAttributes(display, children[i], &attrs);  // 獲取窗口參數(shù)
        if (attrs.map_state == IsViewable)                   // 只處理可見的窗口, 三個(gè)狀態(tài):IsUnmapped, IsUnviewable, IsViewable
        {
#if 0
            QRect rect(attrs.x + 1, attrs.y, attrs.width, attrs.height);  // 這里x+1防止全屏顯示,如果不+1,設(shè)置的大小等于屏幕大小是會自動切換成全屏顯示狀態(tài),后面就無法縮小了
#else
            QRect rect(attrs.x, attrs.y, attrs.width, attrs.height);
#endif
            if(rect.contains(point))                        // 判斷鼠標(biāo)坐標(biāo)是否在窗口范圍內(nèi)
            {
                recte = rect;                               // 記錄最后一個(gè)窗口的范圍
            }
        }
    }
#if 0  // 在linux下使用setGeometry設(shè)置窗口會有一些問題
    this->showNormal();               // 第一次顯示是如果是屏幕大小,則后面無法縮小,這里需要設(shè)置還原
    this->setGeometry(recte);         // 設(shè)置窗口邊框
#else  // 使用setFixedSize+move可以避免這些問題
    this->move(recte.x(), recte.y());
    this->setFixedSize(recte.width(), recte.height());
#endif
//    qDebug() << this->rect() <<recte<< this->windowState();

    // 注意釋放資源
    XFree(children);
    XCloseDisplay(display);

#elif defined(Q_OS_MAC)
#endif
}

5、源代碼

總結(jié)

到此這篇關(guān)于Qt跨平臺窗口選擇功能實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt跨平臺窗口選擇功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++讀取和寫入TXT文件的整理方法

    c++讀取和寫入TXT文件的整理方法

    今天小編就為大家分享一篇c++讀取和寫入TXT文件的整理方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • 在Visual Studio Code中使用CSSComb格式化CSS文件的教程

    在Visual Studio Code中使用CSSComb格式化CSS文件的教程

    這篇文章主要介紹了在Visual Studio Code中使用CSSComb格式化CSS文件,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 詳解C語言對字符串處理函數(shù)的實(shí)現(xiàn)方法

    詳解C語言對字符串處理函數(shù)的實(shí)現(xiàn)方法

    這篇文章主要為大家介紹了C語言對字符串處理函數(shù)的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • 深度剖析C語言結(jié)構(gòu)體

    深度剖析C語言結(jié)構(gòu)體

    今天小編就為大家分享一篇關(guān)于深度剖析C語言結(jié)構(gòu)體,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C++ opencv實(shí)現(xiàn)的把藍(lán)底照片轉(zhuǎn)化為白底照片功能完整示例

    C++ opencv實(shí)現(xiàn)的把藍(lán)底照片轉(zhuǎn)化為白底照片功能完整示例

    這篇文章主要介紹了C++ opencv實(shí)現(xiàn)的把藍(lán)底照片轉(zhuǎn)化為白底照片功能,結(jié)合完整實(shí)例形式詳細(xì)分析了C++使用opencv模塊進(jìn)行圖片轉(zhuǎn)換操作的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-12-12
  • clion最新激活碼+漢化的步驟詳解(親測可用激活到2089)

    clion最新激活碼+漢化的步驟詳解(親測可用激活到2089)

    這篇文章主要介紹了clion最新版下載安裝+破解+漢化的步驟詳解,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • C++的matlab接口轉(zhuǎn)換方法詳解

    C++的matlab接口轉(zhuǎn)換方法詳解

    這篇文章主要為大家詳細(xì)介紹了C++的matlab接口轉(zhuǎn)換方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C++利用socket傳輸大文件的實(shí)現(xiàn)代碼

    C++利用socket傳輸大文件的實(shí)現(xiàn)代碼

    這篇文章主要為大家詳細(xì)介紹了C/C++如何使用socket傳輸大文件的實(shí)現(xiàn)代碼,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下
    2023-10-10
  • 如何用C寫一個(gè)web服務(wù)器之I/O多路復(fù)用

    如何用C寫一個(gè)web服務(wù)器之I/O多路復(fù)用

    本文主要介紹了如何用C寫一個(gè)web服務(wù)器之I/O多路復(fù)用,本次選擇了 I/O 模型的優(yōu)化,因?yàn)樗欠?wù)器的基礎(chǔ),這個(gè)先完成的話,后面的優(yōu)化就可以選擇各個(gè)模塊來進(jìn)行,不必進(jìn)行全局化的改動了。
    2021-05-05
  • C++ 類的賦值運(yùn)算符''''=''''重載的方法實(shí)現(xiàn)

    C++ 類的賦值運(yùn)算符''''=''''重載的方法實(shí)現(xiàn)

    這篇文章主要介紹了C++ 類的賦值運(yùn)算符'='重載的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02

最新評論