Qt在透明QWidget上自由繪畫

Qt在透明控件上自由繪畫


初學Qt,需要實現這樣的效果:

在透明的QWidget上自由的繪圖,看起來就像是直接在桌面繪圖一樣。

要達到這樣的效果,需要分成兩個步驟:

  1. 如何將QWidget變得透明。
  2. 如何在透明的QWidget控件上使用QPainter繪製圖形。

當然,第二點是Widget透明之後才發現的需求,發現的過程後面會說。

兩個需求完成總共花了大概3個小時。這期間剛開始一直中文搜索國內博客,沒有解決任何一個問題。最後20來分鐘沒辦法了,英文Google一下吧,結果沒多久就找到了答案。

不知道爲啥,國內各大博客論壇,關於Qt方面的內容又舊又亂,難道是沒有什麼新鮮血液進入了麼?

爲了讓這3個小時價值最大化,於是有了這篇博客,也算是爲Qt社羣增磚添瓦吧。

此前,先把給我了幫助的兩個網頁掛出來鳴謝一下:

關於透明Widget的
Qt centre(關於透明繪製的)

接下來是正題:

如何讓QWidget變透明

先說一下被誤導的嘗試,關於讓窗體透明,網友辦法很多。

  1. setWindowOpacity(0) ;:設置完之後,窗體不會有任何顯示,就像你從來沒運行過程序一樣。除了特殊用途,一般也不會有這種需求。
  2. setPalette(palette): 其中palette給一個透明的圖片當背景,或者透明的顏色值。錯誤,設置完後,窗體會變成黑色。
  3. …不一一列舉了…

進入整體,其實透明窗體實現相對簡單,對你想要透明的QWidget對象設置三個屬性就行了(構造函數還是main函數中設置,看你心情)。僞代碼如下:

setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_TransparentForMouseEvents);

稍微解釋一下:

  • Qt::FramelessWindowHint:

    Produces a borderless window. The user cannot move or resize a borderless window via the window system.

    產生一個無邊框的窗體。用戶無法通過窗體系統移動窗體或者改變窗體的大小

    上面是Qt幫助文檔中關於FramelessWindowHint標籤的解釋,該說的都說明了。需要關注的是,用戶無法通過普通的方式改變窗體的大小和位置了。

  • Qt::WA_NoSystemBackground

    Indicates that the widget has no background.

    表明指定的widget沒有背景。

    而默認的QWidget通常會自帶一個白色的背景。計算機中,對於顏色,沒有的意思一般都是黑色。例如,RGB值爲0,0,0時。eum~有點懵,爲啥要設置這個??不糾結

  • Qt::WA_TranslucentBackground:

    Indicates that the widget should have a translucent background, i.e., any non-opaque regions of the widgets will be translucent because the widget will have an alpha channel. Setting this flag causes WA_NoSystemBackground to be set.

    表明widget應該具有半透明的背景,即任何不透明的區域都將是半透明的。設置這個標籤將導致Qt::WA_NoSystemBackground被同時設置。

    嗯~~不透明的區域半透明!!!什麼鬼話。翻譯一下,大概就是,應該透明的地方透明,不應該透明的地方不透明,例如:沒有任何控件的地方就是應該透明的,有子QWidget或者QPainter畫出的線條的地方就不是透明的。

    還有一點挺有意思的,這個標籤被設置後,Qt::WA_NoSystemBackground也會被設置,所以其實僞代碼中的setAttribute(Qt::WA_NoSystemBackground);是可以省略的。

  • Qt::WA_TransparentForMouseEvents:

    When enabled, this attribute disables the delivery of mouse events to the widget and its children.

    啓用該屬性時,將禁止想widget和它的子widget傳遞鼠標事件。

    這個屬性後面會用,順便在這裏解釋先。

最後,來看一下每個屬性的效果,我的工程主窗體就是一個普通的QWidget,方便起見,顯示了一個Label,並重寫了paintEvent函數,使用QPainter畫了一個橢圓。源碼會在文末附上。
在這裏插入圖片描述

上面是什麼屬性都沒有設置的QWidget,藍色文字部分是一個Label,紅色的橢圓是用QPainter畫在QWidget上的。我們要達到的透明效果是,只能看到藍色文字和紅色橢圓,其它都是桌面背景。下面通過屬性的一步步設置,仔細觀察一下界面的變化吧。
在這裏插入圖片描述

上面是setWindowFlags(Qt::FramelessWindowHint);代碼執行後的界面。QWidget的標題框消失了,但是還有默認背景。
在這裏插入圖片描述
上面是setAttribute(Qt::WA_NoSystemBackground);執行後的界面,還真的變黑了。不糾結
在這裏插入圖片描述

噹噹噹~,這不就是我要的效果麼。這是執行了setAttribute(Qt::WA_TranslucentBackground);的效果。背景其實就是我的桌面背景。

接下來一個問題就是,當QWidget變成透明背景後,居然沒法響應鼠標了,鼠標的事件沒有發給我的widget。來看看效果:
在這裏插入圖片描述

途中的Lable的拖動效果,是因爲我重寫了QWidgetmouseMoveEventmousePressEvent函數。代碼大概是這樣的:

void Widget::mousePressEvent(QMouseEvent *event) {
       mousePosition = event->globalPos();
}
void Widget::mouseMoveEvent(QMouseEvent *event) {
       const QPoint position = pos() + event->globalPos() - mousePosition;
       move(position.x(), position.y());
       mousePosition = event->globalPos();
}

只有鼠標在Label上時,widget才能夠響應鼠標的拖拽動作,在QPainter畫出的紅色橢圓上,鼠標的效果和在桌面的效果沒啥區別。無法效應鼠標,肯定就不能作畫,畢竟我們的手繪是需要靠鼠標或者觸控筆輸入的。那麼,怎麼讓透明的窗體相應鼠標的各種事件呢?

透明QWidget上響應鼠標事件

在透明的窗體上,不能響應鼠標事件,應該是透明這個因素造成的。更深層次的原因,暫時還不瞭解。但實驗結果如此。

說說最終的解決思路吧:

  1. 申明一個QPixmap,變量名爲cacheMap。它將作爲整個窗體(QWidget)的底圖。
  2. 初始化cacheMap,用近乎透明的顏色填充。
  3. 在paintEvent函數中,通過QPainter將cacheMap繪製到窗體上,充作背景。

最終,雖然整個QWidget將會有一個近乎透明的背景圖,也就是cacheMap存在,但它幾乎時透明到人眼無法識別。對於計算機來說,就不是這樣了。於是我們完成了目標。

核心代碼如下:

/* 頭文件 */    
QPixmap *cacheMap;
/* cpp文件 構造函數中 */       
cacheMap = new QPixmap(this->size());
cacheMap->fill(QColor(0, 0, 0, 1));
/* cpp文件 paintEvent函數中 */     
void Widget::paintEvent(QPaintEvent *e) {
    QPainter painter(this);
    painter.drawPixmap(0, 0, *cacheMap);
}

最終效果如下:
在這裏插入圖片描述
對於繪圖來講,拿到了鼠標的事件,只需要記錄鼠標軌跡,就可以通過QPainter來繪製出軌跡了。簡單粗暴點的,可以直接將鼠標的軌跡點全部用直線鏈接起來,也可以用Bezier曲線擬合,另外可以通過控制cacheMap的繪製與否,來控制鼠標是否穿透到桌面來實現特殊的效果。當然,這些又是另的話題了。

源碼下載鏈接:https://download.csdn.net/download/qq_25333681/11979920

在csdn下載可能需要點積分,如果你恰好沒有,留言留下郵箱,我發給你。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章