欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【学习笔记】C++ GUI Qt4 第三章 3.2 创建菜单和工具栏和3.3设置状态栏

程序员文章站 2022-05-22 19:55:52
...

3.2 创建菜单和工具栏

绝大多数现代图形用户界面应用程序都会提供一些菜单、上下文菜单和工具栏。菜单可以让用户浏览应用程序并且可以学会如何处理一些新的事情,上下文菜单和工具栏则提供了对那些经常使用的功能进行快速访问的方法。

【学习笔记】C++ GUI Qt4 第三章 3.2 创建菜单和工具栏和3.3设置状态栏
Qt通过“动作”的概念简化了有关菜单和工具栏的编程。一个动作(action)就是一个可以添加到任意数量的菜单和工具栏上的项。在Qt中,创建菜单和工具栏包括以下这些步骤:
● 创建并且设置动作。
● 创建菜单并且把动作添加到菜单上。
● 创建工具栏并且把动作添加到工具栏上。

void MainWindow::createActions()
{
    newAction = new QAction(tr("&New"), this);
    newAction->setIcon(QIcon(":/images/new.png"));
    newAction->setShortcut(QKeySequence::New);
    newAction->setStatusTip(tr("Create a new spreadsheet file"));
    connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
    /* 动作New有一个加速键(&New即Alt+N)、一个父对象(主窗口)、一个图标、一个快捷键和一个状态提示。
     * 大多数窗口系统都有用于特定动作的标准化的键盘快捷键。
     * 例如,在Windows、KDE和GNOME中,这个New动作就有一个快捷键Ctrl+N,而在MacOSX中则是Command+N。
     * 通过使用适当的QKeySequence::StandardKey枚举值,就可以确保Qt能够为应用程序在其运行的平台上提供正确的快捷键。
     * 把这个动作的tiggered()信号连接到主窗口的私有槽newFile()——将会在下一节实现它。
     * 这个连接可以确保在用户选择File New菜单项、选择工具栏上的New按钮或者按下Ctrl+N时,都可以调用newFile()槽。
     * 由于菜单中的Open、Save和SaveAs动作与New动作非常相似,
     * 所以将会直接跳到File菜单中的“recently opened files"(最近打开的文件)的部分。
     */

    openAction = new QAction(tr("&Open..."), this);
    openAction->setIcon(QIcon(":/images/open.png"));
    openAction->setShortcut(QKeySequence::Open);
    openAction->setStatusTip(tr("Open an existing spreadsheet file"));
    connect(openAction, SIGNAL(triggered()), this, SLOT(open()));

    saveAction = new QAction(tr("&Save"), this);
    saveAction->setIcon(QIcon(":/images/save.png"));
    saveAction->setShortcut(QKeySequence::Save);
    saveAction->setStatusTip(tr("Save the spreadsheet to disk"));
    connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));

    saveAsAction = new QAction(tr("Save &As..."), this);
    saveAsAction->setStatusTip(tr("Save the spreadsheet under a new "
                                  "name"));
    connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()));

    for (int i = 0; i < MaxRecentFiles; ++i) {
        recentFileActions[i] = new QAction(this);
        recentFileActions[i]->setVisible(false);
        connect(recentFileActions[i], SIGNAL(triggered()),
                this, SLOT(openRecentFile()));
    }
    /* 我们为recentFileActions数组添加动作。每个动作都是隐式的,并且会被连接到openRecentFile()槽。
     * 稍后,将会看到如何让这些最新文件中的动作变得可见并且可用。
     */

    exitAction = new QAction(tr("E&xit"), this);
    exitAction->setShortcut(tr("Ctrl+Q"));
    exitAction->setStatusTip(tr("Exit the application"));
    connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
    /* 这个Exit动作与目前为止所看到的那些动作稍微有些不同。由于没有用于终止应用程序的标准化键序列,所以需要在这里明确指定键序列。
     * 另外一个不同之处是:我们连接的是窗口的close()槽,而它是由Qt提供的。
     */

    cutAction = new QAction(tr("Cu&t"), this);
    cutAction->setIcon(QIcon(":/images/cut.png"));
    cutAction->setShortcut(QKeySequence::Cut);
    cutAction->setStatusTip(tr("Cut the current selection's contents "
                               "to the clipboard"));
    connect(cutAction, SIGNAL(triggered()), spreadsheet, SLOT(cut()));

    copyAction = new QAction(tr("&Copy"), this);
    copyAction->setIcon(QIcon(":/images/copy.png"));
    copyAction->setShortcut(QKeySequence::Copy);
    copyAction->setStatusTip(tr("Copy the current selection's contents "
                                "to the clipboard"));
    connect(copyAction, SIGNAL(triggered()), spreadsheet, SLOT(copy()));

    pasteAction = new QAction(tr("&Paste"), this);
    pasteAction->setIcon(QIcon(":/images/paste.png"));
    pasteAction->setShortcut(QKeySequence::Paste);
    pasteAction->setStatusTip(tr("Paste the clipboard's contents into "
                                 "the current selection"));
    connect(pasteAction, SIGNAL(triggered()),
            spreadsheet, SLOT(paste()));

    deleteAction = new QAction(tr("&Delete"), this);
    deleteAction->setShortcut(QKeySequence::Delete);
    deleteAction->setStatusTip(tr("Delete the current selection's "
                                  "contents"));
    connect(deleteAction, SIGNAL(triggered()),
            spreadsheet, SLOT(del()));

    selectRowAction = new QAction(tr("&Row"), this);
    selectRowAction->setStatusTip(tr("Select all the cells in the "
                                     "current row"));
    connect(selectRowAction, SIGNAL(triggered()),
            spreadsheet, SLOT(selectCurrentRow()));

    selectColumnAction = new QAction(tr("&Column"), this);
    selectColumnAction->setStatusTip(tr("Select all the cells in the "
                                        "current column"));
    connect(selectColumnAction, SIGNAL(triggered()),
            spreadsheet, SLOT(selectCurrentColumn()));

    selectAllAction = new QAction(tr("&All"), this);
    selectAllAction->setShortcut(QKeySequence::SelectAll);
    selectAllAction->setStatusTip(tr("Select all the cells in the "
                                     "spreadsheet"));
    connect(selectAllAction, SIGNAL(triggered()),
            spreadsheet, SLOT(selectAll()));
    //由于槽seletAll()是由QTableWidget的父类之一的QAbstractItemView提供的,所以就没有必要再去亲自实现它。

    findAction = new QAction(tr("&Find..."), this);
    findAction->setIcon(QIcon(":/images/find.png"));
    findAction->setShortcut(QKeySequence::Find);
    findAction->setStatusTip(tr("Find a matching cell"));
    connect(findAction, SIGNAL(triggered()), this, SLOT(find()));

    goToCellAction = new QAction(tr("&Go to Cell..."), this);
    goToCellAction->setIcon(QIcon(":/images/gotocell.png"));
    goToCellAction->setShortcut(tr("Ctrl+G"));
    goToCellAction->setStatusTip(tr("Go to the specified cell"));
    connect(goToCellAction, SIGNAL(triggered()),
            this, SLOT(goToCell()));

    recalculateAction = new QAction(tr("&Recalculate"), this);
    recalculateAction->setShortcut(tr("F9"));
    recalculateAction->setStatusTip(tr("Recalculate all the "
                                       "spreadsheet's formulas"));
    connect(recalculateAction, SIGNAL(triggered()),
            spreadsheet, SLOT(recalculate()));

    sortAction = new QAction(tr("&Sort..."), this);
    sortAction->setStatusTip(tr("Sort the selected cells or all the "
                                "cells"));
    connect(sortAction, SIGNAL(triggered()), this, SLOT(sort()));

    showGridAction = new QAction(tr("&Show Grid"), this);
    showGridAction->setCheckable(true);
    showGridAction->setChecked(spreadsheet->showGrid());
    showGridAction->setStatusTip(tr("Show or hide the spreadsheet's "
                                    "grid"));
    connect(showGridAction, SIGNAL(toggled(bool)),
            spreadsheet, SLOT(setShowGrid(bool)));
#if QT_VERSION < 0x040102
    // workaround for a QTableWidget bug in Qt 4.1.1
    connect(showGridAction, SIGNAL(toggled(bool)),
            spreadsheet->viewport(), SLOT(update()));
#endif
    /* Show Grid是一个复选(checkable)动作。复选动作在菜单中显示时会带一个复选标记,并且在工具栏中它可以实现成切换(toggle)按钮。
     * 当启用这个动作时,Spreadsheet组件就会显示一个网格。
     * 我们用Spreadsheet组件的默认值来初始化这个动作,这样它们就可以从一开始就同步起来。
     * 然后,把Show Grid动作的toggled(bool)信号和Spreadsheet组件的setShowGrid(bool)槽连接起来,这个槽继承自QTableWidget。
     * 一旦把这个动作添加到菜单或者工具栏中,用户就可以对网格的显示与否进行切换了。
     * Show Grid动作和AutoRecalculate动作是相互独立的两个复选动作。通过QActionGroup类的支持,Qt也可以支持相互排斥的动作。
     */

    autoRecalcAction = new QAction(tr("&Auto-Recalculate"), this);
    autoRecalcAction->setCheckable(true);
    autoRecalcAction->setChecked(spreadsheet->autoRecalculate());
    autoRecalcAction->setStatusTip(tr("Switch auto-recalculation on or "
                                      "off"));
    connect(autoRecalcAction, SIGNAL(toggled(bool)),
            spreadsheet, SLOT(setAutoRecalculate(bool)));

    aboutAction = new QAction(tr("&About"), this);
    aboutAction->setStatusTip(tr("Show the application's About box"));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));

    aboutQtAction = new QAction(tr("About &Qt"), this);
    aboutQtAction->setStatusTip(tr("Show the Qt library's About box"));
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
    //对于About Qt动作,通过访问qApp全局变量,我们可以使用QApplication对象的aboutQt()槽。
}

void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(newAction);
    fileMenu->addAction(openAction);
    fileMenu->addAction(saveAction);
    fileMenu->addAction(saveAsAction);
    separatorAction = fileMenu->addSeparator();
    for (int i = 0; i < MaxRecentFiles; ++i)
        fileMenu->addAction(recentFileActions[i]);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAction);
    /* 在Qt中,菜单都是QMenu的实例。addMenu()函数可以用给定的文本创建一个QMenu窗口部件,并且会把它添加到菜单栏中。
     * QMainWindow::menuBar()函数返回一个指向.QMenuBar的指针。
     * 菜单栏会在第一次调用menuBar()函数的时候就创建出来。
     * 从创建File菜单开始,然后再把New、Open、Save和SaveAs动作添加进去。
     * 插入一个间隔器(separator),可以从视觉上把关系密切的这些项放在一起。
     * 使用一个for循环从recentFileActions数组中添加一些动作(最初是隐藏起来的),然后在最后添加一个exitAction动作。
     * 我们已经让一个指针指向了这些间隔器中的某一个。
     * 这样就可以允许隐藏(如果没有最近文件的话)或者显示那个间隔器,因为不希望出现在两个间隔器之间什么都没有的情况。
     */

    editMenu = menuBar()->addMenu(tr("&Edit"));
    editMenu->addAction(cutAction);
    editMenu->addAction(copyAction);
    editMenu->addAction(pasteAction);
    editMenu->addAction(deleteAction);
    //现在来创建Edit菜单,就像在File菜单中所做的那样使用QMenu::addMenu()函数添加各个动作,并且在希望出现子菜单的地方使用OMenu::addMenu()函数添加子菜单。
    //一个子菜单与它所属的菜单一样,也是一个QMenu。

    selectSubMenu = editMenu->addMenu(tr("&Select"));
    selectSubMenu->addAction(selectRowAction);
    selectSubMenu->addAction(selectColumnAction);
    selectSubMenu->addAction(selectAllAction);

    editMenu->addSeparator();
    editMenu->addAction(findAction);
    editMenu->addAction(goToCellAction);

    toolsMenu = menuBar()->addMenu(tr("&Tools"));
    toolsMenu->addAction(recalculateAction);
    toolsMenu->addAction(sortAction);

    optionsMenu = menuBar()->addMenu(tr("&Options"));
    optionsMenu->addAction(showGridAction);
    optionsMenu->addAction(autoRecalcAction);

    menuBar()->addSeparator();

    helpMenu = menuBar()->addMenu(tr("&Help"));
    helpMenu->addAction(aboutAction);
    helpMenu->addAction(aboutQtAction);
    //通过类似的方式创建Tools、Options和Help菜单。在Options菜单和Help菜单之间插人一个间隔器。
    //对于Motif和CDE风格,这个间隔器会把Help菜单放到菜单栏的最右端;对于其他的风格,则将会忽略这个间隔器。
}

void MainWindow::createContextMenu()
{
    spreadsheet->addAction(cutAction);
    spreadsheet->addAction(copyAction);
    spreadsheet->addAction(pasteAction);
    spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu);
    /* 任何Qt窗口部件都可以有一个与之相关联的QActions列表。
     * 要为该应用程序提供一个上下文菜单,可以将所需要的动作添加到Spreadsheet窗口部件中,并且将那个窗口部件的上下文菜单策略设置为一个显示这些动作的上下文菜单。
     * 当用户在一个窗口部件上单击鼠标右键,或者是在键盘上按下一个与平台相关的按键时,就可以**这些上下文菜单。
     */
}

void MainWindow::createToolBars()
{
    fileToolBar = addToolBar(tr("&File"));
    fileToolBar->addAction(newAction);
    fileToolBar->addAction(openAction);
    fileToolBar->addAction(saveAction);

    editToolBar = addToolBar(tr("&Edit"));
    editToolBar->addAction(cutAction);
    editToolBar->addAction(copyAction);
    editToolBar->addAction(pasteAction);
    editToolBar->addSeparator();
    editToolBar->addAction(findAction);
    editToolBar->addAction(goToCellAction);
    //创建工具栏与创建菜单的过程很相似,我们据此创建一个File工具栏和一个Edit工具栏。就像菜单一样,工具栏也可以有多个间隔器。
} 

3.3 设置状态栏

void MainWindow::createStatusBar()
{
    /* 随着菜单和工具栏的完成,已经为设置Spreadsheet应用程序的状态栏做好了准备。
     * 在程序的普通模式下,状态栏包括两个状态指示器:当前单元格的位置和当前单元格中的公式。
     * 状态栏也用于显示状态提示和其他一些临时消息。见下图给出了各种情况下的状态栏。
     */
    locationLabel = new QLabel(" W999 ");
    locationLabel->setAlignment(Qt::AlignHCenter);
    locationLabel->setMinimumSize(locationLabel->sizeHint());
    /* 状态栏指示器是一些简单的QLabel,可以在任何需要的时候改变它们的文本。
     * 当把这些QLabel添加到状态栏的时候,它们会自动被重定义父对象,以便让它们成为状态栏的子对象。
     * 当QStatusBar摆放这些指示器窗口部件时,它会尽量考虑由QWidget::sizeHint()提供的每一个窗口部件的理想大小,然后再对那些可伸展的任意窗口部件进行伸展以填满全部可用空间。
     * 一个窗口部件的理想大小取决于这个窗口部件的内容以及改变内容时的变化大小。
     * 为了避免对定位指示器连续不断地重定义大小,设置它的最小尺寸大小为它所能包含的最大字符数(“W999")和一些空格的总大小。
     * 还把它的对齐方式设置为Qt::AlignHCenter,以便可以在水平方向上居中对齐它的文本。
     */

    formulaLabel = new QLabel;
    formulaLabel->setIndent(3);
    // 已经在formulaLabel中添加了一个缩进格式,以便让那些在它里面显示的文本能够与它的左侧边有一个小的偏移量。

    statusBar()->addWidget(locationLabel);
    statusBar()->addWidget(formulaLabel, 1);
    /* QMainWindow::statusBar()函数返回一个指向状态栏的指针。[在第一次调用statusBar()函数的时候会创建状态栏。]
     * 两个标签都有不同的空间需求。单元格定位指示器只需要非常小的空间,并且在重新定义窗口大小时,任何多余的空间都会分配给位于右侧的单元格公式指示器。
     * 这是通过在公式标签的QStatusBar::addWidge()调用中指定一个伸展因子1而实现的。位置指示器的默认伸展因子为0,这也就意昧着它不喜欢被伸展。
     */

    connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)),
            this, SLOT(updateStatusBar()));
    connect(spreadsheet, SIGNAL(modified()),
            this, SLOT(spreadsheetModified()));
    //在函数结尾的附近,把Spreadsheet的两个信号和MainWindow的两个槽, updateStatusBar()和spreadsheetModifed(),连接了起来。

    updateStatusBar();
    //该槽也可以作为一个普通函数而在createStatusBar()的最后用于初始化这些指示器。
    //因为Spreadsheet不会在一开始的时候就发射currentCellChanged()消息,所以还必需这样做。
}

void MainWindow::updateStatusBar()
{
    //updatestatusBar()槽可以更新单元格定位指示器和单元格公式指示器。
    //只要用户把单元格光标移动到一个新的单元格,这个槽就会得到调用。
    locationLabel->setText(spreadsheet->currentLocation());
    formulaLabel->setText(spreadsheet->currentFormula());
}

void MainWindow::spreadsheetModified()
{
    //spreadsheeModified()槽把windowModified属性设置为true,用以更新标题栏。
    //这个函数也会更新位置和公式指示器,以便可以让它们反映事件的当前状态。
    setWindowModified(true);
    updateStatusBar();
}

【学习笔记】C++ GUI Qt4 第三章 3.2 创建菜单和工具栏和3.3设置状态栏

相关标签: Qt笔记 qt