由于老周的示例代碼都是用 VS Code + CMake + Qt 寫的,為了不誤匯入,在標題中還是加上“VS Code”好一些,
上次咱們研究了剪貼板的基本用法,也了解了叫 QMimeData 的重要類,為啥要強調這個類?因為接下來扯到的拖放操作也是和它有關系,哦,對了,咱們先避開一下主題,關于剪貼板,咱們還要說一點:就是如何監聽剪貼板內資料的變化并做出回應,這個嘛,就有點像迅雷監聽剪貼板的功能,發現你復制的東西里包含有下載地址的話,就自動彈出新下載任務視窗,
QClipboard 類有好幾個滿足此功能的信號,說這個前咱們要先知道一下 QClipboard 類包含一個 Mode 列舉,這個列舉定義了三個成員:
QClipboard::Clipboard:資料存盤在全域剪貼板中,此模式是各系統通用的,尤其是 Windows,
QClipboard::Selection:通過滑鼠選取資料,X 視窗系統是 C/S 架構,資料選擇后會發送到目標視窗,可用滑鼠中鍵粘貼,
QClipboard::FindBuffer:macOS 專用的粘貼方式,
所以,我們寫代碼時一般不刻意指定某個 Mode,以保證好的兼容性,現在,咱們回頭再看看 QClipboard 類的幾個信號,
selectionChanged:當全域滑鼠選取的資料改變時發出,這個用在 Linux/X11 視窗系統上,
findBufferChanged:一樣道理,只在 macOS 上能用到,
dataChanged:這個比較推薦,不考慮 Mode,只要剪貼板上的資料有變化就會發出,通用性好,
changed:這個最靈活,在發出信號時,會帶上一個 Mode 引數,你在代碼中處理時可以對 Mode 進行分析,
綜上所述,要是只關心剪貼板上的資料變化,連接 dataChanged 信號最合適,下面來個例子,
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.0) set(CMAKE_AUTOMOC ON) project(myapp VERSION 0.1.0) find_package(Qt6 COMPONENTS Core Gui Widgets) # 頭檔案與原始碼檔案都在當前目錄下,“.”是當前目錄 include_directories(.) set(SRC_LIST CustWindow.cpp main.cpp) add_executable(myapp ${SRC_LIST}) target_link_libraries(myapp PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets )
CustWindow.h:
#ifndef CUST_H #define CUST_H #include <QApplication> #include <QWidget> #include <QMimeData> #include <QClipboard> #include <QListWidget> class MyWindow : public QListWidget { Q_OBJECT public: MyWindow(QWidget* parent=nullptr); private: void onDataChanged(); }; #endif
onDataChanged 是私有成員,待會兒用來連接 QClipboard::dataChanged 信號,這個例子中,老周選用的基類是 QListWidget,它是 QListView 的子類,但用起來比 QListView 方便,不需要手動設定 View / Model,直接可以 addItem,很省事,此處老周是想當剪貼板上放入新的文本資料時在 QListWidget 上添加一個子項,
下面是實作代碼:
#include "CustWindow.h" MyWindow::MyWindow(QWidget *parent) : QListWidget(parent) { // 獲取剪貼板參考 QClipboard* clb = QGuiApplication::clipboard(); // 連接信號 connect(clb, &QClipboard::changed, this, &MyWindow::onDataChanged); } void MyWindow::onDataChanged() { QClipboard* clipbd = QApplication::clipboard(); QString s = clipbd ->text(); // 如果剪貼板中包含文本,那么字串不為空 if(!s.isEmpty()) { // 顯示文本 this->addItem("你復制了:" + s); } }
代碼并不復雜,重要事情有二:第一,連接 QClipboard::dataChanged 信號,與 onDataChanged 方法系結,第二,在 onDataChanged 方法內,讀取剪貼板上的文本資料,組成新的字串,呼叫 addItem 方法,把字串添加到 QListWidget (基類)物件中,
main 函式的代碼就那樣了,先創建應用程式物件,然后初始化、顯示視窗,再進入事件回圈,都是老套路了,
#include "CustWindow.h" #include <QApplication> int main(int argc, char** argv) { QApplication app(argc, argv); MyWindow win; win.setWindowTitle("監視粘貼板"); win.resize(350, 320); win.show(); return app.exec(); }
順便說一下,exec 其實是靜態成員,但呼叫時用變數名或類然都可以,變數名就用成員運算子“.”,類名就用成員運算子“::”,
運行程式后,隨便找個地方復制一些文本,然后回到程式視窗,你會有驚喜的,
上圖表明,程式已經能監聽剪貼板的資料變化了,
------------------------------------------------------------- 量子分隔線 ------------------------------------------------------------
好了,下面開始咱們的主題——拖放,這兩個動詞言簡意賅,包含了兩個行為:
a、拖(Drag):資料發送者,發起資料共享操作,此行為一般是滑鼠(或筆,或手指,或其他)在某個物件上按下并移動特定距離后觸發,
b、放(Drop):把拖動的資料放置到目標物件上,資料接收者提取到資料內容,并結束整個共享操作,一般是松開滑鼠按鍵(或筆,或手指,或其他)時結束拖放操作,
由于拖放操作是由滑鼠等指標設備引發的,為了減少誤操作,通常會附加兩個約束條件:
1、滑鼠按下后一段時間,這個時間可以很短,可通過 QApplication::setStartDragTime 方法設定你喜歡的值,單位是毫秒,默認 500 ms,
2、滑鼠按下后必須移動一定的距離,這個距離可以從 QApplication::startDragDistance 方法獲取,也可以通過 setStartDragDistance 方法修改,這距離指的是“曼哈頓”距離,這個距離是兩個點在與X軸和Y軸平行的距離之和,就是正東、正西、正南、正北的方向,總之不是直線距離,這是為了避開大量浮點、開平方等復雜運算,提升速度,具體可以查資料,不懂這個也不影響編程,Qt 的 QPoint 類自帶 manhattanLength 方法,可以獲得兩點相減后的曼哈頓距離,
----------------------------------------------------------------------------------------------------
QDrag類
這個類是拖放操作的核心,因為它的 exec 方法會啟動一個拖放操作,拖放操作與剪貼板類似,也是使用 QMimeData 類來封送資料的,在呼叫 QDrag::exec 之前要用 setMimeData 方法設定要傳遞的資料,
exec 方法回傳時,拖放操作已結束,其回傳值是 Qt::DropAction 列舉,拖放操作完成時所回傳的值可由資料接收者設定,
DropAction列舉
該列舉定義下面幾個值:
1、CopyAction:表示拖放操作將復制資料;
2、MoveAction:表示拖放操作會移動資料;
3、LinkAction:僅僅建立從資料源到資料目標的鏈接;
4、IgnoreAction:無操作(忽略),
其實,這些 Action 是反饋給用戶看的,在資料傳遞的程序中毫無干擾,也就是說,不管是 Copy 還是 Move,只不過是一種“語意”,具體怎么處理資料,還是 coder 說了算,
DropAction 的不同取值會改變滑鼠指標的圖示,所以說這些值是給用戶看的,詳細可粗略看看下面表格,不需要深挖,
![]() |
復制 |
![]() |
移動 |
![]() |
鏈接 |
“復制”是箭頭右下角顯示加號(+),“移動”是顯示向右的箭頭,“鏈接”是一個“右轉”大箭頭,如果忽略或禁止拖放,就是大家熟悉的一個圈圈里面一條斜線——,
在呼叫 QDrag::exec 方法時你可以指定 DropAction 值,通常有兩個引數要賦值:
Qt::DropAction exec(Qt::DropActions supportedActions = Qt::MoveAction); Qt::DropAction exec(Qt::DropActions supportedActions, Qt::DropAction defaultAction);supportedActions 引數表示你規定資料接收者只能使用的 DropAction 值,比如,你在發起拖放時指定 supportedActions = CopyAction | MoveAction,那么,資料接收者在讀取資料時,你只能向用戶反饋“復制”或“移動”圖示,你不能用 LinkAction, 而 defaultAction 引數是給資料接收者建議的操作,只能在 supportedActions 的值里面選, 比如,supportedAction = Move | Copy | Link,那么,defaultAction = Copy 可以,defaultAction = Move 也可以,
拖放操作啟動的條件
拖放操作是與滑鼠有關的,一般會在處理 mousePress、mouseMove 事件時觸發,資料接收者
接收資料是在 Drop 操作時發生——即東西已順利拖到目標上,并且放開滑鼠按鍵, 資料接收者將通過處理下面幾個神秘事件來提取資料的: 1、dragEnter:滑鼠把某個東西拖進當前物件的邊界時發生,假設當前物件是一個視窗,那么,當拖動進入視窗的邊沿時就會觸發該事件; 2、dragMove:東西被拖進來了,過了邊界,但滑鼠仍在移動,此時會不斷觸發 dragMove 事件,這事件是連續發生的,除非你滑鼠不動,要是一時手抖放開了滑鼠左鍵,那就觸發了 drop 事件,拖放操作結束; 3、dragLeave:東西拖進來后,沒有釋放,繼續拖,最終離開當前物件的邊界——拖出去了(斬了),就會觸發 dragLeave 事件; 4、drop:釋放滑鼠,標志拖放操作結束,此時你得讀出別人傳給你的資料了, dragLeave 事件一般很少處理,干得比較多的是 dragEnter 和 drop,當 dragEnter 發生時,通常要分析一下,拖過來的資料是不是我想要的,別人扔給你的有可能是炸彈,所以要判斷一下,不接受的資料直接 ignore(忽略),如果資料是你想要的,就 accept 它,然后在釋放時會發生 drop 事件;在 drop 事件中把你要的資料讀出來就完事了,當然了,QMimeData 中的資料你不見得要全讀出來,你只取你所要的部分,如果在 dragEnter 事件中拒絕資料,那么釋放時是不會發生 drop 事件的, 為了讓大伙伴們更好地理解,drag 和 drop 兩個程序咱們分開說, 接下來,我們實作把文本資料從當前視窗拖到其他程式(如記事本), 下面是 CMake 檔案,cmake_minimum_required(VERSION 3.20) project(DragDemo LANGUAGES CXX) set(CMAKE_AUTOMOC ON) find_package( Qt6 COMPONENTS Core Gui Widgets REQUIRED ) # 找到專案下所有頭檔案和源檔案 file(GLOB_RECURSE SRC_LIST include/*.h src/*.cpp) include_directories(include) add_executable(DragDemo WIN32 ${SRC_LIST}) target_link_libraries( DragDemo PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets )
代碼插件沒有 CMake 的,老周用的是 C++ 的插入,因為里面出現了 /*,被識別成了注釋,所以上面內容后半部分全綠了,
專案結構是這樣的:
下面是頭檔案,
#pragma once #include <QWidget> #include <QPainter> #include <QMouseEvent> class Demo : public QWidget { Q_OBJECT public: Demo(QWidget* parent=nullptr); protected: void paintEvent(QPaintEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; private: // 這個私有變數用來臨時存盤滑鼠按下的坐標 QPoint m_curpt; };
這個類沒什么特別的,就是一個自定義視窗,其中,重寫 paintEvent 方法,在視窗上畫提示文字,這個只為了好看,你可以省略,
重點是重寫 mousePress 和 mouseMove 兩個事件,mousePress 時記下滑鼠按下的坐標,然后在 mouseMove 中再次獲取滑鼠的坐標,和按下時的坐標相減,看看它們的曼哈頓距離是否符合啟動拖放的條件,
咱們來看實作代碼,
Demo::Demo(QWidget *parent) { this->setWindowTitle("拖動示例"); this->resize(258, 240); this->move(659, 520); } void Demo::paintEvent(QPaintEvent *event) { QRect rect=event->rect(); // 在視窗上繪制文本 QFont font; font.setFamily("華文仿宋"); //字體名稱 font.setPointSize(24); //字體大小(點) font.setBold(true); //加粗 QPainter painter(this); // 設定字體 painter.setFont(font); // 計算一下文本所占空間 QString textToDraw = "從此視窗拖動"; QRect textRect = painter.fontMetrics().boundingRect(rect, Qt::AlignCenter, textToDraw); // 移動文本矩形,讓它的中心點和視窗矩形的中心點對齊 textRect.moveCenter(rect.center()); // 設定繪制文本的畫筆 QPen pen; pen.setColor(QColor("red")); painter.setPen(pen); // 開始涂鴉 painter.drawText(textRect.toRectF(), textToDraw); painter.end(); } void Demo::mousePressEvent(QMouseEvent *event) { // 獲取滑鼠按下的坐標點 m_curpt = event->pos(); } void Demo::mouseMoveEvent(QMouseEvent *event) { // 獲取滑鼠現在的位置坐標 QPoint curloc = event->pos(); // 和剛才按下去的坐標比較 if((m_curpt - curloc).manhattanLength() < QApplication::startDragDistance()) { // 距離不夠,不啟動拖放 return; } // 準備拖放 QString str = "石灰水化死尸可作化肥"; //要傳送的資料 QMimeData* mdata = https://www.cnblogs.com/tcjiaan/archive/2023/05/28/new QMimeData; // 打包 mdata -> setText(str); // 發快遞 // QDrag(QObject *dragSource) // dragSource 指的是發起拖放操作的物件 // 這里是當前視窗 QDrag drag(this); // 設定資料 drag.setMimeData(mdata); // 出發 auto result = drag.exec(Qt::CopyAction | Qt::LinkAction, Qt::CopyAction); QString displaymsg = "資料傳遞完畢,操作結果:"; if(result & Qt::CopyAction) { displaymsg += "復制"; } else if(result & Qt::LinkAction) { displaymsg += "鏈接"; } else if(result & Qt::IgnoreAction) { displaymsg += "忽略"; } else { displaymsg += "未知"; } QMessageBox::information(this, "提示", displaymsg, QMessageBox::Ok); }
paintEvent 的重寫不是重點,不過老周簡單說下,
a、創建 QFont 實體,你看名字都知道是什么鬼了,是的,設定字體引數;
b、計算文本”從此視窗拖動“要占多少空間,核心是呼叫 QFontMetrics 類的 boundingRect 方法,這里要注意,呼叫的是這個多載:
QRect QFontMetrics::boundingRect(const QRect &r, int flags, const QString &text, int tabstops = 0, int *tabarray = (int *)nullptr) const
也就是說,不能呼叫只傳文本的多載,那個多載計算出來的 rect 寬度會變小,導致繪制出來的字串少了一個字符(原因不明),但,呼叫上面這個有N多引數的多載是沒問題,區別就在于給也一個 r 引數,這個引數提供一個矩形區域作為約束,這里老周用整個視窗的空間作為約束,可能是給的空間足夠大,所以計算出來的寬度就足夠,于是老周厚著臉皮翻了一下 Qt 的原始碼,這兩多載所使用的處理方法不一樣,引數比較多的那個里面呼叫的是 qt_format_text 函式,引數較少的那個里面用的是 QStackTextEngine 類,有興趣的伙伴可以去翻翻,
moveCenter 是使矩形平移,并且中心點對準視窗矩形區域的中心點,這里可以讓繪制的文本處在視窗的中央,
接下來說說 mousePress 事件,這里就很簡單了,就是直接記錄滑鼠的位置,不過,有點不嚴謹,拖放操作沒聽說過用滑鼠右鍵操作的吧?所以,此處最好判斷一下,是不是左鍵按下,
void Demo::mousePressEvent(QMouseEvent *event) { if(!(event -> buttons() & Qt::LeftButton)) { return; } // 獲取滑鼠按下的坐標點 m_curpt = event->pos(); }
mouseMove 事件也是如此,
void Demo::mouseMoveEvent(QMouseEvent *event) { if(!(event -> buttons() & Qt::LeftButton)) { return; } …… }
QDrag::exec 方法是在 mouseMove 事件中啟動的,這個就和剪貼板的操作相似了,先創建 QMimeData,設定文本資料,然后創建 QDrag 實體,設定 MimeData,然后就呼叫 exec 方法,
最后是整個程式的 main 函式,
int main(int argc, char* argv[]) { QApplication app(argc, argv); Demo window; window.show(); return QApplication::exec(); }
運行示例后,打開一個文本編輯器(如記事本),在視窗上按下滑鼠左鍵,拖到文本編輯器,文本就發送到目標視窗了,
然后,咱們來看 drop 操作,
要想讓某個組件支持放置行為,你必須呼叫:
setAcceptDrops(true);
默認是不開啟的,所以必須呼叫一次 setAcceptDrops 方法,
當某個組件(可以是視窗,按鈕,標簽,文本框等)支持放置行為后,把資料拖到該組件上會引發 dragEnter、dragMove 等事件;釋放滑鼠時會發生 drop 事件,表示整個拖放操作結束,這個上文已講過,下面重點看幾個事件引數,注意了,這幾個廝實際上是有繼承關系的,
class QDropEvent : public QEvent class QDragMoveEvent : public QDropEvent class QDragEnterEvent : public QDragMoveEvent // 下面這個是特例 class QDragLeaveEvent : public QEvent
QDragLeaveEvent 是直接派生自 QEvent 的,因為它是在 dragLeave 事件發生時使用,資料被拖出當前物件,一般不需要額外攜帶什么引數,所以這個事件類比較特殊,
QDropEvent 類用于 drop 事件,因為這時候你得讀取資料了,所以它會夾帶私貨,這些私貨分兩類:
1、跟滑鼠有關的,比如 buttons 回傳滑鼠按下了哪個鍵;modifiers 回傳值表示用戶是否在拖動的同時按下 Ctrl、Alt、Shift 等按鍵,position 回傳滑鼠指標的當前坐標,這些引數咱們通常用不上的,
2、和共享的資料相關的,這個是最需要的,mimeData 回傳 QMimeData 物件的指標,然后咱們就能讀資料了,source 回傳發起拖放操作的物件,一般我們的程式不太關注資料源,
不管讀不讀取資料,作為資料接收者,我們是文明的,有禮貌的,拖放操作完成時咱們應該回應一下發送者—— QDrag::exec 方法(如果資料是從其他程式拖過來的,那么,拖放的發起者就不一定是呼叫 exec 方法,畢竟人家不見得是用 Qt 寫的,說不定是用 WPF 做的),
扯遠了,回到主題,向資料發送者反饋,還是涉及到了 DropActions 的事,DropEvent 提供了這些成員,可以訪問 action,
1、possibleActions 方法,對應的是 exec 方法的 supportedActions 引數;
2、proposedAction 方法,對應 exec 方法的 defaultAction 引數,
還記得前文說過的 exec 方法的兩個引數嗎?嗯,是滴,possibleActions 就是 supportedActions 引數提供的有效范圍,你只能在這些值中選一個,proposedAction 是建議的值,也就是 defaultAction 引數提供的默認值,
所以,如果我們的程式比較在意使用什么 action 的話,你得好好分析一下這兩個方法回傳的值了,不過,多數時候,我們只關心 mimeData 回傳的內容,因為那是要提取的資料,
如果你成功接收了資料,那么要呼叫 acceptProposedAction 方法,表示資料和 defaultAction 你都接受了,
如果你不想用 defaultAction 引數推薦的默認 action,那么,你可以呼叫 QDropEvent::setDropAction 方法自己設定一個 action,但你設定的 action 必須在 possibleActions 中允許的,如果你呼叫了 setDropAction 方法,就等于修改了默認 action,所以這時候你只能呼叫 accept 方法來接受,不能再呼叫 acceptProposedAction 方法了,不然,acceptProposedAction 方法會還原默認 action 的值,
如果你發現資料不是你想要的,或者資料發送者給的 DropAction 你不接受,那你就呼叫 ignore 方法忽略,或者你什么都不做也可以(默認會 ignore 掉事件),
QDragEnterEvent 和 QDragMoveEvent 都是 QDropEvent 的子類,所以成員都是差不多的,就不用老周再廢話了,
了解這幾個類的關系,你就知道怎么處理接收拖動的程序了,下面我們來個例子,把圖片檔案拖到咱們的程式,然后會顯示該圖片,就是拖動打開檔案了,
從 QLabel 類派生出一個類,咱們就用它來接收并顯示圖片,Qt 沒有專門顯示圖片的組件,一般用 QLabel 來顯示圖片,當然,QPushButton 等按鈕組件也可以顯示圖片,不過通常用作顯示小圖示,有大伙伴會說,QGraphicsView 什么什么的不用嗎?那個就太大動作了,簡直是殺小強用牛刀,沒有必須,我就想顯示個圖片而已,
#pragma once #include <QLabel> #include <QDragEnterEvent> #include <QResizeEvent> #include <QDropEvent> class MyLabel : public QLabel { Q_OBJECT public: MyLabel(QWidget* parent=nullptr); protected: void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; void resizeEvent(QResizeEvent* event) override; private: // 用來快取影像 QPixmap m_image; };
事件不算多,就重寫三個事件,另外,還宣告了一個私有成員 m_image 用來存影像資源,你可能會問了,QLabel 不是可以設定和獲取 QPixmap 物件嗎,為什么要特地用一個私有成員來保存?因為 QLabel 上顯示的影像,咱們一般會縮小一下再顯示,經過縮小后的 QPixmap 物件,再重新放大就變得很模糊了,所以,QLabel::Pixmap 不保存原圖,
在建構式中,讓這個標簽組件支持放置,
MyLabel::MyLabel(QWidget *parent) : QLabel(parent) { this->setAcceptDrops(true); this->setStyleSheet("background-color: gray"); }
setAcceptDrops 開啟 drop 支持,還有一個是 setStyleSheet,這里老周是用 QSS 來設定標簽的背景顏色為難看的灰色,這是 Qt 搞的裝X玩意兒,用起來有點像 HTML 中的 CSS,
又有伙伴問了,QLabel 不是有個帶 text 引數的建構式嗎?對,不過這里不需要,咱們這個自定義組件不顯示文本,
然后,實作 resizeEvent,當大小改變時,咱們也調整一下標簽上的影像大小(其實是重新加載縮放過的影像),
void MyLabel::resizeEvent(QResizeEvent *event) { if(!m_image.isNull()) { // 獲取當前新調整的大小 QSize labelsize = event->size(); // 縮放影像 auto pixmap = m_image.scaled(labelsize, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 重新設定影像 this->setPixmap(pixmap); } }
最后就是跟drop 有關的兩個事件了,
void MyLabel::dragEnterEvent(QDragEnterEvent *event) { // 檢查一下是不是所需要的資料 const QMimeData *data = https://www.cnblogs.com/tcjiaan/archive/2023/05/28/event->mimeData(); if (data->hasUrls()) { event->acceptProposedAction(); } } void MyLabel::dropEvent(QDropEvent *event) { // 再次驗證一下資料 const QMimeData *data = https://www.cnblogs.com/tcjiaan/archive/2023/05/28/event->mimeData(); if (data->hasUrls()) { // 讀資料 QList<QUrl> paths = data->urls(); if (paths.size() > 0) { QUrl p = paths.at(0); QString locfile = p.toLocalFile(); m_image.load(locfile); } // 縮放一下 auto pix = m_image.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); this->setPixmap(pix); event->acceptProposedAction(); } }
dragEnter 的時候,只是看看有沒有想要的資料,不讀,讀取是在 drop 事件中完成,但是為了防止概率 0.001% 的靈異事件發生,在 drop 事件處理時還要再檢驗一下資料是不是有效,
檔案拖進來,一般是 URL 型別,獲取到的物件是 QUrl 型別,它的格式是 file:///xxxxx,這個路徑在 load 方法中加載不了,于是得用 toLocalFile 方法,將 URL 轉換為本地檔案路徑,這樣就能在 QPixmap::load 方法中加載影像了,
下面,定義一個視窗,實體化兩個 MyLabel 組件,放在網格布局中第一行的兩個單元格內,
/* 頭檔案 */ #pragma once #include <QWidget> #include "custlabel.h" #include <QGridLayout> class MyWindow : public QWidget { Q_OBJECT public: MyWindow(QWidget* parent=nullptr); private: MyLabel *lbImg1, *lbImg2; QLabel *lb1, *lb2; QGridLayout *layout; };
兩個 QLabel 組件用來顯示普通文本,咱們自己弄的 MyLabel 組件用來顯示圖片,QGridLayout 是布局用的,以網格形式布局(行、列),
MyWindow::MyWindow(QWidget *parent) :QWidget(parent) { setWindowTitle("放置影像"); resize(450, 400); // 初始化 lb1 = new QLabel("美琪", this); lb2 = new QLabel("美雪", this); lb1->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); lb2->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); lbImg1 = new MyLabel(this); lbImg2 = new MyLabel(this); layout = new QGridLayout(this); // 布局 layout->addWidget(lbImg1, 0, 0); layout->addWidget(lbImg2, 0, 1); layout->addWidget(lb1, 1, 0); layout->addWidget(lb2, 1, 1); }
setSizePolicy 那兩行是為了讓 QLabel 組件的高度固定,因為 QGridLayout 這個王八不能設定固定的行高和列寬,所以只能出此下策了,
寫上 main 函式,
int main(int argc, char* argv[]) { QApplication app(argc, argv); MyWindow win; win.show(); return QApplication::exec(); }
運行程式后,就可以把圖片檔案拖到兩個 MyLabel 上了,注意左邊是美琪,右邊是美雪,下面的標簽是她倆的名字,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553639.html
標籤:其他
下一篇:返回列表