创建菜单和工具栏
绝大多数现代图形用户界面应用程序都会提供一些菜单、上下文菜单和工具栏。菜单可以让用户浏览应用程序并且可以学会如何处理一些新的事情,上下文菜单和工具栏则提供了对那些经常使用的功能进行快速访问的方法。
Qt通过“动作”的概念简化了有关菜单和工具栏的编程。一个动作(action)就是一个可以添加到任意数量的菜单和工具栏上的项。在Qt中,创建菜单和工具栏包括以下这些步骤:
● 创建并且设置动作。
● 创建菜单并且把动作添加到菜单上。
● 创建工具栏并且把动作添加到工具栏上。
动作是在createActions()函数中创建的:
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()));
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()));
}
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()));
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
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::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"(最近打开的文件)的部分。
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提供的。
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提供的,所以就没有必要再去亲自实现它。
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)));
Show Grid是一个复选(checkable)动作。复选动作在菜单中显示时会带一个复选标记,并且在工具栏中它可以实现成切换(toggle)按钮。当启用这个动作时,Spreadsheet组件就会显示一个网格。我们用Spreadsheet组件的默认值来初始化这个动作,这样它们就可以从一开始就同步起来。然后,把Show Grid动作的toggled(bool)信号和Spreadsheet组件的setShowGrid(bool)槽连接起来,这个槽继承自QTableWidget。一旦把这个动作添加到菜单或者工具栏中,用户就可以对网格的显示与否进行切换了。
Show Grid动作和AutoRecalculate动作是相互独立的两个复选动作。通过QActionGroup类的支持,Qt也可以支持相互排斥的动作。
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);
selectSubMenu = editMenu->addMenu(tr("&Select"));
selectSubMenu->addAction(selectRowAction);
selectSubMenu->addAction(selectColumnAction);
selectSubMenu->addAction(selectAllAction);
editMenu->addSeparator();
editMenu->addAction(findAction);
editMenu->addAction(goToCellAction);
现在来创建Edit菜单,就像在File菜单中所做的那样使用QMenu::addMenu()函数添加各个动作,并且在希望出现子菜单的地方使用OMenu::addMenu()函数添加子菜单。一个子菜单与它所属的菜单一样,也是一个QMenu。
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工具栏。就像菜单一样,工具栏也可以有多个间隔器。