Qt 常用控件 - 4https://blog.csdn.net/Small_entreprene/article/details/149830464
前文補充?
QRadioButton(單選按鈕)
QRadioButton 是單選按鈕,允許在多個選項中選擇一個。作為 QAbstractButton 和 QWidget 的子類,前面介紹的屬性和用法對 QRadioButton 同樣適用。
QAbstractButton 中與 QRadioButton 關系較大的屬性
屬性 | 說明 |
---|---|
checkable | 是否能選中 |
checked | 是否已經被選中。checkable 是 checked 的前提條件。 |
autoExclusive | 是否排他。選中一個按鈕后是否會取消其他按鈕的選中。QRadioButton 默認排他。 |
代碼示例:選擇性別
-
在界面上創建一個 label 和 3 個單選按鈕,文本分別為“男”“女”“其他”,objectName 分別為
radioButton_male
、radioButton_female
、radioButton_other
。 -
修改
widget.cpp
,編輯三個 QRadioButton 的 slot 函數:
void Widget::on_radioButton_male_clicked()
{// 將界面上的內容進行更新ui->label->setText("您選擇的性別為:男");
}void Widget::on_radioButton_female_clicked()
{// 將界面上的內容進行更新ui->label->setText("您選擇的性別為:女");
}void Widget::on_radioButton_other_clicked()
{// 將界面上的內容進行更新ui->label->setText("您選擇的性別為:其他");
}
運行程序,選擇不同單選按鈕,label 提示文字隨之變化。開始沒有任何的選中
程序啟動時默認不選任何選項,可修改為默認選中“男”:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//添加一個默認的選型ui->radioButton_male->setChecked(true);//此時單選按鈕就被選中了ui->label->setText("您選擇的性別為:男");
}
可禁用“其他”選項:在構造函數之后接續代碼:
// 僅禁止選中但仍能觸發點擊 --- 文本還是會變化
ui->radioButton_other->setCheckable(false);// 徹底禁用(無法選中也無法響應輸入)
ui->radioButton_other->setEnabled(false);
// ui->radioButton_other->setDisabled(true);//這個也一樣的作用
代碼示例:click, press, release, toggled 的區別
-
clicked
:一次“點擊”(按下+釋放 = pressed + released)我們無參的使用多了,我們下面使用帶參的 -
pressed
:鼠標“按下” -
released
:鼠標“釋放” -
toggled
:按鈕狀態切換(checked 屬性改變)
-
在界面上創建四個單選按鈕,objectName 為
radioButton
至radioButton_4
。 -
分別創建槽函數:
void Widget::on_radioButton_clicked(bool checked)
{// 這個 checked 參數就是表示當前 radiobutton 的選中狀態qDebug() << "clicked: " << checked;
}void Widget::on_radioButton_2_pressed()
{qDebug() << "pressed";
}void Widget::on_radioButton_3_released()
{qDebug() << "releaseed";
}void Widget::on_radioButton_4_toggled(bool checked)
{// checked 狀態發生改變,就會觸發這個信號qDebug() << "toggled: " << checked;
}
運行程序,觀察不同信號觸發時機。
總結:toggled 最適合 QRadioButton!!!
代碼示例:單選框分組
基于 RedioButton,實現一個簡單的模擬點餐的功能!
-
在界面上創建 6 個單選框,objectName 為
radioButton
至radioButton_6
,默認是全部排他。 -
一旦頁面上是需要存在“多組單選按鈕”的時候,希望組合組之間不要有影響。
-
Qt 引入
QButtonGroup
?這樣的工具類進行分組,每組內部排他,組間互不影響:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 使用 QButtonGroup 對單選按鈕進行分組QButtonGroup* group1 = new QButtonGroup(this);QButtonGroup* group2 = new QButtonGroup(this);QButtonGroup* group3 = new QButtonGroup(this);// 把對應單選按鈕,放在不同的組里group1->addButton(ui->radioButton);group1->addButton(ui->radioButton_2);group1->addButton(ui->radioButton_3);group2->addButton(ui->radioButton_4);group2->addButton(ui->radioButton_5);group2->addButton(ui->radioButton_6);group3->addButton(ui->radioButton_7);group3->addButton(ui->radioButton_8);
}
再次運行程序,即可實現正確的分組排他。
QCheckBox(復選按鈕)
QCheckBox 表示復選按鈕,允許選中多個。與 QCheckBox 最相關的屬性同樣繼承自 QAbstractButton:checkable
與 checked
。
tristate
為 QCheckBox 獨有,可實現“三態復選框”,較冷門,暫時不討論。
代碼示例:獲取復選按鈕的取值
-
在界面上創建三個復選按鈕和一個普通按鈕,objectName 分別為
checkBox_eat
、checkBox_sleep
、checkBox_play
及pushButton
。 -
為
pushButton
添加 slot 函數:
void Widget::on_pushButton_clicked()
{QString result = "今天你的安排是: ";if(ui->checkBox_study->isChecked()){result += ui->checkBox_study->text() + " ";}if(ui->checkBox_game->isChecked()){result += ui->checkBox_game->text() + " ";}if(ui->checkBox_work->isChecked()){result += ui->checkBox_work->text() + " ";}ui->label->setText(result);
}
運行程序,點擊確認按鈕,控制臺輸出選中的內容。
Tool Button(QToolButton)
QToolButton 的大部分功能與 QPushButton 一致,但主要應用于工具欄、菜單等場景,此處暫不介紹。
介紹完了按鈕類控件,接下來,我們開啟對顯示類控件的介紹!
顯示類控件
QLabel(標簽)
QLabel 可以用來顯示文本和圖片。核心屬性如下:
屬性 | 說明 |
---|---|
text | QLabel 中的文本 |
textFormat | 文本的格式:
|
pixmap | QLabel 內部包含的圖片 --- 這是單純為了放圖片的更好的選擇! |
scaledContents | 設為 true 表示內容自動拉伸填充 QLabel;設為 false 則不會自動拉伸 |
alignment | 對齊方式。可以設置水平和垂直方向如何對齊(左/右居中,上/下居中) |
wordWrap | 設為 true 內部的文本會自動換行;設為 false 則內部文本不會自動換行(文本長度超出了Label本身,是否要自動換行,QLabel 是不會給我們提供滾動條的,有的控件,QTEXTEdit --- 多行編輯框是有滾動條的) |
indent | 設置文本縮進。水平和垂直方向都生效(兩個方向) |
margin | 內部文本和邊框之間的邊距。不同于 indent,上下左右四個方向同時有效 |
openExternalLinks | 是否允許打開一個外部的鏈接(當 QLabel 文本內容包含 url 時涉及到) |
buddy | 給 QLabel 關聯一個“伙伴”,點擊 QLabel 時就能激活對應的伙伴。例如伙伴是一個 QCheckBox,則該 QCheckBox 就會被選中 |
代碼示例:顯示不同格式的文本 - textFormat
-
在界面上創建三個 QLabel,尺寸放大一些,objectName 分別為
label
、label_2
、label_3
。 -
修改
widget.cpp
,設置三個 label 的屬性:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 把第一個label 設置成顯示純文本ui->label->setTextFormat(Qt::PlainText);ui->label->setText("這是一段純文本");// 把第二個label 設置成顯示富文本ui->label_2->setTextFormat(Qt::RichText);ui->label_2->setText("<b>這是一段富文本<\b>");//<b>標簽:進行文本加粗// 把第三個label 設置成顯示markdownui->label_3->setTextFormat(Qt::MarkdownText);ui->label_3->setText("# 這是 markdown 文本");//# :讓文本成為一級標題
}
運行程序,觀察效果:
代碼示例:顯示圖片
雖然 QPushButton 也可以通過設置圖標的方式設置圖片,但并非好選擇。更多時候希望通過 QLabel 作為更單純的顯示圖片方式。
-
在界面上創建一個 QLabel,objectName 為
label
。 -
創建
resource.qrc
文件,并把圖片導入到 qrc 中。 -
修改
widget.cpp
,給 QLabel 設置圖片:
先將 Qlable 設置為和窗口一樣大,并且被這個 QLabel 左上角設置到窗口的左上角
讓整個 QLabel 鋪滿整個窗口,我們可以通過窗口的 geometry 來獲取:
// 先將 Qlable 設置為和窗口一樣大,并且被這個 QLabel 左上角設置到窗口的左上角
// 讓整個 QLabel 鋪滿整個窗口
QRect windowRect = this->geometry();
ui->label->setGeometry(0, 0, windowRect.width(), windowRect.height());
// ui->label->setGeometry(0, 0, 800, 600);//硬編碼QPixmap pixmap(":/dog.png");
ui->label->setPixmap(pixmap);
執行程序,觀察效果。我的圖片本身尺寸是 200×200,并沒有把 QLabel 填充滿。
修改代碼,設置 scaledContents
屬性:來實現自動拉伸的填充 Label
// 設置內容伸縮
ui->label->setScaledContents(true);
再次運行,圖片已被拉伸填滿窗口。
此時拖動窗口大小,圖片不會同步變化。上面的代碼是在構造函數中,進行的如上的尺寸設置,這個設置相當于是一次性的,一旦程序運行起來之后,QLabel 的尺寸就固定下來了,窗口發生改變,此時,QLabel 是不會變化的!
我們需要介紹一個知識點 --- 事件
對于之前的信號和槽,用戶的操作會對應一些信號(點擊按鈕,觸發指定信號),有些用戶操作,對應的不是信號,而是事件!
所以在 Qt 中,表示用戶的操作,有兩類概念:一個是信號,另一個是事件!對于事件,后續會詳細介紹,當前我們就來簡單起個頭:
就像我們鼠標拖動窗口大小的時候,就會讓我們的 Qt 程序觸發一個 resize 事件(resizeEvent),像這種事件是連續觸發的:將窗口尺寸從 A 拖到 B 這個過程中,會連續觸發一系列的 resizeEvent!之前的信號是點一下,只觸發一下信號
此時接可以借助 resizeEvent 來完成上述的功能!
為解決此問題,可在 Widget 中重寫父類(QWidget)的?resizeEvent
?虛函數:
在實際編程中,指定回調函數其實有很多種寫法:
- 設置函數指針
- 設置仿函數函數對象
- 設置 lambda 表達式
- 重寫父類虛函數 --- (框架中拿著父類的指針調用這個函數,如果你創建的子類重寫了這個函數,此時在多態機制下,實際執行的就是子類的函數了)
// 重寫 resizeEvent. 這個函數會在窗口大小發生改變時被自動調用。
void Widget::resizeEvent(QResizeEvent *event) {// 可以直接通過 this->width() 和 this->height() 設置 label 新的尺寸, 也可以通過 event 參數拿到新的尺寸// ui->label->setGeometry(0, 0, this->width(), this->height());ui->label->setGeometry(0, 0, event->size().width(), event->size().height());qDebug() << event->size();
}
執行程序,改變窗口大小,圖片隨之變化,同時在控制臺能看到尺寸變化過程。
? 注意:
此處的 resizeEvent
函數我們沒有手動調用,但能在窗口大小變化時自動調用。這個過程依賴 C++ 中的多態實現。Qt 框架內部管理著 QWidget 對象表示窗口,在窗口大小改變時,Qt 會自動調用 resizeEvent
函數。由于實際表示窗口的并非 QWidget,而是 QWidget 的子類(即我們寫的 Widget),此時雖然通過父類調用函數,實際執行的卻是子類的函數(也就是重寫后的 resizeEvent
)。此處屬于多態機制的經典用法,通過上述過程可把自定義代碼插入到框架內部執行,相當于“注冊回調函數”。
代碼示例:文本對齊、自動換行、縮進、邊距
創建四個 label,objectName 分別為 label
到 label_4
,并在 QFrame 中設置 frameShape
為 Box(設置邊框后看起來更清晰)。
QFrame 是 QLabel 的父類,frameShape
屬性用來設置邊框性質:
- QFrame::Box:矩形邊框
- QFrame::Panel:帶有可點擊區域的面板邊框
- QFrame::WinPanel:Windows 風格的邊框
- QFrame::HLine:水平線邊框
- QFrame::VLine:垂直線邊框
- QFrame::StyledPanel:帶有可點擊區域的面板邊框,樣式取決于窗口主題
編寫 widget.cpp
,給四個 label 設置屬性:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 設置文字居中對齊ui->label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);ui->label->setText("垂直and水平居中的文本");// 設置自動換行ui->label_2->setAlignment(Qt::AlignTop | Qt::AlignLeft);ui->label_2->setWordWrap(true);ui->label_2->setText("這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本");// 設置首行縮進ui->label_3->setAlignment(Qt::AlignTop | Qt::AlignLeft);ui->label_3->setIndent(20);ui->label_3->setText("這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本");// 設置邊距ui->label_4->setAlignment(Qt::AlignTop | Qt::AlignLeft);ui->label_4->setMargin(20);ui->label_4->setText("這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本這是一個很長的文本");
}
運行程序,效果如下:
-
第一個 label 垂直水平居中
-
第二個 label 設置了 wordWrap,能夠自動換行
-
第三個 label 設置了 indent,左側和上方與邊框有間距,右側則沒有
-
第四個 label 設置了 margin,四個方向均有間距(圖上僅體現三個方向,下方看不出來)
代碼示例:設置伙伴
QLabel 的伙伴:就是可以將 QLabel 和單選框或者復選框這樣的控件綁定成伙伴關系 --- 此時就可以通過 QLabel 來觸發單選框/復選框的選擇操作!
創建兩個 label 和兩個 radioButton,objectName 分別為 label
、label_2
、radioButton
、radioButton_2
。
此處把 label 中的文本設置為 "快捷鍵 &A" 這種形式,其中 &
后面的字符就是快捷鍵,可通過 Alt + A 觸發。注意這里的快捷鍵和 QPushButton 不同,需要搭配 Alt 和單個字母才能觸發。
編寫 widget.cpp
,設置 buddy 屬性(也可在 Qt Designer 直接設置):
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 設置 label 的伙伴 widgetui->label->setBuddy(ui->radioButton);ui->label_2->setBuddy(ui->radioButton_2);
}
運行程序,按下快捷鍵 Alt + A 或 Alt + B,即可選中對應選項。
Qt 中,QLbel 中寫的文本,是可以指定“快捷鍵”的,此處的快捷鍵的規則功能上要比 QPushButton 弱很多,使用方式是:
在文本中使用"&"跟上一個字符來表示快捷鍵,比如:
"&A" --- 通過鍵盤上的 Alt + a 來觸發這個快捷鍵;
"&B" --- 通過鍵盤上的 Alt + b?來觸發這個快捷鍵。
綁定了伙伴關系之后,通過快捷鍵就可以選中對應的單選框/復選框了!
LCDNumber(數字顯示)
QLCDNumber 是專門用來顯示數字的控件,類似老式計算器的效果。
核心屬性
屬性 | 說明 |
---|---|
intValue | QLCDNumber 顯示的數字值(int)。 |
value | QLCDNumber 顯示的數字值(double)與 intValue 聯動; 例如給 value 設為 1.5,intValue 的值就是?2; 另外,設置 value 和 intValue 的方法名為 display,而非 setValue 或 setIntValue。 |
digitCount | 顯示幾位數字。 |
mode | 數字顯示形式:
僅十進制可顯示小數點后內容 |
segmentStyle | 顯示風格:
|
smallDecimalPoint | 設置較小的小數點。 |
代碼示例:倒計時
界面放一個 QLCDNumber,初始值設為 10,objectName 為 lcdNumber。
// 設置初始值
ui->lcdNumber->display("10");
修改 widget.h 代碼,增加成員:創建一個 QTimer 成員,和一個 updateTime 函數
QTimer* timer;
void updateTime();
widget.cpp 構造函數初始化 QTimer
- QTimer 表?定時器,這個類創建出來的對象,就會產生一個 timeout 這樣的信號,通過 start ?法啟動定時器之后,并且參數中設定觸發 timeout 信號的周期,就會每隔?定周期,觸發?次 QTimer::timeout 信號?--- C++標準庫沒有提供定時器的實現,但是boost庫中提供了對應的功能!Qt 封裝了對應的定時器(結合了信號槽機制)
- 我們就可以結合 connect,把這個 timeout 信號綁定到需要的槽函數中,就可以執行邏輯,修改 LCDNumber 中的數字了 --- 使? connect 把 QTimer::timeout 信號和 Widget::updateTime 連接起來,意味著每次觸發 QTimer::timeout 都會執? Widget::updateTime
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 設置初始值ui->lcdNumber->display("10");// 創建一個 QTimer 實例timer = new QTimer(this);// 把 QTimer 的 timeout 信號和咱們自己的槽函數進行連接connect(timer, &QTimer::timeout, this, &Widget::updateTime);// 啟動定時器,參數是觸發 timeout 的周期,單位是 mstimer->start(1000);
}
widget.cpp 實現 updateTime
- 通過 intValue 獲取到 QLCDNumber 內部的數值。
- 如果 value 的值歸 0 了,就停止 QTimer --- timer->stop()。接下來 QTimer 也就不會觸發 timeout 信號了。
void Widget::updateTime()
{// qDebug()<<"過了一秒";// 先拿到 LCDNumber 中的數字int value = ui->lcdNumber->intValue();if(value <= 0){// 數字減到0了,要停止定時器timer->stop();//timer 設置成成員變量!!!return;}ui->lcdNumber->display(value-1);
}
運行程序,每秒鐘數字減 1。
針對上述代碼的兩個問題:
1.上述代碼如果直接在 Widget 構造函數中,通過一個循環 + sleep 的方式是否可以呢?
代碼形如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);int value = ui->lcdNumber->intValue();while(true){// 先休眠 1秒 --- 獲取當前線程的sleepstd::this_thread::sleep_for(std::chrono::seconds(1));if(value <=0 ){break;}ui->lcdNumber->display(--value);}
}
我們執行程序,一段時間之間內是沒有顯示出來執行的程序的,而是等了一會,但是出來的界面已經是0了!
顯然,這個代碼是不行的。循環會使 Widget 的構造函數無法執行完畢,此時界面是不能正確構造和顯示的。而是需要將 Widget 構造完成,才可以執行后續的界面顯示!都沒顯示出來?有什么用???
2.上述代碼如果是在 Widget 構造函數中,另起一個線程,在新線程中完成循環 + sleep 是否可以呢?那我們另外搞一個線程不就不會阻塞當前線程了嘛,不就可以讓構造函數順利執行完,順利進行展示窗口了嘛???
代碼形如:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);std::thread t([this]() {int value = this->ui->lcdNumber->intValue();while (true) {std::this_thread::sleep_for(std::chrono::seconds(1));if (value <= 0) {break;}this->ui->lcdNumber->display(value - 1);}});
}
這個代碼同樣是不行的。Qt 中規定,任何對于 GUI 上內容的操作,必須在主線程中完成。像 Widget 構造函數,以及 connect 連接的 slot 函數,都是在主線程中調用的。而我們自己創建的線程則不是。當我們自己的線程中嘗試對界面元素進行修改時,Qt 程序往往會直接崩潰。
? 這樣的約定主要是因為 GUI 中的狀態往往是牽一發而動全身的,修改一個地方,就需要同步地對其他內容進行調整。
比如調整了某個元素的尺寸,就可能影響到內部的文字位置,或者其他元素的位置。這里一連串的修改,都是需要按照一定的順序來完成的。
由于多線程執行的順序無法保障,因此 Qt 從根本上禁止了其他線程修改 GUI 狀態,避免后續的一系列問題。
綜上所述,使用定時器,是實現上述功能的最合理方案。后續如果我們也有類似的需要“周期性修改界面狀態”的需求,也需要優先考慮使用定時器。
QProgressBar(進度條)
使用 QProgressBar 表示一個進度條。
注意:不要把 ProgessBar 拼寫成 ProcessBar!
核心屬性
屬性 | 說明 |
---|---|
minimum | 進度條最小值 |
maximum | 進度條最大值 |
value | 進度條當前值 |
alignment | 文本在進度條中的對齊方式
|
textVisible | 進度條的數字是否可見 |
orientation | 進度條的方向是水平還是垂直 |
invertAppearance | 是否是朝反方向增長進度 |
textDirection | 文本的朝向 |
format | 展示的數字格式
|
代碼示例:設置進度條按時間增長
在界面上創建進度條,objectName 為 progressBar
其中最小值設為 0,最大值設為 100當前值設為 0
修改 widget.h,創建 QTimer 和 updateProgressBar 函數
QTimer* timer;
void updateProgressBar();
修改 widget.cpp,初始化 QTimer
此處設置 100ms 觸發一次 timeout 信號,也就是一秒鐘觸發 10 次!
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &Widget::updateProgressBar);timer->start(100); // 100ms觸發一次timeout信號,即一秒鐘觸發10次
}
修改 widget.cpp,實現 updateProgressBar
void Widget::updateProgressBar() {int value = ui->progressBar->value();if (value >= 100) {timer->stop();return;}ui->progressBar->setValue(value + 1);
}
運行程序,可以看到進度條中的進度在快速增長
實際開發中,進度條的取值通常根據當前任務的實際進度設置:
例如讀取大文件時,可以根據文件總大小和已讀取大小設置進度條比例
Qt禁止在其他線程修改界面,因此進度條更新通常需要搭配定時器完成
-
通過定時器周期觸發信號
-
主線程調用對應的slot函數
-
在slot函數中計算當前任務進度并更新界面
代碼示例:創建紅色進度條
上述的進度條使用表示紫色的,但是考慮到有些人可能不喜歡紫色,因此改成紅色的進度條。
不要忘了,QProgressBar 同樣也是 QWidget 的子類,因此可以使用 styleSheet 通過樣式來修改進度條的顏色。
在界面上創建一個進度條
在Qt Designer右側屬性編輯器中,找到QWidget的styleSheet屬性,編輯如下內容:
QProgressBar::chunk {background-color: #FF0000;
}
同時把QProgressBar的alignment屬性設置為垂直水平居中
注意:如果不設置alignment,進度條中的數字可能會跑到左上角,這可能是Qt的bug,暫時只能手動調整對齊方式
執行程序,可以看到紅色進度條效果:
通過上述方式,也可以修改文字的顏色、字體大小等樣式