QT学习教程(十一)

article/2025/7/30 16:16:41

​​​​​​实现文件菜单(Implementing the File Menu

我们实现与文件菜单有关的槽函数和相关的私有函数,以使文件菜单可以工作,同时管理最近打开文件列表。

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

newFile()槽函数在用户点击了File|New 菜单或者工具条上的 New 按钮后调用。

okToContinue()是一个私有函数,在这里如果需要存盘,程序会询问用户“Do you want to save your changes ?(是否存盘提示)”,如果用户选择了 Yes 或者No,函数返回 true,如果用户选择了 Cancel,返回false。Spreadsheet::clear()函数清楚所有 spreadsheet 控件的格子和公式。setCurrentFile()也是一个私有函数,它更新窗口标题,重新设置 curFile 变量,更新最近打开的文件列表,为用户开始编辑没有名字的新文档做好准备。

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

在 okToContinue()函数中,检查windowModified 属性的状态,如果为true,那么就会显示如下的消息框。这个消息框有Yes,No,和Cancel 按钮。QMessageBox::Default 说明Yes 为默认的按钮, QMessageBox::Escape 说明按键Esc 和Cancel 按钮等效

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

QMessageBox::warning(parent, title, message, button0, button1, ...);

QMessageBox 还提供其他函数如:information(),question()和 critical(),每一个函数都有他们自己特殊的显示图标:

open()相应菜单File|Open,它首先也是调用okToContinue()处理为保存的信息。然后使用 QFileDialog::getOpenFileName(),这个函数弹出一个对话框,让用户选择一个文件的名字,如果用户选择了一个文件,那么函数返回文件的名字,如果用户点击了 Cancel 按钮,则返回一个空字符串。

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

QFileDialog::getOpenFileName()的第一个参数是它的父控件。父子关系对于对话框来说和其他控件有些不同,一个对话框总是显示为一个窗口,如果它有父控件,那么它一般显示在父控件的中上位置 , A child dialog also shares its parent's taskbar entry.(怎么准确翻译那,好像是共享父控件的一些东西,taskbar)

第二个参数是对话框使用的标题。第三个参数是显示的初始目录,”.”表示的是程序的当前目录 。

第四个参数用来说明文件过滤器,即确定文件类型。文件过滤器由一个描述性的文本和通配符格式组成。如果我们在spreadsheet 程序中除了支持自定义的文件格式外,还支持了Comma- separated values 文件和Lotus 1-2-3 文件,那么过滤器就要这样:

tr("Spreadsheet files (*.sp)\n"

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

loadFile()是一个私有函数,用来加载文件。把这段代码独立出来是因为在打开最近文件时我们还要使用它:

Spreadsheet::readFile()来读取硬盘的文件。如果读取成功,调用setCurrentFile() 更新窗口标题。否则,该函数给出一个错误的提示框。通常,在低级别的控件中给出相信的错误信息是个好的习惯,这样可以清楚知道出错的原因。

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;}

不管成功与否,程序的状态条上都显示 2 秒(2000 毫秒)的状态信息,告诉用户程序的正在做的事情。

菜单File|Save 是 save()函数相应的。如果文件已经有了名字,或者是在磁盘上打开的,或者已经保存过,函数直接调用saveFile(),文件名字不变。否则调用saveAs()。

bool MainWindow::save()
{
if (curFile.isEmpty()) { return saveAs();
} else {
return saveFile(curFile);
}
}bool MainWindow::saveFile(const QString &fileName)
{
if (!spreadsheet->writeFile(fileName)) {
statusBar()->showMessage(tr("Saving canceled"), 2000); return false;
}setCurrentFile(fileName);
statusBar()->showMessage(tr("File saved"), 2000); return true;
}bool MainWindow::saveAs()
{
QString fileName = QFileDialog::getSaveFileName(this,
tr("Save Spreadsheet"), ".", tr("Spreadsheet files (*.sp)"));
if (fileName.isEmpty()) return false;
return saveFile(fileName);
}

菜单 File|SaveAs 相应函数为saveAs()。QFileDialog::getSaveFileName()提示用户输入文件名。如果用户点击了 Cancel 按钮,函数返回false,并将状态传递给调用者。如果文件已经存在, getSaveFileName()询问用户是否要覆盖。在getSaveFileName()的一个默认参数就是是否要覆盖,默认参数为QFileDialog::DontConfirmOverwrite。

当用户点击了File|Close 菜单或者窗口标题栏上的关闭按钮,QWidget::close()就会被调用。并发送 close()信号。重新实现 QWidget::closeEvent()能够拦截这个消息,以便确定是否真的要关闭窗口,防止误操作。

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

如果需要存盘或者用户选择了Cancel,那么就忽视这个事件,不关闭窗口。通常如果接受了这个事件,Qt 就会隐藏这个窗口。私有函数 writeSettings()保存应用程序当前的设置。当最后一个窗口也关闭后,应用程序中止。如果不需要这个功能,可以设置QApplication 的quitOnLastWindowClosed属性为false。这样,程序会一直运行,直到我们调用函数QApplication::quit()。

setCurrentFile()函数中,我们让 curFile 这个私有变量保存当前正在编辑的文件的名字。这个变量保存的是全路径名,我们用函数 strippedName()删除掉文件的路径,再在窗口的标题栏显示这个文件的名字。

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

每一个QWidget 都有一个windowModified 属性,如果有文件没有保存,那么就设置为true。否则设置为false。在Mac OS X 平台,如果有没有保存的文件,在窗口的标题栏的关闭按钮旁有一个小点。在其他平台,在文件名后面加一个“*”表示。只要我们保持更新 windowModified 属性,把“[*]”放在合适的地方,Qt 就能够自动处理。

传递给setWindowTitle()的文本是:

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

QString::arg()函数用自己的参数代替文本中的数字%n,并返回结果字符创。上面语句有两个.arg(),分别用来代替%1,%2 。如果文件名为“budget.sp”,且没有加载翻译文件,那么显示的字符串就是“budget.sp[*] - Spreadsheet”。也可以简写如下:

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

但是使用arg()更加灵活且容易实现国际化。

打开文件后,我们要更新rencentFiles(最近打开文件列表)。使用 removeAll()函数删除列表里的这个文件名,然后把它加在列表的前面。最后调用updateRecentFileActions()更新 File 菜单项。

首先我们用一个java 样式的迭代器删除不存在的文件,因为有些文件可能在列表中但是已经被删除掉了。recentFiles 的变量类型是 QStringList。第11 章详细介绍容器,迭代器及它们与c++标准模板库(STL)的关系。

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]->setVisibl e(true);
} else {
recentFileActions[j]->setVisible(false);
}
}
separatorAction->setVisible(!recentFiles.isEmpty());
}

再看文件列表,后一部分我们使用了数组索引方式。每一个文件用一个&号,数字序号,一个空格,和文件名组成,行为名字就是这个字符串。例如,如果第一个文件是C:\My Documents\tab04.sp,那么第一个行为显示的文本就是“&1 tab04.sp”。

每一个行为都有一个大 data 项,存储 QVariant 类型的数据。QVariant 能够存贮很多 c++数据类型和Qt 数据类型,将在第 11 章进行介绍。这里我们存储文件的全名,这样在将来我们打开文件时就可以很方便的找到它。

如果用户选择了一个最近打开的文件,openRecentFile()就被调用。okToContinue()用来检查是否需要存盘。这个函数特别的地方就是用QObject::sender()得到信号的发送者。

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

qobject_case<T>()实现基于moc 生成的元信息的动态类型转换。它返回一个 QObject 类的子类对象的指针,如果这个对象不能转换成类型T,返回一个空指针。和标准c++的dynamic_case<T>不同,qobject_cast<T>()只在动态库内使用。在这个例子中,我们把一个 QObject 指针变为一个 QAction 指针。如果转换成功,调用loadFile(),打开保存在 QAction 的data 属性中保存的文件。需要说明的是,因为我们知道发送者是一个 QAction 对象,如果使用static_cast<T>或者一个传统的C 样式的类型转换都能正确。


http://www.hkcw.cn/article/gqCnZDPRrw.shtml

相关文章

【MATLAB代码】制导方法——平行接近法引导,二维环境,动态目标|附代码的下载链接

平行接近法是一种导引方法&#xff0c;其目标是保持目标瞄准线在空间中的平行移动。 本文所述的代码实现了二维平行接近法导引的动态仿真&#xff0c;模拟导弹追踪移动目标的过程。通过实时调整导弹速度方向&#xff0c;确保其逐渐逼近目标&#xff0c;最终在设定距离内完成拦截…

解决win自动重启(自用,留链接)

2025-05-30修改&#xff0c;如果再出现重启回来修改。没动静就是没事了 1、依据系统事件查看器确认错误代码 事件查看器步骤 &#xff08;上图没啥用&#xff09; 下图错误代码&#xff0c;如果原因一致 2、禁用“用户体验改善计划”点击此处步骤

AI入门示例

市面上有很多AI大模型&#xff0c;这里以 智谱的大模型 为示例 1.先要注册智谱AI开放平台 2.注册成功后&#xff0c;会赠送3个月的免费额度&#xff0c;如下 3.然后去控制台&#xff0c;创建一个API KEY 4.接着就可以开始写代码了 提前导入包&#xff1a; openai 示例1&…

仿真每日一练 | 静力学分析与动力学分析的区别

很多有限元初学者都在纠结一个问题&#xff0c;就是静力学分析和动力学分析有什么区别&#xff0c;今天以一个时变载荷的例子&#xff0c;带大家领悟其中奥妙。 首先来了解一下二者的物理方程&#xff1a; 静力学所解决的问题&#xff1a;KxF 动力学所解决的问题&#xff1…

Python数据处理中的查找和排序使用详解

概要 在编程中,查找和排序是两个常见且重要的操作,几乎所有数据处理任务都会涉及到这两个内容。Python 作为一门功能强大的编程语言,为我们提供了多种查找和排序的方式,不仅包括内置函数,还支持通过算法来自定义操作。本文将详细介绍 Python 中常用的查找与排序方法,包括…

面试-【搜索引擎】

elasticsearch分布式架构原理 index -> type -> mapping -> document -> field elasticsearch 7.x取消了type mapping类似于schema信息 document代表一行数据 field代表一个字段值 elasticsearch读写流程底层剖析 (1)es写数据过程 1)客户端选择一个 node 发送…

引领机器人交互未来!MANUS数据手套解锁精准手部追踪

MANUS数据手套为机器人技术带来高精度手部追踪&#xff0c;助力实现人与机器的自然交互&#xff01;近年&#xff0c;越来越多客户希望利用这项技术精准操控机械臂、灵巧手和人形机器人&#xff0c;不断提升设备的智能化水平和交互体验。 MANUS数据手套是高精度人机交互设备&am…

国标GB28181设备管理软件EasyGBS实现生产全流程可视化监控与精细化管理

一、引言​ 在全球经济一体化与工业化浪潮的推动下&#xff0c;市场竞争愈发激烈&#xff0c;企业想要在行业中占据优势&#xff0c;实现生产流程的高效管理与精准把控成为关键。生产流程不仅是产品质量的生命线&#xff0c;更是提升生产效率、保障经济效益的核心环节。国标GB…

数据治理系统是什么?数据治理工具有什么用?

目录 一、数据治理系统是什么&#xff1f; 二、数据治理系统的重要性 1. 保障数据质量 2. 确保数据安全 3. 促进数据共享与协作 三、常见的数据治理工具及其特点 1. 数据质量管理工具 2. 数据集成工具 3. 元数据管理工具 四、数据治理工具有哪些作用&#xff1f; 1.…

AD9361 的工作原理

AD9361 由Analog Devices 公司设计并产出的。该器件集成了相当多的滤波器&#xff0c;频率合成器&#xff0c;数字处理模块以及接收端自动增益控制模块等。拥有上千个寄存器可供配置&#xff0c;通过对寄存器存入数值的更改可对该器件进行工作控制&#xff0c;正是因为此芯片配…

AI Agent开发入门笔记(1)

目录 1️⃣ 选择框架2️⃣开发操作导入Python库创建功能函数装载环境变量创建Agent运行Agent 学习参考资料&#xff1a; 微软 AI Agents for Beginners 代码仓库 1️⃣ 选择框架 semantic-kernel开发框架 导入库创建功能函数&#xff08;Agent 要完成什么功能&#xff09;…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.11 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.11 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 dataframe<-data.frame( densityc(570,565,…

Cadence学习笔记终章

目录 01 | 引 言 02 | 文章汇总 03 | 结 语 01 | 引 言 在历时一个半月后&#xff0c;终于更新完Cadence的原理图与PCB的设计流程&#xff1b; 本篇文章主要是将全部的Cadence学习文章汇总起来&#xff0c;以供朋友们以及自己日后进行翻阅。 02 | 文章汇总 文章从上至下的…

第100+41步 ChatGPT学习:R语言实现误判病例分析

本期是《第33步 机器学习分类实战&#xff1a;误判病例分析》的R版本。 尝试使用Deepseek-R1来试试写代码&#xff0c;效果还不错。 下面上R语言代码&#xff0c;以Xgboost为例&#xff1a; # 加载必要的库 library(caret) library(pROC) library(ggplot2) library(xgboost)…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.8 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.8 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 (a) dataframe<-data.frame( Lightc(580,568…

OptiStruct实例:消声器前盖ERP分析(2)RADSND基础理论

13.2 Radiated Sound Output Analysis( RADSND ) RADSND 方法通过瑞利积分来求解结构对外的辐射噪声。其基本思路是分为两个阶段&#xff0c;如图 13-12 所示。 图13-12 结构辐射噪声计算示意图 第一阶段采用有限元方法&#xff0c;通过频响分析(模态叠加法、直接法)工况计算结…

安装RHEL9.x操作系统

本案例需要的设备和软件 1.1台安装有Windows操作系统的计算机 2.RHEL 9的ISO映像文件一套 3.VMware Workstation 17.5 Pro软件一套 4.相关软件及镜像下载链接 VMware17.5&#xff0c;链接&#xff1a;https://pan.quark.cn/s/6208d19dbd48 提取码&#xff1a;22Wk rhel-9…

API:解锁网络世界的无限可能

“在数字化时代&#xff0c;互联网已成为我们生活、工作和学习中不可或缺的一部分。然而随着网络环境的日益复杂&#xff0c;访问限制、数据追踪、IP封锁等问题也随之而来。为了应对这些挑战&#xff0c;代理IP API应运而生&#xff0c;它不仅为我们提供了一种绕过限制、保护隐…

js 动画库、2048核心逻辑、面试题add[1][2][3]+4

1、js 动画库 web animation api &#xff08;1&#xff09;初始化代码 hmtl、css 部分 初始化全局背景黑色初始化黄色小球 js 部分 监听全局点击事件创建并添加元素 class"pointer" 的 div 标签 设置 left、top 位置监听动画结束事件&#xff0c;移除该元素 定位小…

Linux:Shell脚本基础

一、变量的命名规则 变量名称中只能包含数字、大小写字母以及下划线 二、变量的调用方法 三、字符的转义及引用 批量转义用“” 双引号是弱引用&#xff0c;单引号是强引用 四、用命令的执行结果定义变量 $1 $2 $3代表脚本后的第几个字节 $* $表示的是所有字节&#xff0c;但…