每日一言
????????你比想象中更有韌性,那些看似艱難的日子,終將成為勛章。
自定義按鈕
????????我們要知道自定義控件就需要我們創建一個新的類加上繼承父類,但是我們還要注意一個點,就是如果我們是自己重頭開始造控件的話,那么我們就直接可以繼承QWidget就可以了,因為這個屬于是極致的定制開發,如果是只需要在QT已有的控件上添加QT官方在這個控件上沒有的功能的話就需要我們自己創建新的類之后還需要將原有的控件進行提升為這個我們創建的新類,提升成自己的類,那么我們寫的新控件就等于是在原有控件上的改變,變的擁有了更多功能,但是可以說又保存了90%的原有控件的功能,這個就是兩種不同的自定義控件的方法
好的那我們現在來自定義一個按鈕吧(實際上是自定義一個控件,看起來像按鈕一樣的功能)
就是我們這個說實話不叫自定義按鈕,應該是叫做自定義控件,只是看起來像一個按鈕,現在我來講講怎么搞,首先我們先創建一個C++的類叫做MyButton隨后繼承這個QWidget,那么就是意味著是全部是自己極致開發,全部自己干,所以我們需要重寫的事件有鼠標點擊事件、鼠標的進入事件
Widget::enterEvent
MyButton::enterEvent
這個兩個是不一樣的效果的,一個是在窗口的時候,鼠標進入到窗口就會觸發事件,一個是要在Mybutton這個控件上的時候才會觸發事件,
每一個控件都有自己的(鼠標、鍵盤)事件
好的現在我們想要我們的自定義按鈕是一個圖片的樣子我們怎么操作呢?
那么我們這邊就需要進行接收新的知識點了
QPixmap??這個類是專門用于畫圖的
這里有一個load這個函數,就是可以載入我們的資源文件的,值得注意的是我們去看這個函數的參數
漲知識啦
核心是理解?const QString &?作為函數參數的特性—— 它允許直接接收臨時對象(比如字符串字面量轉換的臨時 QString),而不需要顯式定義一個變量。我們一步步拆解:
一、const QString &?為什么能直接接收字符串字面量?
當你寫?pic.load(":/QTicon/open2.png")?時,發生了這幾步:
- 字符串字面量?":/QTicon/open2.png"?是?const char*?類型,Qt 會自動調用?QString?的構造函數,創建一個臨時的 QString 對象(這個對象在內存中短暫存在,沒有名字)。
- 函數參數?const QString &fileName?是常量引用,它可以直接綁定到這個臨時 QString 對象上(這是 C++ 標準允許的,const?引用的特殊權限)。
二、為什么不需要先用變量接收?
非 const 引用(如?QString &)必須綁定到一個 “有名字的、已存在的變量”,不能綁定臨時對象。例如:
void?func(QString &s)?{}??// 非const引用參數func("hello");??// 報錯!不能綁定臨時對象
但?const?引用?被設計為可以綁定到:
- 已存在的變量(如?QString path = "..."; load(path);)
- 臨時對象(如字符串字面量轉換的臨時 QString)
這是 C++ 的語法規則,目的是方便使用(不需要顯式定義變量),同時保證安全性(const?確保不會修改這個臨時對象)。
三、const QString &?中 “引用” 的作用是什么?
引用的核心作用是?“避免不必要的拷貝,提高效率”:
如果函數參數是?QString fileName(按值傳遞):
調用時會把傳遞的字符串(無論是變量還是臨時對象)完整拷貝一份到函數內部的?fileName?中。對于長字符串(比如路徑很長),拷貝會浪費內存和時間。
如果參數是?const QString &fileName(按 const 引用傳遞):
不會拷貝,fileName?只是一個 “別名”,直接指向傳遞進來的對象(可能是變量,也可能是臨時對象)。既節省了拷貝的開銷,又通過?const?保證不會意外修改這個對象。
四、總結你的代碼為什么可行?
pic.load(":/QTicon/open2.png")?的過程:
- 字符串字面量被轉換為臨時 QString 對象(存儲路徑)。
- const QString &fileName?綁定到這個臨時對象(不需要顯式變量)。
- 函數內部通過引用直接訪問這個臨時對象,避免拷貝,高效且安全。
簡單說:const?引用的特性就是?“既能直接用字面量 / 臨時對象,又不浪費內存”,所以不需要先定義一個 QString 變量~
那么我們的代碼怎么寫呢?
我們需要在類這邊寫QPixmap pic這個對象,還記得這個是什么操作嗎,這個就是我們的類的組合的操作,在類中創建其他類的對象,可以實現獲得其他類的屬性和方法,然后我們去這個cpp中調用他的load函數
我來一個一個講,這個MyButton的這個里面的??pic.load(":/QTicon/open.png");這個是在構造函數里面寫的嘛,說明這個就是我們設置的這個程序第一眼看到的樣子,我們畢竟是自定義按鈕嘛,這些初始化的設置肯定是在構造函數里面寫的
就是這個樣子,
其他的幾個事件也都是一樣的意思,這個圖片文件的話是我通過添加Qt Resources文件添加的就像我們給記事本的按鈕添加上圖片作為圖標一樣做的前期工作,需要將我們想要的圖片先放在資源文件里面
setFixedSize(pic.size());
這個是什么意思呢,這個就是設置我們的控件的尺寸,尺寸為pic.size()調用這個函數,就是表示我們設置這個按鈕的尺寸為這個圖片的尺寸
后面會發現沒有任何的結果啊,不顯示啊,這也是因為我們是繼承的QWidget這個是為了極致的開發的,什么都自己干,所以連很基礎的繪圖都沒有,因此我們也有一個繪圖事件,對于這個事件我們可以去QWidget這個類里面去尋找
我們會找到這個繪制事件,然后我們可以通過這個事件找到QPaint這個類
我們通過這個類創建一個對象,調用這個drawPixmap()這個函數,這個函數的話就是,可以將我們的圖片繪制出來,
void?MyButton::paintEvent(QPaintEvent?*event)
{
????QPainter?painter(this);
????painter.drawPixmap(rect(),pic);
}
會發現我們的painter(this)要加上這個才會正常運行,this的意思就是這個Mybutton這個控件,
我們也可以看這個案例,會發現這個用法painter(this)要這樣寫
目的是:作用:作為?QPainter?的構造參數時,它告訴?QPainter?在哪個控件上進行繪制。
對于這個drawPixmap(),他需要一個矩形區域,還有一個就是QPixmap 的對象的引用,說實話有點像我們的之前的那個font對象的那個操作,那個ui->textEdit->setFont(font)
這樣的操作都是傳的是對象的引用,
好的說一下這個矩形
?rect()?作為目標矩形(控件的完整區域),讓圖片自適應控件大小:
painter.drawPixmap(rect(), pic); // 圖片填滿整個控件(可能拉伸)
最后一步就是我們需要在加載完這個圖片的時候需要手動更新一下update一下,這個是為什么呢?而且我之前那個設置字體的縮放的時候好像也不需要這樣啊?
因為:
- 內置屬性(如字體):Qt 已處理自動更新,無需手動?update()。
- 自定義繪圖:必須手動調用?update()?來觸發重繪,否則界面不會更新。
這種設計體現了 Qt 的靈活性:內置控件負責基礎屬性的自動管理,而自定義繪圖則交給開發者控制,避免了不必要的性能開銷。
其實到最后如果我前面沒有講提升的問題的話,那么我們就需要進行提升
就是因為我們是繼承這個QWidget的,所以我們需要拉一個widget就好了
我提出一個問題??是不是就是我由于是繼承的QWidget那么就是意味著我需要的是極致的自己開發控件,所以也就是只能去拉一個widget出來,然后我去提升他
是的,你的理解非常準確!我用更簡潔的方式總結一下:
為什么必須拉一個?QWidget?并提升?
因為你繼承?QWidget?時,相當于從頭開始開發一個全新控件,而 Qt Designer 默認沒有你的控件。具體邏輯鏈:
繼承?QWidget?的本質:
QWidget?是最基礎的控件類,幾乎什么都沒實現(沒有按鈕外觀、沒有點擊事件)。你繼承它,就像 “從零開始造一輛車”,所有功能都要自己寫。
“提升控件” 如何解決問題?
“提升” 操作的本質是:告訴 Qt Designer,把這個普通的?QWidget?當成我的?MyButton?來用。
具體步驟的意義:
- 拖一個普通?QWidget?到界面上:這是因為 Qt Designer 沒有?MyButton?控件可選,只能用?widget?占位。就是因為我們是極致開發啊,記住我們這個是自己開發的按鈕,不是在QT已有的控件上的開發,另外的是我選擇繼承的是QWidget,就說明全部自己干,說明就是拖一個Widget就好了
- 右鍵 → 提升為?MyButton:
- Qt Designer 會記錄這個操作,并在生成的代碼中使用?MyButton?而非?Widget。
- 生成的代碼會自動包含?mybutton.h?頭文件。
最后完成以上所有的操作就可以實現啦
效果為:我鼠標移到上方的時候就是紫色,點擊的時候就是黑色,鼠標離開這個控件的時候就是藍色,一打開程序的時候也是藍色