QTableView示例

ui界面,这回的ui也很简单,不熟的先熟悉组件

主要还是model的实现。QTableView一般使用QStandardItemModel作为模型,这和之前的QStringListModel不同,它是基于项的模型类,每个项是一个QStandardItem对象。而之前QStringListModel就没有这种设定。

同时还有QItemSelectionModel类这个选择模型,它的功能是跟踪视图View组件上的选择操作,给出选择范围。比如给QTableView组件设置一个选择模型,在QTableView组件上选择多个单元格时,通过选择模型就可以得到所有被选择单元格的模型索引

具体功能会在给出代码时同步讲解。

本示例的功能是打开一个txt文本,把文本内容用表格形式显示,文本示例如图

程序运行效果如图

按照惯例,先看头文件的内容,这里我直接把整个类的定义放着

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QLabel *labcurFile,*labcellpos,*labcelltext;//这些label是左下角显示信息用的
    const int fixedColumnCount=6;//列数
    QStandardItemModel *m_model;//数据模型
    QItemSelectionModel *m_selection;//选择模型
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    void iniModelData(QStringList &);//初始化数据模型的函数
private slots://以下都是槽函数,定义时会详细讲解
    void do_currentChanged(const QModelIndex &current,const QModelIndex &previous);
    void on_actionopen_triggered();
    void on_actionsee_triggered();
    void on_actionaddrow_triggered();
    void on_actioninsertrow_triggered();
    void on_actiondeleterow_triggered();
    void on_actionleft_triggered();
};

这次的头文件到不是很多,而且槽函数不需要自己在头文件动手敲,直接在designer里选对应组件转到槽自动生成就行了。

下面是构造函数

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setCentralWidget(ui->splitter);//设置中心组件
    labcurFile=new QLabel("当前文件",this);//初始化label
    labcurFile->setMaximumWidth(200);
    labcellpos=new QLabel("当前单元格",this);
    labcellpos->setMaximumWidth(200);
    labcelltext=new QLabel("当前单元格内容",this);
    labcelltext->setMaximumWidth(200);
    ui->statusbar->addWidget(labcurFile);//把label加到状态栏里
    ui->statusbar->addWidget(labcellpos);
    ui->statusbar->addWidget(labcelltext);
    m_model=new QStandardItemModel(2,fixedColumnCount,this);//设置行和列,行不一定正确,之后会改的
    m_selection=new QItemSelectionModel(m_model,this);//根据数据模型构造选择模型
    ui->tableView->setModel(m_model);
    ui->tableView->setSelectionModel(m_selection);
    ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
    connect(m_selection,&QItemSelectionModel::currentChanged,this,&MainWindow::do_currentChanged);

}

QStandardItemModel 的构造函数

QStandardItemModel::QStandardItemModel(int rows, int columns, QObject *parent = nullptr)

参数说明:

  • rows:模型中的行数。
  • columns:模型中的列数。
  • parent:父对象,可以是另一个对象的子对象,用于管理内存生命周期。

很好懂,下一个

在Qt中,QItemSelectionModel 是一个用于管理视图(比如 QTableViewQTreeView)中项的选择状态的类。它的构造函数如下:

QItemSelectionModel::QItemSelectionModel(QAbstractItemModel *model, QObject *parent = nullptr)

参数说明:

  • model:要关联的数据模型,通常是一个 QStandardItemModel 或其它派生类。
  • parent:父对象,用于管理内存生命周期。

setSelectionMode 是用于设置选择模式的函数。setSelectionMode 允许你指定用户在 QTableView 中选择项的方式。

以下是一些常见的选择模式:

  • QAbstractItemView::SingleSelection: 单选模式,用户只能选择一个项。
  • QAbstractItemView::MultiSelection: 多选模式,用户可以选择多个项,但不能同时选择多个单元格。
  • QAbstractItemView::ExtendedSelection: 扩展选择模式,用户可以通过按住 Ctrl 键或 Shift 键进行扩展的多选。
  • QAbstractItemView::ContiguousSelection: 连续选择模式,用户可以通过鼠标拖动来选择多个项。
  • QAbstractItemView::NoSelection: 无选择模式,用户不能选择任何项。

setSelectionBehavior 是用于设置 QAbstractItemView(包括 QTableView)的选择行为的函数。它定义了用户在视图中选择项时的行为方式。

以下是一些常见的选择行为:

  • QAbstractItemView::SelectItems: 选择项的行为,即用户点击一个项时,只选中该项,不会选中整行或整列。
  • QAbstractItemView::SelectRows: 选择整行的行为,即用户点击一个项时,选中整行。
  • QAbstractItemView::SelectColumns: 选择整列的行为,即用户点击一个项时,选中整列。

相信看了上面的内容,对于构造函数新出现的一些set函数能够理解了

我们整个程序的启动应该是打开文件的action,为它写一个trigger的槽函数。我们会读取txt文件的内容

void MainWindow::on_actionopen_triggered()//打开文件
{
    QString curPath=QCoreApplication::applicationDirPath();//这个会到你生成的debug或release文件的目录,不是在你编辑项目的目录
    QString filename=QFileDialog::getOpenFileName(this,"打开一个文件",curPath,
        "数据文件(*.txt);;所有文件(*.*)");//这样可以选择文件类型
    if(filename.isEmpty()) return ;

    QFile aFile(filename);
    if(!aFile.open(QIODevice::ReadOnly|QIODevice::Text)) return;
    QStringList aFileContent;
    ui->plainTextEdit->clear();
    QTextStream aStream(&aFile);
    while(!aStream.atEnd()){
        QString str=aStream.readLine();
        ui->plainTextEdit->appendPlainText(str);
        aFileContent.append(str);
    }
    aFile.close();
    labcurFile->setText("当前文件:"+filename);//状态栏更新
    //一些action设置为可使用
    ui->actionaddrow->setEnabled(true);
    ui->actioninsertrow->setEnabled(true);
    ui->actionsave->setEnabled(true);
    ui->actiondeleterow->setEnabled(true);

    iniModelData(aFileContent);//根据文件内容去初始化数据模型
}

关于文件读取的有关代码先不讲,以后可能会出到文件io章节的内容,也可能烂尾了,反正总之是读到了,而且是一行一行读的,所以用了QStringList存储,每一项就是txt一行的内容。

然后就是根据这个QStringList去初始化我们的数据模型了

void MainWindow::iniModelData(QStringList &content)
{
    int rowcnt=content.size();//获得txt内容的行数
    m_model->setRowCount(rowcnt-1);//,重新设定行数,因为第一行是标题,所以数据模型的行数减1
    QString header=content.at(0);//content的第一项数据是那些标题
    //通过split+正则表达式来分割,把一个String分成一个StringList,熟悉python的肯定知道python字符串的split函数,效果差不多
    QStringList headerList=header.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
    m_model->setHorizontalHeaderLabels(headerList);//设置水平的头标题
    QStandardItem *item;//数据模型的每一项都是QStandardItem
    int j=0;
    for(int i=1;i<rowcnt;i++){//从1开始
        QString alinetext=content.at(i);
        //同样的分割
        QStringList tmp=alinetext.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
        for(j=0;j<fixedColumnCount-1;j++){
            item=new QStandardItem(tmp.at(j));//根据每个数据(String)去初始化一个QStandardItem 
            m_model->setItem(i-1,j,item);//在某个位置设置QStandardItem 
        }
        //上面少了一列,少的是测井取样的那一列
        item=new QStandardItem(tmp.at(j));
        item->setCheckable(true);
        item->setBackground(QBrush(Qt::red));//把那一项位置的背景变成红色
        if(tmp.at(j)=="0"){
            item->setCheckState(Qt::Unchecked);//如果是0,不打勾
        }
        else item->setCheckState(Qt::Checked);//打勾
        m_model->setItem(i-1,j,item);
    }
}

我认为新出现的函数都会在下面给出解释(正则就不解释了,只要知道是根据空格分割字符串)

setHorizontalHeaderLabels(const QStringList &labels)

你可以设置表格的水平表头标签(列标签)

在 Qt 中,QStandardItem 是用于在模型中存储数据的类。它的构造函数有多个重载形式,可以根据需要选择不同的构造方式。以下是其中一种常见的构造函数:

QStandardItem::QStandardItem(const QString &text = QString())

这个构造函数接受一个字符串参数 text,该字符串可以是项的文本。

QStandardItemModelsetItem 函数是用于在模型中设置某个位置的项(item)的。它的声明如下:

void QStandardItemModel::setItem(int row, int column, QStandardItem *item)

这个函数将给定的 item 对象放置在模型的指定行和列位置。

QStandardItem 类提供了 setCheckable 函数,用于设置项是否可勾选(checkbox)。这个函数的声明如下:

void QStandardItem::setCheckable(bool checkable)

通过调用这个函数,你可以将 QStandardItem 对象设置为可勾选或不可勾选的状态。

setBackgroundQStandardItem 类的一个方法,用于设置项的背景色。

QStandardItem 类提供了 setCheckState 方法,用于设置项的复选框状态。这个方法允许你在 QStandardItem 对象上设置一个复选框,并指定其初始状态。

以下是 setCheckState 方法的基本定义:

void QStandardItem::setCheckState(Qt::CheckState state)

其中,state 是一个枚举类型 Qt::CheckState,它表示复选框的状态,可以是以下值之一:

  • Qt::Unchecked: 未选中状态
  • Qt::PartiallyChecked: 部分选中状态
  • Qt::Checked: 选中状态

这一行最终效果是这样的

然后是数据预览这个action,它就是遍历了一遍数据模型,然后打印输出在旁边的空白编辑区

void MainWindow::on_actionsee_triggered()
{
    ui->plainTextEdit->clear();
    QStandardItem *item;
    QString str;
    for(int i=0;i<m_model->columnCount();i++){
        item=m_model->horizontalHeaderItem(i);//根据索引得到头标签
        str+=item->text();//用text来获得文本
        str+="\t";
    }
    ui->plainTextEdit->appendPlainText(str);
    for(int i=0;i<m_model->rowCount();i++){
        str="";
        for(int j=0;j<m_model->columnCount()-1;j++){
            item=m_model->item(i,j);
            str+=item->text();
            str+="\t";
        }
        item=m_model->item(i,m_model->columnCount()-1);
        if(item->checkState()==Qt::Checked){
            str+="是";
        }
        else str+="false";
        ui->plainTextEdit->appendPlainText(str);
    }

}

QStandardItem 类具有 text 方法,该方法用于获取项的文本内容。这个方法返回项的显示文本,该文本通常用于在视图(例如表格或列表)中显示。

添加行:


void MainWindow::on_actionaddrow_triggered()
{
    QList<QStandardItem *> aitemlist;
    QStandardItem *item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        aitemlist<<item;
    }
    QString str=m_model->headerData(m_model->columnCount()-1,Qt::Horizontal).toString();
    item=new QStandardItem(str);
    item->setCheckable(true);
    item->setBackground(QBrush(Qt::red));
    aitemlist<<item;
    m_model->insertRow(m_model->rowCount(),aitemlist);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(m_model->index(m_model->rowCount()-1,0),
                                 QItemSelectionModel::Select);
}

在Qt中,headerDataQAbstractItemModel类中的一个虚拟函数,用于检索与模型头部相关的数据。以下是该函数的原型:

QVariant QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

这个函数有三个参数:

  • section: 表示头部的索引或行/列号。
  • orientation: 表示头部的方向,可以是 Qt::Horizontal(水平)或 Qt::Vertical(垂直)。
  • role: 表示请求的数据的角色,例如 Qt::DisplayRole(用于显示的基本数据)、Qt::ToolTipRole(工具提示)等。默认是 Qt::DisplayRole

函数返回一个 QVariant 对象,其中包含请求的头部数据。

这里返回的str是测井取样

bool QStandardItemModel::insertRow(int row, const QStringList &texts);

  • row: 表示要插入的行的索引。
  • texts: 表示一行中每个项的文本内容。

这个函数会在指定的行中插入一个包含 texts 中每个字符串的项。

我们在最后一行插入,效果如图

QItemSelectionModel 类提供了 clearSelection 方法,用于清除模型中的选择。以下是该方法的原型:

void QItemSelectionModel::clearSelection();

这个方法会清除模型中的所有选择,将选择状态重置为未选择。如果你有一个 QItemSelectionModel 对象,调用这个方法将取消当前的选择。

QItemSelectionModelsetCurrentIndex 方法用于设置当前选择项的索引。以下是该方法的函数原型:

void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);

这个方法有两个参数:

  • index: 要设置为当前选择项的模型索引。
  • command: 指定选择的行为,是一个枚举类型 QItemSelectionModel::SelectionFlags

SelectionFlags 枚举包括以下值:

  • QItemSelectionModel::NoUpdate: 不更新选择。
  • QItemSelectionModel::Clear: 清除所有已选择的项。
  • QItemSelectionModel::Select: 选择新的项。
  • QItemSelectionModel::Deselect: 取消选择项。

你可以使用位操作符(例如 |)来组合多个标志。

所以本示例代码的添加行的最终结果是最新行的第一列被选择

插入行:

void MainWindow::on_actioninsertrow_triggered()
{
    QList<QStandardItem *> aitemlist;
    QStandardItem *item;
    for(int i=0;i<m_model->columnCount()-1;i++){
        item=new QStandardItem("0");
        aitemlist<<item;
    }
    QString str=m_model->headerData(m_model->columnCount()-1,Qt::Horizontal).toString();
    item=new QStandardItem(str);
    item->setCheckable(true);
    item->setBackground(QBrush(Qt::red));
    aitemlist<<item;
    QModelIndex curindex=m_selection->currentIndex();//这里
    m_model->insertRow(curindex.row(),aitemlist);
    m_selection->clearSelection();
    m_selection->setCurrentIndex(curindex,
                                 QItemSelectionModel::Select);
}

QItemSelectionModelcurrentIndex 方法用于获取当前选择项的模型索引。以下是该方法的函数原型:

QModelIndex QItemSelectionModel::currentIndex() const;

这个方法不接受任何参数,它返回一个 QModelIndex 对象,表示当前选择项的模型索引。

请对比之前QListView的不同:(代码来自我的QListView示例)

不同之处在于QListView直接通过自己得到当前被选择的项,而本示例需要通过QItemSelectionModel

当你在 QListView 上进行选择时,Qt 会自动为其创建一个默认的选择模型,除非你显式设置了其他选择模型。这个默认的选择模型会管理视图的选择状态。

你可以使用 currentIndex() 直接从 QListView 中获取当前被选择的项的索引。这是因为 QListView 在内部已经处理了与选择相关的操作。

实际上,QTableView也可以用默认的选择模型,也就是不显示设置选择模型,但是本例中有不得不用选择模型完成的功能,之后会看到

删除行:

void MainWindow::on_actiondeleterow_triggered()
{
    QModelIndex curindex=m_selection->currentIndex();
    m_model->removeRow(curindex.row());
    if(curindex.row()!=m_model->rowCount())
        m_selection->setCurrentIndex(curindex,QItemSelectionModel::Select);
}

居左:

void MainWindow::on_actionleft_triggered()
{
    if(!m_selection->hasSelection()){
        return;
    }
    
    QModelIndexList indexlist=m_selection->selectedIndexes();
    for(auto &index:indexlist){
        m_model->itemFromIndex(index)->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);//让该项的文字显示居左
    }
}

QItemSelectionModel 类提供了 hasSelection 方法,用于检查是否存在选择。以下是该方法的原型:

bool QItemSelectionModel::hasSelection() const;

这个方法返回一个布尔值,表示是否存在选择。如果有任何选择项,返回 true;否则,返回 false

QItemSelectionModel 类提供了 selectedIndexes 方法,用于获取当前选择的所有项的模型索引。以下是该方法的原型:

QModelIndexList QItemSelectionModel::selectedIndexes() const;

这个方法返回一个 QModelIndexList,其中包含了当前选择的所有项的模型索引。

在 Qt 中,QStandardItemModel 中的 itemFromIndex 方法用于从给定的模型索引获取对应的 QStandardItem。这个方法可以让你通过模型索引找到对应的标准项,从而方便地操作和修改项的数据、状态等。

以下是 itemFromIndex 方法的定义:

QStandardItem* QStandardItemModel::itemFromIndex(const QModelIndex &index) const;

  • index 是你要获取标准项的模型索引。

这个方法返回一个指向 QStandardItem 的指针,表示在给定模型索引处的标准项。如果索引无效或没有相应的项,返回 nullptr

居右居中就省略了,和居左一模一样的操作。

不知道还记得我们构造函数里关联的信号和槽不,那个槽函数还没看呢。

connect(m_selection,&QItemSelectionModel::currentChanged,this,&MainWindow::do_currentChanged);

QItemSelectionModel 类中,确实存在 currentChanged 信号,该信号在当前项发生变化时触发。以下是 QItemSelectionModelcurrentChanged 信号的声明:

void QItemSelectionModel::currentChanged(const QModelIndex &current, const QModelIndex &previous);

  • current: 表示当前选择项的模型索引。
  • previous: 表示之前的选择项的模型索引。

你可以通过连接到这个信号来捕获当前选择项的变化。

这就是选择模型的用处了,不过我们本示例并没有需要用当之前项的地方,选择切换只会在状态栏显示最新的项的信息

void MainWindow::do_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    if(current.isValid()){
        labcellpos->setText(QString::asprintf("当前单元格:%d行,%d列",current.row(),current.column()));
        QStandardItem *aitem=m_model->itemFromIndex(current);
        labcelltext->setText("单元内容"+aitem->text());
        ui->actionbold->setChecked(aitem->font().bold());//会根据当前项是否是粗体来设置action的状态
    }
}

仅仅是本示例的需求的话,用QTableView的clicked信号也是能完成的,但是如果需要前后两项的信息的话还是用选择模型的信号。

我们回顾以下QTableView的可能的基础用法:

1.设置数据模型和选择模型

setModel , setSelectionModel  ,setSelectionMode,  setSelectionBehavior

2.为数据模型添加数据

setHorizontalHeaderLabels,  setItem,  item->setCheckable,  item->setBackground,  item->setCheckState, item->setText

3.根据选择模型获得当前选项的信息

selection->currentIndex,  selection->clearSelection,  selection->setCurrentIndex , selection->hasSelection, selection->selectedIndexes

源码下次再放,下次会根据这次的示例添加代理,到时候一起发出来

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值