C++ Qt開發:ToolBar與MenuBar選單元件

2023-12-16 12:00:39

Qt 是一個跨平臺C++圖形介面開發庫,利用Qt可以快速開發跨平臺表單應用程式,在Qt中我們可以通過拖拽的方式將不同元件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹ToolBar工具列元件以及與之類似的MenuBar選單欄元件的常用方法及靈活運用。

1.1 QToolBar 工具列

QToolBar 是 Qt 中用於建立工具列的元件,它為使用者提供了一個方便的方式來組織和存取應用程式中的各種工具和操作。工具列通常用於快速存取常用的功能,提高使用者體驗。

1.1.1 主要特點

  1. 工具按鈕: QToolBar 主要由工具按鈕組成,每個工具按鈕代表一個功能或操作。工具按鈕可以包含文字、圖示,也可以與相應的槽函數關聯,實現使用者點選按鈕時觸發相應的操作。
  2. 分組和彈出選單: 工具列支援將工具按鈕分組,使介面更加清晰。還可以為工具按鈕新增彈出選單,以提供額外的選項。
  3. 可調整性: 使用者可以在工具列上自由拖動工具按鈕,重新排列它們的位置。這增加了使用者客製化介面的靈活性。
  4. 自定義小部件: 除了工具按鈕,工具列還支援新增自定義的小部件,例如搜尋方塊、進度條等,以滿足特定需求。
  5. 樣式和佈局: 可以通過設定樣式和佈局來客製化工具列的外觀,包括工具按鈕的樣式、大小和排列方式。

以下是 QToolBar 類的一些常用方法的說明和概述,以表格形式列出:

方法 描述
QToolBar(QWidget *parent = nullptr) 建構函式,建立一個 QToolBar 物件。
addAction(QAction *action) 向工具列中新增一個動作。
addWidget(QWidget *widget) 向工具列中新增一個小部件。
addSeparator() 向工具列中新增一個分隔符。
clear() 清除工具列上的所有動作和小部件。
setOrientation(Qt::Orientation orientation) 設定工具列的方向,可以是水平 (Qt::Horizontal) 或垂直 (Qt::Vertical)。
setMovable(bool movable) 設定工具列是否可以被使用者移動。
setIconSize(const QSize &size) 設定工具列中動作的圖示大小。
setToolButtonStyle(Qt::ToolButtonStyle style) 設定工具按鈕的樣式,可以是文字和圖示一起顯示、只顯示圖示、只顯示文字等。
toggleViewAction() 返回一個切換工具列可見性的動作。
addWidget(QWidget *widget) 在工具列中新增一個自定義小部件。
clear() 清除工具列上的所有動作和小部件。
setAllowedAreas(Qt::ToolBarAreas areas) 設定工具列允許停靠的區域,可以是上、下、左、右、所有區域的組合。
setFloatable(bool floatable) 設定工具列是否可以浮動。
setWindowTitle(const QString &title) 設定工具列的標題。
addWidget(QWidget *widget) 在工具列中新增一個自定義小部件。
widgetForAction(QAction *action) const 返回與給定動作相關聯的小部件。

這些方法提供了對 QToolBar 進行動作、小部件和外觀等方面的控制,使其適應不同的應用場景。你可以根據具體需求使用這些方法,客製化工具列的外觀和行為。

1.2 QMenuBar 選單欄

QMenuBar 是 Qt 中用於建立選單欄的元件,它提供了一種方便的方式來組織和管理應用程式的選單。選單欄通常用於將應用程式的功能劃分為不同的選單,使使用者可以輕鬆存取各種操作。

1.2.1 主要特點

  1. 選單項: QMenuBar 主要由選單項組成,每個選單項代表一個功能或操作。選單項可以包含子選單,形成層級關係,用於更好地組織功能。
  2. 快捷鍵: 每個選單項可以關聯一個快捷鍵,使用者可以通過鍵盤快捷鍵來觸發相應的操作。
  3. 分組和分割線: 選單欄支援在選單項之間新增分組和分割線,用於更好地區分不同的功能模組。
  4. 動作關聯: 選單項通常與具體的動作(QAction)關聯,點選選單項時觸發相應的動作。
  5. 上下文選單: QMenuBar 也可以用作上下文選單(右鍵選單),在特定區域點選右鍵時顯示相應的選單項。

以下是 QMenuBar 類的一些常用方法的說明和概述,以表格形式列出:

方法 描述
QMenuBar(QWidget *parent = nullptr) 建構函式,建立一個 QMenuBar 物件。
addMenu(const QString &title) 新增一個具有給定標題的選單,並返回一個指向新選單的指標。
addMenu(QMenu *menu) 新增給定的選單。
addSeparator() 在選單欄上新增一個分隔符。
addActions(QList<QAction*> actions) 新增給定的動作列表到選單欄。
clear() 清除選單欄上的所有選單和分隔符。
setNativeMenuBar(bool nativeMenuBar) 設定是否使用本地選單欄,如果為 true,則選單欄將使用本地系統的選單欄實現。
setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopLeftCorner) 在指定的角落放置一個小部件。
addMenu(QMenu *menu) 新增給定的選單。
setActiveAction(QAction *action) 設定活動動作,該動作將在選單欄上顯示為活動狀態。
addMenu(const QString &title) 新增一個具有給定標題的選單,並返回一個指向新選單的指標。
setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopLeftCorner) 在指定的角落放置一個小部件。
clear() 清除選單欄上的所有選單和分隔符。
setNativeMenuBar(bool nativeMenuBar) 設定是否使用本地選單欄,如果為 true,則選單欄將使用本地系統的選單欄實現。
setActiveAction(QAction *action) 設定活動動作,該動作將在選單欄上顯示為活動狀態。
setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopLeftCorner) 在指定的角落放置一個小部件。

這些方法提供了對 QMenuBar 進行選單管理、外觀設定以及與其他小部件的互動等方面的控制。你可以根據具體需求使用這些方法,客製化選單欄的外觀和行為。

1.3 使用選單元件

通常情況下ToolBarMenuBar兩者會配合使用,在5.14.2版本中,表單建立後會預設包含一個MenuBar元件,對於老版本的Qt則會自帶一個ToolBar元件,ToolBar工具列元件與MenuBar選單欄元件,在所有表單應用程式中都廣泛被使用,使用這兩種元件可以很好的規範選單功能分類,使用者可根據選單欄來選擇不同的功能,實現靈活的使用者互動。

頂部工具列ToolBar元件的定義有多種方式,我們可以直接通過程式碼生成,也可以使用圖形介面UI新增,當需要使用UI實現時,只需要在MainWindow中選擇新增工具來新增,預設會在視窗頂部增加,如果想要在四面增加可以使用Add Tool Bar to Other Area選項實現;

1.3.1 應用選單元件

通常情況下我們不會使用UI的方式來使用工具列,通過程式碼將很容易的實現建立,如下程式碼中我們通過屬性setAllowedAreas()可以實現將ToolBar元件放置到上下左右四個不同的方位上,通過程式碼的方式實現一個頂部選單欄,該選單欄中可以通過SetIcon(QIcon("://image/.ico"));指定圖示,也可以使用setShortcut(Qt::CTRL | Qt::Key_C);為其指定特殊的快捷鍵。

#include <iostream>
#include <QMenuBar>
#include <QToolBar>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // ----------------------------------------------------------
    // 建立選單欄
    // ----------------------------------------------------------
    QMenuBar *bar = menuBar();
    this->setMenuBar(bar);                      // 將選單欄放入主視窗
    QMenu * fileMenu = bar->addMenu("檔案");     // 建立父節點

    // 新增子選單
    QAction *newAction = fileMenu->addAction("新建檔案");      // 設定名字
    newAction->setIcon(QIcon("://image/file.ico"));          // 設定可用圖示
    newAction->setShortcut(Qt::CTRL | Qt::Key_A);            // 設定快捷鍵ctrl+a

    fileMenu->addSeparator();                                // 新增分割線
    QAction *openAction = fileMenu->addAction("開啟檔案");     // 設定名字
    openAction->setIcon(QIcon("://image/lock.ico"));         // 設定可用圖示
    openAction->setShortcut(Qt::CTRL | Qt::Key_C);           // 設定快捷鍵ctrl+c

    // ----------------------------------------------------------
    //建立工具列 (可遮蔽掉 遮蔽掉後底部將失去控制元件欄位)
    // ----------------------------------------------------------
    QToolBar *toolBar = new QToolBar(this);    // 建立工具列
    addToolBar(Qt::BottomToolBarArea,toolBar); // 設定預設停靠範圍 [預設停靠底部]

    toolBar->setAllowedAreas(Qt::TopToolBarArea |Qt::BottomToolBarArea);   // 允許上下拖動
    toolBar->setAllowedAreas(Qt::LeftToolBarArea |Qt::RightToolBarArea);   // 允許左右拖動

    toolBar->setFloatable(false);       // 設定是否浮動
    toolBar->setMovable(false);         // 設定工具列不允許移動

    // 工具列新增選單項
    toolBar->addAction(newAction);
    toolBar->addSeparator();
    toolBar->addAction(openAction);

    // ----------------------------------------------------------
    // 繫結槽函數
    // ----------------------------------------------------------
    connect(newAction,&QAction::triggered,this,[=](){
        QMessageBox::information(nullptr,"提示","觸發新建檔案",QMessageBox::Ok);
    });

    connect(openAction,&QAction::triggered,this,[=](){
        QMessageBox::information(nullptr,"提示","觸發開啟檔案",QMessageBox::Ok);
    });
}

由於通過connect繫結到了每一個Action上,所以當用戶點選不同的選單時將會觸發不同的匿名槽函數,程式碼中實現了彈窗提示,此處也可以替換成任意程式碼,執行效果圖如下所示;

1.3.2 二級選單聯動

如上所示的生成案例實現了單一選單的生成,其實QMenuBar元件同樣可實現二級選單的聯動,二級頂部選單與一級選單完全一致,只是在一級選單的基礎上進行了延申,當然只要遵循選單的巢狀規則理論上我們可以無限延伸下去,當然為了開發程式碼邏輯清晰,筆者並不建議選單層級超過三級。

#include <iostream>
#include <QMenuBar>
#include <QToolBar>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // ----------------------------------------------------------
    // 多層選單導航欄
    // ----------------------------------------------------------
    QMenuBar *MainMenu = new QMenuBar(this);
    this->setMenuBar(MainMenu);

    // 1.定義父級選單
    QMenu *EditMenu = MainMenu->addMenu("檔案編輯");

    // 1.1 定義 EditMemu 下面的子選單
    QAction *text = new QAction(EditMenu);
    text->setText("編輯檔案");                     // 設定文字內容
    text->setShortcut(Qt::CTRL | Qt::Key_A);      // 設定快捷鍵ctrl+a
    text->setIcon(QIcon(":/image/about.ico"));    // 增加圖示
    EditMenu->addAction(text);

    // 在設定模式與編輯檔案之間增加虛線
    EditMenu->addSeparator();

    QAction *option = new QAction(EditMenu);
    option->setText("設定模式");
    option->setIcon(QIcon(":/image/file.ico"));
    EditMenu->addAction(option);

    // 1.1.2 定義Option設定模式下的子選單
    QMenu *childMenu = new QMenu();
    QAction *set_file = new QAction(childMenu);
    set_file->setText("設定檔案內容");
    set_file->setIcon(QIcon(":/image/lock.ico"));
    set_file->setShortcut(Qt::CTRL | Qt::Key_B);

    childMenu->addAction(set_file);

    QAction *read_file = new QAction(childMenu);
    read_file->setText("讀取檔案內容");
    read_file->setIcon(QIcon(":/image/about.ico"));
    childMenu->addAction(read_file);
    read_file->setShortcut(Qt::CTRL | Qt::Key_C);

    // ----------------------------------------------------------
    // 註冊選單到表單中
    // ----------------------------------------------------------
    // 首先將childMenu註冊到option中
    option->setMenu(childMenu);
    // 然後再將childMenu加入到EditMenu中
    EditMenu->addMenu(childMenu);

    // ----------------------------------------------------------
    // 繫結訊號和槽
    // ----------------------------------------------------------
    connect(text,&QAction::triggered,this,[=](){
       QMessageBox::information(nullptr,"提示","觸發編輯檔案",QMessageBox::Ok);
    });

    connect(set_file,&QAction::triggered,this,[=](){
       QMessageBox::information(nullptr,"提示","觸發設定檔案",QMessageBox::Ok);
    });

    connect(read_file,&QAction::triggered,this,[=](){
      QMessageBox::information(nullptr,"提示","觸發讀取檔案",QMessageBox::Ok);
    });
}

程式碼執行後讀者可看到如下圖所示的效果,在設定模式中增加了兩個子選單,每個子選單分別繫結到了一個槽函數上,而其父選單僅僅只是展示功能此處可以不增加任何實質性的功能。

1.3.3 增加右鍵選單

Qt中的選單還可以實現任意位置的彈出,該功能的實現依賴於QMainWindow主表單中的customContextMenuRequested()事件,該事件是Qt中的一個訊號,通常與右鍵選單(上下文選單)相關。該訊號在使用者請求上下文選單時觸發,例如通過右鍵單擊某個小部件(如視窗、按鈕、表格等)時。

我們可以將右擊customContextMenuRequested()事件繫結到主視窗中,實現在表單任意位置右擊都可以彈出選單欄,讀者可以直接在主介面中點選右鍵轉到槽,如下圖;

當讀者點選主表單中的右鍵時則會觸發on_MainWindow_customContextMenuRequested事件,該事件的內部則實現了建立選單的功能,並通過pMenu->exec(QCursor::pos())的方式顯示在滑鼠點選位置處,其程式碼如下所示;

#include <iostream>
#include <QMenuBar>
#include <QToolBar>
#include <QCursor>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 設定小部件(QWidget)的上下文選單策略
    this->setContextMenuPolicy(Qt::CustomContextMenu);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 觸發右鍵建立選單
void MainWindow::on_MainWindow_customContextMenuRequested(const QPoint &pos)
{

    // 建立選單物件
    QMenu *pMenu = new QMenu(this);

    QAction *pNewTask = new QAction(tr("新建選單"), this);
    QAction *pEditTask = new QAction(tr("編輯選單"), this);
    QAction *pDeleteTask = new QAction(tr("刪除選單"), this);

    // 設定屬性值編號: 1=>新建 2=>設定 3=>刪除
    pNewTask->setData(1);
    pEditTask->setData(2);
    pDeleteTask ->setData(3);

    // 把QAction物件新增到選單上
    pMenu->addAction(pNewTask);
    pMenu->addAction(pEditTask);
    pMenu->addAction(pDeleteTask);

    // 增加圖示
    pNewTask->setIcon(QIcon(":/image/about.ico"));
    pEditTask->setIcon(QIcon(":/image/file.ico"));
    pDeleteTask->setIcon(QIcon(":/image/lock.ico"));

    // 連線滑鼠右鍵點選訊號
    connect(pNewTask, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
    connect(pEditTask, SIGNAL(triggered()), this, SLOT(onTaskBoxContextMenuEvent()));
    connect(pDeleteTask, SIGNAL(triggered()), SLOT(onTaskBoxContextMenuEvent()));

    // 在滑鼠右鍵點選的地方顯示選單
    pMenu->exec(QCursor::pos());

    //釋放記憶體
    QList<QAction*> list = pMenu->actions();
    foreach (QAction* pAction, list) delete pAction;
    delete pMenu;
}

接著就需要繫結到特定的槽函數上,用於接收使用者點選的選單選項,並根據選項做出相應的判斷,這裡我們定義一個onTaskBoxContextMenuEvent函數,並在MainWindow.h標頭檔案進行宣告,其實現部分如下所示;

// 處理傳送過來的訊號
void MainWindow::onTaskBoxContextMenuEvent()
{
    // this->sender()就是訊號傳送者 QAction
    QAction *pEven = qobject_cast<QAction *>(this->sender());

    // 獲取編號: 1=>新建 2=>設定 3=>刪除
    int iType = pEven->data().toInt();

    switch (iType)
    {
    case 1:
        QMessageBox::information(nullptr,"提示","觸發新建任務",QMessageBox::Ok);
        break;
    case 2:
        QMessageBox::information(nullptr,"提示","觸發設定任務",QMessageBox::Ok);
        break;
    case 3:
        QMessageBox::information(nullptr,"提示","觸發刪除任務",QMessageBox::Ok);
        break;
    default:
        break;
    }
}

至此當我們再次使用右鍵點選主頁面時,則會彈出一個個性化選單欄,如下圖所示;

1.3.4 增加頂部通欄

通常情況下我們需要頂部按鈕的排布,這有助於增加頁面的圖形化顯示效果,為了讓頁面只保留一個ToolBar元件,通常情況下會將預設的menuBar元件進行隱藏,隱藏的方式是通過呼叫setVisible(false)來實現,對外只展示出一個ToolBar控制元件欄位,而在ToolBar控制元件欄中只保留ICO圖示與底部文字描述,這樣能顯得更加清爽一些。

#include <iostream>
#include <QMenuBar>
#include <QToolBar>
#include <QCursor>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 隱藏選單欄上的右擊選單
    this->setContextMenuPolicy(Qt::NoContextMenu);

    // ----------------------------------------------------------
    // 建立menuBar元件
    // ----------------------------------------------------------
    // 建立基礎頂部選單並讓其隱藏
    QMenuBar *bar = menuBar();
    this->setMenuBar(bar);
    QMenu * fileMenu = bar->addMenu("Ptr");

    // 隱藏選單
    bar->setVisible(false);

    // 新增子選單
    QAction *NewAction = fileMenu->addAction("新建檔案");
    QAction *OpenAction = fileMenu->addAction("開啟檔案");
    QAction *ReadAction = fileMenu->addAction("讀入檔案");

    // 分別設定圖示
    NewAction->setIcon(QIcon(":/image/about.ico"));
    OpenAction->setIcon(QIcon(":/image/file.ico"));
    ReadAction->setIcon(QIcon(":/image/lock.ico"));

    // 建立工具列
    QToolBar *toolBar = new QToolBar(this);
    addToolBar(Qt::TopToolBarArea,toolBar);

    // 將選單項依次新增到工具列
    toolBar->addAction(NewAction);
    toolBar->addAction(OpenAction);
    toolBar->addAction(ReadAction);

    // 設定禁止移動屬性,工具列預設貼在上方
    toolBar->setFloatable(false);
    toolBar->setMovable(false);
    toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

    // ----------------------------------------------------------
    // 繫結槽函數
    // ----------------------------------------------------------
    connect(NewAction,&QAction::triggered,this,[=](){
        QMessageBox::information(nullptr,"提示","觸發新建檔案按鈕",QMessageBox::Ok);
    });

    connect(OpenAction,&QAction::triggered,this,[=](){
        QMessageBox::information(nullptr,"提示","觸發開啟檔案按鈕",QMessageBox::Ok);
    });

    connect(ReadAction,&QAction::triggered,this,[=](){
        QMessageBox::information(nullptr,"提示","觸發讀取檔案按鈕",QMessageBox::Ok);
    });
}

執行後讀者可看到如下圖所示的案例,我們只保留了最基本的按鈕欄,這樣看起來更加的清爽。