一、Model/View结构
- 数据:如数据库的一个数据表或者SQL查询结果,如内存中的数据类对象,或者磁盘文件结构等
- Model:与数据通信,并作为视图组件提供数据接口
- View:屏幕界面组件,视图从数据模型获得每个数据项的模型索引,通过数据模型索引(model index)获取数据
- 代理:定制数据的界面或编辑方式,在标准的视图组件中,代理功能显示一个数据,当数据被编辑时,提供一个编辑器,一般时QLineEdit。
模型、视图和代理之间使用信号和槽通信
1、数据模型
- QStringListModel:用于处理字符串列表数据的数据模型类
- QStandardItemModel:标准的基于数据项的数据模型类,每个数据项都可以是任何数据类型
- QFileSystemModel:计算机上文件系统的数据模型类
- QSortFilterProxyModel:与其他数据模型结合,提供排序和过滤功能的数据类型模型类
- QSelQueryModel:用于数据库SQL查询结果的数据模型类
- QSqlTableModel:用于数据库的一个数据表的数据模型类
- QSqlRelationTableModel:用于关系类型数据表的数据模型
继承关系
QAbstractItemmodelQAbstractListModelQStringListModelQAbstractProxyModelQSortFilterProxyModelQAbstractTableModelQSqlQueryModelQSlTableModelQSlRelationTableModelQStandardItemModelQFileSystemModel
2、视图组件
显示数据时,只需要调用视图类的setModel()函数
QAbstractItemViewQListViewQListWidgetQTableViewQTableWidgetQTreeViewQTreeWidgetQColumnViewQHeaderView
3、代理
代理时在视图组件上为编辑数据提供编辑器,QAbstractItemDeleget是所有代理类的基类。
二、QFileSystemModel
如Windows则资源管理器,使用QFileSystemModel童工的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获取文件的详细信息。
1、实现工具
(1)创建项目,基于QMainWindow
(2)添加组件
(3)实现功能
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);fileModel = new QFileSystemModel(this);fileModel->setRootPath(QDir::currentPath());ui->treeView->setModel(fileModel);ui->listView->setModel(fileModel);ui->tableView->setModel(fileModel);ui->tableView->verticalHeader()->setVisible(false);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->labelName->setText(fileModel->fileName(index));unsigned int sz = fileModel->size(index)/1024;if(sz < 1024){ui->labelSize->setText(QString::asprintf("%d KB", sz));}else{ui->labelSize->setText(QString::asprintf("%.2f MB", (float)sz/1024));}ui->labelType->setText(fileModel->type(index));ui->labelPath->setText(fileModel->filePath(index));ui->checkBox->setChecked(fileModel->isDir(index));
}
三、QStringListModel
1、实现工具
(1)创建项目,基于Widget
(2)添加组件
(3)添加功能
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);theModel = new QStringListModel(this);QStringList strList;strList<<"Item1"<<"Item2"<<"Item3"<<"Item4"<<"Item5"<<"Item6";theModel->setStringList(strList);ui->listView->setModel(theModel);// 编辑:双击或者选择项单击ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::SelectedClicked);
}Widget::~Widget()
{delete ui;
}void Widget::on_btnDisplay_clicked()
{QStringList strList = theModel->stringList();foreach (auto str, strList) {ui->plainTextEdit->appendPlainText(str);}
}void Widget::on_btnClear_clicked()
{ui->plainTextEdit->clear();
}void Widget::on_btnInitList_clicked()
{QStringList strList;strList<<"Item1"<<"Item2"<<"Item3"<<"Item4"<<"Item5"<<"Item6";theModel->setStringList(strList);
}void Widget::on_btnListClear_clicked()
{theModel->removeRows(0, theModel->rowCount());
}void Widget::on_btnListDelete_clicked()
{theModel->removeRow(ui->listView->currentIndex().row());
}void Widget::on_btnListAppend_clicked()
{theModel->insertRow(theModel->rowCount());QModelIndex index = theModel->index(theModel->rowCount()-1, 0); // 添加之后count会加一theModel->setData(index, "new Item", Qt::DisplayRole);ui->listView->setCurrentIndex(index);
}void Widget::on_btnInsert_clicked()
{theModel->insertRow(ui->listView->currentIndex().row());QModelIndex index = theModel->index(ui->listView->currentIndex().row()-1, 0); // 插入之后count会加一theModel->setData(index, "insert Item", Qt::DisplayRole);ui->listView->setCurrentIndex(index);
}void Widget::on_listView_clicked(const QModelIndex &index)
{ui->label->setText(QString::asprintf("行:%d列:%d", index.row(), index.column()));
}
四、QStandardItemModel
以项数据为基础的标准数据模型类,通常与QTableView组合成为Model/View结构,实现通用的二位数据管理功能。
- QStandardItemModel:可以处理二维数。每一项是一个QStandardItem类的变量,用于存储项的数据、字体格式、对其方式等
- QTableView:一个单元格显示QStandardItemModel数据模型中的一项。
- QItemSelectionModel:跟踪视图组件的单元格选择状态的类,通过QItemSelectionModel可以获得选中的单元格的模型索引。
1、实现工具
(1)创建项目,基于QMainWindow
(2)添加图标资源文件、添加组件
(3)设置文本不自动换行
(4)设置信号与槽
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);labCurrFile = new QLabel("当前文件", this);labCellPos = new QLabel("当前单元格", this);labCellText = new QLabel("单元格内容", this);theModel = new QStandardItemModel(10, fixedColoumCount,this);theSelection = new QItemSelectionModel(theModel);ui->tableView->setModel(theModel);ui->tableView->setSelectionModel(theSelection);labCurrFile->setMinimumWidth(200);labCellPos->setMinimumWidth(150);labCellText->setMinimumWidth(200);ui->statusBar->addWidget(labCurrFile);ui->statusBar->addWidget(labCellPos);ui->statusBar->addWidget(labCellText);connect(theSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex)),this, SLOT(on_currentChanged(QModelIndex,QModelIndex)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{Q_UNUSED(previous)if(! current.isValid()){return;}QStandardItem *item = theModel->itemFromIndex(current);labCellPos->setText(QString::asprintf("当前单元格:%d行:%d列", current.row(), current.column()));labCellText->setText("单元格内容:"+item->text());QFont font = item->font();ui->actFontBold->setChecked(font.bold());
}void MainWindow::initModelFromStringList(QStringList &fileContent)
{theModel->setRowCount(fileContent.count()-1);// 表头QString header = fileContent.at(0);QStringList headerList = header.split(",");theModel->setHorizontalHeaderLabels(headerList);// 表格数据int col;QStandardItem *item;for (int row = 1; row < fileContent.count(); ++row) {QString lineText = fileContent.at(row);QStringList colList = lineText.split(",");for (col = 0; col < fixedColoumCount-1; ++col) {item = new QStandardItem(colList.at(col));theModel->setItem(row-1, col, item);}item = new QStandardItem(headerList.at(col));if(colList.at(col) == "1"){item->setCheckState(Qt::Checked);}else{item->setCheckState(Qt::Unchecked);}theModel->setItem(row-1, col, item);}
}#include <QFileDialog>
#include <QFile>
#include <QTextStream>
void MainWindow::on_actOpen_triggered()
{QString strCurrPath = QCoreApplication::applicationDirPath();QString strFileName = QFileDialog::getOpenFileName(this, "打开一个文件",strCurrPath, "数据文件(*.txt);;所有文件(*.*)");if(strFileName.isEmpty()){return;}QStringList fileContent;QFile file(strFileName);if(file.open(QIODevice::ReadOnly|QIODevice::Text)){QTextStream stream(&file);ui->plainTextEdit->clear();while (!stream.atEnd()) {QString str = stream.readLine();ui->plainTextEdit->appendPlainText(str);if(!str.isEmpty()){fileContent.append(str);}}file.close();this->labCurrFile->setText("当前文件:"+strFileName);initModelFromStringList(fileContent);ui->tableView->resizeColumnsToContents();ui->tableView->resizeRowsToContents();ui->actSave->setEnabled(true);ui->actAppend->setEnabled(true);ui->actDelete->setEnabled(true);ui->actInsert->setEnabled(true);}
}void MainWindow::on_actSave_triggered()
{QString strCurrPath = QCoreApplication::applicationDirPath();QString strFileName = QFileDialog::getSaveFileName(this, "另存为文件",strCurrPath, "数据文件(*.txt);;所有文件(*.*)");if(strFileName.isEmpty()){return;}QStringList fileContent;QFile file(strFileName);if(file.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)){QTextStream stream(&file);ui->plainTextEdit->clear();QString str;int i;for (i = 0; i < theModel->columnCount()-1; ++i) {QStandardItem *item = theModel->horizontalHeaderItem(i);str += item->text()+",";}QStandardItem *item = theModel->horizontalHeaderItem(i);str += item->text();stream<<str<<"\n";ui->plainTextEdit->appendPlainText(str);int j;for (int i = 0; i < theModel->rowCount(); ++i) {str = "";for (j = 0; j < theModel->columnCount()-1; ++j) {QStandardItem *item = theModel->item(i,j);str += item->text()+",";}QStandardItem *item = theModel->item(i,j);if(item->checkState() == Qt::Checked){str += "1";}else{str += "0";}stream<<str<<"\n";ui->plainTextEdit->appendPlainText(str);}file.close();}
}void MainWindow::on_actModelData_triggered()
{ui->plainTextEdit->clear();QString str;for (int i = 0; i < theModel->columnCount(); ++i) {QStandardItem *item = theModel->horizontalHeaderItem(i);str += item->text()+",";}ui->plainTextEdit->appendPlainText(str);int j;for (int i = 0; i < theModel->rowCount(); ++i) {str = "";for (j = 0; j < theModel->columnCount()-1; ++j) {QStandardItem *item = theModel->item(i,j);str += item->text()+",";}QStandardItem *item = theModel->item(i,j);if(item->checkState() == Qt::Checked){str += "1";}else{str += "0";}ui->plainTextEdit->appendPlainText(str);}
}void MainWindow::on_actAppend_triggered()
{QList<QStandardItem*> itemList;QStandardItem *item;for (int i = 0; i < fixedColoumCount-1; ++i) {item = new QStandardItem("0");itemList << item;}QString str = theModel->headerData(theModel->columnCount()-1,Qt::Horizontal, Qt::DisplayRole).toString();item = new QStandardItem(str);item->setCheckState(Qt::Checked);itemList << item;theModel->appendRow(itemList);theSelection->clearSelection();QModelIndex index = theModel->index(theModel->rowCount()-1, 0);theSelection->setCurrentIndex(index, QItemSelectionModel::Select);
}void MainWindow::on_actInsert_triggered()
{QList<QStandardItem*> itemList;QStandardItem *item;for (int i = 0; i < fixedColoumCount-1; ++i) {item = new QStandardItem("0");itemList << item;}QString str = theModel->headerData(theModel->columnCount()-1,Qt::Horizontal, Qt::DisplayRole).toString();item = new QStandardItem(str);item->setCheckState(Qt::Checked);itemList << item;theModel->insertRow(theSelection->currentIndex().row(), itemList);theSelection->clearSelection();QModelIndex index = theModel->index(theSelection->currentIndex().row(), 0);theSelection->setCurrentIndex(index, QItemSelectionModel::Select);
}void MainWindow::on_actDelete_triggered()
{if(theSelection->currentIndex().row() ==theModel->rowCount()-1){theModel->removeRow(theSelection->currentIndex().row());}else{theModel->removeRow(theSelection->currentIndex().row());QModelIndex index = theModel->index(theSelection->currentIndex().row(), 0);theSelection->setCurrentIndex(index, QItemSelectionModel::Select);}}void MainWindow::on_actAlignLeft_triggered()
{if(!theSelection->hasSelection()){return;}QModelIndexList selectedIndexes = theSelection->selectedIndexes();for (int i = 0; i < selectedIndexes.count(); ++i) {QModelIndex index = selectedIndexes.at(i);QStandardItem *item = theModel->itemFromIndex(index);item->setTextAlignment(Qt::AlignLeft);}
}void MainWindow::on_actAlignCenter_triggered()
{if(!theSelection->hasSelection()){return;}QModelIndexList selectedIndexes = theSelection->selectedIndexes();for (int i = 0; i < selectedIndexes.count(); ++i) {QModelIndex index = selectedIndexes.at(i);QStandardItem *item = theModel->itemFromIndex(index);item->setTextAlignment(Qt::AlignCenter);}
}void MainWindow::on_actAlignRight_triggered()
{if(!theSelection->hasSelection()){return;}QModelIndexList selectedIndexes = theSelection->selectedIndexes();for (int i = 0; i < selectedIndexes.count(); ++i) {QModelIndex index = selectedIndexes.at(i);QStandardItem *item = theModel->itemFromIndex(index);item->setTextAlignment(Qt::AlignRight);}
}void MainWindow::on_actFontBold_triggered(bool checked)
{if(!theSelection->hasSelection()){return;}QModelIndexList selectedIndexes = theSelection->selectedIndexes();for (int i = 0; i < selectedIndexes.count(); ++i) {QModelIndex index = selectedIndexes.at(i);QStandardItem *item = theModel->itemFromIndex(index);QFont font = item->font();font.setBold(checked);item->setFont(font);}
}
五、自定义代理
代理是介于Model和View之间,主要用于数据修改。
- QAbstractItemDelegate:所有代理类的查抽象基类
- QStyledItemDelegatte视图组件使用的缺省的代理类,可以使用当前样式表设置来绘制组件,建议使用此类
- QItemDelegate:类似与QStyledItemDelegate,不支持使用当前样式绘制组件
QAbstractItemDelegateQStyledItemDelegateQItemDelegateQSqlRelationalDelegate
必须实现以下几个函数:
- createEditor():创建用于编辑模型数据的widget组件
- setEditorData():从数据模型获取数据,供widget组件进行编辑
- setModelData():将widget上的数据更新到数据模型
- updateEditorGeometry():用于给widget组件设置一个合适的大小
1、实现功能
(1)拷贝上一个项目
(2)添加类
(3)实现类功能
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);labCurrFile = new QLabel("当前文件", this);labCellPos = new QLabel("当前单元格", this);labCellText = new QLabel("单元格内容", this);theModel = new QStandardItemModel(10, fixedColoumCount,this);theSelection = new QItemSelectionModel(theModel);ui->tableView->setModel(theModel);ui->tableView->setSelectionModel(theSelection);labCurrFile->setMinimumWidth(200);labCellPos->setMinimumWidth(150);labCellText->setMinimumWidth(200);ui->statusBar->addWidget(labCurrFile);ui->statusBar->addWidget(labCellPos);ui->statusBar->addWidget(labCellText);ui->tableView->setItemDelegateForColumn(0, &intSpinDelegate); //给0列设置代理connect(theSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex)),this, SLOT(on_currentChanged(QModelIndex,QModelIndex)));
}
#include "qintdelegate.h"
#include <QSpinBox>
QIntDelegate::QIntDelegate()
{}QWidget *QIntDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{Q_UNUSED(index)Q_UNUSED(option)QSpinBox *edit = new QSpinBox(parent);edit->setMinimum(0);edit->setMaximum(1000);edit->setFrame(false); //无边框return edit;
}void QIntDelegate::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 QIntDelegate::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 QIntDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{Q_UNUSED(index)editor->setGeometry(option.rect);
}