1.概要
模式对话框,会截断主线程的执行。所以应该快速的退出,不能时间过长。且这段时间,给主线程发的信号都不会响应。
实验1:
现在想做这样的一个实验,打开一个弹出,弹窗结束后,会返回主线程执行一个处理。这个处理是否会受到打开的弹出模式影响,比如show()和模式对话框是否有差别。
结论:没有差别
实验2:
当打开弹窗的时候,如果这个弹窗的父亲窗体不是当前的主画面,会有什么影响,会不会显示在屏幕的最前面、
结论:无论是否把当前画面作为弹窗的父对象,弹窗都会显示在窗口的最前面。但如果不是将当前的主窗体设置为父对象,那么第二次触发显示的时候,不会是窗体的最前面。
如果要在最前面显示,需要设置raise();
2.内容
1.工程结构
1.结构
2.工程文件
cmake_minimum_required(VERSION 3.5) project(untitled3 VERSION 0.1 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) set(PROJECT_SOURCES main.cpp mainwindow.cpp mainwindow.h mainwindow.ui ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(untitled3 MANUAL_FINALIZATION ${PROJECT_SOURCES} dialog.h dialog.cpp dialog.ui ) # Define target properties for Android with Qt 6 as: # set_property(TARGET untitled3 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # ${CMAKE_CURRENT_SOURCE_DIR}/android) # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation else() if(ANDROID) add_library(untitled3 SHARED ${PROJECT_SOURCES} ) # Define properties for Android with Qt 5 after find_package() calls as: # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") else() add_executable(untitled3 ${PROJECT_SOURCES} ) endif() endif() target_link_libraries(untitled3 PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. if(${QT_VERSION} VERSION_LESS 6.1.0) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.untitled3) endif() set_target_properties(untitled3 PROPERTIES ${BUNDLE_ID_OPTION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) include(GNUInstallDirs) install(TARGETS untitled3 BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(untitled3) endif()2.代码
1.主函数
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }2.主窗体
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "dialog.h" #include <QProgressBar> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: void show_QProgressBar(); private slots: //打开弹窗 void on_pushButton_clicked(); //弹窗关闭返回的值 void slo_fis_ret(int); //打开进度条窗体-槽 void slo_test(); //打开进度条 void on_pushButton_2_clicked(); private: Ui::MainWindow *ui; //弹窗 Dialog* dialog; //进度条控件 QProgressBar* qb; signals: //打开进度条信号 void sig_test(); }; #endif // MAINWINDOW_H#include "mainwindow.h" #include "./ui_mainwindow.h" #include <QProgressBar> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); dialog = new Dialog(this); connect(dialog,&Dialog::finished,this,&MainWindow::slo_test); //如果不讲父窗体设为父对象,进度条第一次显示会在窗口最前面,但是第二次显示就不在最前面了。 qb = new QProgressBar(); //qb = new QProgressBar(this); } MainWindow::~MainWindow() { delete ui; } //打开弹窗 void MainWindow::on_pushButton_clicked() { //两种无差别,不会对返回后的逻辑有影响。但exec执行期间,主线程会被停止。 //dialog->show(); dialog->exec(); } //弹窗关闭返回的值 void MainWindow::slo_fis_ret(int ret){ emit sig_test(); } //打开进度条窗体-槽 void MainWindow::slo_test(){ show_QProgressBar(); } //进度条窗体显示 void MainWindow::show_QProgressBar(){ qb->show(); qb->raise(); } //打开进度条 void MainWindow::on_pushButton_2_clicked() { show_QProgressBar(); }<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <widget class="QPushButton" name="pushButton"> <property name="geometry"> <rect> <x>100</x> <y>100</y> <width>75</width> <height>23</height> </rect> </property> <property name="text"> <string>打开弹出</string> </property> </widget> <widget class="QLineEdit" name="lineEdit"> <property name="geometry"> <rect> <x>330</x> <y>100</y> <width>113</width> <height>21</height> </rect> </property> </widget> <widget class="QPushButton" name="pushButton_2"> <property name="geometry"> <rect> <x>100</x> <y>150</y> <width>75</width> <height>23</height> </rect> </property> <property name="text"> <string>打开进度条</string> </property> </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>21</height> </rect> </property> </widget> <widget class="QStatusBar" name="statusbar"/> </widget> <resources/> <connections/> </ui>3.弹窗
#ifndef DIALOG_H #define DIALOG_H #include <QDialog> namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = nullptr); ~Dialog(); private slots: void on_buttonBox_accepted(); void on_pushButton_clicked(); void on_pushButton_2_clicked(); private: Ui::Dialog *ui; }; #endif // DIALOG_H#include "dialog.h" #include "ui_dialog.h" Dialog::Dialog(QWidget *parent) : QDialog(parent) , ui(new Ui::Dialog) { ui->setupUi(this); } Dialog::~Dialog() { delete ui; } void Dialog::on_buttonBox_accepted() { } //确定 void Dialog::on_pushButton_clicked() { accept(); } //取消 void Dialog::on_pushButton_2_clicked() { reject(); }<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Dialog</class> <widget class="QDialog" name="Dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>257</width> <height>121</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <widget class="QPushButton" name="pushButton"> <property name="geometry"> <rect> <x>30</x> <y>70</y> <width>75</width> <height>23</height> </rect> </property> <property name="text"> <string>确定</string> </property> </widget> <widget class="QPushButton" name="pushButton_2"> <property name="geometry"> <rect> <x>150</x> <y>70</y> <width>75</width> <height>23</height> </rect> </property> <property name="text"> <string>取消</string> </property> </widget> </widget> <resources/> <connections/> </ui>3.运行结果
1.主页
2.打开弹窗
3.弹窗进度条
3.关联链接
4.关联知识
1.QDialog
QDialog是 Qt 框架中用于创建对话框窗口的类,继承自QWidget。它通常用于实现模态(modal)或非模态(modeless)的用户交互窗口,比如登录窗口、设置窗口、消息提示等。
基本用法
1. 包含头文件
#include <QDialog>2. 创建一个简单的自定义对话框类
// MyDialog.h #ifndef MYDIALOG_H #define MYDIALOG_H #include <QDialog> #include <QLineEdit> #include <QPushButton> #include <QVBoxLayout> class MyDialog : public QDialog { Q_OBJECT public: explicit MyDialog(QWidget *parent = nullptr); private slots: void onOkClicked(); private: QLineEdit *lineEdit; QPushButton *okButton; }; #endif // MYDIALOG_H// MyDialog.cpp #include "MyDialog.h" #include <QMessageBox> MyDialog::MyDialog(QWidget *parent) : QDialog(parent) { lineEdit = new QLineEdit(this); okButton = new QPushButton("OK", this); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(lineEdit); layout->addWidget(okButton); setLayout(layout); connect(okButton, &QPushButton::clicked, this, &MyDialog::onOkClicked); } void MyDialog::onOkClicked() { QString text = lineEdit->text(); if (text.isEmpty()) { QMessageBox::warning(this, "警告", "输入不能为空!"); } else { accept(); // 关闭对话框并返回 QDialog::Accepted } }3. 在主窗口中使用对话框
// mainwindow.cpp 中调用 MyDialog dialog(this); if (dialog.exec() == QDialog::Accepted) { // 用户点击了 OK }exec():以模态方式显示对话框(阻塞当前窗口)。show():以非模态方式显示(不阻塞)。
注意:非模态对话框一般需要动态分配(new),并设置
setAttribute(Qt::WA_DeleteOnClose),否则可能造成内存泄漏或提前析构。
常用方法
| 方法 | 说明 |
|---|---|
exec() | 显示模态对话框,返回Accepted或Rejected |
show() | 显示非模态对话框 |
accept() | 接受对话框,关闭并返回Accepted |
reject() | 拒绝对话框,关闭并返回Rejected |
done(int r) | 通用关闭函数,可指定返回值 |
setModal(true/false) | 设置是否为模态 |
示例:非模态对话框
MyDialog *dialog = new MyDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动 delete dialog->show(); // 非模态内置标准对话框(推荐优先使用)
Qt 提供了许多现成的对话框:
QMessageBox:消息提示QFileDialog:文件选择QInputDialog:简单输入QColorDialog:颜色选择QFontDialog:字体选择
例如:
QString text = QInputDialog::getText(this, "输入", "请输入姓名:");2.如何在QDialog中实现文件选择功能
在QDialog中实现文件选择功能,最常用的方式是使用 Qt 提供的标准对话框类QFileDialog。你可以将文件选择逻辑集成到自定义的QDialog中,例如让用户点击一个按钮后弹出文件选择对话框,并将选中的文件路径显示在界面上(如QLineEdit或QLabel)。
下面是一个完整的示例,展示如何在自定义QDialog中实现文件选择功能:
✅ 示例:在 QDialog 中添加“选择文件”按钮
1. 头文件FileSelectDialog.h
#ifndef FILESELECTDIALOG_H #define FILESELECTDIALOG_H #include <QDialog> #include <QLineEdit> #include <QPushButton> #include <QVBoxLayout> #include <QFileDialog> class FileSelectDialog : public QDialog { Q_OBJECT public: explicit FileSelectDialog(QWidget *parent = nullptr); // 可选:提供获取所选文件路径的接口 QString selectedFile() const; private slots: void onBrowseButtonClicked(); private: QLineEdit *filePathEdit; QPushButton *browseButton; QPushButton *okButton; }; #endif // FILESELECTDIALOG_H2. 源文件FileSelectDialog.cpp
#include "FileSelectDialog.h" #include <QMessageBox> FileSelectDialog::FileSelectDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("选择文件"); filePathEdit = new QLineEdit(this); filePathEdit->setReadOnly(true); // 只读,防止手动输入错误路径 browseButton = new QPushButton("浏览...", this); okButton = new QPushButton("确定", this); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(new QLabel("请选择一个文件:")); layout->addWidget(filePathEdit); layout->addWidget(browseButton); layout->addWidget(okButton); setLayout(layout); // 连接信号槽 connect(browseButton, &QPushButton::clicked, this, &FileSelectDialog::onBrowseButtonClicked); connect(okButton, &QPushButton::clicked, this, &QDialog::accept); } void FileSelectDialog::onBrowseButtonClicked() { // 弹出文件选择对话框 QString fileName = QFileDialog::getOpenFileName( this, "选择文件", // 对话框标题 QDir::homePath(), // 初始目录(可选) "所有文件 (*.*);;文本文件 (*.txt);;图像文件 (*.png *.jpg)" // 文件过滤器 ); if (!fileName.isEmpty()) { filePathEdit->setText(fileName); } } QString FileSelectDialog::selectedFile() const { return filePathEdit->text(); }3. 在主窗口中使用该对话框(例如在MainWindow中)
// mainwindow.cpp FileSelectDialog dialog(this); if (dialog.exec() == QDialog::Accepted) { QString file = dialog.selectedFile(); if (!file.isEmpty()) { qDebug() << "用户选择了文件:" << file; // 在这里处理文件,比如加载、解析等 } }🔧 补充说明
QFileDialog::getOpenFileName():用于选择单个已存在的文件。- 如果要选择多个文件,使用
QFileDialog::getOpenFileNames()。 - 如果要保存文件(指定新文件名),使用
QFileDialog::getSaveFileName()。 - 如果要选择目录,使用
QFileDialog::getExistingDirectory()。
例如,选择多个文件:
QStringList files = QFileDialog::getOpenFileNames( this, "选择多个文件", QDir::homePath(), "图像文件 (*.png *.jpg)" );💡 小技巧
- 可以将
filePathEdit的内容绑定到类成员变量,避免重复调用text()。 - 若希望对话框非模态(不阻塞主窗口),改用
show()并注意内存管理(建议配合setAttribute(Qt::WA_DeleteOnClose))。
如果你有更具体的需求,比如:
- 限制只能选择特定类型文件(如
.csv) - 自动预览文件内容
- 在对话框中嵌入
QTreeView实现自定义文件浏览器
3.QProgressBar
QProgressBar是 Qt 框架中用于显示任务进度的控件,常用于文件传输、数据加载、安装过程等需要可视化进度的场景。
📌 基本用法(C++)
1.头文件
#include <QProgressBar>2.常用属性与方法
| 方法 | 说明 |
|---|---|
void setMinimum(int minimum) | 设置最小值(默认为 0) |
void setMaximum(int maximum) | 设置最大值(默认为 100) |
void setRange(int minimum, int maximum) | 同时设置最小和最大值 |
void setValue(int value) | 设置当前进度值 |
int value() const | 获取当前值 |
void setOrientation(Qt::Orientation) | 设置方向(水平/垂直,默认水平) |
void setTextVisible(bool visible) | 是否显示百分比文本(默认 true) |
QString format() const/setFormat(const QString &) | 自定义进度文本格式 |
💡 当
minimum == maximum时,进度条进入“繁忙”模式(无限循环动画),适用于未知总进度的情况。
3.简单示例:水平进度条
#include <QApplication> #include <QProgressBar> #include <QTimer> #include <QWidget> #include <QVBoxLayout> int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; QProgressBar *progressBar = new QProgressBar(&window); progressBar->setRange(0, 100); // 可省略,默认就是 0~100 progressBar->setValue(0); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(progressBar); window.setLayout(layout); window.resize(300, 80); window.show(); // 模拟进度增加 QTimer *timer = new QTimer(&window); int value = 0; QObject::connect(timer, &QTimer::timeout, [&]() { value += 5; progressBar->setValue(value); if (value >= 100) { timer->stop(); } }); timer->start(200); // 每 200ms 更新一次 return app.exec(); }4.“繁忙”模式(不确定进度)
progressBar->setRange(0, 0); // 或 setMinimum(0); setMaximum(0); // 此时进度条会显示循环动画,表示任务正在进行但无法确定完成时间5.自定义文本格式
progressBar->setFormat("已完成 %p% (%v/%m)"); // %p = 百分比,%v = 当前值,%m = 最大值 // 默认格式通常是 "%p%"6.垂直进度条
progressBar->setOrientation(Qt::Vertical);⚠️ 注意事项
线程安全:不要在非 GUI 线程中直接调用
setValue()。若需从工作线程更新进度,请使用信号槽机制(跨线程连接)。示例:
// 工作线程 emit progressUpdated(int value); connect(workerThread, &Worker::progressUpdated, progressBar, &QProgressBar::setValue);性能:频繁更新(如每毫秒)可能导致界面卡顿,建议合理控制更新频率(如每 50~100ms)。
🧩 常见应用场景
- 文件复制/下载进度
- 数据库导入/导出
- 安装向导步骤
- 后台计算任务反馈