昨天的 D04: Qt 排版系統裡,我們已經可以在視窗上添加按鈕了,今天我們就要來說明怎麼讓按鈕發生作用。
大多數的 GUI 圖形介面程式都是「事件驅動」的。什麼叫「事件驅動」呢?
比方說使用者「按下列印按鈕」後開始列印文件,使用者「按了 Ctrl+C」把內容保存到剪貼簿,使用者「點了滑鼠右鍵」彈出右鍵選單。「按鈕被按下」「敲擊鍵盤」「滑鼠點擊」這些就是事件。程式收到事件後,做出行動回應事件。如果沒有事件發生,那程式就靜靜的等待。
所以撰寫 GUI 程式時,很重要的一部分就是連結「事件」和「回應的行動」。
今天要介紹的 signal/slot 信號槽,就是 Qt 處理這部份的機制。
今天的範例一樣是由一個 Qt 專案檔 (.pro
) + 一個 CPP 組成。不知道怎麼設定跟編譯 Qt 程式的人,請參考 D03: 第一支 Qt 程式 。
這是 Qt 專案檔,檔名 myqt-d05.pro
:
QT += widgets # 引入 Qt widgets 模組
TEMPLATE = app # 這是一支應用程式 (app)
TARGET = QtLayout # 指定執行檔名字
SOURCES = myqt-d05.cpp # 這支專案有一個 CPP
這是 CPP 檔,檔名 myqt-d05.cpp
// 這是 myqt-d05.cpp
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget w;
QHBoxLayout* layout = new QHBoxLayout;
w.setLayout(layout);
QPushButton* button = new QPushButton("關於 Qt");
layout->addWidget(button);
QObject::connect(button, &QPushButton::clicked, &app, &QApplication::aboutQt); //建立連結
w.show();
return app.exec();
}
如果你很快的掃過程式碼,你會知道這隻CPP跟昨天的CPP大同小異,唯一差別是今天視窗上只有一個按鈕。而且呼叫了一個沒看過的 QObject::connect()
函數。
Qt 信號槽機制的奧秘就在 QObject::connect()
函數裡。
QObject::connect(監聽的物件, 監聽的事件, 事件接收者, 事件的處理函數);
前面我們提過撰寫 GUI 程式時連結事件和回應行動是關鍵。QObject::connect()
的任務就是建立這個連結。QObject::connect()
的前兩個參數指定你要監聽的對象和事件,後兩個參數則是指定事件的接收者和怎麼處理。
以我們的程式碼為例子:
QObject::connect(button, &QPushButton::clicked, &app, &QApplication::aboutQt);
這行程式碼的意思就是,我們想要監聽 button
按鈕物件的 clicked
(點擊)事件。一旦點擊事件發生了,就呼叫 app
物件的 QApplication::aboutQt()
函數。
QApplication::aboutQt() 是 QApplication 的成員函數,功用是彈出當前 Qt 的版本和版權等等資訊。
接著請你編譯並執行程式碼,點擊按鈕看看,是不是能看到彈出視窗。
在 Qt 裡面,我們通常把這些事件稱作「signal(信號)」。QPushButton 物件最常用的信號當然就是 clicked (點擊),你可到官方文件查找還有哪些其他信號。
而事件的回應行動,就叫做「slot(槽)」,在目前的 Qt 版本中 slot 通常是一個物件的成員函數。一旦建立連接,該物件就成了專門負責處理這事件的負責人。
請注意,連結 signal/slot 的時候,傳入的是成員函數的指標,函數名前面要加上一個 &
符號。(例如 &QPushButton::clicked
和&QApplication::aboutQt
)
QObject::connect()
除了連接成員函數,其實也可以連接 C++11 的 lambda 函數。以下我們就示範用 lambda 寫一個亂數產生器。
// 亂數產生器
#include <QApplication>
#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <cstdlib>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget w;
QHBoxLayout* layout = new QHBoxLayout;
w.setLayout(layout);
QPushButton* button = new QPushButton("產生亂數");
layout->addWidget(button);
QObject::connect(button, &QPushButton::clicked, [=]
{
button->setText(QString::number(rand() % 100));
});
w.show();
return app.exec();
}
在這個例子裡,按鈕點擊事件的回應函數變成了 lambda 函數。而在這個 lambda 函數裡面,我們產生一個範圍在 0-99 之間的隨機亂數,並且透過 QPushButton::setText()
把數字顯示在按鈕上。
QString::number(i)
這個函數的作用是變數轉型,把 int 轉型成 Qt 的字串型態 (QString)。
明天開始,我們會逐漸開始介紹各種常用的 Qt 視窗元件,並試著寫一些簡單的小應用出來,敬請期待。