|
|
|
|
@ -1,123 +1,289 @@
|
|
|
|
|
#include "mainwindow.h"
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
#include <QInputDialog>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QMenuBar>
|
|
|
|
|
//#include <QFile>
|
|
|
|
|
//#include <QTextStream>
|
|
|
|
|
//#include <QFileDialog>
|
|
|
|
|
|
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
|
|
|
: QMainWindow(parent)
|
|
|
|
|
{
|
|
|
|
|
setupUI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MainWindow::~MainWindow() {}
|
|
|
|
|
|
|
|
|
|
void MainWindow::setupUI()
|
|
|
|
|
{
|
|
|
|
|
QWidget *central = new QWidget(this);
|
|
|
|
|
setCentralWidget(central);
|
|
|
|
|
|
|
|
|
|
// 表格
|
|
|
|
|
tableView = new QTableView(this);
|
|
|
|
|
model = new QStandardItemModel(this);
|
|
|
|
|
model->setHorizontalHeaderLabels({"学号", "姓名", "成绩"});
|
|
|
|
|
tableView->setModel(model);
|
|
|
|
|
// 顶部菜单栏
|
|
|
|
|
QMenuBar *menuBar = this->menuBar();
|
|
|
|
|
|
|
|
|
|
// 输入框
|
|
|
|
|
idEdit = new QLineEdit();
|
|
|
|
|
idEdit->setPlaceholderText("学号"); // 占位符 && 水印文字
|
|
|
|
|
nameEdit = new QLineEdit();
|
|
|
|
|
nameEdit->setPlaceholderText("姓名");
|
|
|
|
|
scoreEdit = new QLineEdit();
|
|
|
|
|
scoreEdit->setPlaceholderText("成绩");
|
|
|
|
|
fileMenu = menuBar->addMenu("文件(&F)"); // 快捷键 alt + F
|
|
|
|
|
editMenu = menuBar->addMenu("编辑(&E)"); // 快捷键 alt + E
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
// 按钮
|
|
|
|
|
addButton = new QPushButton("添加");
|
|
|
|
|
deleteButton = new QPushButton("删除");
|
|
|
|
|
// 左侧班级树控件
|
|
|
|
|
tree = new QTreeWidget;
|
|
|
|
|
tree->setHeaderLabel("班级列表");
|
|
|
|
|
tree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
|
|
|
|
|
// 显示学生信息控件
|
|
|
|
|
table = new QTableWidget;
|
|
|
|
|
table->setColumnCount(5);
|
|
|
|
|
table->setHorizontalHeaderLabels({"姓名", "性别", "年龄", "学号", "电话"});
|
|
|
|
|
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
|
|
|
|
|
|
|
|
// 输入学生信息控件
|
|
|
|
|
nameEdit = new QLineEdit;
|
|
|
|
|
genderEdit = new QLineEdit;
|
|
|
|
|
ageEdit = new QLineEdit;
|
|
|
|
|
idEdit = new QLineEdit;
|
|
|
|
|
phoneEdit = new QLineEdit;
|
|
|
|
|
|
|
|
|
|
addBtn = new QPushButton("新增学生");
|
|
|
|
|
delBtn = new QPushButton("删除学生");
|
|
|
|
|
|
|
|
|
|
// 布局
|
|
|
|
|
// Edit水平布局
|
|
|
|
|
QHBoxLayout *formLayout_Edit = new QHBoxLayout();
|
|
|
|
|
formLayout_Edit->addWidget(idEdit);
|
|
|
|
|
formLayout_Edit->addWidget(nameEdit);
|
|
|
|
|
formLayout_Edit->addWidget(scoreEdit); // 使各个Edit之间水平分布
|
|
|
|
|
|
|
|
|
|
// Button水平布局
|
|
|
|
|
QHBoxLayout *formLayout_button = new QHBoxLayout();
|
|
|
|
|
formLayout_button->addWidget(addButton);
|
|
|
|
|
formLayout_button->addWidget(deleteButton);
|
|
|
|
|
|
|
|
|
|
// 垂直布局
|
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
|
|
|
|
mainLayout->addWidget(tableView); // index 0:垂直布局中第0个元素(表格)
|
|
|
|
|
mainLayout->addLayout(formLayout_Edit); // index 1:垂直布局中第1个元素(水平排列的输入框/按钮)
|
|
|
|
|
mainLayout->addLayout(formLayout_button); // index 2:垂直布局中第2个元素 (按钮)
|
|
|
|
|
|
|
|
|
|
// 控制大小
|
|
|
|
|
mainLayout->setStretch(0, 1); // 表格撑满
|
|
|
|
|
mainLayout->setStretch(1, 0); // 输入区最小
|
|
|
|
|
mainLayout->setContentsMargins(10, 10, 10, 10); // 让每一个控件都与边缘保持10个像素的距离
|
|
|
|
|
|
|
|
|
|
central->setLayout(mainLayout);
|
|
|
|
|
|
|
|
|
|
tableView->setSizePolicy(
|
|
|
|
|
QSizePolicy::Expanding,
|
|
|
|
|
QSizePolicy::Expanding);
|
|
|
|
|
|
|
|
|
|
// 信号槽
|
|
|
|
|
connect(addButton, &QPushButton::clicked, this, &MainWindow::on_addButton_clicked);
|
|
|
|
|
connect(deleteButton, &QPushButton::clicked, this, &MainWindow::on_deleteButton_clicked);
|
|
|
|
|
QVBoxLayout *rightLayout = new QVBoxLayout; // 垂直布局
|
|
|
|
|
rightLayout->addWidget(new QLabel("姓名")); // index 0
|
|
|
|
|
rightLayout->addWidget(nameEdit); // index 1
|
|
|
|
|
rightLayout->addWidget(new QLabel("性别"));
|
|
|
|
|
rightLayout->addWidget(genderEdit);
|
|
|
|
|
rightLayout->addWidget(new QLabel("年龄"));
|
|
|
|
|
rightLayout->addWidget(ageEdit);
|
|
|
|
|
rightLayout->addWidget(new QLabel("学号"));
|
|
|
|
|
rightLayout->addWidget(idEdit);
|
|
|
|
|
rightLayout->addWidget(new QLabel("电话"));
|
|
|
|
|
rightLayout->addWidget(phoneEdit);
|
|
|
|
|
rightLayout->addWidget(addBtn);
|
|
|
|
|
rightLayout->addWidget(delBtn);
|
|
|
|
|
rightLayout->addStretch(); // 设置可伸缩对齐
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *mainLayout = new QHBoxLayout(central); // 水平布局
|
|
|
|
|
mainLayout->addWidget(tree, 1); // index 1
|
|
|
|
|
mainLayout->addWidget(table, 3); // index 3
|
|
|
|
|
mainLayout->addLayout(rightLayout, 2); // index 2
|
|
|
|
|
|
|
|
|
|
// 指定信号 槽
|
|
|
|
|
// 右键点击弹出增删改班级窗口
|
|
|
|
|
connect(tree, &QTreeWidget::customContextMenuRequested, // 右键点击信号
|
|
|
|
|
this, &MainWindow::showTreeMenu);
|
|
|
|
|
// 左键点击班级名 刷新中间的学生信息
|
|
|
|
|
connect(tree, &QTreeWidget::itemClicked, // 左键点击信号
|
|
|
|
|
this, &MainWindow::onClassClicked);
|
|
|
|
|
// 左键点击执行添加学生逻辑
|
|
|
|
|
connect(addBtn, &QPushButton::clicked,
|
|
|
|
|
this, &MainWindow::addStudent);
|
|
|
|
|
// 左键点击执行删除学生逻辑
|
|
|
|
|
connect(delBtn, &QPushButton::clicked,
|
|
|
|
|
this, &MainWindow::deleteStudent);
|
|
|
|
|
// 点击学生单元格 回显学生信息
|
|
|
|
|
connect(table, &QTableWidget::cellClicked, // 单元格点击信号
|
|
|
|
|
this, &MainWindow::tableItemClicked);
|
|
|
|
|
|
|
|
|
|
// 确认窗口大小
|
|
|
|
|
resize(900, 600);
|
|
|
|
|
setMinimumSize(800, 500);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 这个析构还必须得有
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
|
void MainWindow::showTreeMenu(const QPoint &pos)
|
|
|
|
|
{
|
|
|
|
|
// 右键点击 后触发这个函数
|
|
|
|
|
QMenu menu;
|
|
|
|
|
QAction *add = menu.addAction("新建班级");
|
|
|
|
|
QAction *del = menu.addAction("删除班级");
|
|
|
|
|
QAction *ren = menu.addAction("重命名班级");
|
|
|
|
|
|
|
|
|
|
// 显示右键菜单 并获取用户选择
|
|
|
|
|
QAction *ret = menu.exec(tree->viewport()->mapToGlobal(pos));
|
|
|
|
|
|
|
|
|
|
// 执行用户选择的操作
|
|
|
|
|
if (ret == add)
|
|
|
|
|
addClass();
|
|
|
|
|
else if (ret == del)
|
|
|
|
|
deleteClass();
|
|
|
|
|
else if (ret == ren)
|
|
|
|
|
renameClass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_addButton_clicked()
|
|
|
|
|
void MainWindow::addClass()
|
|
|
|
|
{
|
|
|
|
|
// 添加学生信息的逻辑
|
|
|
|
|
Student stu;
|
|
|
|
|
stu.id = idEdit->text();
|
|
|
|
|
stu.name = nameEdit->text();
|
|
|
|
|
stu.score = scoreEdit->text().toDouble();
|
|
|
|
|
// qDebug() << "id : " << stu.id
|
|
|
|
|
// << "name : " << stu.name
|
|
|
|
|
// << "score : " << stu.score;
|
|
|
|
|
|
|
|
|
|
// 判空F
|
|
|
|
|
if (stu.id.isEmpty() || stu.name.isEmpty())
|
|
|
|
|
// 弹出对话框 输入班级名称
|
|
|
|
|
QString name = QInputDialog::getText(this, "新建班级", "班级名称");
|
|
|
|
|
if (name.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
// 检查班级是否已存在
|
|
|
|
|
if (classData.contains(name))
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::warning(this, "错误", "学号和姓名不能为空!");
|
|
|
|
|
QMessageBox::warning(this, "错误", "班级已存在,请重新输入");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
students.push_back(stu);
|
|
|
|
|
// 将新班级添加到数据结构和树控件中
|
|
|
|
|
// 由于是新班级,初始化为空列表
|
|
|
|
|
classData[name] = {};
|
|
|
|
|
// 在树控件中添加新节点
|
|
|
|
|
// 数据与UI同步更新
|
|
|
|
|
tree->addTopLevelItem(new QTreeWidgetItem({name}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 刷新表格
|
|
|
|
|
refreshTable();
|
|
|
|
|
void MainWindow::deleteClass()
|
|
|
|
|
{
|
|
|
|
|
// 获取当前选中班级 // auto == QTreeWidgetItem*
|
|
|
|
|
auto item = tree->currentItem();
|
|
|
|
|
if (!item)
|
|
|
|
|
return;
|
|
|
|
|
QString name = item->text(0); // 获取选中项的班级名称
|
|
|
|
|
// 弹出确认对话框
|
|
|
|
|
if (QMessageBox::question(this, "确认", "确定删除班级?") == QMessageBox::Yes)
|
|
|
|
|
{
|
|
|
|
|
// 从数据结构和树控件中删除班级
|
|
|
|
|
classData.remove(name);
|
|
|
|
|
// 数据与UI同步更新
|
|
|
|
|
delete item; // 避免内存泄漏
|
|
|
|
|
table->clearContents(); // 清理学生信息内容
|
|
|
|
|
table->setRowCount(0); // 清空表格行数
|
|
|
|
|
}
|
|
|
|
|
QMessageBox::information(this, "提示", "班级已删除");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 刷新输入框内容
|
|
|
|
|
idEdit->clear();
|
|
|
|
|
nameEdit->clear();
|
|
|
|
|
scoreEdit->clear();
|
|
|
|
|
void MainWindow::renameClass()
|
|
|
|
|
{
|
|
|
|
|
// 获取当前选中班级
|
|
|
|
|
auto item = tree->currentItem();
|
|
|
|
|
if (!item)
|
|
|
|
|
return;
|
|
|
|
|
// 获取旧班级名称
|
|
|
|
|
QString oldName = item->text(0);
|
|
|
|
|
// 弹出对话框 输入新名称
|
|
|
|
|
QString newName = QInputDialog::getText(this, "重命名", "新名称");
|
|
|
|
|
// 检查新名称有效性
|
|
|
|
|
if (newName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
if (classData.contains(newName))
|
|
|
|
|
QMessageBox::warning(this, "提示", "班级已存在");
|
|
|
|
|
// 更新数据结构和树控件
|
|
|
|
|
classData[newName] = classData.take(oldName); // 从数据结构中移除旧键值对,并返回旧名称关联的值,赋给新名称
|
|
|
|
|
item->setText(0, newName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::onClassClicked(QTreeWidgetItem *item)
|
|
|
|
|
{
|
|
|
|
|
// 用户点击班级节点后触发此函数
|
|
|
|
|
Q_UNUSED(item) // 避免未使用参数警告
|
|
|
|
|
refreshTable(); // 刷新右侧学生表格内容
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString MainWindow::currentClass() const // 只读成员函数
|
|
|
|
|
{
|
|
|
|
|
// 获取当前选中班级名称 auto == QTreeWidgetItem*
|
|
|
|
|
auto item = tree->currentItem();
|
|
|
|
|
return item ? item->text(0) : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_deleteButton_clicked()
|
|
|
|
|
void MainWindow::refreshTable()
|
|
|
|
|
{
|
|
|
|
|
QString cls = currentClass();
|
|
|
|
|
table->setRowCount(0); // 先清空表格内容
|
|
|
|
|
if (!classData.contains(cls))
|
|
|
|
|
return; // 班级不存在
|
|
|
|
|
|
|
|
|
|
auto &list = classData[cls]; // 获取当前班级的学生列表引用
|
|
|
|
|
table->setRowCount(list.size()); // 设置表格行数为学生数量
|
|
|
|
|
for (int i = 0; i < list.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
// 遍历学生列表 填充表格内容
|
|
|
|
|
table->setItem(i, 0, new QTableWidgetItem(list[i].name));
|
|
|
|
|
table->setItem(i, 1, new QTableWidgetItem(list[i].gender));
|
|
|
|
|
table->setItem(i, 2, new QTableWidgetItem(QString::number(list[i].age)));
|
|
|
|
|
table->setItem(i, 3, new QTableWidgetItem(list[i].id));
|
|
|
|
|
table->setItem(i, 4, new QTableWidgetItem(list[i].phone));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::addStudent()
|
|
|
|
|
{
|
|
|
|
|
int row = tableView->currentIndex().row();
|
|
|
|
|
if (row < 0)
|
|
|
|
|
QString cls = currentClass();
|
|
|
|
|
if (cls.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Student s;
|
|
|
|
|
s.name = nameEdit->text();
|
|
|
|
|
s.gender = genderEdit->text();
|
|
|
|
|
s.id = idEdit->text();
|
|
|
|
|
s.phone = phoneEdit->text();
|
|
|
|
|
|
|
|
|
|
// 处理年龄:用toInt的第二个参数判断是否为有效数字
|
|
|
|
|
bool isAgeValid; // 检测是否转换成功
|
|
|
|
|
int age = ageEdit->text().toInt(&isAgeValid);
|
|
|
|
|
|
|
|
|
|
if (!isAgeValid || age <= 0 || age > 120) // 年龄有效条件:转换成功 + 年龄在合理范围
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::warning(this, "错误", "年龄必须是1-120的整数");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
s.age = age; // 只有年龄有效,才赋值给结构体
|
|
|
|
|
|
|
|
|
|
students.remove(row);
|
|
|
|
|
// 刷新表格
|
|
|
|
|
// qDebug() << s.name << s.id << s.age << s.phone << s.gender;
|
|
|
|
|
if (s.name.isEmpty() || s.gender.isEmpty() || s.id.isEmpty() || s.phone.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::warning(this, "错误", "信息不完整(姓名、性别、学号、电话不能为空)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &st : classData[cls])
|
|
|
|
|
{
|
|
|
|
|
if (st.id == s.id)
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::warning(this, "错误", "学号重复");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
classData[cls].append(s); // 将新学生追加到当前班级的学生列表
|
|
|
|
|
refreshTable();
|
|
|
|
|
|
|
|
|
|
// 刷新输入框内容
|
|
|
|
|
nameEdit->clear();
|
|
|
|
|
genderEdit->clear();
|
|
|
|
|
ageEdit->clear();
|
|
|
|
|
idEdit->clear();
|
|
|
|
|
phoneEdit->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::refreshTable()
|
|
|
|
|
void MainWindow::deleteStudent()
|
|
|
|
|
{
|
|
|
|
|
model->removeRows(0, model->rowCount());
|
|
|
|
|
QString cls = currentClass(); // 拿到班级名称
|
|
|
|
|
int row = table->currentRow(); // 获取当前选中行
|
|
|
|
|
if (cls.isEmpty() || row < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (const Student &stu : students)
|
|
|
|
|
if (QMessageBox::question(this, "确认", "删除该学生?") == QMessageBox::Yes)
|
|
|
|
|
{
|
|
|
|
|
QList<QStandardItem *> row;
|
|
|
|
|
row << new QStandardItem(stu.id)
|
|
|
|
|
<< new QStandardItem(stu.name)
|
|
|
|
|
<< new QStandardItem(QString::number(stu.score));
|
|
|
|
|
model->appendRow(row);
|
|
|
|
|
classData[cls].removeAt(row);
|
|
|
|
|
refreshTable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::tableItemClicked(int row, int) // 点击学生信息进行回显
|
|
|
|
|
{
|
|
|
|
|
QString cls = currentClass();
|
|
|
|
|
if (cls.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
auto s = classData[cls][row];
|
|
|
|
|
|
|
|
|
|
nameEdit->setText(s.name);
|
|
|
|
|
genderEdit->setText(s.gender);
|
|
|
|
|
ageEdit->setText(QString::number(s.age));
|
|
|
|
|
idEdit->setText(s.id);
|
|
|
|
|
phoneEdit->setText(s.phone);
|
|
|
|
|
}
|
|
|
|
|
|