五、Model与View

发布时间:2024年01月16日

一、Model/View结构

  • 数据:如数据库的一个数据表或者SQL查询结果,如内存中的数据类对象,或者磁盘文件结构等
  • Model:与数据通信,并作为视图组件提供数据接口
  • View:屏幕界面组件,视图从数据模型获得每个数据项的模型索引,通过数据模型索引(model index)获取数据
  • 代理:定制数据的界面或编辑方式,在标准的视图组件中,代理功能显示一个数据,当数据被编辑时,提供一个编辑器,一般时QLineEdit。

模型、视图和代理之间使用信号和槽通信

1、数据模型

  • QStringListModel:用于处理字符串列表数据的数据模型类
  • QStandardItemModel:标准的基于数据项的数据模型类,每个数据项都可以是任何数据类型
  • QFileSystemModel:计算机上文件系统的数据模型类
  • QSortFilterProxyModel:与其他数据模型结合,提供排序和过滤功能的数据类型模型类
  • QSelQueryModel:用于数据库SQL查询结果的数据模型类
  • QSqlTableModel:用于数据库的一个数据表的数据模型类
  • QSqlRelationTableModel:用于关系类型数据表的数据模型
继承关系
QAbstractItemmodel
	QAbstractListModel
		QStringListModel
	QAbstractProxyModel
		QSortFilterProxyModel
	QAbstractTableModel
		QSqlQueryModel
			QSlTableModel
				QSlRelationTableModel
	QStandardItemModel
	QFileSystemModel

2、视图组件

显示数据时,只需要调用视图类的setModel()函数
QAbstractItemView
	QListView
		QListWidget
	QTableView
		QTableWidget
	QTreeView
		QTreeWidget
	QColumnView
	QHeaderView

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 &current, 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,不支持使用当前样式绘制组件
QAbstractItemDelegate
	QStyledItemDelegate
	QItemDelegate
		QSqlRelationalDelegate
必须实现以下几个函数:
  • 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);
}

在这里插入图片描述

文章来源:https://blog.csdn.net/liutit/article/details/135588920
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。