一篇文章带你看懂Qt MVC(模型、视图、代理)编程————附带详细图文和代码

0 背景

因为最近在做数据库、表格方面的应用程序,因此用到了很多这方面的知识,比如QTableView、QStandardItemModel、QItemSelectionModel、QSqlQUeryModel等等。查阅了很多资料,问了很多大佬,这篇文章就是为了方便日后再次使用这面知识时,能够快速查到对应的资料。

1 MVC知识

MV编程就是model(模型)、view(视图)、control(Delegate(代理))编程,就是把模型和视图分开来。
在这里插入图片描述

1. 1 视图

视图:例如QTableView、QListView、QTreeView,就是总体数据的呈现方式。
在这里插入图片描述
例如效果如下图。
在这里插入图片描述

1. 2 代理

代理:把视图里改变的值反馈到模型里;把模型里的数据库呈现到视图里。
在这里插入图片描述
例如:如下图里的商品名称和单位就是两个不同的代理,一个是QLineEdit(缺省时的默认代理),另一个是QComboBox。

在这里插入图片描述

1.3 模型

模型:分为数据模型和选择模型。

数据模型(组织数据的方式):
在这里插入图片描述
因为我主要跟数据库打交道,因此常用到的数据库为QStandardItemModel、QSqlQueryModel、QSqlTableModel、QSqlRelationTableModel。(注意带Abstarct为抽象类,要使用继承纯虚函数的子类才可以)

区别(下面的模型,越往下越高级):

  • 1,QStandardItemModel:每项数据可以存任何内容。【最灵活,但是需要手写代码把每个数据导入到模型中】
  • 2,QSqlQueryModel:用于sql查询的模型。(把sql语句查询后的全部结果自动导入模型中)
  • 3,QSqlTableModel:封装了QSqlQueryModel,一次只能读写一个数据表的模型。(处理表格时只需编写很少的代码来修改表格)
    *4,QSqlRelationTableModel:封装了QSqlTableModel,提供对外键的支持。

选择模型(选择/点击模型后的事件):
QAbstractItemModel:用于获得当前选择的model索引
例如:类似于下面的效果

图片取自(https://www.cnblogs.com/lvdongjie/p/4809484.html)

2 QTableVie数据呈现

2.1 使用数据模型

2.1.1 使用QStandardItemModel模型

  • 1, 定义模型
    QStandardItemModel *theModel;//数据模型
    QItemSelectionModel *theSelection;//选择模型
  • 2,初始化模型
 	theModel = new QStandardItemModel(this);//数据模型
    theSelection = new QItemSelectionModel(theModel);//选择模型
  • 3,设置模型
ui->TableView->setModel(theModel);//数据模型
ui->TableView->setSelectionModel(theSelection);//选择模型
  • 4,填充数据
    创建数据库连接并打开数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "connection1");
 db.setDatabaseName("/Users/mac/Qt/test.db");
 db.open();

填充

QSqlDatabase db = QSqlDatabase::database("connection1");//关联数据库
QSqlQuery query(db);//指定数据库
query("SELECT id,name,score FROM stundet");//查询数据库
int queryRow = 0;//统计查询的行数
while(query.next()){
	queryRow++;
}
query.seek(-1);//重置查询的位置到第一个数据的前一位
if(queryRow > 0){//查询到结果时进行填充
	//设置数据库的行数和列数
	theModel->setRowCount(queryRow);
	theModel->setColumnCount(8);
//设置表头
	thetModel->setHeaderData(0, Qt::Horizontal, QStringLiteral("id"));	//设置表头,如不设置则使用数据库中的默认表头
	theModel->setHeaderData(1, Qt::Horizontal, QStringLiteral("姓名"));
	theModel->setHeaderData(2, Qt::Horizontal, QStringLiteral("商成绩"));
	queryRow = 0;//重置,用于表示填充的行数
	while(query.next()){
		//获得模型的索引
		QModelIndex index = theModel->index(queryRow, 0);
		theModel->setData(index, query.value(0).toInt(), Qt::DisplayRole);
		
		index = theModel->index(row, 1);
        theModel->setData(index, query.value(1).toString(), Qt::DisplayRole);
		
		index = theModel->index(row, 1);
        theModel->setData(index, query.value(1).toDouble(), Qt::DisplayRole);
        queryRow++;//行数+1
	}
}

2.1.2 使用QSqlQueryModel模型

  • 1,定义模型
    QSqlQueryModel *sqlQueryModel;//数据模型
    QItemSelectionModel *theSelection;//选择模型
	
  • 2,初始化模型
    创建数据库连接并打开数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "connection1");
 db.setDatabaseName("/Users/mac/Qt/test.db");
 db.open();

初始化

    QSqlDatabase db = QSqlDatabase::database("connection1");
    sqlQueryModel = new QSqlQueryModel(this);
    theSelection = new QItemSelectionModel(sqlQueryModel);//初始化选择模型    
	 sqlQueryModel->setQuery("SELECT id,name,score FROM stundet");//初始化数据模型
	 //设置表头
	    sqlQueryModel->setHeaderData(0, Qt::Horizontal, tr("id"));
    sqlQueryModel->setHeaderData(1, Qt::Horizontal, tr("姓名"));
    sqlQueryModel->setHeaderData(2, Qt::Horizontal, tr("成绩"));	
    //设置模型
    ui->TableView->setModel(sqlQueryModel);
    ui->TableView->setSelectionModel(theSelection); 

2.2 QTableView的样式设计

隐藏最左边的列排序:

ui->TableView->verticalHeader()->hide();
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210121084719992.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzMzc1NTk4,size_16,color_FFFFFF,t_70)

在这里插入图片描述

在这里插入图片描述

设置不可编辑:

ui->TableView->setEditTriggers(QAbstractItemView::NoEditTriggers);

隔行变色:

ui->TableView->setAlternatingRowColors(true);

单选模式:

ui->TableView->setSelectionMode ( QAbstractItemView::SingleSelection);

根据控件长度调整表格的列宽:

ui->TableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

根据内容自适应调整列宽

    ui->theTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    ui->theTableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui->theTableView->horizontalHeader()->setMinimumSectionSize(102);/最小宽度

设置选中后高亮

    ui->TableView->horizontalHeader()->setHighlightSections(true);

设置代理为一个控件,如为QComboBox控件:

QComboBox *tempComboBox = new QComboBox(this);
tempComboBox->addItem("good");
tempComboBox->addItem("bad");
ui->TableView->setIndexWidget(theModel->index(row, 3),  tempComboBox);

如果需要获得QComBox中的值,可以使用:

static_cast<QComboBox *>(ui->TableView->indexWidget(theModel->index(i, 3)))->currentText()

2.3 QTableView事件

2.3.1 选择模型触发的事件

    //选择的项
    connect(theSelection, &QItemSelectionModel::currentChanged,
            this, &MainWindow::onCurrentChanged);
    //选择的索引
    connect(theSelection, &QItemSelectionModel::selectionChanged,
            this, &MainWindow::updateSelection);
void MainWindow::onCurrentChanged(const QModelIndex &current, const QModelIndex &previous){
//....
}

```cpp
void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected){
//...
}

2.3.2 点击模型触发的事件

双击

    connect(ui->TableView, &QTableView::doubleClicked,
            this, &QueryDialog::chooseQuery, Qt::UniqueConnection);// &QueryDialog::chooseQuery为自定义的槽函数

单击:

        connect(ui->newOrderTableView, &QTableView::clicked,
                [=](){
			//槽函数
        });

2.3.3 代理触发的事件

2.3.3.1 使用setIndexWidget

单位为法一的效果,删除为法二的效果:
在这里插入图片描述

法一:用于多个控件重复相同功能,传递参数

QComboBox *tempComboBox = new QComboBox(this);
tempComboBox->addItem("good");//个性化穿参数
tempComboBox->addItem("bad");
//关联事件
  connect(tempComboBox, &QComboBox::currentTextChanged,
                  this, &MainWindow::changeQComboBoxData);
ui->TableView->setIndexWidget(theModel->index(row, 3),  tempComboBox);

法二:用于多个控件重复相同功能,不传递参数
继承重写button

class DeleteToolButton : public QToolButton{
     Q_OBJECT
public:
    DeleteToolButton(QObject *parent = nullptr);
};
DeleteToolButton::DeleteToolButton(QObject *parent)
{
    this->setIcon(QIcon(":/Image/sc.png"));
    this->setStyleSheet("background: transparent");
}

添加并关联事件

    ui->TableView->setIndexWidget(goodsListModel->index(row, 7), new DeleteToolButton(this));
	//
    QList<QToolButton*> deleteToolButtonList =
            this->ui->goodsListTableView->findChildren<QToolButton*>();
    for(auto it = deleteToolButtonList.begin();it != deleteToolButtonList.end();it++){
        connect((*it), &QAbstractButton::clicked, this, &MainWindow::deleteRowData, Qt::UniqueConnection);
    }
2.3.3.1 使用QStyledItemDelegate

价格为效果
在这里插入图片描述

使用

QDoubleSpinDelegate     SpinDelegate;
ui->TableView->setItemDelegateForColumn(5, &SpinDelegate);

创建

class QDoubleSpinDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    QDoubleSpinDelegate(QObject *parent=0);

//自定义代理组件必须继承以下4个函数

//创建编辑组件
    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;
};

QWidget *QDoubleSpinDelegate::createEditor(QWidget *parent,
   const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //创建代理编辑组件
    Q_UNUSED(option);
    Q_UNUSED(index);

    QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setDecimals(2);
    editor->setMaximum(10000);

    return editor;  //返回此编辑器
}

void QDoubleSpinDelegate::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 QDoubleSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{ //将代理组件的数据,保存到数据模型中
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
    spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号
    int value = spinBox->value(); //获取spinBox的值

    model->setData(index, value, Qt::EditRole); //更新到数据模型
}

void QDoubleSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //设置组件大小
    Q_UNUSED(index);
    editor->setGeometry(option.rect);
}

只读代理:

class ReadOnlyDelegate: public QItemDelegate
{

public:
    ReadOnlyDelegate(QWidget *parent = NULL):QItemDelegate(parent)
    {}

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override //final
    {
        Q_UNUSED(parent)
        Q_UNUSED(option)
        Q_UNUSED(index)
        return NULL;
    }
};
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 岁月 设计师:pinMode 返回首页