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>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
// ============================================================================
|
||||
// qdialog_showcase —— QDialog 教学综合示例
|
||||
// 参考川大 wiki「6 对话框 QDialog」(pageId 183664881) 的 6.2–6.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:关闭即销毁"));
|
||||
});
|
||||
|
||||
// ② QMessageBox(6.4) -------------------------------------------------
|
||||
auto *g2 = new QGroupBox(QStringLiteral("② QMessageBox(6.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();
|
||||
}
|
||||
Reference in New Issue
Block a user