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 ¤t,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
是一个用于管理视图(比如QTableView
、QTreeView
)中项的选择状态的类。它的构造函数如下:
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
,该字符串可以是项的文本。
QStandardItemModel
的setItem
函数是用于在模型中设置某个位置的项(item)的。它的声明如下:
void QStandardItemModel::setItem(int row, int column, QStandardItem *item)
这个函数将给定的
item
对象放置在模型的指定行和列位置。
QStandardItem
类提供了setCheckable
函数,用于设置项是否可勾选(checkbox)。这个函数的声明如下:
void QStandardItem::setCheckable(bool checkable)
通过调用这个函数,你可以将
QStandardItem
对象设置为可勾选或不可勾选的状态。
setBackground
是QStandardItem
类的一个方法,用于设置项的背景色。
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中,
headerData
是QAbstractItemModel
类中的一个虚拟函数,用于检索与模型头部相关的数据。以下是该函数的原型:
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
对象,调用这个方法将取消当前的选择。
QItemSelectionModel
的setCurrentIndex
方法用于设置当前选择项的索引。以下是该方法的函数原型:
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);
}
QItemSelectionModel
的currentIndex
方法用于获取当前选择项的模型索引。以下是该方法的函数原型:
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
信号,该信号在当前项发生变化时触发。以下是QItemSelectionModel
的currentChanged
信号的声明:
void QItemSelectionModel::currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
current
: 表示当前选择项的模型索引。previous
: 表示之前的选择项的模型索引。你可以通过连接到这个信号来捕获当前选择项的变化。
这就是选择模型的用处了,不过我们本示例并没有需要用当之前项的地方,选择切换只会在状态栏显示最新的项的信息
void MainWindow::do_currentChanged(const QModelIndex ¤t, 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
源码下次再放,下次会根据这次的示例添加代理,到时候一起发出来