Files
scuc-qt-course/docs/teaching/examples/qdialog_showcase/qt_dialog_showcase.cpp
T
张宗平 01b264fe97 Day4 教学示例补三个:QDialog 综合 / QLineEdit 小眼睛 / 文件 IO(+顶层构建注册)
- qdialog_showcase:wiki「6 对话框 QDialog」6.2–6.5 综合演示。含两套实现:
  · Designer 表单版(dialog.{h,cpp,ui} + resources.qrc + source.gif,QMovie 播 GIF)
  · 纯代码版(qt_dialog_showcase.cpp,单文件,QWidget 布满按钮逐个演示 QDialog 行为)
- lineedit_eye_toggle:子类化 QLineEdit,paintEvent 自绘小眼睛、mouseEvent 命中切换
  echoMode,并与内置 clear button 动态避让(读 objectName=="clearButton" 子 widget 几何)。
  含独立子目录 CMakeLists.txt 供单独打开(顶层 add_subdirectory 不递归)。
- file_io_demo:QFile/QTextStream/QDataStream/QFileInfo/QDir/QTextCodec/QJson 五主题
  综合示例,每个按钮产出一个文件落到 demo_output/ 便于课堂手动查看。

构建注册:examples/CMakeLists.txt 用 add_teaching_example 注册前两个纯代码示例,
mandelbrot 多源文件单独 add_executable;calculator_mainwindow_qss 加 themes.qrc 与
AUTORCC;all_teaching_examples 依赖列表同步补齐。

附 lineedit_eye_toggle 设计文档(docs/superpowers/specs)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:51:09 +08:00

189 lines
9.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// qdialog_showcase —— QDialog 教学综合示例
// 参考川大 wiki「6 对话框 QDialog」(pageId 183664881) 的 6.26.5
// 6.3 模态/非模态(exec / open / show + WA_DeleteOnClose
// 6.4 QMessageBox(六个 static + 实例 API
// 6.2/6.5 标准对话框(QFileDialog / QColorDialog / QFontDialog / QInputDialog
// 形态:一个 QWidget 布满 pushbutton,每个按钮触发一种 QDialog 行为。
// 纯代码(无 .ui);主类不定义信号槽,全部用 lambda 连接 QPushButton::clicked。
// ============================================================================
#include <utility>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QDialog>
#include <QMessageBox>
#include <QFileDialog>
#include <QColorDialog>
#include <QFontDialog>
#include <QInputDialog>
#include <QLineEdit>
#include <QTimer>
#include "dialog.h"
class DialogShowcase : public QWidget {
public:
explicit DialogShowcase(QWidget *parent = nullptr) : QWidget(parent) {
auto *root = new QVBoxLayout(this);
// 反馈标签:显示标准对话框 / question 的返回值
statusLabel = new QLabel(QStringLiteral("点击下方按钮,体验不同的 QDialog 行为。"));
statusLabel->setWordWrap(true);
statusLabel->setMinimumHeight(36);
// ① 模态/非模态(6.3 -------------------------------------------------
auto *g1 = new QGroupBox(QStringLiteral("① 模态/非模态(6.3"));
auto *l1 = new QGridLayout(g1);
addBtn(l1, 0, 0, QStringLiteral("模态 exec"), [this] {
QDialog d(this);
d.setWindowTitle(QStringLiteral("模态对话框(exec"));
d.exec(); // 应用级模态:阻塞整个应用,直到关闭
setStatus(QStringLiteral("exec() 返回 —— 应用级模态已关闭"));
});
addBtn(l1, 0, 1, QStringLiteral("窗口模态 open"), [this] {
auto *d = new QDialog(this);
d->setWindowTitle(QStringLiteral("窗口级模态(open"));
d->setAttribute(Qt::WA_DeleteOnClose);
d->open(); // 窗口级模态:仅阻塞父窗口,其余窗口仍可交互
setStatus(QStringLiteral("open() 弹出窗口级模态(仅阻塞父窗口)"));
});
addBtn(l1, 1, 0, QStringLiteral("非模态 show"), [this] {
// 必须用堆对象:栈对象在 show() 返回后随作用域结束而析构,对话框一闪而过
// (页面 6.3 强调的坑)。这里给 parent this,由 Qt 对象树托管生命周期。
auto *d = new QDialog(this);
d->setWindowTitle(QStringLiteral("非模态对话框(show"));
d->show(); // 非模态:立即返回、不阻塞,可与主窗口同时操作
setStatus(QStringLiteral("show() 非模态弹出(不阻塞)"));
});
addBtn(l1, 1, 1, QStringLiteral("非模态+删除关闭"), [this] {
// 无 parent 的堆对象:靠 WA_DeleteOnClose 在关闭时自动 delete,避免泄漏
auto *d = new QDialog;
d->setWindowTitle(QStringLiteral("WA_DeleteOnClose"));
d->setAttribute(Qt::WA_DeleteOnClose);
d->show();
setStatus(QStringLiteral("show() + WA_DeleteOnClose:关闭即销毁"));
});
// ② QMessageBox6.4 -------------------------------------------------
auto *g2 = new QGroupBox(QStringLiteral("② QMessageBox6.4"));
auto *l2 = new QGridLayout(g2);
addBtn(l2, 0, 0, QStringLiteral("about"), [this] {
QMessageBox::about(this, QStringLiteral("about"),
QStringLiteral("这是 about 对话框(只有一个 OK)。"));
});
addBtn(l2, 0, 1, QStringLiteral("aboutQt"), [this] {
QMessageBox::aboutQt(this, QStringLiteral("aboutQt"));
});
addBtn(l2, 0, 2, QStringLiteral("critical"), [this] {
QMessageBox::critical(this, QStringLiteral("critical"), QStringLiteral("严重错误!"));
});
addBtn(l2, 1, 0, QStringLiteral("information"), [this] {
QMessageBox::information(this, QStringLiteral("information"), QStringLiteral("提示信息。"));
});
addBtn(l2, 1, 1, QStringLiteral("question"), [this] {
const auto r = QMessageBox::question(this, QStringLiteral("question"),
QStringLiteral("你确定吗?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
setStatus(QStringLiteral("question 返回:%1")
.arg(r == QMessageBox::Yes ? QStringLiteral("Yes") : QStringLiteral("No")));
});
addBtn(l2, 1, 2, QStringLiteral("warning"), [this] {
QMessageBox::warning(this, QStringLiteral("warning"), QStringLiteral("警告!"));
});
addBtn(l2, 2, 0, QStringLiteral("自定义消息框"), [this] {
QMessageBox box(QMessageBox::Question, QStringLiteral("自定义消息框"),
QStringLiteral("文档已被修改。"),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this);
box.setInformativeText(QStringLiteral("是否保存修改?"));
box.setDetailedText(QStringLiteral("这里是详细差异信息……"));
box.setDefaultButton(QMessageBox::Save);
const int r = box.exec();
const QString name =
(r == QMessageBox::Save) ? QStringLiteral("保存") :
(r == QMessageBox::Discard) ? QStringLiteral("丢弃") :
QStringLiteral("取消");
setStatus(QStringLiteral("自定义消息框返回:%1").arg(name));
});
// ③ 标准对话框(6.2 / 6.5 -------------------------------------------
auto *g3 = new QGroupBox(QStringLiteral("③ 标准对话框(6.2 / 6.5"));
auto *l3 = new QGridLayout(g3);
addBtn(l3, 0, 0, QStringLiteral("打开文件"), [this] {
const QString f = QFileDialog::getOpenFileName(this, QStringLiteral("打开文件"));
if (!f.isEmpty()) setStatus(QStringLiteral("打开文件:%1").arg(f));
});
addBtn(l3, 0, 1, QStringLiteral("保存文件"), [this] {
const QString f = QFileDialog::getSaveFileName(this, QStringLiteral("保存文件"));
if (!f.isEmpty()) setStatus(QStringLiteral("保存文件:%1").arg(f));
});
addBtn(l3, 1, 0, QStringLiteral("选颜色"), [this] {
const QColor c = QColorDialog::getColor(Qt::white, this, QStringLiteral("选颜色"));
if (c.isValid())
setStatus(QStringLiteral("选中颜色 RGB(%1,%2,%3)")
.arg(c.red()).arg(c.green()).arg(c.blue()));
});
addBtn(l3, 1, 1, QStringLiteral("选字体"), [this] {
bool ok = false;
const QFont f = QFontDialog::getFont(&ok, font(), this, QStringLiteral("选字体"));
if (ok) setStatus(QStringLiteral("选中字体:%1").arg(f.family()));
});
addBtn(l3, 2, 0, QStringLiteral("输入文字"), [this] {
bool ok = false;
const QString s = QInputDialog::getText(this, QStringLiteral("输入文字"),
QStringLiteral("请输入:"),
QLineEdit::Normal, QString(), &ok);
if (ok) setStatus(QStringLiteral("输入:%1").arg(s));
});
addBtn(l3, 2, 0, QStringLiteral("输入文字"), [this] {
bool ok = false;
const QString s = QInputDialog::getText(this, QStringLiteral("输入文字"),
QStringLiteral("请输入:"),
QLineEdit::Normal, QString(), &ok);
if (ok) setStatus(QStringLiteral("输入:%1").arg(s));
});
addBtn(l3, 2, 1, QStringLiteral("自定义对话框"), [] {
QDialog *customDialog = new Dialog();
customDialog->setAttribute(Qt::WA_DeleteOnClose);
customDialog->exec();
});
root->addWidget(g1);
root->addWidget(g2);
root->addWidget(g3);
root->addWidget(statusLabel);
setWindowTitle(QStringLiteral("QDialog 教学综合示例"));
resize(520, 430);
}
private:
QLabel *statusLabel = nullptr;
// 在 QGridLayout(row, col) 放一个按钮,clicked 连到 lambda
template <typename F>
void addBtn(QGridLayout *layout, int row, int col, const QString &text, F &&slot) {
auto *btn = new QPushButton(text, this);
layout->addWidget(btn, row, col);
connect(btn, &QPushButton::clicked, this, std::forward<F>(slot));
}
void setStatus(const QString &s) { statusLabel->setText(s); }
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
DialogShowcase w;
w.show();
// 仅在离屏自动化验证时 200ms 后自动退出(仿 p03 风格);
// 正常运行窗口保持,不自动退出。不点按钮则不触发任何对话框。
if (qgetenv("QT_QPA_PLATFORM") == "offscreen") {
QTimer::singleShot(200, &app, &QApplication::quit);
}
return app.exec();
}