实现File菜单

在这一节中,将实现那些能够让File菜单项正常工作并且能够对最近打开文件进行管理的槽函数和私有函数。

void MainWindow::newFile()
{if (okToContinue()) {spreadsheet->clear();setCurrentFile("");}
}

当用户点击File->New菜单项或者单击工具栏上的New按钮时,就会调用newFile()槽。

如果存在还没有被保存的信息,okToContinue()私有函数就会弹出对话框:“Do you wantto save your changes?”。如果用户选择Yes或者No(保存文档应该选择Yes),这个函数会返回true;如果用户选择Cancel,它就返回false。

Spreadsheet::clear()函数会清空电子制表软件中的全部单元格和公式。

setCurentFile()私有函数会更新窗口的标题,以说明正在编辑的是一个没有标题的文档,它还会设置curFile私有变量并且更新最近打开文件的列表。

bool MainWindow::okToContinue()
{//QMessageBox::warning(parent, title, message, buttons);if (isWindowModified()) {int r = QMessageBox::warning(this, tr("Spreadsheet"),tr("The document has been modified.\n""Do you want to save your changes?"),QMessageBox::Yes | QMessageBox::No| QMessageBox::Cancel);if (r == QMessageBox::Yes) {return save();} else if (r == QMessageBox::Cancel) {return false;}}return true;
}

在okToContinue()函数中,会检测windowModifed属性的状态。如果该属性的值是true,就显示一个如下图所示的消息框。这个消息框包含一个Yes按钮、一个No按钮和一个Cancel按钮。QMessageBox::Default 说明 Yes 为默认的按钮,QMessageBox::Escape 说明按键 Esc 和 Cancel 按钮等效。

QMessageBox提供了许多标准按钮,并且会自动尝试着让其中的一个成为默认的确认按钮(在用户按下Enter键时会得到激活) ,一个成为默认的退出按钮(在用户按下Esc时会得到激活)。选择一些特殊的按钮作为默认的确认按钮和退出按钮也是有可能的,用户还可以自定义按钮中将要显示的文本内容。

咋一看,QMessageBox::warning() 看起来有些复杂,实际是很简单明了的。
QMessageBox::warning(parent, title, message, button0, button1, …)

除了warning()之外,QMessageBox还提供了information()、question()和critical()函数,它们每一个都有自己特定的图标。

void MainWindow::open()
{if (okToContinue()) {QString fileName = QFileDialog::getOpenFileName(this,tr("Open Spreadsheet"), ".",tr("Spreadsheet files (*.sp)"));if (!fileName.isEmpty())loadFile(fileName);}
}

open()槽对File->Open做出响应。就像newFile()一样,它首先调用okToContiniue()函数来处理任何没有被保存的变化。然后它使用方便的QFileDialog::getOpenFileName()静态函数从用户那里获得一个新的文件名。这个函数会弹出一个文件对话框,让用户选择一个文件,并且返回这个文件名——或者,如果用户单击了Cancel按钮,则返回一个空字符串。

传递给QFileDialog::getOpenFileName()函数的第一个参数是它的父窗口部件。用于对话框和其他窗口部件的这种父子对象关系意义并不相同。对话框通常都拥有自主权,但是如果它有父对象,那么在默认情况下,它就会居中放到父对象上。一个子对话框也会共用它的父对象的任务栏。

第二个参数是这个对话框应当使用的标题。

第三个参数告诉它应当从哪一级目录开始,在这个例子中就是当前目录。

第四个参数指定了文件过滤器。文件过滤器(filter)由一个描述文本和一个通配符组成。如果除了要支持Spreadsheet本地文件格式以外,还需要支持采用逗号分隔的数据文件和Lotus 1-2-3 文件,就应当使用如下的文件过滤器:

tr("Spreadsheet files (* . sp)\n""Comma-separated values files (* . csv)\n""Lotus 1-2-3 files (* .wk1 * . wks)")

loadFile()私有函数是在open()中得到调用的,它用来载入文件。我们让它成为一个独立的函数,是因为会在载入最近打开的文件中使用同样的功能。

bool MainWindow::loadFile(const QString &fileName)
{    if (!spreadsheet->readFile(fileName)) {statusBar()->showMessage(tr("Loading canceled"), 2000);return false;}setCurrentFile(fileName);statusBar()->showMessage(tr("File loaded"), 2000);return true;
}

我们使用Spreadsheet::readFile()函数从磁盘中读取文件。

如果载入成功,会调用setCurentFile()函数来更新这个窗口的标题;否则,Spreadsheet::readFile()将会通过一个消息框把遇到的问题通知给用户。

在通常情况下,让底层组件来报告错误消息是一个不错的习惯,这是因为它们可以提供准确的错误细节信息。

在上述两种情况下,都会在状态栏中显示一个消息2秒(2000毫秒),这样可以通知用户应用程序正在做什么。

bool MainWindow::save()
{if (curFile.isEmpty()) {return saveAs();} else {return saveFile(curFile);}
}

save()槽对File->Save做出响应。如果因为这个文件是之前打开的文件或者它是一个已经保存过的文件,这样已经有了一个名字,那么save()函数就会用这个名字调用saveFile()函数;否则,它只是简单地调用saveAs()函数。

bool MainWindow::saveAs()
{QString fileName = QFileDialog::getSaveFileName(this,tr("Save Spreadsheet"), ".",tr("Spreadsheet files (*.sp)"));if (fileName.isEmpty())return false;return saveFile(fileName);
}

saveAs()槽对File->Save As做出响应。调用QFileDialog::getSaveFileName()函数来从用户那里得到一个文件名。

如果用户单击了Cancel,则返回false,这将会使这个结果向上传递给它的调用者[save()或者okToContinue()]。

如果给定的文件已经存在,geSaveFileName( )函数将会要求用户确认是否需要覆盖该文件。但通过给getSveFileNarne()函数传递一个QFileDialog::DontConfirmOverwite附加参数,则可以改变这一行为。

void MainWindow::closeEvent(QCloseEvent *event)
{if (okToContinue()) {writeSettings();event->accept();} else {event->ignore();}
}

当用户单击File->Exit或者单击窗口标题栏中的关闭按钮时,将会调用QWidget::close()槽。该槽会给这个窗口部件发射一个close事件。通过重新实现QWidget::closeEvent()函数就可以中途截取对这个主窗口的关闭操作,并且可以确定到底是不是真的要关闭这个窗口。

如果存在未保存的更改并且用户选择了Cancel, 就会“忽略”这个关闭事件并且让这个窗口不受该操作的影响。

一般情况下,我们会接受这个事件,这会让Qt隐藏该窗口。也可以调用私有函数writeSettings()来保存这个应用程序的当前设置。

当最后一个窗口关闭后,这个应用程序就结束了。如果需要,通过把QApplication的quitOnLastWindowClosed属性设置为false,可以禁用这种行为。
在这种情况下,该应用程序将会持续保持运行,直到调用QApplication:: quit()函数,程序才会结束。

void MainWindow::setCurrentFile(const QString &fileName)
{curFile = fileName;setWindowModified(false);QString shownName = tr("Untitled");if (!curFile.isEmpty()) {shownName = strippedName(curFile);recentFiles.removeAll(curFile);recentFiles.prepend(curFile);updateRecentFileActions();}setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("Spreadsheet")));
}

setCurrentFile()中,对保存正在编辑的文件名的curFile私有变量进行了设置。

每个QWidget都有一个windowModifed属性,如果该窗口的文档存在没有保存的变化,则应当把它设置为true,否则应当将其设置为falsle。

在Mac OS X下,未保存的文档是通过窗口标题栏上关闭按钮中的一个点来表示的;在其他平台下,则是通过文件名字后跟一个星号来表示的。

Qt会自动处理这一行为,只要始终让windowModified属性保持为当前最新状态,并且当需要显示星号的时候,把“[ * ]"标记放在窗口的标题栏上即可。

传递给setWindowTitle()函数的文本是:

tr("%1[*] - %2").arg(shownName).arg(tr("Spreadsheet"))

QString::arg()函数将会使用自己的参数替换最小数字的"%n"参数,并且会用它的参数返回结果“%n”字符和最终的结果字符串。

在本例中,arg()被用于两个"%n"参数中。第一个arg()调用会替换参数"%1",第二个arg()调用则会替换参数"%2"。

如果文件名是budget.sp并且没有载入翻译文件,那么结果字符串将是"budget.sp[ * ]- Spreadsheet"。
这本应更简单地写作如下代码:

setWindowTitle(shownName + tr("[*] - Spreadsheet"));

但使用arg()函数可以为翻译人员提供更多的灵活性。

如果存在文件名,就需要更新应用程序的最近打开文件列表recentFiles。在把这个文件名显示在标题栏中之前,需要使用strippedName()函数移除文件名中的路径字符,这样可以使文件名看起来更友好一些。可以调用removeAll()从列表中移除任何已经出现过的文件名,从而避免该文件名的重复。

然后,可以调用prepend()把这个文件名作为文件列表的第一项添加进去。在更新了文件列表之后,可以调用私有函数updateRecentFileActions()更新File菜单中的那些条目。

void MainWindow::updateRecentFileActions()
{QMutableStringListIterator i(recentFiles);while (i.hasNext()) {if (!QFile::exists(i.next()))i.remove();}for (int j = 0; j < MaxRecentFiles; ++j) {if (j < recentFiles.count()) {QString text = tr("&%1 %2").arg(j + 1).arg(strippedName(recentFiles[j]));recentFileActions[j]->setText(text);recentFileActions[j]->setData(recentFiles[j]);recentFileActions[j]->setVisible(true);} else {recentFileActions[j]->setVisible(false);}}separatorAction->setVisible(!recentFiles.isEmpty());
}

使用一个Java风格的迭代器,可以移除任何不再存在的文件。一些文件或许已经在前面的会话中使用过,但在此之前还没被删除掉。
recentFiles变量的类型是QStringList(QString型列表)。像QStringList一样的容器类,其中将会说明它们与C++标准模板库(StandardTemplate Library ,STL)之间的关系,也会说明Qt的Java风格迭代器类的用法。

然后,再遍历一次文件列表,这一次使用数组风格的索引形式。对于每一个文件,创建一个由一个与操作符、一位数字(j+ 1)、一个空格和该文件名(不带路径)组成的字符串。我们要为使用这种文本设置相应的动作。

例如,如果第一个文件是C:\My Documnents\tab04.sp,那么第一个动作的文本将会是“&1 tab04.sp”。

如下图给出了recentFileActions数组和菜单的最终结果之间的对应关系。

每一个动作都可以带一个与之相关的QVariant型data项。QVariant类型可以保存许多C++和Qt型变量。这里,将文件的全名保存在动作的data项中,以便随后可以方便地找到它。还要将这个动作设置为可见。

如果有比最新文件更多的文件动作;那么只需隐藏那些多余的动作即可。

最后,如果至少还存在一个最近打开的文件,那么就应该把间隔器设置为可见。

void MainWindow::openRecentFile()
{if (okToContinue()) {QAction *action = qobject_cast<QAction *>(sender());if (action)loadFile(action->data().toString());}
}

当用户选择了一个最近打开的文件,就会调用openRecentFile()槽。只要有任何未保存的变化,就会调用okToContinue()函数,并且假定用户没有取消,还可以使用QObject::sender()查出是哪个特有动作调用了这个槽。

qobject_cast <T>()函数可在Qt的moc(meta-object comipiler,元对象编译器)所生成的元信息基础上执行动态类型强制转换(dynamic cast)。 它返回一个指向所需QObject子类的指针,或者是在该对象不能被转换成所需的那种类型时返回0。

与标准C++的dynamic_ cast <T> ()不同,Qt的qobject_ cast <T> ()可正确地跨越动态库边界。

在例子中,使用qobject _cast<T > ()把一个QObject指针转换成QAction指针。如果这个转换是成功的(应当是这样的),就可以利用从动作的data项中所提取的文件全名来调用loadFile()函数。

顺便值得一提的是,由于知道这个发射器是一个QAction,如果使用static_cast<T > ()或者传统的C风格的数据类型强制转换代替原有的数据转换方式,这个程序应当仍然是可以运行的。

Qt4_实现File菜单相关推荐

  1. Qt 第三章 创建主窗口--实现File菜单

    今天有空接着Qt创建主窗口File菜单的实现,创建主窗口对于我来说确实有些难度,平时不努力学C/C++,现在从头开始很费劲.现在感慨,书到用时方恨少呀.接下来做一个简单的文本编辑器,给文本编辑器添加信 ...

  2. Matlab的File菜单功能图解 - 导入数据、保存工作空间、搜索路径、系统参数

    File菜单主要是打开/关闭文件.关闭窗口.导入数据.保存工作空间.设置搜索路径.设置系统参数.打印设置: 导入数据: Matlab的toolbox\stats目录下有一些自带的mat文件: 导入对话 ...

  3. 【学习笔记】C++ GUI Qt4 第三章 3.4 实现File菜单

    文章目录 3.4 实现File菜单 3.4 实现File菜单 在这一节中,将实现那些能够让File菜单项正常工作并且能够对最近打开文件进行管理的槽函数和私有函数. void MainWindow::n ...

  4. MATLAB菜单选项在哪儿,matlab菜单之file菜单

    file菜单 1.new 新建菜单 1)script 新建脚本文件(M文件) 2)function 新建函数(M文件) 3)class??新建类 4)figure 新建图形 5)mode 新建仿真模型 ...

  5. C#中使用DevExpress的Ribbton控件中有关Mac office的风格没有系统自带File菜单问题处理

    在DevExpress的Ribbton控件中macoffice模块,它不会直接显示File中的一个applicationButton,需要我们自己去设置才能实现Office中File菜单的效果. 具体 ...

  6. Qt4_实现其他菜单

    实现其他菜单 我们将要实现对Tools和Options菜单做出响应的槽. void Spreadsheet::recalculate() {for (int row = 0; row < Row ...

  7. Qt4_实现Edit菜单

    实现Edit菜单 现在我们开始实现菜单 Edit 相应的槽函数. void Spreadsheet::cut() {copy();del(); } cut()槽可以对Edit->Cut菜单做出响 ...

  8. Qt4_创建菜单和工具栏

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

  9. Eclipse 菜单

    Eclipse 菜单 Eclipse 查看的菜单栏通常包含以下几个菜单: File 菜单 Edit 菜单 Navigate 菜单 Search 菜单 Project 菜单 Run 菜单 Window ...

最新文章

  1. Struts2上传文件的大小设置
  2. 修复远程过程调用 (RPC) 时发生的各种问题KB908521
  3. 程序员面试金典 - 面试题 04.12. 求和路径(二叉树递归)
  4. extjs 获取id的值_Extjs combox获取显示值和ID值
  5. linux spi flash id,SPI Flash(W25Q16DV) 基本操作
  6. 数十名工程师作战 5 天,阿里达摩院连夜研发智能疫情机器人
  7. 客户端可以查询到数据,程序却查询不到数据
  8. 第一次面试总结--中国电子科学研究院
  9. python中read,readline,和readlines的区别 并逐行输出
  10. Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针
  11. 论文中的MS流程01
  12. 一文读懂《“十四五”软件和信息技术服务业发展规划》
  13. python题目-完数
  14. python为什么要安装pip_为什么您应该使用`python -m pip`
  15. SpringBoot整合Cache缓存技术(二十一)
  16. 自然语言处理——字符串基础操作及应用
  17. 【目标检测】YOLOv1代码实现之TensorFlow
  18. 十进制转换八进制代码c语言,利用栈将十进制转换为八进制(C语言)
  19. 云服务器选ssd还是hdd_服务器租用主机硬盘使用机械硬盘还是固态硬盘
  20. jflash烧录教程_Jlink flash 烧录HEX 程序

热门文章

  1. python能熔断吗_在大型项目上,Python 是个烂语言吗?
  2. 军队计算机使用管理规定,军队通用计算机系统使用安全要求.doc
  3. vue跳转静态HTML,Vue-router,在静态切换的世界中翱翔
  4. Java中的序列化问题
  5. (六)linux内核中的offsetof与container_of宏
  6. 利用jQuery点击DIV变颜色的小例子
  7. MYSQL IFNULL函数的使用
  8. 如何打印出lua里table的内容
  9. CAS总结之Ticket篇
  10. 面试指南:新人面试做好三个“第一”