Model/View其实就是QT中的一种数据编排结构,其中Model代表模型,View代表视图,视图是显示和编辑数据的界面组件,而模型则是视图与原始数据之间的接口,通常该类结构都是用在数据库中较多,例如模型结构负责读取或写入数据库,视图结构则负责展示数据,其条理清晰,编写代码更加方便。
我们主要的视图组件有:QListView,QTreeView,QTableView 这三种,而这三种结构其实是QListWidget,QTreeWidget,QTableQWidget三个类的便利类,也可以理解为QListView是基类,继承者是QListWIdget,只不过其功能更加强大,继承后做了二次封装。
一般模型都是以Model结尾,例如QStringListModel可作为StringList的数据模型,QSqlTableModel则可作为数据库中一个数据表的数据模型,这样的结构分明,可以提高程序编写效率与质量。
QFileSystemModel
该模型提供了一个访问本机文件系统的接口,通常QFileSystemModel和视图组件QTreeView结合使用,可用来展示系统中的目录文件层级关系。
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
model = new QFileSystemModel(this); // 初始化,提供单独线程
model->setRootPath(QDir::currentPath()); // 设置文件根目录
QStringList filter;
filter << "*.txt" << "*.mp4";
model->setNameFilters(filter);
model->setNameFilterDisables(false);
ui->treeView->setModel(model); // 设置数据模型
}
MainWindow::~MainWindow()
{
delete ui;
}
// 当TreeView被点击则触发事件
void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
}
QStringListModel
该组件常用于处理字符串列表的数据模型,可作为QListView的模型接口使用,QStringListModel提供了编辑修改删除等功能的实现。
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 初始化一个StringList字符串列表
QStringList theStringList;
theStringList << "北京" << "上海" << "广州";
// 创建并使用数据模型
model = new QStringListModel(this); // 创建模型
model->setStringList(theStringList); // 导入模型数据
ui->listView->setModel(model); // 为listView设置模型
ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked |
QAbstractItemView::SelectedClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
// 添加一行
void MainWindow::on_btnListAppend_clicked()
{
model->insertRow(model->rowCount()); // 在尾部插入一行
QModelIndex index = model->index(model->rowCount()-1,0); // 获取最后一行的索引
QString LineText = ui->lineEdit->text();
model->setData(index,LineText,Qt::DisplayRole); // 设置显示文字
ui->listView->setCurrentIndex(index); // 设置当前行选中
ui->lineEdit->clear();
}
// 插入一行数据到ListView
void MainWindow::on_btnListInsert_clicked()
{
QModelIndex index;
index= ui->listView->currentIndex(); // 获取当前选中行
model->insertRow(index.row()); // 在当前行的前面插入一行
QString LineText = ui->lineEdit->text();
model->setData(index,LineText,Qt::DisplayRole); // 设置显示文字
model->setData(index,Qt::AlignRight,Qt::TextAlignmentRole); // 设置对其方式
ui->listView->setCurrentIndex(index); // 设置当前选中行
}
// 删除当前选中行
void MainWindow::on_btnListDelete_clicked()
{
QModelIndex index;
index = ui->listView->currentIndex(); // 获取当前行的ModelIndex
model->removeRow(index.row()); // 删除选中行
}
// 清除当前列表
void MainWindow::on_btnListClear_clicked()
{
model->removeRows(0,model->rowCount());
}
// 显示数据模型文本到QPlainTextEdit
void MainWindow::on_btnTextImport_clicked()
{
QStringList pList;
pList = model->stringList(); // 获取数据模型的StringList
ui->plainTextEdit->clear(); // 先清空文本框
// 循环追加数据
for(int x=0;x< pList.count();x++)
{
ui->plainTextEdit->appendPlainText(pList.at(x) + QString(","));
}
}
// 当ListView列表项被选中时,显示QModelIndex的行、列号
void MainWindow::on_listView_clicked(const QModelIndex &index)
{
ui->LabInfo->setText(QString::asprintf("当前项:row=%d, column=%d",
index.row(),index.column()));
}
QStandardItemModel
该模型是标准的一项数据为基础的标准数据模型类,通常与QTableView组合成M/V结构,实现通用二维数据的管理功能。
如何添加,菜单栏,只需要在空白处右键选择【添加工具栏】即可。
添加不同的菜单,只需要在下方添加,然后手动将菜单拖到工具栏上即可。
打开文件,可以这样写。
// 【打开文件】:当工具栏中打开文件被点击后则触发
void MainWindow::on_actionOpen_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(); // 关闭文件
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);//状态栏显示
}
文件读写,保存文件可以这样。
// 【保存文件】:当保存文件被点击后触发
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() + " "; // 以TAB制表符隔开
}
aStream << str << "
"; // 文件里需要加入换行符
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(" ");
}
// 对最后一列需要转换一下,如果判断为选中则写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 << "
";
}
}
格式化文件输入输出。 QStandardItem -与表格的导入导出 ,对表格的各种操作。
QAbstractItemDelegate 自定义代理类
代理类的作用是用来实现重写的,例如我们的TableView中默认是可编辑的,这个可编辑的组件是QT默认为我们重写了QLineEdit组件,也可理解为将组件嵌入到了表格中,实现了,可对表格的编辑功能,例如如下案例中,的样子,默认情况下,表格是可编辑的,这就是默认重写。
初始化代码如下。
#include <QMainWindow>
#include <QLabel>
#include <iostream>
#include <QStandardItem>
#include <QItemSelectionModel>
#define FixedColumnCount 5 // 定义最大5列
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QStandardItemModel *model; // 定义数据模型
QItemSelectionModel *selection; // 定义Item选择模型
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
// 默认构造函数
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
model = new QStandardItemModel(4,FixedColumnCount,this);
selection = new QItemSelectionModel(model);
ui->tableView->setModel(model);
ui->tableView->setSelectionModel(selection);
// 添加表头
QStringList HeaderList;
HeaderList << "序号" << "姓名" << "年龄" << "性别" << "婚否";
model->setHorizontalHeaderLabels(HeaderList);
// 批量添加数据
QStringList DataList[3];
QStandardItem *Item;
DataList[0] << "1001" << "admin" << "24" << "男" << "是";
DataList[1] << "1002" << "lyshark" << "23" << "男" << "否";
DataList[2] << "1003" << "lucy" << "37" << "女" << "是";
int Array_Length = DataList->length(); // 获取每个数组中元素数
int Array_Count = sizeof(DataList) / sizeof(DataList[0]); // 获取数组个数
for(int x=0; x<Array_Count; x++)
{
for(int y=0; y<Array_Length; y++)
{
// std::cout << DataList[x][y].toStdString().data() << std::endl;
Item = new QStandardItem(DataList[x][y]);
model->setItem(x,y,Item);
}
}
}
MainWindow::~MainWindow()
{
delete ui;
}
在QT中,代理类是有层级关系的,所有的代理都来自于QAbstractItemDelegate这个抽象基类,下面学习以下代理类的使用。
先来实现一个代理,代理到Spin组件上,首先需要在项目上右键,选择addnew -> C++ Class 输入自定义类名称QWintSpinDelegate,然后基类继承QStyledItemDelegate/QMainWindow,然后下一步结束向导。
qwintspindelegate.h 改为
#ifndef QWINTSPINDELEGATE_H
#define QWINTSPINDELEGATE_H
#include <QMainWindow>
#include <QStyledItemDelegate>
class QWIntSpinDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QWIntSpinDelegate(QObject *parent=0);
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
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);
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();
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);
}
最后在mainwindow.h引用
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <iostream>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include "qwintspindelegate.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QStandardItemModel *model; // 定义数据模型
QItemSelectionModel *selection; // 定义Item选择模型
QWIntSpinDelegate intSpinDelegate; //整型数
};
#endif // MAINWINDOW_H
mainwindow.cpp设置代理
#include "mainwindow.h"
#include "ui_mainwindow.h"
// 默认构造函数
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 初始化模型数据
model = new QStandardItemModel(4,5,this);
selection = new QItemSelectionModel(model);
ui->tableView->setModel(model);
ui->tableView->setSelectionModel(selection);
// 添加表头
QStringList HeaderList;
HeaderList << "序号" << "姓名" << "年龄" << "性别" << "婚否";
model->setHorizontalHeaderLabels(HeaderList);
// 批量添加数据
QStringList DataList[3];
QStandardItem *Item;
DataList[0] << "1001" << "admin" << "24" << "男" << "是";
DataList[1] << "1002" << "lyshark" << "23" << "男" << "否";
DataList[2] << "1003" << "lucy" << "37" << "女" << "是";
int Array_Length = DataList->length(); // 获取每个数组中元素数
int Array_Count = sizeof(DataList) / sizeof(DataList[0]); // 获取数组个数
for(int x=0; x<Array_Count; x++)
{
for(int y=0; y<Array_Length; y++)
{
// std::cout << DataList[x][y].toStdString().data() << std::endl;
Item = new QStandardItem(DataList[x][y]);
model->setItem(x,y,Item);
}
}
// 为各列设置自定义代理组件
ui->tableView->setItemDelegateForColumn(0,&intSpinDelegate);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
}
运行观察代理组件,可发现,第一个位置变成了我们想要的了。
代理组件实现选择列表,通过重写comboxdelegate实现,.h代码
#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H
#include <QItemDelegate>
class QWComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
QWComboBoxDelegate(QObject *parent=0);
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 // QWCOMBOBOXDELEGATE_H
comboxdelegate.cpp
#include "comboxdelegate.h"
#include <QComboBox>
QWComboBoxDelegate::QWComboBoxDelegate(QObject *parent):QItemDelegate(parent)
{
}
QWidget *QWComboBoxDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem("已婚");
editor->addItem("未婚");
editor->addItem("单身");
return editor;
}
void QWComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString str = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentText(str);
}
void QWComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString str = comboBox->currentText();
model->setData(index, str, Qt::EditRole);
}
void QWComboBoxDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
实现效果。
TreeView组件(拓展) 三行代码实现目录枚举
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSplitter>
#include <QDirModel>
#include <QTreeView>
#include <QTextCodec>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
QDirModel *model = new QDirModel;
ui->treeView->setModel(model);
ui->treeView->setRootIndex(model->index("c:\"));
}
MainWindow::~MainWindow()
{
delete ui;
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSplitter>
#include <QDirModel>
#include <QTreeView>
#include <QTextCodec>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
/*
QDirModel *model = new QDirModel;
ui->treeView->setModel(model);
ui->treeView->setRootIndex(model->index("c:\"));
*/
QStandardItemModel *tree = new QStandardItemModel(0,3,this);
ui->treeView->setColumnWidth(0,50); // 设置列chang'd长度
ui->treeView->setColumnWidth(1,200);
ui->treeView->setColumnWidth(2,200);
tree->setHeaderData(0, Qt::Horizontal, tr("序号"));
tree->setHeaderData(1, Qt::Horizontal, tr("姓名"));
tree->setHeaderData(2, Qt::Horizontal, tr("年龄"));
ui->treeView->setModel(tree);
for (int i = 0; i < 4; ++i)
{
// 设置外层
QList<QStandardItem *> items;
for (int i = 0; i < 3; ++i)
{
QStandardItem *item = new QStandardItem(QString("%0").arg(i));
if (0 == i)
item->setCheckable(true);
items.push_back(item);
}
tree->appendRow(items);
// 设置内层
for (int i = 0; i < 2; ++i)
{
QList<QStandardItem *> childItems;
for (int i = 0; i < 3; ++i)
{
QStandardItem *item = new QStandardItem(QString("%0").arg(i));
if (0 == i)
item->setCheckable(true);
childItems.push_back(item);
}
items.at(0)->appendRow(childItems);
}
}
}
MainWindow::~MainWindow()
{
delete ui;
}
添加单列,单独添加。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSplitter>
#include <QDirModel>
#include <QTreeView>
#include <QTextCodec>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
/*
QDirModel *model = new QDirModel;
ui->treeView->setModel(model);
ui->treeView->setRootIndex(model->index("c:\"));
*/
QStandardItemModel *tree = new QStandardItemModel(0,3,this);
ui->treeView->setColumnWidth(0,50); // 设置列chang'd长度
ui->treeView->setColumnWidth(1,200);
ui->treeView->setColumnWidth(2,200);
tree->setHeaderData(0, Qt::Horizontal, tr("序号"));
tree->setHeaderData(1, Qt::Horizontal, tr("姓名"));
tree->setHeaderData(2, Qt::Horizontal, tr("年龄"));
ui->treeView->setModel(tree);
QList<QStandardItem *> ptr;
QStandardItem *item = new QStandardItem("1001");
ptr.push_back(item);
item = new QStandardItem("lyshark");
ptr.push_back(item);
item = new QStandardItem("22");
ptr.push_back(item);
tree->appendRow(ptr);
}