qstandarditemmodel 是标准的以项数据为单位的基于m/v模型的一种标准数据管理方式,model/view 是qt中的一种数据编排结构,其中model代表模型,view代表视图,视图是显示和编辑数据的界面组件,而模型则是视图与原始数据之间的接口,通常该类结构都是用在数据库中较多,例如模型结构负责读取或写入数据库,视图结构则负责展示数据,其条理清晰,编写代码便于维护。
qstandarditemmodel组件通常会配合tableview
组件一起使用,当数据库或文本中的记录发生变化时会自动同步到组件中,首先绘制ui界面。
其次绑定顶部toolbar
菜单,分别对菜单增加对应的功能属性的描述等。
初始化构造函数: 当程序运行时,我们需要对页面中的控件逐一初始化,并将table表格与模型通过调用ui->tableview->setmodel(model)
进行绑定。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <qlabel>
#include <qstandarditem>
#include <qitemselectionmodel>
#include <qfiledialog>
#include <qtextstream>
#include <qlist>
// 默认构造函数
// https://www.cnblogs.com/lyshark
mainwindow::mainwindow(qwidget *parent): qmainwindow(parent), ui(new ui::mainwindow)
{
ui->setupui(this);
// 初始化部分
model = new qstandarditemmodel(3,fixedcolumncount,this); // 数据模型初始化
selection = new qitemselectionmodel(model); // item选择模型
// 为tableview设置数据模型
ui->tableview->setmodel(model); // 设置数据模型
ui->tableview->setselectionmodel(selection); // 设置选择模型
// 默认禁用所有action选项,只保留打开
ui->actionsave->setenabled(false);
ui->actionview->setenabled(false);
ui->actionappend->setenabled(false);
ui->actiondelete->setenabled(false);
ui->actioninsert->setenabled(false);
// 创建状态栏组件,主要来显示单元格位置
labcurfile = new qlabel("当前文件:",this);
labcurfile->setminimumwidth(200);
labcellpos = new qlabel("当前单元格:",this);
labcellpos->setminimumwidth(180);
labcellpos->setalignment(qt::alignhcenter);
labcelltext = new qlabel("单元格内容:",this);
labcelltext->setminimumwidth(150);
ui->statusbar->addwidget(labcurfile);
ui->statusbar->addwidget(labcellpos);
ui->statusbar->addwidget(labcelltext);
//选择当前单元格变化时的信号与槽
connect(selection,signal(currentchanged(qmodelindex,qmodelindex)),this,slot(on_currentchanged(qmodelindex,qmodelindex)));
}
mainwindow::~mainwindow()
{
delete ui;
}
初始化时同时需要绑定一个on_currentchanged(qmodelindex,qmodelindex)
信号,当用户选中指定单元格时相应用户。
// 选择单元格变化时的响应,通过在构造函数中绑定信号和槽函数实现触发
// https://www.cnblogs.com/lyshark
void mainwindow::on_currentchanged(const qmodelindex ¤t, const qmodelindex &previous)
{
q_unused(previous);
if (current.isvalid()) //当前模型索引有效
{
labcellpos->settext(qstring::asprintf("当前单元格:%d行,%d列",current.row(),current.column())); //显示模型索引的行和列号
qstandarditem *aitem;
aitem=model->itemfromindex(current); //从模型索引获得item
this->labcelltext->settext("单元格内容:"+aitem->text()); //显示item的文字内容
}
}
当页面被初始化时,默认界面如下:
打开并填充组件: 当工具栏中打开文件被点击后则触发,打开文件时通过afile.open
打开,循环读入文件,并将文件中的内容逐行追加到qstringlist ffilecontent
中,当追加完毕后,直接调用inimodelfromstringlist(ffilecontent);
完成对页面tableview组件的初始化,并设置其他控件状态为可点击。
void mainwindow::on_actionopen_triggered()
{
qstring curpath=qcoreapplication::applicationdirpath(); // 获取应用程序的路径
// 调用打开文件对话框打开一个文件
// https://www.cnblogs.com/lyshark
qstring afilename=qfiledialog::getopenfilename(this,"打开一个文件",curpath,"数据文件(*.txt);;所有文件(*.*)");
if (afilename.isempty())
{
return; // 如果未选择文件则退出
}
qstringlist ffilecontent; // 文件内容字符串列表
qfile afile(afilename); // 以文件方式读出
if (afile.open(qiodevice::readonly | qiodevice::text)) // 以只读文本方式打开文件
{
qtextstream astream(&afile); // 用文本流读取文件
ui->plaintextedit->clear(); // 清空列表
// 循环读取只要不为空
while (!astream.atend())
{
qstring str=astream.readline(); // 读取文件的一行
ui->plaintextedit->appendplaintext(str); // 添加到文本框显示
ffilecontent.append(str); // 添加到stringlist
}
afile.close(); // 关闭文件
inimodelfromstringlist(ffilecontent); // 从stringlist的内容初始化数据模型
}
// 打开文件完成后,就可以将action全部开启了
ui->actionsave->setenabled(true);
ui->actionview->setenabled(true);
ui->actionappend->setenabled(true);
ui->actiondelete->setenabled(true);
ui->actioninsert->setenabled(true);
// 打开文件成功后,设置状态栏当前文件列
this->labcurfile->settext("当前文件:"+afilename);//状态栏显示
}
如上inimodelfromstringlist(ffilecontent);
函数是后期增加的,我们需要自己实现,该函数的作用是从传入的stringlist
中获取数据,并将数据初始化到tableview
模型中,实现代码如下。
void mainwindow::inimodelfromstringlist(qstringlist& afilecontent)
{
int rowcnt=afilecontent.count(); // 文本行数,第1行是标题
model->setrowcount(rowcnt-1); // 实际数据行数,要在标题上减去1
// 设置表头
qstring header=afilecontent.at(0); // 第1行是表头
// 一个或多个空格、tab等分隔符隔开的字符串、分解为一个stringlist
// https://www.cnblogs.com/lyshark
qstringlist headerlist=header.split(qregexp("\\s+"),qstring::skipemptyparts);
model->sethorizontalheaderlabels(headerlist); // 设置表头文字
// 设置表格中的数据
int x = 0,y = 0;
qstandarditem *item;
// 有多少列数据就循环多少次
// https://www.cnblogs.com/lyshark
for(x=1; x < rowcnt; x++)
{
qstring linetext = afilecontent.at(x); // 获取数据区的一行
// 一个或多个空格、tab等分隔符隔开的字符串、分解为一个stringlist
qstringlist tmplist=linetext.split(qregexp("\\s+"),qstring::skipemptyparts);
// 循环列数,也就是循环fixedcolumncount,其中tmplist中的内容也是.
for(y=0; y < fixedcolumncount-1; y++)
{
item = new qstandarditem(tmplist.at(y)); // 创建item
model->setitem(x-1,y,item); // 为模型的某个行列位置设置item
}
// 最后一个数据需要取出来判断,并单独设置状态
item=new qstandarditem(headerlist.at(y)); // 最后一列是checkable,需要设置
item->setcheckable(true); // 设置为checkable
// 判断最后一个数值是否为0
if (tmplist.at(y) == "0")
item->setcheckstate(qt::unchecked); // 根据数据设置check状态
else
item->setcheckstate(qt::checked);
model->setitem(x-1,y,item); //为模型的某个行列位置设置item
}
}
初始化组件后效果如下:
实现添加一行数据: 为tableview添加一行数据,在文件末尾插入。
void mainwindow::on_actionappend_triggered()
{
qlist<qstandarditem *> itemlist; // 创建临时容器
qstandarditem *item;
// 模拟添加一列的数据
for(int x=0; x<fixedcolumncount-1; x++)
{
item = new qstandarditem("测试(追加行)"); // 循环创建每一列
itemlist << item; // 添加到链表中
}
// 创建最后一个列元素,由于是选择框所以需要单独创建
// https://www.cnblogs.com/lyshark
// 1.获取到最后一列的表头下标,最后下标为6
qstring str = model->headerdata(model->columncount()-1,qt::horizontal,qt::displayrole).tostring();
item=new qstandarditem(str); // 创建 "是否合格" 字段
item->setcheckable(true); // 设置状态为真
itemlist << item; // 最后一个选项追加进去
model->insertrow(model->rowcount(),itemlist); // 插入一行,需要每个cell的item
qmodelindex curindex=model->index(model->rowcount()-1,0); // 创建最后一行的modelindex
selection->clearselection(); // 清空当前选中项
selection->setcurrentindex(curindex,qitemselectionmodel::select); // 设置当前选中项为当前选择行
}
插入代码演示效果:
实现插入一行数据: 为tableview插入一行数据(在文件任意位置插入数据)
// https://www.cnblogs.com/lyshark
void mainwindow::on_actioninsert_triggered()
{
qlist<qstandarditem*> itemlist; // qstandarditem的列表类
qstandarditem *item;
// 模拟插入前五列数据
for(int i=0;i<fixedcolumncount-1;i++)
{
item= new qstandarditem("测试(插入行)"); // 新建一个qstandarditem
itemlist << item; // 添加到列表类
}
qstring str; // 获取表头文字
str=model->headerdata(model->columncount()-1,qt::horizontal,qt::displayrole).tostring();
item=new qstandarditem(str); // 创建item
item->setcheckable(true); // 设置为可使用checkbox
itemlist<<item; // 添加到列表类
qmodelindex curindex=selection->currentindex(); // 获取当前选中项的索引
model->insertrow(curindex.row(),itemlist); // 在当前行的前面插入一行
selection->clearselection(); // 清除当前选中项
selection->setcurrentindex(curindex,qitemselectionmodel::select); // 设置当前选中项为当前选择行
}
插入代码演示效果:
实现删除一行数据: 删除数据之前需要通过selection->currentindex()
确定当前选中行,并通过model->removerow()
移除即可。
// https://www.cnblogs.com/lyshark
void mainwindow::on_actiondelete_triggered()
{
qmodelindex curindex = selection->currentindex(); // 获取当前选择单元格的模型索引
// 先判断是不是最后一行
if (curindex.row()==model->rowcount()-1)
{
model->removerow(curindex.row()); //删除最后一行
}
else
{
model->removerow(curindex.row());//删除一行,并重新设置当前选择行
selection->setcurrentindex(curindex,qitemselectionmodel::select);
}
}
删除代码效果演示:
实现字体数据对齐: 表格中的字体可以实现多种对其方式,对齐方式分为 居中对齐,左对齐,右对齐 三种。
// 设置表格居中对齐
void mainwindow::on_pushbutton_clicked()
{
if (!selection->hasselection())
return;
qmodelindexlist selectedindex=selection->selectedindexes();
qmodelindex index;
qstandarditem *item;
for (int i=0; i<selectedindex.count(); i++)
{
index=selectedindex.at(i);
item=model->itemfromindex(index);
item->settextalignment(qt::alignhcenter);
}
}
// 设置表格左对齐
// https://www.cnblogs.com/lyshark
void mainwindow::on_pushbutton_2_clicked()
{
if (!selection->hasselection()) //没有选择的项
return;
//获取选择的单元格的模型索引列表,可以是多选
qmodelindexlist selectedindex=selection->selectedindexes();
for (int i=0;i<selectedindex.count();i++)
{
qmodelindex aindex=selectedindex.at(i); //获取其中的一个模型索引
qstandarditem* aitem=model->itemfromindex(aindex);//获取一个单元格的项数据对象
aitem->settextalignment(qt::alignleft);//设置文字对齐方式
}
}
// 设置表格右对齐
void mainwindow::on_pushbutton_3_clicked()
{
if (!selection->hasselection())
return;
qmodelindexlist selectedindex=selection->selectedindexes();
qmodelindex aindex;
qstandarditem *aitem;
for (int i=0;i<selectedindex.count();i++)
{
aindex=selectedindex.at(i);
aitem=model->itemfromindex(aindex);
aitem->settextalignment(qt::alignright);
}
}
对齐代码效果演示:
实现字体数据加粗: 将选中行的字体进行加粗显示。
// 设置字体加粗显示
// https://www.cnblogs.com/lyshark
void mainwindow::on_pushbutton_4_clicked()
{
if (!selection->hasselection())
return;
//获取选择单元格的模型索引列表
qmodelindexlist selectedindex=selection->selectedindexes();
for (int i=0;i<selectedindex.count();i++)
{
qmodelindex aindex=selectedindex.at(i); //获取一个模型索引
qstandarditem* aitem=model->itemfromindex(aindex);//获取项数据
qfont font=aitem->font(); //获取字体
font.setbold(true); //设置字体是否粗体
aitem->setfont(font); //重新设置字体
}
}
加粗代码效果演示:
实现保存文件: 当保存文件被点击后触发,通过便利tablewidget模型组件中的数据,并将数据通过astream << str << "\n";
写出到记事本中。
// https://www.cnblogs.com/lyshark
// 【保存文件】
void mainwindow::on_actionsave_triggered()
{
qstring curpath=qcoreapplication::applicationdirpath(); // 获取应用程序的路径
// 调用打开文件对话框选择一个文件
qstring afilename=qfiledialog::getsavefilename(this,tr("选择一个文件"),curpath,"数据文件(*.txt);;所有文件(*.*)");
if (afilename.isempty()) // 未选择文件则直接退出
return;
qfile afile(afilename);
// 以读写、覆盖原有内容方式打开文件
if (!(afile.open(qiodevice::readwrite | qiodevice::text | qiodevice::truncate)))
return;
qtextstream astream(&afile); // 用文本流读取文件
qstandarditem *item;
qstring str;
int x = 0,y = 0;
ui->plaintextedit->clear();
// 获取表头文字
for (x=0; x<model->columncount(); x++)
{
item=model->horizontalheaderitem(x); // 获取表头的项数据
str= str + item->text() + "\t\t"; // 以tab制表符隔开
}
astream << str << "\n"; // 文件里需要加入换行符\n
ui->plaintextedit->appendplaintext(str);
// 获取数据区文字
for ( x=0; x < model->rowcount(); x++)
{
str = "";
for( y=0; y < model->columncount()-1; y++)
{
item=model->item(x,y);
str=str + item->text() + qstring::asprintf("\t\t");
}
// 对最后一列需要转换一下,如果判断为选中则写1否则写0
item=model->item(x,y);
if (item->checkstate()==qt::checked)
str= str + "1";
else
str= str + "0";
ui->plaintextedit->appendplaintext(str);
astream << str << "\n";
}
}
// 【导出txt文件】:将tableview中的数据导出到plaintextedit显示
void mainwindow::on_actionview_triggered()
{
ui->plaintextedit->clear();
qstandarditem *item;
qstring str;
//获取表头文字
int x=0,y=0;
for (x=0; x<model->columncount(); x++)
{ //
item=model->horizontalheaderitem(x);
str= str + item->text() + "\t";
}
ui->plaintextedit->appendplaintext(str);
//获取数据区的每行
for (x=0; x<model->rowcount(); x++)
{
str="";
for(y=0; y<model->columncount()-1; y++)
{
item=model->item(x,y);
str= str + item->text() + qstring::asprintf("\t");
}
item=model->item(x,y);
if (item->checkstate()==qt::checked)
str= str + "1";
else
str= str + "0";
ui->plaintextedit->appendplaintext(str);
}
}
文件保存后如下: