写在前面
博主是一个大一下的计科生,现在正在做C++面向对象程序设计的课程设计,具体功能可以看本专栏的第一篇博客。
目前的进度是:配好MySQL驱动->设计完界面->实现各个界面的切换
这一篇博主要初步实现待办板块的功能,即新建和修改
板块功能分析
(1)预期功能


这个板块博主的预期是:点新建,然后就弹到第二页界面,在第二个界面输入内容之后,点应用就可以保存这些,然后在第一个界面就出现了这个新建的任务,依次类推可以设置很多个这样的任务,然后等界面占满可以用鼠标滚轮上下滚动。点击修改进入第二个界面,然后修改了内容之后第一个界面会对应改变
像里边的按优先级或分类或开始时间排序、提醒功能后续再做
(2)项目难点
1.每次新建任务块都要新建控件吗?那控件需要开多少个才算够?
2.鼠标滚轮滑屏怎么实现?
3.怎么实现每一次修改或新建都对应正确的任务块?
这是博主目前面临的难点,因为我还没学这部分知识
(3)难点解决
受了某位学长的指点,我最后决定使用TableView来解决这个问题。我只能说,做项目最怕的就是你不知道你自己不知道。很多东西其实又方便又好用,但是你根本不知道它的存在,所以就会走弯路
为什么要用TableView呢?因为TableView会自动生成滚轮,而且实现对应也更简单,还不需要再新建控件,只要加一行数据就行,这样的话就只有新建或修改与正确的任务块对应是一个困难了
来说一下博主的天才思路:
当点击新建按钮时,用户输入内容,点击应用后这些内容传到shiftTaskList中,然后这些内容再从shiftTaskList传到tableView中,清空shiftTaskList;当选中一行数据,再点击修改时,通过索引判断选中的行数,然后把tableView里这一行的数据传进shiftTaskList中,然后这些内容通过shiftTaskList传到第二个界面中,用户在这里修改之后点击应用,被修改的数据再传回shiftTaskList里,然后再传回对应的tableView里
这个思路是我想了很久突然一下悟到的,感觉自己特别天才,因为这个真正是博主依靠自己想到的实现思路
(4)实现步骤
在Task里声明一个QList类成员变量,这个成员变量的作用是中介------>把原来的控件删了,把修改按钮移到最下边------>添加TableView控件,并添加表头------>建立表格与新建按钮的联系,这个是通过QList实现的------>将新建界面的内容传回表格里,这个也是通过QList实现的------>设置修改与每一行的对应,同样是通过QList实现------>将表格里的内容传到修改界面里------>将修改界面的内容传回表格里
功能实现
(1)修改控件
关于控件,看过我这个专栏之前的博客就知道它刚开始不是这样的

这个界面的控件博主改了至少三四遍,最后定下了这个版本。程序员一定是碰壁最多的行业
还要在这里实现cmobo box控件的显示,优先级提供P1-P7,提醒时间提供三天前、一天前、一小时前、十分钟前
在设计里右键控件,然后添加即可
(2)在代码里添加TableView
cpp
#include <QTableView>
#include <QStandardItemModel>
先包含必要的头文件
cpp
//这个函数的作用是获取并初始化TableView控件
void Task::GettaskShowTableView()
{
//获取界面中的表格对象
QTableView *taskShow = ui->taskShowTableView;
//设置taskShowTableView的表头
model->setColumnCount(6);
model->setHorizontalHeaderLabels({"任务名", "开始时间","结束时间", "优先级", "分类", "提醒时间"});
taskShow->setModel(model);
}
这个代码的作用是设置表头
(3)新建QList成员变量
cpp
QList<QString> shiftTaskList;
在私有成员部分声明QList类成员变量,这个变量的作用是中介
(4)实现新建功能
实现思路:
因为应用按钮的槽函数是包括修改和新建的,所以根本不知道点击应用时处理的是修改还是新建,要设置一个判断变量,调用新建按钮时这个变量赋值为1,调用修改按钮时这个变量赋值为2,然后就可以用if判断一下按下的是哪个按钮进而处理了。
新建功能在保存函数里实现,先通过变量捕获界面的文本框的文本,然后把这些变量存进QString型变量中,再把这些变量存进tableView里
cpp
void Task::on_task_applyButton_clicked()
{
ui->stackedWidget->setCurrentIndex(0);
if(addOrRevise==1)
{
//这几行代码的作用是获取第二个界面输入框里的文本
QString name=ui->taskNameInput->text();
QString startTime=ui->taskdecide_startTimeInput->text();
QString endTime=ui->taskdecide_endTimeInput->text();
QString category=ui->categoryInput->text();
QString priority=ui->priorityChoice->currentText();
QString remindTime=ui->remindTimeChoice->currentText();
//这几行代码的作用是向中介数组添加数据
shiftTaskList.append(name);
shiftTaskList.append(startTime);
shiftTaskList.append(endTime);
shiftTaskList.append(category);
shiftTaskList.append(priority);
shiftTaskList.append(remindTime);
//这几行代码的作用是向tableView里添加一行
int row = model->rowCount();
model->insertRow(row);
for (int col = 0; col < 6; col++)
{
QStandardItem *item = new QStandardItem(shiftTaskList.at(col));
model->setItem(row, col, item);
}
//这句代码的作用是清空这个数组,方便下次使用
shiftTaskList.clear();
}
}
解释见注释,都是固定套库函数。这里只解释一下连接tableView部分:
先获取当前行数,以免把已有的数据给替换掉;然后在要插入的这一行每一列都创造一个模型对象,它会自动调用它写好的析构函数,不需要我们去释放,然后加进去内容就可以了
在这里补充一下:一定要把ui里的内容重新置空,否则下次点开还是那个内容
(5)实现修改功能
实现思路:这个的难度要比新建再大一点,因为它还要先获取选中的索引再传到第二个界面里
实现步骤:先给tableView设置一个行选择模式,然后用库函数选中这一行,把这一行的数据存进QList类的成员变量里,然后在应用按钮的槽函数的另一个判断里把这些数据传进第二个界面里,在第二个界面里改完再取出来,接着放进tableView被选中的那一行
这里函数内容写的地方也很重要,要搞清楚这个功能的运行顺序:先选中对应行,再点修改,再点应用。
所以设置选择模式并获取行号应该放在所有东西的最前边,向第二个界面传数据的操作应该放在修改按钮的槽函数里
修改按钮的槽函数:
cpp
//这个函数的作用是修改已经存在的任务
void Task::on_reviseButton_clicked()
{
//这段代码的作用是将表格中被选中的行的内容依次存进中介数组
if (currentRow != -1)
{
for (int col = 0; col < 6; col++)
{
QModelIndex index = model->index(currentRow, col);
QVariant data = model->data(index);
shiftTaskList.append(data.toString());
}
}
//这段代码的作用是在界面中设置中介数组中的内容
ui->taskNameInput->setText(shiftTaskList[0]);
ui->taskdecide_startTimeInput->setText(shiftTaskList[1]);
ui->taskdecide_endTimeInput->setText(shiftTaskList[2]);
ui->categoryInput->setText(shiftTaskList[3]);
ui->stackedWidget->setCurrentIndex(1);
}
表格的点击槽函数:
cpp
void Task::on_taskShowTableView_clicked(const QModelIndex &index)
{
currentRow = index.row();
addOrRevise = 2;
}
保存函数的修改模式:
cpp
else if(addOrRevise==2)
{
//这段代码的作用是获取当前界面的输入框的内容
QString name=ui->taskNameInput->text();
QString startTime=ui->taskdecide_startTimeInput->text();
QString endTime=ui->taskdecide_endTimeInput->text();
QString category=ui->categoryInput->text();
QString priority=ui->priorityChoice->currentText();
QString remindTime=ui->remindTimeChoice->currentText();
//这段代码的作用是把捕获到的内容再存进中介数组中
shiftTaskList.append(name);
shiftTaskList.append(startTime);
shiftTaskList.append(endTime);
shiftTaskList.append(category);
shiftTaskList.append(priority);
shiftTaskList.append(remindTime);
//这个循环的作用是把中介数组的内容传进选中的行中
for (int col = 0; col < 6; col++)
{
QStandardItem *item = new QStandardItem(shiftTaskList.at(col));
model->setItem(currentRow, col, item);
}
//清空中介数组方便下次使用
shiftTaskList.clear();
//将界面置空方便下次使用
ui->taskNameInput->setText("");
ui->taskdecide_startTimeInput->setText("");
ui->taskdecide_endTimeInput->setText("");
ui->categoryInput->setText("");
//将选择模式置空,防止干扰下次使用
addOrRevise=0;
}
注意事项
博主在完成这个项目的过程中其实并没有博客体现的这么顺利,不说碰的壁了,就说面对的bug吧
这个选择模式变量在使用完一定要置空,要不然后续可能会产生干扰;中介数组、第二个界面同理
修改的逻辑顺序要搞清楚,博主最开始全部写在了第二个界面的应用按钮,那怎么能行呢?对吧,因为要在第二个界面展示前就把文本框内容设置好
其他的可能出错的倒是没了,这个思路搞清楚还是很容易实现的
还要注意:做到现在并没有实现存储,也就是说每次打开项目先前新建的任务都会消失
因为存储这部分不是博主做,博主还没学数据库,是带博主的学长负责服务器的数据库存储
写在最后
这个项目单单是这个环节,我就卡了一个星期。因为我很多东西都不知道,后来就走了弯路,走了弯路发现实现不了,然后就去找其他方法。
最后这个版本可能不是最好的实现方式,但是这已经是我寻求各方资料+自己沉淀的结果了,就目前我的水平来说,这个已经是巅峰
尽管这个项目只做了冰山一角,但是我的成长已经非常大了。可以说这是博主正经做的第一个项目,虽然它规模不大,技术栈也很窄,但是我确确实实从里边学到了很多。
计算机就是三分学七分练,我现在已经深刻地理解了这句话。今天下午博主做了五个小时,晚上做了两个半小时,最终做到了这一步,实现了我认可的"从无到有"。现在回头看,这个实现其实并不困难,很好理解,但是博主是从什么都不知道到独立实现这个功能的。我真正实现了从无到有的跨越,心中的高兴是难以言表的。
同时博主对Qt的理解和熟练度也提升了很多,这是光学不练永远做不到的,今后博主也会积极做项目,项目做的越多,技术才能越来越强