【技术】Qt对话框讲解
前言
Qt中对话框QDialog是几乎每个项目都会用到的GUI窗口对象。本文通过讲解QDialog及其子类在项目中经常被用到的功能点,帮助小伙伴们理解和掌握其使用方法。
QDialog
QDialog是Qt对话框类树中的基类,Qt为我们提供了很多QDialog子类,即标准对话框,实现了丰富的对话框功能。下面我们先看一下QDialog本身需要关注的一些问题。
模态对话框
一句话描述模态对话框:
除模态对话框外,用户无法选择并操作其他窗口,只有关闭了模态对话框,用户才能操作其他窗口。
下面的代码,用QDialog::exec函数实现了模态对话框(请结合Qt中QDialog的类帮助文档阅读下面的代码):
QDialog dlg; dlg.exec(); // 执行模态对话框
等效于
QDialog dlg; dlg.setModel(true); // 设置为模态 dlg.show(); // 显示对话框
原理上,exec函数是阻塞执行的,直到模态对话框关闭才返回,可以看出,exec内部有一个自己的消息循环,一直在循环处理消息。用代码模拟如下:
class QDialog { public: int exec() { show(); // 显示窗口 m_event_loop.exec(); // 事件循环 hide(); // 隐藏窗口 return result(); // 返回退出码 } private: QEventLoop m_event_loop; // 事件循环对象 }
而调用show函数是不需要对话框关闭就会立即返回的,相当于setVisible(true),仅仅设置了允许窗口显示的标记而已。先调用setModel(true),再调用show()仍然是立即返回的,这种情况下,模态对话框使用的消息循环是main函数中app.exec中的启动的应用全局消息循环。原理上尝试理解即可,暂时不需要掌握,在后面的文章中会有对消息循环(QEventLoop)的专门讲解。
使用场景
exec阻塞执行方式,不会将本来可以在一个函数内实现的业务代码,分散到多个函数中去。
比如下面的例子,弹出对话框让用户输入名称,然后校验用户输入的用户名是否正确,在一个函数内即可完成此功能:
class Test { public: void checkUsername() { QString name; MyDialog dlg; // MyDialog为我们自定义的QDialog子类 // 用户点击取消按钮,直接返回 if (dlg.exec() == QDialog::Rejected) { return; } name = dlg.getName(); // 获取用户名 if (name != "ABC") // 校验用户名 { qDebug() << "Name Error!"; // 用户名校验错误 return; } else { qDebug() << "Pass"; // 用户名校验正确 } } }
如果使用show会怎么样呢?使用show实现同样的功能,代码如下:
class Test : public QObject { Q_OBJECT public: void showNameInputDialog() { // 下面额外监听对话框关闭信号 connect(&m_dlg, SIGNAL(finished(int)), this, SLOT(slotDialogFinished(int))); m_dlg.setModel(true); m_dlg.show(); } // 响应对话框关闭信号的槽 void slotDialogFinished(int code) { if (code == QDialog::Rejected) // 用户点击取消按钮,直接返回 { return; } name = m_dlg.getName(); // 获取用户名 if (name != "ABC") // 校验用户名 { qDebug() << "Name Error!"; return; } else { qDebug() << "Pass"; // 用户名校验正确 } } private: // MyDialog为我们自定义的QDialog子类 // 需要延长dlg的生命周期,把exec例子中的函数内变量,写成类的成员变量 MyDialog m_dlg; }
可以看到,使用show来显示对话框,不仅要使用信号槽,还要添加成员变量、添加槽函数,非常麻烦,而且一个连续的业务流程代码被分散到了多个函数中。
exec()是开发中最常用的模块对话框调用方法。由上面的例子可知,模态对话框一般用来在某个处理过程中,请求用户输入内容,或配置必要的处理参数。而且,这样可以保持业务处理代码连续不分散。
非模态对话框
一句话描述非模态对话框:
非模态对话框不会独占用户鼠标和键盘输入,用户可以随时在其他窗口和非模态窗口之间切换操作。
非模态对话框一般用来实现悬浮窗口。非模态对话框会悬浮在父窗口上层,父窗口通过构造函数的parent参数设置。即使点击了父窗口,非模态对话框也会保持在父窗口上层,变化仅仅是失去焦点。
非模态对话框的典型例子是,使用文本编辑软件如notepad.exe,进行文本编辑时悬浮的文本搜索框,就是一种非模态对话框。
非模态对话框使用比较简单,定义好对话框变量后,直接调用成员函数show即可显示,这里不做过多讲解。
QDialog子类
说完了QDialog,下面开始介绍QDialog子类。
Qt标准对话框
根据使用频率大致对QDialog子类排序,见下表,读者可根据此顺序学习使用:
子类 | 描述 |
---|---|
QMessageBox | 信息对话框,最常用,用于显示提示信息、警告信息、询问信息、致命信息,并接受用户选择下一步操作。 |
QFileDialog | 文件对话框,常用,用于选择文件、文件夹。 |
QInputDialog | 输入对话框,用于获取字符串输入、数值输入、以及选择列表中的某一项。 |
QColorDialog、QFontDialog | 颜色和字体对话框。 |
QProgressDialog | 进度显示对话框。 |
QErrorMessage | 错误信息显示对话框,提供不再显示某条内容的消息的选项。 |
QWizard | 向导对话框,用于引导用户进行某个操作。 |
自定义QDialog子类
自定义QDialog子类是必备技能。很多情况下,标准对话框不满足使用要求,此时就需要我们自定义对话框实现响应的功能。
自定义对话框有以下几点需要说明。
1. done、accept、reject
这三个函数是自定义函数一定需要用到的函数,用于控制以什么样的状态码退出对话框。
done函数用来退出对话框并设置退出码。
对于模态对话框,done相当于:
void QDialog::done(int code) { setResult(code); // 设置退出码 if (isModel()) // 如果是模态对话框 { m_event_loop.exit(code); // 退出消息循环 } else { hide(); // 隐藏窗口 } }
done结束对话框内部消息循环,done函数退出,随后下一个消息循环中,exec函数中的消息循环返回,exec函数也退出,最后返回到调用对话框的代码处。
accept相当于
done(QDialog::Accepted);
reject相当于
done(QDialog::Rejected);
2. 不要用exec的返回值来传递内容信息
exec的返回值可以用来传递退出码。由上述内容可知,done、accept、reject可以用来退出对话框并设置退出码。
笔者在初次自定义对话框时,总想着将对话框中的输入内容,用exec返回。例如,将用户在对话框中选择的列表项索引,用done(index)来返回。这里明确告诉小伙伴们这样的用法是不提倡的。因为如果是字符串,那怎么返回呢?
正确的做法是,使用done返回用户对编辑的确认状态,即确认设置内容还是取消设置,确认内容用accept,取消设置用reject。这样一来,自定义的QDialog类需要提供对话框数据获取接口,当exec返回后,再使用对话框对象实例,调用数据获取接口获取用户编辑的数据。
举个简单的例子:
class MyDialog : public QDialog { public: explicit MyDialog(QWidget *parent = NULL); QString getName() { // 从界面输入框中获取用户输入内容 return ui->lineEdit_name->text(); } QString getPassword() { // 从界面输入框中获取用户输入内容 return ui->lineEdit_password->text(); } private: // 点击确认按钮响应 void slotOkButtonClicked() { accept(); } // 点击取消按钮响应 void slotCancelButtonClicked() { reject(); } }
结语
本文代码略多,粗略地讲解了Qt中对话框的类型和理解对话框工作原理的关键问题。细节的部分需要读者自己去实践,文章中无法讲解得太过详细。讲解太过详细,第一,读起来费时费力,倒不如花时间自己写代码尝试,第二,学习容易出现问题的地方是较难理解的地方,细节不需要面面俱到 ,用到的时候再查就可以。学习Qt某个模块,要先把握住某一个功能模块的运作机理、框架,而不是一头钻进庞杂的内容里,没有方向地去学习,效率很低也很痛苦。
本文是Qt基础文章,笔者想尽快写完Qt的基础部分,除了基础还有很多东西需要掌握,不能一直在基础上转圈圈。小伙伴们如果有疑问,欢迎留言。如果问题很多,笔者会考虑建一个讨论群,尽自己能力帮助想要学习Qt的同学,大家一起进步。
本文首发于微信公众号“Qt未来工程师”。
本文来自博客园,作者:撬动未来的支点,转载请注明原文链接:https://www.cnblogs.com/pivotfuture/p/16297345.html
CSDN:撬动未来的支点,公众号:Qt未来工程师,网站:www.qtfuture.cn
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!