05-2_Qt 5.9 C++开发指南_Model/View结构实例(QFileSystemModel、QStringListModel、QStandardItemModel;编程实例)

2023-11-13

接上篇,本篇主要介绍Model/View框架下的模型类:QFileSystemModel、QStringListModel、QStandardItemModel的使用方法和编程实例。

文章目录

1. QFileSystemModel

1.1 QFileSystemModel类的基本功能

QFileSystemModel提供了一个可用于访问本机文件系统的数据模型。QFileSystemModel 和视图组件QTreeView 结合使用,可以用目录树的形式显示本机上的文件系统,如同 Widnows 的资源管理器一样。使用 QFileSystemModel 提供的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获得文件的详细信息。要通过 QFileSystemModel 获得本机的文件系统,需要用 setRootPath()函数为 QFileSystemModel设置一个根目录,例如:

    QFileSystemModel* model=new QFileSystemModel(this); //QFileSystemModel提供单独线程,推荐使用
    model->setRootPath(QDir::currentPath()); //设置根目录

静态函数QDir::currentPath()获取应用程序的当前路径。

用于获取磁盘文件目录的数据模型类还有一个QDirModel,QDirModel 的功能与 QFileSystemModel类似,也可以获取目录和文件,但是 QFileSystemModel 采用单独的线程获取目录文件结构,而 QDirModel不使用单独的线程。使用单独的线程就不会阻碍主线程,所以推荐使用 QFileSystemModel。
使用QFileSystemModel作为数据模型,QTreeView、QListView 和QTableView 为主要组件设计的实例 samp5 _1运行界面如下图 所示。在 TreeView 中以目录树的形式显示本机的文件系统,单击一个目录时,右边的 ListView 和 TableView 显示该目录下的目录和文件。在 TreeView 上单击个目录或文件节点时,下方的几个标签里显示当前节点的信息。

在这里插入图片描述

1.2 QFileSystemModel的使用

1.2.1 可视化UI设计

实例samp5_1的主窗口是基于 QMainWindow 的,在使用 UI设计器做可视化设计时删除了工具栏和状态栏。主窗口界面布局采用了两个分割条的设计,ListView 和 TableView 采用上下分割布局,然后和左边的 TreeView 采用水平分割布局,水平分割布局再和下方显示信息的 groupBox 在主窗口工作区垂直布局。

在这里插入图片描述

1.2.2 代码解析

主窗口类中定义了一个QFileSystemModel类的成员变量 model。

QFileSystemModel    *model; //定义数据模型变量

主窗口构造函数进行初始化,代码如下:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    model=new QFileSystemModel(this); //QFileSystemModel提供单独线程,推荐使用
    model->setRootPath(QDir::currentPath()); //设置根目录

    ui->treeView->setModel(model); //设置数据模型
    ui->listView->setModel(model); //设置数据模型
    ui->tableView->setModel(model); //设置数据模型

//信号与槽关联,treeView单击时,其目录设置为listView和tableView的根节点
    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
            ui->listView,SLOT(setRootIndex(QModelIndex)));

    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
            ui->tableView,SLOT(setRootIndex(QModelIndex)));
}

3个视图组件都使用 setModel()函数,将 QFileSystemModel 数据模型 model 设置为自己的数据模型。

connect()函数设置信号与槽的关联,实现的功能是:在单击 treeView 的一个节点时,此节点就设置为 listView 和 tableView 的根节点,因为 treeView 的 clicked(QModelIndex)信号会传递一个QModelIndex 变量,是当前节点的模型索引,将此模型索引传递给 listView 和tableView 的槽函数setRootIndex(QModelIndex),listView 和 tableView 就会显示此节点下的目录和文件。

在 treeView 上单击一个节点时,下方的一些标签里会显示节点的一些信息,这是为 treeView的 clicked(const QModelIndex &index)信号编写槽函数实现的,其代码如下:

void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
    ui->chkIsDir->setChecked(model->isDir(index));
    ui->LabPath->setText(model->filePath(index));
    ui->LabType->setText(model->type(index));

    ui->LabFileName->setText(model->fileName(index));

    int sz=model->size(index)/1024;
    if (sz<1024)
        ui->LabFileSize->setText(QString("%1 KB").arg(sz));
    else
        ui->LabFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}

函数有一个传递参数QModelIndex &index,它是单击节点在数据模型中的索引。通过传递来的模型索引index,这段代码使用了 QFileSystemModel 的一些函数来获得节点的一些参数,包括以下几种。

  • bool isDir(QModelIndex &index):判断节点是不是一个目录。
  • String filePath(QModelIndex &index): 返回节点的目录名或带路径的文件名。
  • QString fileName(QModelIndex &index): 返回去除路径的文件夹名称或文件名。
  • QString type(QModelIndex &index): 返回描述节点类型的文字,如硬盘符是“Drive”,文件夹是“File Folder”,文件则用具体的后缀描述,如“txt File”“exe File”“pdfFile”等。
  • qint64 size(QModelIndex &index): 如果节点是文件,返回文件大小的字节数:如果节点是文件夹,返回0。

而QFileSystemModel 是如何获取磁盘目录文件结构的,3 个视图组件是如何显示这些数据的则是其底层实现的问题了。

1.2.3 源码

(1)mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include    <QFileSystemModel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:
    QFileSystemModel    *model; //定义数据模型变量

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_treeView_clicked(const QModelIndex &index);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

(2)mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    model=new QFileSystemModel(this); //QFileSystemModel提供单独线程,推荐使用
    model->setRootPath(QDir::currentPath()); //设置根目录

    ui->treeView->setModel(model); //设置数据模型
    ui->listView->setModel(model); //设置数据模型
    ui->tableView->setModel(model); //设置数据模型

//信号与槽关联,treeView单击时,其目录设置为listView和tableView的根节点
    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
            ui->listView,SLOT(setRootIndex(QModelIndex)));

    connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
            ui->tableView,SLOT(setRootIndex(QModelIndex)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
    ui->chkIsDir->setChecked(model->isDir(index));
    ui->LabPath->setText(model->filePath(index));
    ui->LabType->setText(model->type(index));

    ui->LabFileName->setText(model->fileName(index));

    int sz=model->size(index)/1024;
    if (sz<1024)
        ui->LabFileSize->setText(QString("%1 KB").arg(sz));
    else
        ui->LabFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}

2. QStringListModel

2.1 QStringListModel功能概述

QStringListModel 用于处理字符串列表的数据模型,它可以作为 QListView 的数据模型,在界面上显示和编辑字符串列表。

QStringListModel 的 setStringList()函数可以初始化数据模型的字符串列表的内容,stringList()函数返回数据模型内的字符串列表,在关联的 ListView 组件里编辑修改数据后,数据都会及时更新到数据模型内的字符串列表里。

QStringListModel 提供编辑和修改字符串列表数据的函数,如insertRows()、removeRows()、setData()等,这些操作直接影响数据模型内部的字符串列表,并且修改后的数据会自动在关联的 ListView 组件里刷新显示。

实例 samp5_2采用QStringListModel 作为数据模型,OListView 组件作为视图组件演示了QStringListModel 和 QListView 构成Model/View 结构编辑字符串列表的功能,程序运行时界面如下图所示。

在这里插入图片描述

窗口左侧是对QStringListModel 的一些操作,右侧的 QPlaintextEdit 组件显示 QStringListModel:: stringList()的内容,以查看其是否与界面上 ListView 组件显示的内容一致。

2.2 QStringListModel的使用

2.2.1 可视化UI设计

实例 samp5_2的窗口是从QWidget 继承而来的类 Widget,界面采用可视化设计。

在这里插入图片描述

2.2.2 Model/View 结构对象和组件初始化

在Widget类中定义一个QStringListModel类的变量

QStringListModel   *theModel; //数据模型

在 Widget 类的构造函数中进行变量的创建,完成数据模型与界面视图组件的关联,下面是Widget 类构造函数代码

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QStringList         theStrList; //保存初始 StringList
    theStrList<<"北京"<<"上海"<<"天津"<<"河北"<<"山东"<<"四川"<<"重庆"<<"广东"<<"河南"; //初始化 StringList

    theModel=new QStringListModel(this); //创建数据模型
    theModel->setStringList(theStrList); //为模型设置StringList,会导入StringList的内容
    ui->listView->setModel(theModel); //为listView设置数据模型

    ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
}

QStringListModel的setStringList()函数将一个字符串列表的内容作为数据模型的初始数据内容。
QListView的 setModel()函数为界面视图组件设置一个数据模型。
程序运行后,界面上 ListView 组件里就会显示初始化的字符串列表的内容。

2.2.3 编辑、添加、删除项的操作

  • 编辑项

    QListView::setEditTriggers()函数设置 QListView 的条目是否可以编辑,以及如何进入编辑状态,函数的参数是QAbstractItemView::EditTrigger 枚举类型值的组合。构造函数中设置为:

     ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
    

    表示在双击,或选择并单击列表项后,就进入编辑状态。

    若要设置为不可编辑,则可以设置为:

    ui->listView->setEditTriggers(QAbstractItemView:: NoEditTriggers);
    
  • 添加项

    添加项是要在列表的最后添加一行,界面上“添加项”按钮的槽函数代码如下:

    void Widget::on_btnListAppend_clicked()
    { //添加一行
        theModel->insertRow(theModel->rowCount()); //在尾部插入一空行
        QModelIndex index=theModel->index(theModel->rowCount()-1,0);//获取最后一行
        theModel->setData(index,"new item",Qt::DisplayRole);//设置显示文字
        ui->listView->setCurrentIndex(index); //设置当前选中的行
    }
    

    对数据的操作都是针对数据模型的,所以,插入一行使用的是 QStringListModel 的 insertRow(int row)函数,其中row 是一个行号,表示在row 行之前插入一行。要在列表的最后插入一行,参数row 设置为列表当前的行数即可。
    这样只是在列表尾部添加一个空行,没有任何文字。为了给添加的项设置一个缺省的文字标首先要获得新增项的模型索引,即:

    QModelIndex index=theModel->index(theModel->rowCount()-1,0);//获取最后一行
    

    QStringListModel 的 index()函数根据传递的行号、列号、父项的模型索引生成一个模型索引,这行代码为新增的最后一个项生成一个模型索引 index。

    为新增的项设置一个文字标题“new item”,使用 setData()函数,并用到前面生成的模型索引index。代码如下:

    theModel->setData(index,"new item",Qt::DisplayRole);//设置显示文字
    

    在使用 setData()函数时,必须指定设置数据的角色,这里的角色是 Qt::DisplayRole,它是用于显示的角色,即项的文字标题。

  • 插入项

    “插入项”按钮的功能是在列表的当前行前面插入一行,其实现代码如下:

    void Widget::on_btnListInsert_clicked()
    {//插入一行
        QModelIndex  index;
        index=ui->listView->currentIndex(); //当前 modelIndex
        theModel->insertRow(index.row()); //在当前行的前面插入一行
        theModel->setData(index,"inserted item",Qt::DisplayRole); //设置显示文字
        theModel->setData(index,Qt::AlignRight,Qt::TextAlignmentRole); //设置对齐方式,不起作用
        ui->listView->setCurrentIndex(index); //设置当前选中的行
    }
    

    QListView::currentIndex()获得当前项的模型索引index,index.row()则返回这个模型索引的行号。

  • 删除当前项

    使用QStringListModel 的 removeRow()函数删除某一行的代码如下:

    void Widget::on_btnListDelete_clicked()
    {//删除当前行
        QModelIndex  index;
        index=ui->listView->currentIndex(); //获取当前 modelIndex
        theModel->removeRow(index.row()); //删除当前行
    }
    
  • 删除列表

    删除列表的所有项可使用 QStringListModel 的removeRows(int row,int count)函数,它表示从行号row 开始删除 count 行。代码如下:

    void Widget::on_btnListClear_clicked()
    {//清除ListView的所有项
        theModel->removeRows(0,theModel->rowCount());
    }
    

2.2.4 以文本显示数据模型的内容

以上在对界面上 ListView 的项进行编辑时,实际操作的都是其关联的数据模型 theModel,在对数据模型进行插入、添加、删除项操作后,内容立即在 ListView 上显示出来,这是数据模型与视图组件之间信号与槽的作用,当数据模型的内容发生改变时,通知视图组件更新显示。

同样的,当在 ListView 上双击一行进入编辑状态,修改一个项的文字内容后,这部分内容也保存到数据模型里了,这就是上篇原理图所表示的过程。

那么,数据模型内部应该保存有最新的数据内容,对于 QStringListModel 模型来说,通过stringList()函数可以得到其最新的数据副本。界面上的“显示数据模型的 StringList”按钮获取数据模型的 stringList,并用多行文本的形式显示其内容,以检验对数据模型修改数据,特别是在界面上修改列表项的文字后,其内部的数据是否同步更新了。

以下是界面上的“显示数据模型的 StringList”按钮的 clicked()信号的槽函数代码,它通过数据模型的stringList()函数获取字符串列表,并在 plainTextEdit 里逐行显示:

void Widget::on_btnTextImport_clicked()
{// 显示数据模型的StringList
    QStringList tmpList;
    tmpList=theModel->stringList();//获取数据模型的StringList

    ui->plainTextEdit->clear(); //文本框清空
    for (int i=0; i<tmpList.count();i++)
        ui->plainTextEdit->appendPlainText(tmpList.at(i)); //显示数据模型的StringList()返回的内容
}

程序运行时,无论对 ListView 的列表做了什么编辑和修改,单击“显示数据模型的 StringList"按钮,在文本框里显示的文字内容与 ListView 里总是完全相同的,说明数据模型的数据与界面上显示的内容是同步的。

2.2.5 其他功能

QListView 的 clicked()信号会传递一个 QModelIndex 类型的参数,利用该参数,可以显示当前项的模型索引的行和列的信息,实现代码如下:

void Widget::on_listView_clicked(const QModelIndex &index)
{ //显示QModelIndex的行、列号
    ui->LabInfo->setText(QString::asprintf("当前项:row=%d, column=%d",
                        index.row(),index.column()));
}

2.2.6 整体源码

(1) widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include    <QWidget>
#include    <QStringListModel>


namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

private:
    QStringListModel   *theModel; //数据模型

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_listView_clicked(const QModelIndex &index); //单击事件,显示QModelIndex的内容

    void on_btnIniList_clicked(); //初始化列表

    void on_btnTextClear_clicked();//清除 PlainText 的内容

    void on_btnTextImport_clicked();//ListView 的内容导入到PlainText显示

    void on_btnListClear_clicked(); //清除 ListView

    void on_btnListAppend_clicked(); //添加一行

    void on_btnListInsert_clicked(); //插入一行

    void on_btnListDelete_clicked(); //删除当前行

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

(2) widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QStringList         theStrList; //保存初始 StringList
    theStrList<<"北京"<<"上海"<<"天津"<<"河北"<<"山东"<<"四川"<<"重庆"<<"广东"<<"河南"; //初始化 StringList

    theModel=new QStringListModel(this); //创建数据模型
    theModel->setStringList(theStrList); //为模型设置StringList,会导入StringList的内容
    ui->listView->setModel(theModel); //为listView设置数据模型

    ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_listView_clicked(const QModelIndex &index)
{ //显示QModelIndex的行、列号
    ui->LabInfo->setText(QString::asprintf("当前项:row=%d, column=%d",
                        index.row(),index.column()));
}

void Widget::on_btnIniList_clicked()
{ //重新载入theStrList的内容,初始化theModel的内容
    QStringList         theStrList; //保存初始 StringList
    theStrList<<"北京"<<"上海"<<"天津"<<"河北"<<"山东"<<"四川"<<"重庆"<<"广东"<<"河南"; //初始化 StringList
    theModel->setStringList(theStrList);
}

void Widget::on_btnTextClear_clicked()
{ //清除plainTextEdit的文本
    ui->plainTextEdit->clear();
}

void Widget::on_btnTextImport_clicked()
{// 显示数据模型的StringList
    QStringList tmpList;
    tmpList=theModel->stringList();//获取数据模型的StringList

    ui->plainTextEdit->clear(); //文本框清空
    for (int i=0; i<tmpList.count();i++)
        ui->plainTextEdit->appendPlainText(tmpList.at(i)); //显示数据模型的StringList()返回的内容
}

void Widget::on_btnListClear_clicked()
{//清除ListView的所有项
    theModel->removeRows(0,theModel->rowCount());
}

void Widget::on_btnListAppend_clicked()
{ //添加一行
    theModel->insertRow(theModel->rowCount()); //在尾部插入一空行
    QModelIndex index=theModel->index(theModel->rowCount()-1,0);//获取最后一行
    theModel->setData(index,"new item",Qt::DisplayRole);//设置显示文字
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

void Widget::on_btnListInsert_clicked()
{//插入一行
    QModelIndex  index;
    index=ui->listView->currentIndex(); //当前 modelIndex
    theModel->insertRow(index.row()); //在当前行的前面插入一行
    theModel->setData(index,"inserted item",Qt::DisplayRole); //设置显示文字
    theModel->setData(index,Qt::AlignRight,Qt::TextAlignmentRole); //设置对齐方式,不起作用
    ui->listView->setCurrentIndex(index); //设置当前选中的行
}

void Widget::on_btnListDelete_clicked()
{//删除当前行
    QModelIndex  index;
    index=ui->listView->currentIndex(); //获取当前 modelIndex
    theModel->removeRow(index.row()); //删除当前行
}

在这个实例中,通过 QStringListModel 和 QListView 说明了数据模型与视图组件之间构成Model/View 结构的基本原理。

第4章的实例 samp4_7 中采用 QListWidget 设计了一个列表编辑器,对比这两个实例,可以发现如下两点。

  • 在 Model/View 结构中,数据模型与视图组件是分离的,可以直接操作数据模型以修改数据,在视图组件中做的修改也会自动保存到数据模型里。

  • 在使用 QListWidget 的例子中,每个列表项是一个QListWidgetItem 类型的变量,保存了项的各种数据,数据和显示界面是一体的,对数据的修改操作就是对项关联的变量的修改。

所以,这是 Model/View 结构与便利组件之间的主要区别。

3. QStandardItemModel

3.1 QStandardItemModel功能概述

OStandardItemModel 是标准的以项数据 (item data) 为基础的标准数据模型类,通常与QTableView 组合成Model/View 结构,实现通用的二维数据的管理功能

本节介绍QStandardItemModel的使用,主要用到以下3个类

  • QStandardItemModel: 基于项数据的标准数据模型,可以处理二维数据。维护一个二维的项数据数组,每个项是一个QStandardItem 类的变量,用于存储项的数据、字体格式、对齐方式等。

  • QTableView:二维数据表视图组件,有多个行和多个列,每个基本显示单元是一个单元格,通过 setModel()函数设置一个 QStandardItemModel 类的数据模型之后,一个单元格显示QStandardItemModel 数据模型中的一个项。

  • QItemSelectionModel:一个用于跟踪视图组件的单元格选择状态的类,当在 QTableView选择某个单元格,或多个单元格时,通过 QItemSelectionModel 可以获得选中的单元格的模型索引,为单元格的选择操作提供方便。

这几个类之间的关系是:QTableView 是界面视图组件,其关联的数据模型是 QStandardItemModel,关联的项选择模型是 QItemSelectionModel,QStandardItemModel 的数据管理的基本单元是OStandardItem 。

实例samp5_3 演示QStandardItemModel 的使用,其运行时界面下图 所示
在这里插入图片描述

该实例具有如下功能。

  • 打开一个纯文本文件,该文件是规则的二维数据文件,通过字符串处理获取表头和各行各列的数据,导入到一个QStandardItemModel 数据模型。
  • 编辑修改数据模型的数据,可以插入行、添加行、删除行,还可以在 QTableView 视图组件中直接修改单元格的数据内容。
  • 可以设置数据模型中某个项的不同角色的数据,包括文字对齐方式、字体是否粗体等。
  • 通过 QItemSelectionModel 获取视图组件上的当前单元格,以及选择单元格的范围,对选择的单元格进行操作。
  • 将数据模型的数据内容显示到 OPlainTextEdit 组件里,显示数据模型的内容,检验视图组件上做的修改是否与数据模型同步。
  • 将修改后的模型数据另存为一个文本文件。

3.2 界面设计与主窗口类定义

本实例的主窗口从QMainWindow 继承而来,中间的 TableView 和 PlainTextEdit 组件采用水平分割条布局。在 Action 编辑器中创建如下图所示的一些Action,并由 Action 创建主工具栏上的按钮,下方的状态栏设置了几个 QLabel 组件,显示当前文件名称、当前单元格行号、列号,以及相应内容。
在这里插入图片描述

主窗口类MainWindow 里代码新增的定义如下

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:

//用于状态栏的信息显示
    QLabel  *LabCurFile;  //当前文件
    QLabel  *LabCellPos;    //当前单元格行列号
    QLabel  *LabCellText;   //当前单元格内容

    QStandardItemModel  *theModel;//数据模型
    QItemSelectionModel *theSelection;//Item选择模型

    void    iniModelFromStringList(QStringList&);//从StringList初始化数据模型

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
 //当前选择单元格发生变化
    void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);


    void on_actOpen_triggered(); //打开文件

    void on_actAppend_triggered(); //添加行

    void on_actInsert_triggered();//插入行

    void on_actDelete_triggered();//删除行

    void on_actModelData_triggered();  //到处模型数据

    void on_actSave_triggered();//保存文件

    void on_actAlignCenter_triggered();//居中对齐

    void on_actFontBold_triggered(bool checked);//粗体字体

    void on_actAlignLeft_triggered(); //居左对齐

    void on_actAlignRight_triggered();//居右对齐

private:
    Ui::MainWindow *ui;
};

这里定义了数据模型变量 theModel,项数据选择模型变量 theSelection。
定义的私有函数iniModelFromStringList()用于在打开文件时,从一个 QStringList 变量的内容创建数据模型。
自定义槽函数on currentChanged()用于在 TableView 上选择单元格发生变化时,更新状态栏的信息显示,这个槽函数将会与项选择模型 theSelection 的 currentChanged()信号关联。

3.3 QStandardItemModel的使用

3.3.1 系统初始化

在 MainWindow 的构造函数中进行界面初始化,数据模型和选择模型的创建,以及与视图组件的关联,信号与槽的关联等设置,代码如下:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    theModel = new QStandardItemModel(2,FixedColumnCount,this); //数据模型
    theSelection = new QItemSelectionModel(theModel);//Item选择模型

//选择当前单元格变化时的信号与槽
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));

    //为tableView设置数据模型
    ui->tableView->setModel(theModel); //设置数据模型
    ui->tableView->setSelectionModel(theSelection);//设置选择模型
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);//
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);

    setCentralWidget(ui->splitter); //

//创建状态栏组件
    LabCurFile = new QLabel(QString::fromLocal8Bit("当前文件:"),this);
    LabCurFile->setMinimumWidth(200);

    LabCellPos = new QLabel(QString::fromLocal8Bit("当前单元格:"),this);
    LabCellPos->setMinimumWidth(180);
    LabCellPos->setAlignment(Qt::AlignHCenter);

    LabCellText = new QLabel(QString::fromLocal8Bit("单元格内容:"),this);
    LabCellText->setMinimumWidth(150);


    ui->statusBar->addWidget(LabCurFile);
    ui->statusBar->addWidget(LabCellPos);
    ui->statusBar->addWidget(LabCellText);
}

在构造函数里首先创建数据模型 theModel,创建数据选择模型时需要传递一个数据模型变量作为其参数。这样,数据选择模型 theSelection 就与数据模型 theModel 关联,用于表示 theModel的项数据选择操作。
创建数据模型和选择模型后,为 TableView 组件设置数据模型和选择模型:

 ui->tableView->setModel(theModel); //设置数据模型
 ui->tableView->setSelectionModel(theSelection);//设置选择模型

构造函数里还将自定义的槽函数on_currentChanged()与theSelection的currentChanged()信号关联,用于界面上 tableView 选择单元格发生变化时,显示单元格的行号、列号、内容等信息,槽函数代码如下:

void MainWindow::on_currentChanged(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(QString::fromLocal8Bit("单元格内容:")+aItem->text()); //显示item的文字内容

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

3.3.2 从文本文件导入数据

QStandardItemModel 是标准的基于项数据的数据模型,以类似于二维数组的形式管理内部数据,适合于处理表格型数据,其显示一般采用 QTableView。

QStandardItemModel 的数据可以是程序生成的内存中的数据,也可以来源于文件。例如,在实际数据处理中,有些数据经常是以纯文本格式保存的,它们有固定的列数,每一列是一项数据实际构成一个二维数据表。图下图 是本实例程序要打开的一个纯文本文件的内容,文件的第1行是数据列的文字标题,相当于数据表的表头,然后以行存储数据,以TAB 键间隔每列数据。

当单击工具栏上的“打开文件”按钮时,需要选择一个这样的文件导入到数据模型,并在tableView 上进行显示和编辑。下图的数据有6列,第1列是整数,第 2至4列是浮点数,第5列是文字,第6列是逻辑型变量,“1”表示 true。

在这里插入图片描述

以下是"打开文件"按钮的槽函数代码

void MainWindow::on_actOpen_triggered()
{ //打开文件
    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); //添加到 StringList
        }
        aFile.close();//关闭文件

        this->LabCurFile->setText(QString::fromLocal8Bit("当前文件:")+aFileName);//状态栏显示
        ui->actAppend->setEnabled(true); //更新Actions的enable属性
        ui->actInsert->setEnabled(true);
        ui->actDelete->setEnabled(true);
        ui->actSave->setEnabled(true);

        iniModelFromStringList(fFileContent);//从StringList的内容初始化数据模型
    }
}

这段代码让用户选择所需要打开的数据文本文件,然后用只读和文本格式打开文件,逐行读取其内容,将每行字符串显示到界面上的 plainTextEdit 里,并且添加到一个临时的 OStringList类型的变量 fFileContent 里。
然后调用自定义函数 iniModelFromStringList(),用 fFileContent 的内容初始化数据模型。下面是iniModelFromStringList()函数的代码:

void MainWindow::iniModelFromStringList(QStringList& aFileContent)
{ //从一个StringList 获取数据,初始化数据Model
    int rowCnt=aFileContent.count(); //文本行数,第1行是标题
    theModel->setRowCount(rowCnt-1); //实际数据行数

//设置表头
    QString header=aFileContent.at(0);//第1行是表头
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
    QStringList headerList=header.split(QRegExp("\\s+"),QString::SkipEmptyParts);
    theModel->setHorizontalHeaderLabels(headerList); //设置表头文字

//设置表格数据
    int j;
    QStandardItem   *aItem;
    for (int i=1;i<rowCnt;i++)
    {
        QString aLineText=aFileContent.at(i); //获取 数据区 的一行
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
        QStringList tmpList=aLineText.split(QRegExp("\\s+"),QString::SkipEmptyParts);
        for (j=0;j<FixedColumnCount-1;j++) //tmpList的行数等于FixedColumnCount, 固定的
        { //不包含最后一列
            aItem=new QStandardItem(tmpList.at(j));//创建item
            theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
        }

        aItem=new QStandardItem(headerList.at(j));//最后一列是Checkable,需要设置
        aItem->setCheckable(true); //设置为Checkable
        if (tmpList.at(j)=="0")
            aItem->setCheckState(Qt::Unchecked); //根据数据设置check状态
        else
            aItem->setCheckState(Qt::Checked);
        theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
    }
}

传递来的参数aFileContent 是文本文件所有行构成的 StringList,文件的每一行是aFileContent
的一行字符串,第1行是表头文字,数据从第 2 行开始。

程序首先获取字符串列表的行数,然后设置数据模型的行数,因为数据模型的列数在初始化时已经设置了。
然后获取字符串列表的第1行,即表头文字,用QString::split()函数分割成一个 QStringList,设置为数据模型的表头标题。
QString::split()函数根据某个特定的符号将字符串进行分割。例如,header 是数据列的标题每个标题之间通过一个或多个 TAB 键分隔,其内容是:

测深(m)		垂深(m)		方位(°)	总位移(m)	固井质量	测井取样		

那么通过上面的 split()函数操作,得到一个字符串列表 headerList,其内容是:

测深(m)		
垂深(m)		
方位(°)	
总位移(m)	
固井质量	
测井取样	

也就是分解为一个6行的 StringList。然后使用此字符串列表作为数据模型,设置表头标题的函数 setHorizontalHeaderLabels()的参数,就可以为数据模型设置表头了。

同样,在逐行获取字符串后,也采用 split()函数进行分解,为每个数据创建一个QStandardItem类型的项数据 atem,并赋给数据模型作为某行某列的项数据。
QStandardItemModel 以二维表格的形式保存项数据,每个项数据对应着QTableView 的一个单元格。项数据不仅可以存储显示的文字,还可以存储其他角色的数据。
数据文件的最后一列是一个逻辑型数据,在 tableView 上显示时为其提供一个 CheckBox 组件,此功能通过调用 QStandardItem 的 setCheckable()函数实现。

3.3.3 数据修改

当 TableView 设置为可编辑时,双击一个单元格可以修改其内容,对于使用 CheckBox 的列,改变 CheckBox 的勾选状态,就可以修改单元格关联项的选择状态。
在实例主窗口工具栏上有“添加行”“插入行”“删除行”按钮,它们实现相应的编辑操作这些操作都是直接针对数据模型的,数据模型被修改后,会直接在 TableView 上显示出来。

  • 添加行

    添加行”操作是在数据表的最后添加一行,其实现代码如下:

    void MainWindow::on_actAppend_triggered()
    { //在表格最后添加行
        QList<QStandardItem*>    aItemList; //容器类
        QStandardItem   *aItem;
        for(int i=0;i<FixedColumnCount-1;i++) //不包含最后1列
        {
            aItem=new QStandardItem("0"); //创建Item
            aItemList<<aItem;   //添加到容器
        }
    //获取最后一列的表头文字
        QString str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
        aItem=new QStandardItem(str); //创建 "测井取样"Item
        aItem->setCheckable(true);
        aItemList<<aItem;   //添加到容器
    
        theModel->insertRow(theModel->rowCount(),aItemList); //插入一行,需要每个Cell的Item
        QModelIndex curIndex=theModel->index(theModel->rowCount()-1,0);//创建最后一行的ModelIndex
        theSelection->clearSelection();//清空选择项
        theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行
    }
    

    使用QStandardItemModel::insertRow()函数插入一行,其函数原型是:
    void insertRow(int row, const QList<QStandardItem *> &items)
    其中,row 是一个行号,表示在此行号之前插入一行,若 row 等于或大于总行数,则在最后添加一行。QList<QStandardItem*>&items 是一个QStandardItem类型的列表类,需要为插入的-行的每个项数据创建一个QStandardItem 类型的项,然后传递给 insertRow()函数。

    在这段程序中,为前5列创建QStandardItem 对象时,都使用文字“0”,最后一列使用表头的标题,并设置为 Checkable。创建完每个项数据对象后,使用 insertRow()函数在最后添加行。

  • 插入行

    “插入行”按钮的功能是在当前行的前面插入一行,实现代码与“添加行”类似。

  • 删除行

    “删除行”按钮的功能是删除当前行,首先从选择模型中获取当前单元格的模型索引,然后从模型索引中获取行号,调用 removeRow(int row)删除指定的行。

    void MainWindow::on_actDelete_triggered()
    { //删除行
        QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引
    
        if (curIndex.row()==theModel->rowCount()-1)//最后一行
            theModel->removeRow(curIndex.row()); //删除最后一行
        else
        {
            theModel->removeRow(curIndex.row());//删除一行,并重新设置当前选择行
            theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
        }
    }
    

3.3.4 单元格格式设置

工具栏上有3 个设置单元格文字对齐方式的按钮,还有一个设置字体粗体的按钮。当在 TableView中选择多个单元格时,可以同时设置多个单元格的格式。例如,“居左”按钮的代码如下:

void MainWindow::on_actAlignLeft_triggered()
{//设置文字居左对齐
    if (!theSelection->hasSelection()) //没有选择的项
        return;

//获取选择的单元格的模型索引列表,可以是多选
    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    for (int i=0;i<selectedIndex.count();i++)
    {
        QModelIndex aIndex=selectedIndex.at(i); //获取其中的一个模型索引
        QStandardItem* aItem=theModel->itemFromIndex(aIndex);//获取一个单元格的项数据对象
        aItem->setTextAlignment(Qt::AlignLeft);//设置文字对齐方式
    }
}

QItemSelectionModel::selectedIndexes()函数返回选择单元格的模型索引列表,然后通过此列表获取每个选择的单元格的模型索引,再通过模型索引获取其项数据,然后调用 OStandardItem::setTextAlignment()设置一个项的对齐方式即可。
“居中”和“居右”按钮的代码与此类似。

“粗体”按钮设置单元格的字体是否为粗体,在选择单元格时,actFontBold 的 check 状态根据当前单元格的字体是否为粗体自动更新。actFontBold 的 triggered(bool)的槽函数代码如下,与设置对齐方式的代码操作方式类似:

void MainWindow::on_actFontBold_triggered(bool checked)
{//设置字体粗体
    if (!theSelection->hasSelection())
        return;

//获取选择单元格的模型索引列表
    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    for (int i=0;i<selectedIndex.count();i++)
    {
        QModelIndex aIndex=selectedIndex.at(i); //获取一个模型索引
        QStandardItem* aItem=theModel->itemFromIndex(aIndex);//获取项数据
        QFont font=aItem->font(); //获取字体
        font.setBold(checked); //设置字体是否粗体
        aItem->setFont(font); //重新设置字体
    }

}

3.3.5 数据另存为文件

在视图组件上对数据的修改都会自动更新到数据模型里,单击工具栏上的“模型数据预览”按钮,可以将数据模型的数据内容显示到 PlainTextEdit 里。

数据模型里的数据是在内存中的,工具栏上的“另存文件”按钮可以将数据模型的数据另存为一个数据文本文件,同时也显示在 PlainTextEdit 里,其实现代码如下:

void MainWindow::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";
    }
}

3.3.6 源码

(1)mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include    <QMainWindow>
#include    <QLabel>
#include    <QStandardItemModel>
#include    <QItemSelectionModel>

#define     FixedColumnCount    6       //文件固定6列

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:

//用于状态栏的信息显示
    QLabel  *LabCurFile;  //当前文件
    QLabel  *LabCellPos;    //当前单元格行列号
    QLabel  *LabCellText;   //当前单元格内容

    QStandardItemModel  *theModel;//数据模型
    QItemSelectionModel *theSelection;//Item选择模型

    void    iniModelFromStringList(QStringList&);//从StringList初始化数据模型

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
 //当前选择单元格发生变化
    void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);


    void on_actOpen_triggered(); //打开文件

    void on_actAppend_triggered(); //添加行

    void on_actInsert_triggered();//插入行

    void on_actDelete_triggered();//删除行

    void on_actModelData_triggered();  //到处模型数据

    void on_actSave_triggered();//保存文件

    void on_actAlignCenter_triggered();//居中对齐

    void on_actFontBold_triggered(bool checked);//粗体字体

    void on_actAlignLeft_triggered(); //居左对齐

    void on_actAlignRight_triggered();//居右对齐

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

(2)mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include    <QFileDialog>
#include    <QTextStream>


void MainWindow::iniModelFromStringList(QStringList& aFileContent)
{ //从一个StringList 获取数据,初始化数据Model
    int rowCnt=aFileContent.count(); //文本行数,第1行是标题
    theModel->setRowCount(rowCnt-1); //实际数据行数

//设置表头
    QString header=aFileContent.at(0);//第1行是表头
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
    QStringList headerList=header.split(QRegExp("\\s+"),QString::SkipEmptyParts);
    theModel->setHorizontalHeaderLabels(headerList); //设置表头文字

//设置表格数据
    int j;
    QStandardItem   *aItem;
    for (int i=1;i<rowCnt;i++)
    {
        QString aLineText=aFileContent.at(i); //获取 数据区 的一行
//一个或多个空格、TAB等分隔符隔开的字符串, 分解为一个StringList
        QStringList tmpList=aLineText.split(QRegExp("\\s+"),QString::SkipEmptyParts);
        for (j=0;j<FixedColumnCount-1;j++) //tmpList的行数等于FixedColumnCount, 固定的
        { //不包含最后一列
            aItem=new QStandardItem(tmpList.at(j));//创建item
            theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
        }

        aItem=new QStandardItem(headerList.at(j));//最后一列是Checkable,需要设置
        aItem->setCheckable(true); //设置为Checkable
        if (tmpList.at(j)=="0")
            aItem->setCheckState(Qt::Unchecked); //根据数据设置check状态
        else
            aItem->setCheckState(Qt::Checked);
        theModel->setItem(i-1,j,aItem); //为模型的某个行列位置设置Item
    }
}


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    theModel = new QStandardItemModel(2,FixedColumnCount,this); //数据模型
    theSelection = new QItemSelectionModel(theModel);//Item选择模型

//选择当前单元格变化时的信号与槽
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));

    //为tableView设置数据模型
    ui->tableView->setModel(theModel); //设置数据模型
    ui->tableView->setSelectionModel(theSelection);//设置选择模型
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);//
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);

    setCentralWidget(ui->splitter); //

//创建状态栏组件
    LabCurFile = new QLabel(QString::fromLocal8Bit("当前文件:"),this);
    LabCurFile->setMinimumWidth(200);

    LabCellPos = new QLabel(QString::fromLocal8Bit("当前单元格:"),this);
    LabCellPos->setMinimumWidth(180);
    LabCellPos->setAlignment(Qt::AlignHCenter);

    LabCellText = new QLabel(QString::fromLocal8Bit("单元格内容:"),this);
    LabCellText->setMinimumWidth(150);


    ui->statusBar->addWidget(LabCurFile);
    ui->statusBar->addWidget(LabCellPos);
    ui->statusBar->addWidget(LabCellText);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_currentChanged(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(QString::fromLocal8Bit("单元格内容:")+aItem->text()); //显示item的文字内容

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

void MainWindow::on_actOpen_triggered()
{ //打开文件
    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); //添加到 StringList
        }
        aFile.close();//关闭文件

        this->LabCurFile->setText(QString::fromLocal8Bit("当前文件:")+aFileName);//状态栏显示
        ui->actAppend->setEnabled(true); //更新Actions的enable属性
        ui->actInsert->setEnabled(true);
        ui->actDelete->setEnabled(true);
        ui->actSave->setEnabled(true);

        iniModelFromStringList(fFileContent);//从StringList的内容初始化数据模型
    }
}

void MainWindow::on_actAppend_triggered()
{ //在表格最后添加行
    QList<QStandardItem*>    aItemList; //容器类
    QStandardItem   *aItem;
    for(int i=0;i<FixedColumnCount-1;i++) //不包含最后1列
    {
        aItem=new QStandardItem("0"); //创建Item
        aItemList<<aItem;   //添加到容器
    }
//获取最后一列的表头文字
    QString str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
    aItem=new QStandardItem(str); //创建 "测井取样"Item
    aItem->setCheckable(true);
    aItemList<<aItem;   //添加到容器

    theModel->insertRow(theModel->rowCount(),aItemList); //插入一行,需要每个Cell的Item
    QModelIndex curIndex=theModel->index(theModel->rowCount()-1,0);//创建最后一行的ModelIndex
    theSelection->clearSelection();//清空选择项
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//设置刚插入的行为当前选择行
}

void MainWindow::on_actInsert_triggered()
{//插入行
    QList<QStandardItem*>    aItemList;  //QStandardItem的列表类
    QStandardItem   *aItem;
    for(int i=0;i<FixedColumnCount-1;i++)
    {
        aItem=new QStandardItem("0"); //新建一个QStandardItem
        aItemList<<aItem;//添加到列表类
    }
//    aItem=new QStandardItem("优"); //新建一个QStandardItem
//    aItemList<<aItem;//添加到列表类

    QString str;    //获取表头文字
    str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
    aItem=new QStandardItem(str); //创建Item
    aItem->setCheckable(true);//设置为可使用CheckBox
    aItemList<<aItem;//添加到列表类


    QModelIndex curIndex=theSelection->currentIndex(); //获取当前选中项的模型索引
    theModel->insertRow(curIndex.row(),aItemList);  //在当前行的前面插入一行
    theSelection->clearSelection();//清除已有选择
    theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}

void MainWindow::on_actDelete_triggered()
{ //删除行
    QModelIndex curIndex=theSelection->currentIndex();//获取当前选择单元格的模型索引

    if (curIndex.row()==theModel->rowCount()-1)//最后一行
        theModel->removeRow(curIndex.row()); //删除最后一行
    else
    {
        theModel->removeRow(curIndex.row());//删除一行,并重新设置当前选择行
        theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
    }
}

void MainWindow::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 MainWindow::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 MainWindow::on_actAlignCenter_triggered()
{//文字居中对齐
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;

    for (int i=0;i<selectedIndex.count();i++)
    {
        aIndex=selectedIndex.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        aItem->setTextAlignment(Qt::AlignHCenter);
    }
}

void MainWindow::on_actFontBold_triggered(bool checked)
{//设置字体粗体
    if (!theSelection->hasSelection())
        return;

//获取选择单元格的模型索引列表
    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    for (int i=0;i<selectedIndex.count();i++)
    {
        QModelIndex aIndex=selectedIndex.at(i); //获取一个模型索引
        QStandardItem* aItem=theModel->itemFromIndex(aIndex);//获取项数据
        QFont font=aItem->font(); //获取字体
        font.setBold(checked); //设置字体是否粗体
        aItem->setFont(font); //重新设置字体
    }

}

void MainWindow::on_actAlignLeft_triggered()
{//设置文字居左对齐
    if (!theSelection->hasSelection()) //没有选择的项
        return;

//获取选择的单元格的模型索引列表,可以是多选
    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    for (int i=0;i<selectedIndex.count();i++)
    {
        QModelIndex aIndex=selectedIndex.at(i); //获取其中的一个模型索引
        QStandardItem* aItem=theModel->itemFromIndex(aIndex);//获取一个单元格的项数据对象
        aItem->setTextAlignment(Qt::AlignLeft);//设置文字对齐方式
    }
}

void MainWindow::on_actAlignRight_triggered()
{//设置文字居右对齐
    if (!theSelection->hasSelection())
        return;

    QModelIndexList selectedIndex=theSelection->selectedIndexes();

    QModelIndex aIndex;
    QStandardItem   *aItem;

    for (int i=0;i<selectedIndex.count();i++)
    {
        aIndex=selectedIndex.at(i);
        aItem=theModel->itemFromIndex(aIndex);
        aItem->setTextAlignment(Qt::AlignRight);
    }
}


4. 自定义代理

4.1 自定义代理的功能

在前一节的实例 samp5_3 中,导入数据文件进行编辑时,QTableView 组件为每个单元格提供的是缺省的代理编辑组件,就是一个 QLineEdit 组件。在编辑框里可以输入任何数据,所以比较通用。但是有些情况下,希望根据数据的类型限定使用不同的编辑组件,例如在 samp5_3 的实例的数据中第1列“测深”是整数,使用QSpinBox 作为编辑组件更合适“垂深”“方位”“总位移”是浮点数使用QDoubleSpinBox 更合适;而“固井质量”使用一个QComboBox,从一组列表文字中选择更合适。

要实现这些功能,就需要为 TableView 的某列或某个单元格设置自定义代理组件。本节在实例samp5_3的基础上,为 TableView 增加自定义代理组件功能。设定自定义代理组件之后的 TableView运行时,其编辑状态的效果如下图 所示

在这里插入图片描述

4.2 自定义代理类的基本设计要求

Qt中有关代理的几个类的层次结构如下图所示

在这里插入图片描述

QAbstractItemDelegate 是所有代理类的抽象基类,QStyledItemDelegate 是视图组件使用的缺省的代理类,QItemDelegate 也是类似功能的类。QStyledItemDelegate 与 QItemDelegate 的差别在于:QStyledItemDelegate 可以使用当前的样式表设置来绘制组件,因此建议使用 QStyledItemDelegate 作为自定义代理组件的基类。

不管从 QStyledItemDelegate 还是 QItemDelegate 继承设计自定义代理组件,都必须实现如下的4个函数:

  • createEditor()函数创建用于编辑模型数据的 widget 组件,如一个 QSpinBox 组件,或一个QComboBox 组件;
  • setEditorData()函数从数据模型获取数据,供widget 组件进行编辑;
  • setModelData()将 widget 上的数据更新到数据模型;
  • updateEditorGeometry()用于给 widget 组件设置一个合适的大小。

4.3 基于QSpinBox的自定义代理类

4.3.1 自定义代理类的基本结构

下面设计一个基于QSpinBox 类的自定义代理类,用于“测深”数据列的编辑。

在Qt Creator 里单击“File”->"New File or Project”菜单项,在出现的“New File or Project”对话框里选择新建一个C++ class 文件,在出现的对话框里,输入自定义类的名称为QWIntSpinDelegate,设置基类为 QStyledItemDelegate,单击下一步后结束向导,系统会自动生成头文件和源文件,并添加到项目里。

在头文件 qwintspindelegate.h 中包含对自定义类QWIntSpinDelegate 的定义,在其中添加4个需要重定义的函数的定义,qwintspindelegate.h 的内容如下:

#ifndef QWINTSPINDELEGATE_H
#define QWINTSPINDELEGATE_H

//#include    <QObject>
//#include    <QWidget>
#include    <QStyledItemDelegate>

class QWIntSpinDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QWIntSpinDelegate(QObject *parent=0);

//自定义代理组件必须继承以下4个函数

//创建编辑组件
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const Q_DECL_OVERRIDE;

//从数据模型获取数据,显示到代理组件中
    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;

//将代理组件的数据,保存到数据模型中
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const Q_DECL_OVERRIDE;

//更新代理编辑组件的大小
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const Q_DECL_OVERRIDE;
};

#endif // QWINTSPINDELEGATE_H

自定义代理组件必须重新实现这 4 个数,函数的原型都是固定的。

4.3.2 createEditor()函数的实现

createEditor()函数用于创建需要的编辑组件,QWIntSpinDelegate 类希望创建一个 QSpinBox作为编辑组件,函数的实现如下:

QWidget *QWIntSpinDelegate::createEditor(QWidget *parent,
   const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //创建代理编辑组件
    Q_UNUSED(option);
    Q_UNUSED(index);

    QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBox
    editor->setFrame(false); //设置为无边框
    editor->setMinimum(0);
    editor->setMaximum(10000);

    return editor;  //返回此编辑器
}

这段代码创建了一个QSpinBox类型的编辑器editor,parent指向视图组件;然后对创建的editor做一些设置,将 editor 作为函数的返回值。

4.3.3 setEditorData()函数

setEditorData()函数用于从数据模型获取数值,设置为编辑器的显示值。当双击一个单元格进入编辑状态时,就会自动调用此函数,其实现代码如下:

void QWIntSpinDelegate::setEditorData(QWidget *editor,
                      const QModelIndex &index) const
{//从数据模型获取数据,显示到代理组件中
//获取数据模型的模型索引指向的单元的数据
    int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  //强制类型转换
    spinBox->setValue(value); //设置编辑器的数值
}

4.3.4 setModelData()函数

setModelData()函数用于将代理编辑器上的值更新给数据模型,当用户在界面上完成编辑时会自动调用此函数,将界面上的数据更新到数据模型。其代码如下:

void QWIntSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{ //将代理组件的数据,保存到数据模型中
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
    spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号
    int value = spinBox->value(); //获取spinBox的值

    model->setData(index, value, Qt::EditRole); //更新到数据模型
}

程序先获取代理组件编辑器里的数值,然后利用传递来的数据模型 model 和模型索引参数index 将编辑器的最新值更新到数据模型里。

4.3.5 updateEditorGeometry()函数

updateEditorGeometry()函数用于为代理组件设置一个合适的大小,函数传递的参数option 的rect变量定义了单元格适合显示代理组件的大小,直接设置为此值即可。代码如下。

void QWIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //设置组件大小
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

4.3.6 源码

(1)qwintspindelegate.h

#ifndef QWINTSPINDELEGATE_H
#define QWINTSPINDELEGATE_H

//#include    <QObject>
//#include    <QWidget>
#include    <QStyledItemDelegate>

class QWIntSpinDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QWIntSpinDelegate(QObject *parent=0);

//自定义代理组件必须继承以下4个函数

//创建编辑组件
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const Q_DECL_OVERRIDE;

//从数据模型获取数据,显示到代理组件中
    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;

//将代理组件的数据,保存到数据模型中
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const Q_DECL_OVERRIDE;

//更新代理编辑组件的大小
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const Q_DECL_OVERRIDE;
};

#endif // QWINTSPINDELEGATE_H

(2)qwintspindelegate.cpp

#include "qwintspindelegate.h"

#include    <QSpinBox>

QWIntSpinDelegate::QWIntSpinDelegate(QObject *parent):QStyledItemDelegate(parent)
{

}

QWidget *QWIntSpinDelegate::createEditor(QWidget *parent,
   const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //创建代理编辑组件
    Q_UNUSED(option);
    Q_UNUSED(index);

    QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBox
    editor->setFrame(false); //设置为无边框
    editor->setMinimum(0);
    editor->setMaximum(10000);

    return editor;  //返回此编辑器
}

void QWIntSpinDelegate::setEditorData(QWidget *editor,
                      const QModelIndex &index) const
{//从数据模型获取数据,显示到代理组件中
//获取数据模型的模型索引指向的单元的数据
    int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  //强制类型转换
    spinBox->setValue(value); //设置编辑器的数值
}

void QWIntSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{ //将代理组件的数据,保存到数据模型中
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
    spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号
    int value = spinBox->value(); //获取spinBox的值

    model->setData(index, value, Qt::EditRole); //更新到数据模型
}

void QWIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //设置组件大小
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

4.4 自定义代理类的使用

同样的,可以创建基于QDoubleSpinBox 的自定义代理组件类QWFloatSpinDelegate,用于编辑浮点数,还可以创建基于QComboBox 的自定义组件类 QWComboBoxDelegate。在主窗口的类定义中定义3 个代理类的实例变量(省略了其他定义内容):

    QWIntSpinDelegate    intSpinDelegate; //整型数
    QWFloatSpinDelegate  floatSpinDelegate; //浮点数
    QWComboBoxDelegate   comboBoxDelegate; //列表选择

在MainWindow 的构造函数中,为 tableView 的某些列设置自定义代理组件。增加了自定义代理组件的构造函数代码如下(去掉了初始化状态栏等一些不重要的内容):

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    theModel = new QStandardItemModel(2,FixedColumnCount,this); //创建数据模型
    theSelection = new QItemSelectionModel(theModel);//Item选择模型
    connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
            this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));

    //为tableView设置数据模型
    ui->tableView->setModel(theModel); //设置数据模型
    ui->tableView->setSelectionModel(theSelection);//设置选择模型

//为各列设置自定义代理组件
    ui->tableView->setItemDelegateForColumn(0,&intSpinDelegate);  //测深,整数
    ui->tableView->setItemDelegateForColumn(1,&floatSpinDelegate);  //浮点数
    ui->tableView->setItemDelegateForColumn(2,&floatSpinDelegate); //浮点数
    ui->tableView->setItemDelegateForColumn(3,&floatSpinDelegate); //浮点数
    ui->tableView->setItemDelegateForColumn(4,&comboBoxDelegate); //Combbox选择型

}

为 TableView 的某一列设置自定义代理组件,使用 setItemDelegateForColumn()函数;为某一行设置自定义代理组件,可使用 setItemDelegateForRow()函数;若为整个 TableView 设置一个自定义代理组件,则调用 setItemDelegate()函数。
如此增加了自定义代理功能后,在编辑“测深”列时,会在单元格的位置出现一个 SpinBox组件,用于输入整数;在编辑第 2 至4 列的浮点数时,会出现一个 DoubleSpinBox 组件,用于输入浮点数;而在编辑“固井质量”列时,会自动出现一个 ComboBox 组件,用于从下拉列表中选择一个字符串。

4.5 samp5_4软件结构

samp5_4软件结构如下图所示

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

05-2_Qt 5.9 C++开发指南_Model/View结构实例(QFileSystemModel、QStringListModel、QStandardItemModel;编程实例) 的相关文章

  • 为什么libc++的shared_ptr实现使用完整内存屏障而不是宽松内存屏障?

    在boost的实现中shared ptr 它用放松内存排序以增加其引用计数 https github com boostorg smart ptr blob master include boost smart ptr detail sp
  • OpenCv读/写视频色差

    我试图简单地使用 openCV 打开视频 处理帧并将处理后的帧写入新的视频文件 我的问题是 即使我根本不处理帧 只是打开视频 使用 VideoCapture 读取帧并使用 VideoWriter 将它们写入新文件 输出文件看起来比输入更 绿
  • 如何创建可以像 UserControl 一样编辑的 TabPage 子类?

    我想创建一个包含一些控件的 TabPage 子类 并且我想通过设计器来控制这些控件的布局和属性 但是 如果我在设计器中打开子类 我将无法像在 UserControl 上那样定位它们 我不想创建一个带有 UserControl 实例的 Tab
  • 如何在 Android NDK 中创建新的 NativeWindow 而无需 Android 操作系统源代码?

    我想编译一个 Android OpenGL 控制台应用程序 您可以直接从控制台启动 Android x86 运行 或者从 Android x86 GUI 内的 Android 终端应用程序运行 这个帖子 如何在 Android NDK 中创
  • 对齐 GridView 中的行值

    我需要在 asp net 3 5 中右对齐 gridview 列中的值 我怎样才能做到这一点
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • 检查算术运算中的溢出情况[重复]

    这个问题在这里已经有答案了 可能的重复 检测 C C 中整数溢出的最佳方法 https stackoverflow com questions 199333 best way to detect integer overflow in c
  • 如何从网站下载 .EXE 文件?

    我正在编写一个应用程序 需要从网站下载 exe 文件 我正在使用 Visual Studio Express 2008 我正在使用以下代码 private void button1 Click object sender EventArgs
  • C 语言中 =+(等于加)是什么意思?

    我碰到 与标准相反 今天在一些 C 代码中 我不太确定这里发生了什么 我在文档中也找不到它 In ancientC 版本 相当于 它的残余物与最早的恐龙骨头一起被发现 例如 B 引入了广义赋值运算符 使用x y to add y to x
  • 将数据打印到文件

    我已经超载了 lt lt 运算符 使其写入文件并写入控制台 我已经为同一个函数创建了 8 个线程 并且我想输出 hello hi 如果我在无限循环中运行这个线程例程 文件中的o p是 hello hi hello hi hello hi e
  • 在非活动联合成员上使用“std::addressof”是否定义明确[重复]

    这个问题在这里已经有答案了 下面的代码是尝试实现constexpr的版本offsetof在 C 11 中 它可以在 gcc 7 2 0 和 clang 5 0 0 中编译 这取决于申请std addressof工会非活跃成员的成员 这是明确
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 尚未处理时调用 Form 的 Invoke 时出现 ObjectDisposeException

    我们得到一个ObjectDisposedException从一个电话到Invoke在尚未处理的表格上 这是一些演示该问题的示例代码 public partial class Form2 Form void Form2 Load object
  • 有没有一种简单的方法可以让 Visual Studio 2015 使用特定的 ToolsVersion?

    使用特定版本构建项目或解决方案时msbuild我可以使用以下命令选择早期的 net 工具链 toolsversion or tv switch C Program Files x86 MSBuild 14 0 bin msbuild tv
  • 在类的所有方法之前运行一个方法

    在 C 3 或 4 中可以做到这一点吗 也许有一些反思 class Magic RunBeforeAll public void BaseMethod runs BaseMethod before being executed public
  • 双精度类型二维多维数组的 pinvoke 编组作为 c# 和 c++ 之间的输入和输出

    我有以下我正在尝试解决的双物质类型的 2d 多维数组的 c 和 c pinvoke 编组 我已经查看了以下热门内容以获得我目前拥有的内容使用双精度数组进行 P Invoke 在 C 和 C 之间编组数据 https stackoverflo
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 带重定向标准流的 C# + telnet 进程立即退出

    我正在尝试用 C 做一个 脚本化 telnet 项目 有点类似于Tcl期望 http expect nist gov 我需要为其启动 telnet 进程并重定向 和处理 其 stdin stdout 流 问题是 生成的 telnet 进程在

随机推荐

  • 关于数据库的备份个人见解

    一 关于数据备份和还原 1 在工作中 经常碰到生产环境上面数据库数据需要进行一些变更或者改动 这个时候呢 很多人的第一反应就是先备份整张表为一张临时表 然后就开始对表数据进行操作 如果出现数据异常 需要回退的时候 就直接删除现在表 然后把备
  • 50个知名的开源网站

    1 http snippets dzone com tag c 数以千计的有用的C语言源代码片段 2 http www hotscripts com category c cpp scripts programs Hotscripts 提供
  • Python 文件的读写操作

    文章目录 1 文件对象 1 1 文件打开方式 1 1 1 打开文件 1 1 2 关闭文件 1 1 3 访问模式 1 2文件读取 1 2 1 read 1 2 2 readline 1 2 3 readlines 1 3 文件迭代 1 4 文
  • 最短路径:迪杰斯特拉算法

    算法步骤 1 初始化 1 将源点v0加到S中 即S v0 true 2 将v0到各个终点的最短路径长度初始化为权值 即D i G arcs v0 vi vi属于V S 3 如果v0和顶点vi之间有弧 则将vi的前驱置为v0 即Path i
  • 硬件系统工程师宝典(28)-----关于LDO,应该知道的事

    各位同学大家好 欢迎继续做客电子工程学习圈 今天我们继续来讲这本书 硬件系统工程师宝典 上篇我们说到BJT配合MOSFET控制电源开关的四种电路以及MOSFET的均流电路 今天我们来讲讲LDO的应用分析 LDO的结构 LDO Low Dro
  • XML格式数据集转TXT(YOLO)

    我从网上下载了一个数据集 underwater 它们提供了xml格式的数据 但是我想用yolov5进行训练 所以需要将xml格式转化为txt格式 正常的xml格式的数据集可以参考 目标检测中将已有的 xml数据集转换成 txt数据集 附代码
  • 深入浅出VA函数的使用技巧

    深入浅出VA函数的使用技巧 作者 钟小兵 发文时间 2005 04 11 本文主要介绍可变参数的函数使用 然后分析它的原理 程序员自己如何对它们实现和封装 最后是可能会出现的问题和避免措施 VA函数 variable argument fu
  • MySQL——模糊查询(LIKE关键字与通配符:百分号%和下划线_的使用和理解)——(运用场景+通俗易懂)

    使用mysql模糊查询主要点 LIKE关键字和这两个通配符配合使用 任意一个字符 任意0或多个字符 那么我们立即上手吧 一 使用LIKE和通配符 场景1 我要搜索一个名字 可我都忘记叫什么了 只知道是3个字符的 那怎么搜索呢 三个下划线 代
  • elasticsearch7.9 修改指定JDK

    错误 future versions of Elasticsearch will require Java 11 your Java version from usr local nlp java jdk1 8 0 162 jre does
  • 大致解决 Ubuntu 18.04 系统启动极慢的问题

    早在一两个月之前就想升级到 18 04 或是 18 10 了 然而那会儿用 LiveCD 测试得速度实在是慢 启动要很久 干脆没过多纠缠 旧暂时不管了 现因实际需要 不得不升级系统 以便获取到更新的软件包 从 17 10 向上升级很方便 直
  • hdoj 题目分类

    1001 整数求和 水题 1002 C语言实验题 两个数比较 水题 1003 1 2 3 4 5 简单题 1004 渊子赛马 排序 贪心的方法归并 1005 Hero In Maze 广度搜索 1006 Redraiment猜想 数论 容斥
  • 论文笔记——CVPR 2017 Annotating Object Instances with a Polygon-RNN

    文章主页 http www cs toronto edu polyrnn 1 简介 文章作者基于深度学习提出一种半自动目标事例标注 semi automatic annotation of object instances 的算法 大多数前
  • @property基本概念

    1 什么是 property property是编译器的指令 什么是编译器的指令 编译器指令就是用来告诉编译器要做什么 property会让编译器做什么呢 property 用在声明文件中告诉编译器声明成员变量的的访问器 getter se
  • maven自定义archetype

    在开发过程中我们经常会创建一系列结构类似的新项目 这些项目结构和基础配置基本或完全一致 maven就提供了archetype类型来规定新建项目的结构及基础配置 利用archetype就可以快速简单的搭建新项目 一 创建Maven项目的一般步
  • 网络安全笔记7——防火墙技术

    网络安全笔记7 防火墙技术 参考课程 中国大学MOOC 网络安全 北京航空航天大学 文章目录 网络安全笔记7 防火墙技术 防火墙概述 防火墙的类型及结构 防火墙的发展史 防火墙的分类 OSI模型与防火墙的关系 静态包过滤防火墙 操作 工作原
  • es的配置文件(elasticsearch.yml)

    config目录下有2个配置文件 es的配置文件 elasticsearch yml 和日志配置文件 logging yml cluster name elasticsearch 配置es的集群名称 默认是elasticsearch es会
  • arduino IDE搭建ESP8266开发环境和简单使用

    arduino IDE搭建ESP8266开发环境和简单使用 文章目录 arduino IDE搭建ESP8266开发环境和简单使用 安装 下载IDE 在Arduino IDE上安装esp8266库 下载安装esp8266库 使用 选择开发板
  • Java进阶3 - 易错知识点整理(待更新)

    Java进阶3 易错知识点整理 待更新 该章节是Java进阶2 易错知识点整理的续篇 在前一章节中介绍了 ORM框架 中间件相关的面试题 而在该章节中主要记录关于项目部署中间件 监控与性能优化等常见面试题 文章目录 Java进阶3 易错知识
  • 使用mybatis-plus的insert方法遇到的坑(添加时id值不存在异常)

    在使用mybatis plus的insert方法的时候 报错 java sql SQLException Field id doesn t have a default value 后来了解到使用mybatis plus的insert方法
  • 05-2_Qt 5.9 C++开发指南_Model/View结构实例(QFileSystemModel、QStringListModel、QStandardItemModel;编程实例)

    接上篇 本篇主要介绍Model View框架下的模型类 QFileSystemModel QStringListModel QStandardItemModel的使用方法和编程实例 文章目录 1 QFileSystemModel 1 1 Q