QStandardItemModel


程序样例

QStandardItemModel 是标准的,基于项数据的数据模型。适合处理表格型的数据。下面,是样例程序:

  • sample53.png
  • sample53.png

分段代码解读

初始化方法

由于初始化的对象,是类二维数组,所以用了两层 for 循环

在数据准备部分,iniModelFromStringList 函数接收一个 QStringList& aFileContent 传参,这个参数中的内容,是规范化的表格数据。

首先,用 rowCntaFileContent 的个数计量(其实,就是相等于计行)。对于已定义过的 theModel,做行数定义时,传入 rowCnt

QString header=aFileContent.at(0) ,将 QStringList 类型的 aFileContent 首个元素(QString类型)的作为表头,但是这个表头是有问题的,因为这个数据是原始数据,是这样的:测深(m) 垂深(m) 方位(°) 总位移(m) 固井质量 测井取样

所以,下面 QStringList headerList = header.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts); 对拿到的QString类型元素切割,做成 QStringList 类型。

这样,就可以放心传入水平行了:theModel->setHorizontalHeaderLabels(headerList);

第一行结束,下面的部分,就是单元格设置了。

theModeQStandardItemModel 实例化的对象,它的基础数据类型是 QStandardItem,我们就先做一个指针,指向这个基础项,并做一个临时的 QStringList 对象,用来对接其余行切割后的数据类型

下面,进入双层 for 循环,首先,i行计量值 rowCnt 限制,而 j 被预定义的 列计量值 FixedColumnCount 限制。

单次循环时,取出一行值 QString aLineText=aFileContent.at(i),切割好后,用 QStringList tmpList 接收。

tmpList=aLineText.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts);

进入内层循环,每次刷 j 的节点时,实例化一个 aItem 来接收 QStringList tmpList 的元素值,并写入到数据模型的项数据中。

aItem=new QStandardItem(tmpList.at(j));
theModel->setItem(i-1,j,aItem);//为指定行(项)的特定列来设置值

指定行的每个列格子都设好值时,跳出双层循环。此时,由于在限制 j 时,用了 FixedColumnCount-1 ,其实,还有 最后一列 没做。

如上所述,最后一列,最终要转化为复选框。

aItem = new QStandardItem(headerList.at(j)); aItem 是个项,它的值是 headerList.at(j),内容是 01

这个的状态,调整为可选中aItem->setCheckable(true);//设置 Checkable

最后,再调用数据模型的 void QStandardItemModel::setItem(int row, QStandardItem *item) 方法,将这个调好的项,丢给任意 i行 的最后一个格子里。

void myMainWindow::iniModelFromStringList(QStringList& aFileContent){
    //从一个 StringList 获取数据,初始化数据模型
    int rowCnt=aFileContent.count();
    theModel->setRowCount(rowCnt-1);
    //设置表头,一个或多个空格、TAB等分隔符隔开的字符串,分解为一个 StringList
    QString header=aFileContent.at(0);//第1行是表头
    QStringList headerList = header.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts);
    theModel->setHorizontalHeaderLabels(headerList);//设置表头文字
    //设置表格数据
    QStandardItem *aItem;
    int j;
    for(int i=1;i<rowCnt;i++)
    {
        QString aLineText=aFileContent.at(i);
        QStringList tmpList=aLineText.split(QRegularExpression("\\s+"),Qt::SkipEmptyParts);
        for (j=0;j<FixedColumnCount-1;j++)
        {
            //不包含最后一列
            aItem=new QStandardItem(tmpList.at(j));
            theModel->setItem(i-1,j,aItem);//为模型的某个行列位置设置
        }
        aItem=new QStandardItem(headerList.at(j));//最后一列
        aItem->setCheckable(true);//设置为 Checkable
        if(tmpList.at(j)=="0")
            aItem->setCheckState(Qt::Unchecked);
        else
            aItem->setCheckState(Qt::Checked);
        theModel->setItem(i-1,j,aItem);
    }

};

选中事件的响应函数

下面,解读另一个程序块儿,首先,我们这个槽函数是对 选中模型(#include <QItemSelectionModel>) 的信号对接。

我们来看这个 SiGNAL:

void currentChanged(const QModelIndex &current, const QModelIndex &previous)

所以,设计对应的 SLOT 时,要保证 传参类型相同 ,这是基本要求。

用不到的参数,给声明一下 Q_UNUSED(previous),然后,设计函数功能:

bool QModelIndex::isValid() const ,返回一个布尔值,判断指针是否有效。指针有效,意味着焦点事件已捕捉。

这样,我们就可以在状态栏上,使用标签显示各种状态了。

在这里,由于已经捕获了光标焦点,actFontBold 就应该复位一下,准备给当前项上刑。

void myMainWindow::currentChangedsig(const QModelIndex &current, const QModelIndex &previous){


    Q_UNUSED(previous);

    if (current.isValid()) //当前模型索引有效
    {
        LabCellPos->setText(QString::asprintf("当前单元格:%d行,%d列",
                                              current.row(),current.column())); //显示模型索引的行和列号
        QStandardItem   *aItem;
        aItem=theModel->itemFromIndex(current); //从模型索引获得Item
        this->LabCellText->setText("单元格内容:"+aItem->text()); //显示item的文字内容

        QFont   font=aItem->font(); //获取item的字体
        ui->actFontBold->setChecked(font.bold()); //更新actFontBold的check状态
    }
};

打印数据模型函数

void myMainWindow::on_actModelData_triggered()
{//模型数据导出到PlainTextEdit显示
    ui->plainTextEdit->clear(); //清空
    QStandardItem   *aItem;
    QString str;

//获取表头文字
    int i,j;
    for (i=0;i<theModel->columnCount();i++)
    { //
        aItem=theModel->horizontalHeaderItem(i); //获取表头的一个项数据
        str=str+aItem->text()+"\t"; //用TAB间隔文字
    }
    ui->plainTextEdit->appendPlainText(str); //添加为文本框的一行

//获取数据区的每行
    for (i=0;i<theModel->rowCount();i++)
    {
        str="";
        for(j=0;j<theModel->columnCount()-1;j++)
        {
            aItem=theModel->item(i,j);
            str=str+aItem->text()+QString::asprintf("\t"); //以 TAB分隔
        }

        aItem=theModel->item(i,j); //最后一行是逻辑型
        if (aItem->checkState()==Qt::Checked)
            str=str+"1";
        else
            str=str+"0";

         ui->plainTextEdit->appendPlainText(str);
    }
}

另存为新文件函数

void myMainWindow::on_actSave_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   *aItem;
    int i,j;
    QString str;

    ui->plainTextEdit->clear();

//获取表头文字
    for (i=0;i<theModel->columnCount();i++)
    {
        aItem=theModel->horizontalHeaderItem(i); //获取表头的项数据
        str=str+aItem->text()+"\t\t";  //以TAB见隔开
    }
    aStream<<str<<"\n";  //文件里需要加入换行符 \n
    ui->plainTextEdit->appendPlainText(str);

//获取数据区文字
    for ( i=0;i<theModel->rowCount();i++)
    {
        str="";
        for( j=0;j<theModel->columnCount()-1;j++)
        {
            aItem=theModel->item(i,j);
            str=str+aItem->text()+QString::asprintf("\t\t");
        }

        aItem=theModel->item(i,j); //最后一列是逻辑型
        if (aItem->checkState()==Qt::Checked)
            str=str+"1";
        else
            str=str+"0";

         ui->plainTextEdit->appendPlainText(str);
         aStream<<str<<"\n";
    }
}

打开文本文件函数

void myMainWindow::actOpentrigggered(){
    QString curPath=QCoreApplication::applicationDirPath();
    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);
        }
        aFile.close();
        this->LabCurFile->setText("当前文件: "+aFileName);
        iniModelFromStringList(fFileContent);
    }
};

构造函数

myMainWindow::myMainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::myMainWindow)
{
    ui->setupUi(this);
    theModel = new QStandardItemModel(2,FixedColumnCount,this);//数据模型
    theSelection=new QItemSelectionModel(theModel);//选择模型
    connect(theSelection,&QItemSelectionModel::currentChanged,this,&myMainWindow::currentChangedsig);
    ui->tableView->setModel(theModel);//设置数据模型
    ui->tableView->setSelectionModel(theSelection);//设置选择模型
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);//设置为可以选中多个目标
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);//选中的方式为可选单个格子
    //创建状态栏组件
    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(ui->actOpen,&QAction::triggered,this,&myMainWindow::actOpentrigggered);
}

头文件

#include <QMainWindow>
#include    <QLabel>
#include    <QStandardItemModel>
#include    <QItemSelectionModel>
#include    <QFileDialog>
#include    <QTextStream>
#include    <QRegularExpression>
#define     FixedColumnCount    6       //文件固定6列
QT_BEGIN_NAMESPACE
namespace Ui { class myMainWindow; }
QT_END_NAMESPACE

class myMainWindow : public QMainWindow
{
    Q_OBJECT

public:
    myMainWindow(QWidget *parent = nullptr);
    ~myMainWindow();

private:
    Ui::myMainWindow *ui;

private:
//用于状态栏的信息显示
    QLabel  *LabCurFile;  //当前文件
    QLabel  *LabCellPos;    //当前单元格行列号
    QLabel  *LabCellText;   //当前单元格内容
    QStandardItemModel  *theModel;//数据模型
    QItemSelectionModel *theSelection;//Item选择模型
    void iniModelFromStringList(QStringList&);//从StringList初始化数据模型
private slots:
    void currentChangedsig(const QModelIndex &current, const QModelIndex &previous);
    void actOpentrigggered();
    void on_actSave_triggered();//保存文件
    void on_actModelData_triggered();
};

文章作者: 五笔小筑
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 五笔小筑 !
评论