当前位置: 首页 > article >正文

Qt 模型Model/视图View/代理Delegate

一、模型/视图/代理架构简介
1、 应用程序中往往要存储大量的数据,并对它们进行处理,然后可以通过各种形式显示给用户,用户需要时还可以对数据进行编辑。

2、 Qt中的模型/视图架构就是用来实现大量数据的存储、处理及其显示的。

3、 MVC设计模式:
(1)、模型(Model)是应用对象,用来表示数据;
(2)、视图(View)是模型的用户界面,用来显示数据;
(3)、控制(Controller)定义了用户界面对用户输入的反应方式。

4、 Qt的模型/视图架构:
(1)、将MVC设计模式中视图和控制两种组件结合起来,就形成了Qt的模型/视图架构。
(2)、这同样将数据的存储和数据向用户的展示进行了分离,但提供了更为简单的框架。
(3)、相同的数据可以在多个不同的视图上显示。

5、 Qt的模型/视图/代理架构(MVD):
(1)、为了对用户输入进行灵活处理,引入了委托/代理Delegate。
(2)、模型(Model)与数据源进行通信,为架构中的其它组件提供了接口。
(3)、视图(View)从模型中获得模型索引(Model Index,用来表示数据项),进行渲染显示。
(4)、委托/代理(Delegate),可以定制数据项的渲染和编辑方式。用户编辑数据项时,代理使用模型索引直接与模型进行通信。
*模型/视图/代理通过信号和槽进行通信。
*数据源的数据发生改变时,模型发出信号通知视图。
*用户操作界面时,视图发出信号来提供交互信息。
*当编辑数据项时,委托/代理发出信号,告知模型和视图编辑器的状态。

6、 Qt的模型/视图/代理架构,这三个组件的每一个都使用了一个抽象基类来定义,提供了一些通用接口和一些功能的默认实现。

二、模型
1、 所有的模型类都基于QAbstractItemModel类,这个类提供的接口,可以供视图和代理来访问数据。

2、 注意,数据本身并不一定要存储在模型中,可以存储在一个数据结构、独立的类、文件、数据库或其他一些组件中。

3、 Qt提供了一些现成的模型来处理数据:
(1)、QAbstractListModel为基于列表的数据结构提供了一些常见功能的默认实现,可以被子类化提供模型。
(2)、QAbstractTableModel为基于表格的数据结构提供了一些常见功能的默认实现,可以被子类化提供模型。
(3)、QStandardItemModel用于管理复杂的树形结构数据项,每一个数据项可以包含任意的数据。注意没有QAbstractTreeModel类哦!!!
(4)、QStringListModel用来存储一个简单的QString项目列表。
(5)、QFileSystemModel提供了本地文件系统中文件和目录的信息。
(6)、QSqlQueryModel、QSqlTableModel和QSqlRelationTableModel用来访问数据库。

4、 注意,无论数据项是怎样存储在何种底层数据结构中的,QAbstractItemModel子类都会以层次结构来表示数据,视图按照这种约定来访问模型中的数据项。

5、 常见的3种模型分别是列表模型、表格模型、树模型,它们都是层次结构。

6、 模型索引:
(1)、每一块可以通过模型获取的数据都使用一个模型索引来表示,视图和代理都使用这些模型索引来请求数据项并显示。
(2)、模型索引由QModelIndex类提供,它是对一块数据的临时引用,可以用来检索或者修改模型中的数据。因为临时引用,所以不需要存储模型索引。
(3)、如果需要对一块数据进行长时间的引用,则必须使用QPersistentModelIndex创建模型索引。(不经常用)
(4)、模型索引包含一个指针,指向创建它们的模型,使用多个模型时可以避免混淆。
(5)、如果要获得一个数据项的模型索引,则必须指定模型的3个属性:行号、列号、父项的模型索引,
如: QModelIndex index = model->index(row, column, parent);。

7、 行和列:
(1)、列表模型、表格模型中,有一个不存储数据的根项(root item)。
(2)、列表模型和表格模型,都可以看作表格来访问,每个数据项可以使用行号、列号来定位,行号和列号都是从0开始。
(3)、注意,列表模型和表格模型的所有数据项都是以根项(root item)为父项,这些数据项被称为顶层数据项(top level item)。
(4)、获取顶层数据项的模型索引时,父项模型索引可以用QModelIndex()表示,
如: QModelIndex indexA = model->index(0,0,QModelIndex());。

8、 父项:
(1)、树模型中,有一个不存储数据的根项(root item)。
(2)、树模型中,每一个数据项都可能成为其它数据项表格的父项。
(3)、当为数据项请求一个索引时,除了提供行号和列号,还必须提供该数据项的父项模型索引。
(4)、注意,当一个数据项A成为父项时,A的子数据项表格又是从row为0、column为0开始。

9、 项角色:
(1)、通过向模型指定数据项的模型索引以及特定角色,可以获取需要的类型的数据,如: QVariant value = model->data(index, role);。
(2)、数据项可以包含不同角色的数据,这些角色由枚举类型Qt::ItemDataRole定义。
(3)、角色指出了从模型数据项中获取哪种类型的数据,也可以为视图和代理提供提示,告知数据应该怎样展现给用户。
(4)、常用的角色类型:
-> Qt::DisplayRole,数据为QString类型,数据被渲染为文本。
-> Qt::DecorationRole,数据为QColor、QIcon、QPixmap等类型,数据被渲染为图标等装饰。
-> Qt::EditRole,数据为QString类型,数据可以在编辑器中进行编辑。
-> Qt::ToolTipRple,数据为QString类型,数据显示在数据项的工具提示中。
-> Qt::StatusTipRole,数据为QString类型,数据显示在状态栏中。
-> Qt::SizeHintRole,数据为QSize类型,数据项的大小提示,将会应用到视图。

10、 为已存在的数据结构创建新的模型,考虑使用哪种类型的模型?
(1)、若数据结构可以表示为项目列表或者表格,可以子类化QAbstractListModel或QAbstractTableModel。
(2)、若数据结构只能表示具有层次的树结构,可以子类化QAbstractItemModel。(QStandardItemModel管理树结构,不是抽象类)

11、 总结,模型类中就是将各种操作转换为对具体数据源的操作,从而对外提供了一个统一的接口供用户使用。

三、视图简介
1、 所有的视图类都基于QAbstractItemView类。

2、 Qt提供了几种不同类型的视图:
(1)、QListView将模型的数据项显示为一个列表。
(2)、QTableView将模型的数据项显示在一个表格中。
(3)、QTreeView将模型的数据项显示在具有层次的列表(树)中。

3、 视图的作用:
(1)、管理从模型获取的数据的整体布局。
(2)、处理数据项间的导航。
(3)、处理数据项的选择(选择行为、选择模式)。
(4)、实现了上下文菜单、拖放等基本的用户接口特性。
(5)、可以为数据项提供默认的编辑,也可以和代理一起提供一个自定义的编辑器。

4、 QTableView和QTreeView显示标头;QListView不用显示标头。

5、 QHeaderView是标头类,使用QAbstractItemModel::headerData()从模型中获取数据,然后用标签显示标头信息。

6、 视图类的选择行为(QAbstractItemView::SelectionBehavior):
(1)、QAbstractItemView::SelectItems 选择单个数据项。
(2)、QAbstractItemView::SelectRows 选择行。
(3)、QAbstractItemView::SelectColumns 选择列。

7、 视图类的选择模式(QAbstractItemView::SelectionMode)
(1)、QAbstractItemView::SingleSelection 用户选择了一个(数据项/行/列),则其它已经选择的(数据项/行/列)成为未选择状态,且无法在已经选择的(数据项/行/列)上单击来取消选择。
(2)、QAbstractItemView::ContiguousSelection 用户在单击一个项目的同时按着Shift键,则所有当前项目和单击项目之间的项目都将被选择或者取消选择,这依赖于被单击项目的状态。
(3)、QAbstractItemView::ExtendedSelection 和ContiguousSelection一样特性,也可以按Ctrl键,进行不连续的选择。
(4)、QAbstarctItemView::MultiSelection 用户选择一个项目时不影响其它已经选择的项目。
(5)、QAbstractItemView::NoSelection 项目无法被选择。

8、 处理项目选择:
(1)、在视图中被选择的数据项信息存储在一个QItemSelectionModel实例中。
(2)、选择可以看作是在选择模型QItemSelectionModel中保存的一个模型索引集合。
(3)、标准视图类提供了默认的选择模型,可以通过selectionModel()函数获得,多个视图可以使用setSelectionModel()函数共享该选择模型(此时多个视图共享选择)。
(4)、QItemSelection是一个项目选择块,需要指定它的左上角和右下角数据项索引。
(5)、QItemSelectionModel::SelectionFlag,选择标志,是选择模型更新时的方式,比如QItemSelectionModel::Select表明所有指定的索引都将被选择; 比如QItemSelectionModel::Toggle表明会将指定索引的当前状态切换为相反的状态;等。
(6)、视图类也提供了几种比较方便的函数来进行选择,如selectAll()、selectColumn()、selectColumns()、selectRow()、selectRows()等。

9、 当前项目和被选择的项目:
(1)、视图中当前项目和被选择的项目,是两个独立的状态。
(2)、视图负责确保总是有一个项目作为当前项目来实现键盘导航。
(3)、只能有一个当前项目,可以有多个被选择的项目。
(4)、使用键盘导航键或鼠标按键可以改变当前项目。
(5)、按下F2键或者双击鼠标都可以编辑当前项目。
(6)、当前项目会显示焦点矩形(蚂蚁线),被选择的项目会使用选择矩形来表示。

四、代理简介
1、 所有的代理类都基于QAbstractItemDelegate类。

2、 Qt提供了几种不同类型的代理:
*QStyledItemDelegate使用当前的样式来为视图中的项目绘制和提供编辑器。
*QItemDelegate为视图中的项目绘制和提供编辑器,与QStyledItemDelegate是相互独立的。

3、 代理的作用:
(1)、渲染视图中的独立数据项(代理通过实现paint()和sizeHint()来渲染它们自身的内容);
(2)、提供数据项的输入编辑功能。

4、 简单的基于部件的代理,可以通过子类化QStyleItemDelegate或QItemDelegate来实现,不需要使用QAbstractItemDelegate,这样可以使用默认的函数实现。

5、 代理的编辑器可以通过两种方式来实现,一种使用部件来管理编辑过程(例子八),另一种直接处理事件。

6、 代理在视图中,Qt的标准视图QListView、QTableView、QTreeView都使用QItemDelegate实例,QItemDelegate的默认接口实现为标准视图的每一个数据项提供了普通风格的渲染和编辑。

7、 使用视图的itemDelegate()函数获取视图中使用的代理;使用视图的setItemDelegate()函数为视图安装一个自定义代理。

五、简单例子: QFileSystemModel和QListView、QTreeView一起使用,来分别显示同一个目录中内容。

#include <QtWidgets/QApplication>
#include <QFileSystemModel>
#include <QListView>
#include <QTreeView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
   
	//创建文件系统模型
	QFileSystemModel model;
	//指定要监视的目录
	model.setRootPath(QDir::currentPath());

	//创建树型视图
	QTreeView tree;
	//为视图指定模型
	tree.setModel(&model);
	//指定根索引
	tree.setRootIndex(model.index(QDir::currentPath()));

	//创建列表视图
	QListView list;
	//为视图指定模型
	list.setModel(&model);
	//指定根索引
	list.setRootIndex(model.index(QDir::currentPath()));

	tree.show();
	list.show();

    return a.exec();
}

六、模型举例: QStandardItemModel

#include <QtWidgets/QApplication>
#include <QTreeView>
#include <QDebug>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

	//创建标准项模型(树模型)
	QStandardItemModel model;
	//获取模型的根项(root item),根项是不可见的
	QStandardItem * parentItem = model.invisibleRootItem();

	//创建item0项,顶层数据项, row = 0, column = 0, 并设置显示文本A、图标红色、工具提示indexA。
	QStandardItem * item0 = new QStandardItem;
	item0->setText("A");        
	QPixmap pixmap0(50, 50);
	pixmap0.fill("red");
	item0->setIcon(QIcon(pixmap0));
	item0->setToolTip("indexA");
	parentItem->appendRow(item0);

	//创建item1项,作为item0的子项,row = 0, column = 0, 并设置显示文本B、图标蓝色、工具提示indexB。
	QStandardItem * item1 = new QStandardItem;
	item1->setText("B");
	QPixmap pixmap1(50, 50);
	pixmap1.fill("blue");
	item1->setIcon(QIcon(pixmap1));
	item1->setToolTip("indexB");
	item0->appendRow(item1);

	//创建item2项,作为item0的子项,row = 1, cloumn = 0, 并用新的方式设置显示文本C、图标绿色、工具提示indexC。
	QStandardItem * item2 = new QStandardItem;
	item2->setData("C", Qt::EditRole); //双击是可以编辑的
	QPixmap pixmap2(50, 50);
	pixmap2.fill("green");
	item2->setData(QIcon(pixmap2), Qt::DecorationRole);
	item2->setData("indexC", Qt::ToolTipRole);
	item0->appendRow(item2);

	//在树视图中显示模型
	QTreeView view;
	view.setModel(&model);
	view.show();

	//获取数据
	QModelIndex indexA = model.index(0, 0, QModelIndex());
	qDebug() << "indexA row count: " << model.rowCount(indexA);

	QModelIndex indexB = model.index(0, 0, indexA);
	qDebug() << "indexB text:" << model.data(indexB, Qt::EditRole).toString();
	qDebug() << "indexB tooltip:" << model.data(indexB, Qt::ToolTipRole).toString();
	
    return a.exec();
}

七、自定义模型举例:数据源为QStringList,自定义列表模型,重写各种接口,然后在表格视图和列表视图中显示、编辑、操作。

//StringListModel.h
#pragma once

#include <QAbstractListModel>
#include <QStringList>

//QAbstractListModel本身不存储任何数据, 仅提供一些接口供视图访问数据。
//因为模型是列表模型,不是树模型,所以不用考虑父子关系,所以不用实现index()和parent()。
class StringListModel : public QAbstractListModel
{
	Q_OBJECT

public:
	StringListModel(const QStringList & strings, QObject * parent = 0) : QAbstractListModel(parent), stringList(strings) {}

	int rowCount(const QModelIndex & parent = QModelIndex()) const;
	QVariant data(const QModelIndex & index, int role) const;
	//提供标头内容, QTreeView和QTableView会显示标头;QListView不显示标头。
	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
	//让代理知道数据项的状态。此处想要数据项可编辑。代理创建编辑器之前会检测数据项是否可编辑,所以模型的flags()得让代理知道数据项是可编辑的,因此flags()内部会为每一个数据项返回一个正确的标识。
	Qt::ItemFlags flags(const QModelIndex & index) const;
	//不用知道代理怎样执行编辑操作,只需要为代理向模型中设置数据提供一条路径即可,即setData()。
	bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
	//为视图调用。index为父项索引,position为将在此位置之前插入,rows插入的总行数。
	bool insertRows(int position, int rows, const QModelIndex & index = QModelIndex());
	//为视图调用。index为父项索引,position为将从此位置开始删除,rows删除的总行数。
	bool removeRows(int position, int rows, const QModelIndex & index = QModelIndex());

private:
	QStringList stringList; //内部数据源
};


//StringListModel.cpp
#include "StringListModel.h"

//列表项都是顶层数据项,此处简单忽略了parent索引。
int StringListModel::rowCount(const QModelIndex & parent) const
{
	return stringList.count();
}

//当提供的index是有效的,且行号在字符串列表的size范围内,且角色是支持的角色之一,返回一个有效的QVariant值。
QVariant StringListModel::data(const QModelIndex & index, int role) const
{
	if (!index.isValid())
	{
		return QVariant();
	}

	if (index.row() >= stringList.size() || index.row() < 0)
	{
		return QVariant();
	}

	if (role == Qt::DisplayRole || role == Qt::EditRole)
	{
		return stringList.at(index.row());
	}
	else
	{
		return QVariant();
	}
}

QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (role != Qt::DisplayRole)
	{
		return QVariant();
	}

	if (orientation == Qt::Horizontal)
	{
		return QString("Column %1").arg(section);
	}
	else
	{
		return QString("Row %1").arg(section);
	}
}

Qt::ItemFlags StringListModel::flags(const QModelIndex & index) const
{
	if (!index.isValid())
	{
		return Qt::ItemIsEnabled;
	}

	return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

bool StringListModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
	if (index.isValid() 
		&& index.row() >= 0 && index.row() < stringList.size()
		&& role == Qt::EditRole)
	{
		stringList.replace(index.row(), value.toString());

		//当数据被设置后,模型必须让视图知道数据已经改变了,通过发射dataChanged()信号实现。
		//因为即使发送信号,所以当在一个视图中更改数据,另一个视图也会立马更新数据。
		emit dataChanged(index, index);
		return true;
	}

	return false;
}

bool StringListModel::insertRows(int position, int rows, const QModelIndex & index)
{
	//告知其它组件(视图),指定的行将要发生改变,新行的第一行行号为position,最后一行行号为position + rows -1。
	beginInsertRows(QModelIndex(), position, position + rows - 1);
	for (int row = 0; row < rows; ++row)
	{
		stringList.insert(position, "");
	}
	//告知其它组件(视图),该模型的大小发生了变化。
	endInsertRows();
	return true;
}

bool StringListModel::removeRows(int position, int rows, const QModelIndex & index)
{
	beginRemoveRows(QModelIndex(), position, position + rows - 1);
	for (int row = 0; row < rows; ++row)
	{
		stringList.removeAt(position);
	}
	endRemoveRows();
	return true;
}


//main.cpp
#include <QtWidgets/QApplication>
#include "StringListModel.h"
#include <QListView>
#include <QTableView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

	QStringList list;
	list << "a" << "b" << "c";

	StringListModel model(list);

	QListView listView;
	listView.setModel(&model);
	listView.show();

	QTableView tableView;
	tableView.setModel(&model);
	tableView.show();

	//没有为视图写插入/删除的响应函数,故在此调用测试。
	model.insertRows(3, 2);
	model.removeRows(0, 1);
	
    return a.exec();
}

八、视图和代理举例:

//MainWindow.h
#pragma once

#include <QMainWindow>
#include "ui_MainWindow.h"

class QTableView;
class QItemSelection;
class QModelIndex;

class MainWindow : public QMainWindow
{
	Q_OBJECT

public:
	MainWindow(QWidget *parent = nullptr);
	~MainWindow();

public slots:
	//输出当前数据项的内容
	void getCurrentItemData();
	//切换选择的数据项
	void toggleSelection();

	//选择的数据项变化(selected:新的选择的数据项, deselected:先前选择的项目)
	void updateSelection(const QItemSelection & selected, const QItemSelection & deselected);
	//当前数据项变化(current:新的当前项的索引, previous:先前的当前项的索引)
	void changeCurrent(const QModelIndex & current, const QModelIndex & previous);

private:
	Ui::MainWindowClass ui;
	QTableView * tableView;
	QTableView * tableView2;
};


//MainWindow.cpp
#include "MainWindow.h"
#include <QTableView>
#include <QStandardItemModel>
#include <QDebug>
#include "SpinBoxDelegate.h"

MainWindow::MainWindow(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

	QStandardItemModel * model = new QStandardItemModel(7, 4, this);
	for (int row = 0; row < 7; ++row)
	{
		for (int column = 0; column < 4; column++)
		{
			QStandardItem * item = new QStandardItem(QString("%1").arg(row * 4 + column));
			model->setItem(row, column, item);
		}
	}

	tableView = new QTableView;
	tableView->setModel(model);

	setCentralWidget(tableView);

	//获取视图的选择模型
	QItemSelectionModel * selectionModel = tableView->selectionModel();

	//定义左上角和右下角的索引,然后使用这两个索引创建选择
	QModelIndex topLeft;
	QModelIndex bottomRight;
	topLeft = model->index(1, 1, QModelIndex());
	bottomRight = model->index(5, 2, QModelIndex());
	QItemSelection selection(topLeft, bottomRight);
	selectionModel->select(selection, QItemSelectionModel::Select);

	//在工具栏中添加两个动作图标
	ui.mainToolBar->addAction(tr("当前项目"), this, &MainWindow::getCurrentItemData);
	ui.mainToolBar->addAction(tr("切换选择"), this, &MainWindow::toggleSelection);

	//连接选择模型的信号和本类槽函数
	connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &MainWindow::updateSelection);
	connect(selectionModel, &QItemSelectionModel::currentChanged, this, &MainWindow::changeCurrent);

	//多个视图共享模型,共享选择模型,则共享选择。
	tableView2 = new QTableView;
	tableView2->setWindowTitle("tableView2");
	tableView2->resize(400, 300);
	tableView2->setModel(model);
	tableView2->setSelectionModel(selectionModel);
	tableView2->show();

	SpinBoxDelegate * delegate = new SpinBoxDelegate(this);
	tableView->setItemDelegate(delegate);
}

MainWindow::~MainWindow()
{
	delete tableView2;
}

void MainWindow::getCurrentItemData()
{
	qDebug() << tr("当前项目的内容:") << tableView->selectionModel()->currentIndex().data().toString();
}

void MainWindow::toggleSelection()
{
	QModelIndex topLeft = tableView->model()->index(0, 0, QModelIndex());
	QModelIndex bottomRight = tableView->model()->index(6, 3, QModelIndex());
	QItemSelection curSelection(topLeft, bottomRight);
	tableView->selectionModel()->select(curSelection, QItemSelectionModel::Toggle);
}

void MainWindow::updateSelection(const QItemSelection & selected, const QItemSelection & deselected)
{
	QModelIndex index;
	QModelIndexList list = selected.indexes();

	//为现在选择的项目填充值
	foreach(index, list)
	{
		QString text = QString("(%1, %2)").arg(index.row()).arg(index.column());
		tableView->model()->setData(index, text);
	}
	list = deselected.indexes();

	//清空上一次选择的项目的内容
	foreach(index, list)
	{
		tableView->model()->setData(index, "");
	}
}

void MainWindow::changeCurrent(const QModelIndex & current, const QModelIndex & previous)
{
	qDebug() << tr("move(%1, %2) to (%3, %4)").arg(previous.row()).arg(previous.column()).arg(current.row()).arg(current.column());
}


//SpinBoxDelegate.h
#pragma once

#include <QItemDelegate>


class SpinBoxDelegate : public QItemDelegate
{
	Q_OBJECT

public:
	explicit SpinBoxDelegate(QObject * parent = 0);

	//创建编辑器(当视图需要一个编辑器时,它会告知代理为被修改的数据项提供一个编辑器部件)
	QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const;
	//为编辑器设置数据(代理必须将模型中的数据复制到编辑器中)
	void setEditorData(QWidget * editor, const QModelIndex & index) const;
	//将数据写入到模型(当用户完成了对QSpinBox部件中数据的编辑时,视图会通过调用setModelData()函数来告知代理,将编辑好的数据存储到模型中)
	void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const;
	//更新编辑器几何布局(代理有责任管理编辑器的几何布局,必须在创建编辑器以及视图中数据项大小或位置改变时设置它的几何布局,option是视图提供的所有需要的几何布局信息)
	void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const;
};


//SpinBoxDelegate.cpp
#include "SpinBoxDelegate.h"
#include <QSpinBox>

SpinBoxDelegate::SpinBoxDelegate(QObject * parent) : QItemDelegate(parent)
{
}

QWidget * SpinBoxDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
	QSpinBox * editor = new QSpinBox(parent);

	editor->setMinimum(0);
	editor->setMaximum(100);

	return editor;
}

void SpinBoxDelegate::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 SpinBoxDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
	QSpinBox * spinBox = static_cast<QSpinBox *>(editor);
	spinBox->interpretText(); //确保获得的是QSpinBox中最近更新的数值
	int value = spinBox->value();
	model->setData(index, value, Qt::EditRole);
}

void SpinBoxDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
	editor->setGeometry(option.rect);
}


//main.cpp
#include <QtWidgets/QApplication>
#include "MainWindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

	MainWindow m;
	m.show();
	
    return a.exec();
}

http://www.kler.cn/a/455494.html

相关文章:

  • 解锁 Claude 的无限潜力:Prompt Engineering 从入门到精通
  • 游戏开发线性空间下PS工作流程
  • 使用c#制作坐标
  • MySql索引(基础篇)
  • 【UE5 C++课程系列笔记】12——Gameplay标签的基本使用
  • 聊聊强化学习在无人机中的前沿应用
  • act小试牛刀
  • CCF-GESP 等级考试 2023年9月认证C++五级真题解析
  • vue+elementUI 表单项赋值后无法修改的问题
  • php怎么去除数点后面的0
  • 第十九章 C++ 日期 时间
  • 前端学习DAY26(华为平板页面)
  • “智能控制的新纪元:2025年机器学习与控制工程国际会议引领变革
  • 嵌入式学习-QT-Day01
  • FFMPEG解码+SDL2播放视频
  • Oracle 11G还有新BUG?ORACLE 表空间迷案!
  • Debian安装配置RocketMQ
  • 组件库TDesign的表格<t-table>的使用,行列合并以及嵌入插槽实现图标展示,附踩坑
  • UGUI源码分析 --- UI的更新入口
  • “游戏信息化”:游戏后台系统的未来发展