教学示例补 5 个:3 个坑点复现 + QTableWidget + Day5 学员管理器参考实现

- pitfall_stack_widget / pitfall_lambda_capture / pitfall_double_connect:
  默认跑正确写法(离屏验证退出码 0),--crash / --dangle 开关课堂现场触发
  真实崩溃/悬空访问
- table_widget:QTableWidget 单元格事件最小示例(新安排 Day4「表格事件」)
- student_manager:Day5 综合案例参考实现,离屏自测覆盖 JSON round-trip
  与 QThread 后台导入闭环
- 均接入 examples/CMakeLists.txt 的 add_teaching_example,手工维护区不影响生成器
This commit is contained in:
张宗平
2026-07-05 12:49:20 +08:00
parent 6640baffae
commit 6d1895d696
11 changed files with 798 additions and 1 deletions
@@ -0,0 +1,80 @@
// ============================================================================
// qt_table_widget_basics —— 教学补充示例:QTableWidget 表格与单元格事件
// 对应教案:OUTLINE_4DAY.md Day4 上午「表格」环节(新安排原文「表格事件」)
// 配套文档:docs/teaching/examples/table_widget/README.md
// 离屏验证:QT_QPA_PLATFORM=offscreen tools/run_qt.sh qt_table_widget_basics
//
// QTableWidget 是「item 类」控件:数据直接塞进控件里(QTableWidgetItem),
// 上手快、够中小项目用;数据/展示彻底分离的完整 Model/View 架构见
// ../model_view/qt_model_view_basics.cpp(延伸钩子)。
// ============================================================================
#include <QApplication>
#include <QHeaderView>
#include <QLabel>
#include <QTableWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>
class TableDemo : public QWidget {
Q_OBJECT
public:
explicit TableDemo(QWidget* parent = nullptr) : QWidget(parent) {
m_table = new QTableWidget(3, 3, this); // 行列数在构造时给定
m_table->setHorizontalHeaderLabels(
{QStringLiteral("姓名"), QStringLiteral("成绩"), QStringLiteral("标签")});
m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
const char* names[] = {"张三", "李四", "王五"};
const char* scores[] = {"91.5", "88", "76"};
const char* tags[] = {"组长", "美工", "测试"};
for (int row = 0; row < 3; ++row) {
// 每个单元格一个 QTableWidgetItemnew 出来交给表格接管所有权
m_table->setItem(row, 0, new QTableWidgetItem(QString::fromUtf8(names[row])));
m_table->setItem(row, 1, new QTableWidgetItem(QString::fromUtf8(scores[row])));
m_table->setItem(row, 2, new QTableWidgetItem(QString::fromUtf8(tags[row])));
}
m_status = new QLabel(QStringLiteral("点击任意单元格试试"), this);
auto* layout = new QVBoxLayout(this);
layout->addWidget(m_table);
layout->addWidget(m_status);
// 「表格事件」以信号形式暴露:cellClicked / cellDoubleClicked /
// itemChanged(编辑单元格后触发)…… 不需要重写 event 函数。
connect(m_table, &QTableWidget::cellClicked, this, &TableDemo::onCellClicked);
connect(m_table, &QTableWidget::itemChanged, this, [this](QTableWidgetItem* item) {
m_status->setText(QStringLiteral("(%1,%2) 被改为 %3")
.arg(item->row()).arg(item->column()).arg(item->text()));
});
}
private slots:
void onCellClicked(int row, int column) {
QTableWidgetItem* item = m_table->item(row, column);
m_status->setText(QStringLiteral("点击了 (%1,%2)%3")
.arg(row).arg(column)
.arg(item ? item->text() : QStringLiteral("<空>")));
}
private:
QTableWidget* m_table = nullptr;
QLabel* m_status = nullptr;
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
TableDemo demo;
demo.setWindowTitle(QStringLiteral("QTableWidget 基础"));
demo.resize(420, 240);
demo.show();
if (qgetenv("QT_QPA_PLATFORM") == "offscreen") {
QTimer::singleShot(200, &app, &QApplication::quit);
}
return app.exec();
}
#include "qt_table_widget_basics.moc"