Qt实现表格控件

2023-01-04 0 782

一、简述

前段时间在研究QtableView全力支持多层字段的事情,腾讯了下网路上数据资料却是挺多的。同时实现的形式总体而言有2种,效用都还极好,最主要是比如说其中的基本原理,努力做到有鉴于此。

同时实现多层字段的形式有以下三种计划

行字段和列字段都是用两个表单去演示改写QHeadView

以上三种形式都能同时实现多层字段,科紫萁,因此已经有人资金投入工程项目使用。

我对个人却是较为偏重于第三种形式,即使这种他们才能更快的介绍Qt的下层,介绍Qt的图形监督机制,因此这种同时实现的工作效率也是较为高的,而且科学合理许多,较为受控(对个人认知)。

后来我在网路上找出了两个哥们儿写的命令行,工程项目英文名字叫作RbTableHeaderView,真极好的,能同时实现他们要的机能,但效用却是差许多,如果须要更亲善的可视化效用,那么还须要在继续健全那个demo。

今天科泽县,找出了两个开放源码的中文网站,下边好些Qt的库,虽然有许多是很久以前的东西,但也很值得称赞他们去自学。为什么会提及这个中文网站呢?即使那个中文网站上就有他们要的那个多层字段案例,和下边提及的那个哥们儿的案例大相迳庭。

想自学更多开放源码案例的能到openDesktop上来看一看。还有我自己收录于的牛逼貌似的Qt库

上面他们就来传授这个六节字段的同时实现形式,标识符较为简单,主要是我们认知下那个同时实现形式,能予以扩充。

先期的该文中就要在写一则关于树命令行多层字段的案例,这儿先把该文中文名称装载这儿,先期发布后就能看到–Qt同时实现表单树命令行-全力支持多层字段

二、效用展现

多层字段的效用右图右图,很糙粗的两个demo,我们将就着哈哈。

Qt实现表格控件

三、订制字段

订制字段他们主要是要改写2个东西,分别是数据源QAbstractTableModel和字段QHeaderView

1、改写数据源

数据源就是为视图提供数据的model,他们的所有显示的内容数据都来自那个model。

对于外部程序填充数据时和往常使用同样的形式

for (int i = 0; i < 10; i++)QList items;for (int j = 0; j < 8; j++)items.append(new QStandardItem(QString(“item(%1, %2)”).arg(i).arg(j)));dataModel->appendRow(items);

改写了那个数据源后,他们主要是为了完成data的返回数据过程,View最关心的就是那个接口

class RbTableHeaderModel : public QAbstractTableModelQ_OBJECTpublic:// overridevirtual QVariant data(const QModelIndex &index, int role) const;private:// propertiesint row_count_prop;int column_count_prop;// inherent featuresRbTableHeaderItem* root_item;

上面就是data的函数同时实现,是不是大失所望,所有的额操作好像被封装到RbTableHeaderItem那个节点中去了。

QVariant RbTableHeaderModel::data(const QModelIndex & index, int role) constif (!index.isValid())return QVariant();if (index.row() >= row_count_prop || index.row() < 0 || index.column() >= column_count_prop || index.column() < 0)return QVariant();RbTableHeaderItem * item = static_cast(index.internalPointer());return item->data(role);

RbTableHeaderItem结构表示了两个单元格,而且他还维护了所有的表单cell子节点。

注意看上面index的构造,把index和RbTableHeaderItem那个结构绑定在了一起。

index中的很多数据也都存储在了RbTableHeaderItem那个结构中,先期他们在讲视图的时候我们就会发现了。

Model也就这么多内动了,View才是他们的重头戏。

QModelIndex RbTableHeaderModel::index(int row, int column, const QModelIndex & parent) constRbTableHeaderItem * parentItem;if (!parent.isValid())parentItem = root_item; // parent item is always the root_item on table modelelseparentItem = static_cast(parent.internalPointer()); // no effectRbTableHeaderItem * childItem = parentItem->child(row, column);if (!childItem)childItem = parentItem->insertChild(row, column);returncreateIndex(row, column, childItem);return QModelIndex();2、重写QHeaderView

改写字段时,公有接口用于设置单元格行高、列宽、背景色和前景色的,单元格合并等。

保护接口都是改写父类的方法,在合适的实际会被框架进行调用。

inherent features注释下的方法是自己封装的方法,方便其他函数调用。

class RbTableHeaderView : public QHeaderViewvoid setRowHeight(int row, int rowHeight);void setColumnWidth(int col, int colWidth);void setSpan(int row, int column, int rowSpanCount, int columnSpanCount);void setCellBackgroundColor(const QModelIndex & index, const QColor &);void setCellForegroundColor(const QModelIndex & index, const QColor &);protected:// overridevirtual void mousePressEvent(QMouseEvent * event);virtual void paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const;protected Q_SLOTS:void onSectionResized(int logicalIdx, int oldSize, int newSize);Q_SIGNALS:void sectionPressed(int from, int to);

上面他们分析几个较为重要的函数

1、mousePressEvent鼠标按下

当鼠标按下时mousePressEvent函数被触发,然后他们须要去计算那个单元格被按下了,并通知视图,让视图去选择某些cell集合。

那个函数的处理逻辑会较为负责许多,那个dmeo做的有问题,这儿我就不按照demo中的标识符来传授了。

首先他们却是得根据自己的需求来同时实现那个鼠标按下事件,对于大多数的程序来说,可能都是鼠标按下时,选中视图中的单元格集合,那么他们这儿也就按照那个思路来分析。

如右图右图,他们在程序加载过程中,给字段头设置了合并属性,对于合并了的表单项,他们对象的index中都是存储了红色文字信息的。

Qt实现表格控件

当他们点击了某两个item时,程序就须要去判断是否点击了那个大的合并sell,然后去选择tableview视图上的cell集合。

就是这么简单,但同时实现起来却是有一定难度的。

思路就到这儿了,具体逻辑我们能去思考下。

2、paintSection绘制函数

UI上真正的绘制函数其实就是paintSection函数,当那个函数回调的时候,他们只须要在程序给定的区域内绘制上文本即可,那么问题来了,那个区域是这么计算出来的,既然他们要合并了列和行,那么每两个区域的大小应该都是不一样的。

分析的一点都没错,那个区域的大小Qt已经帮他们留好了接口–sectionSizeFromContents

他们只须要改写那个函数即可,根据他们之前保存的index上合并列和行的数据进行计算,计算出两个合适的区域,然后把值返回即可。

QSize RbTableHeaderView::sectionSizeFromContents(int logicalIndex) constconst RbTableHeaderModel * tblModel = qobject_cast(this->model());const int OTN = orientation();const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount();QSize siz = QHeaderView::sectionSizeFromContents(logicalIndex);for (int i = 0; i < LEVEL_CNT; ++i)QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIndex) : tblModel->index(logicalIndex, i);QModelIndex colSpanIdx = columnSpanIndex(cellIndex);QModelIndex rowSpanIdx = rowSpanIndex(cellIndex);siz = cellIndex.data(Qt::SizeHintRole).toSize();if (colSpanIdx.isValid())int colSpanFrom = colSpanIdx.column();int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt();int colSpanTo = colSpanFrom + colSpanCnt – 1;siz.setWidth(columnSpanSize(colSpanIdx.row(), colSpanFrom, colSpanCnt));if (OTN == Qt::Vertical) i = colSpanTo;if (rowSpanIdx.isValid())int rowSpanFrom = rowSpanIdx.row();int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt();int rowSpanTo = rowSpanFrom + rowSpanCnt – 1;siz.setHeight(rowSpanSize(rowSpanIdx.column(), rowSpanFrom, rowSpanCnt));if (OTN == Qt::Horizontal) i = rowSpanTo;return siz;

3、列大小改变

当手动拖拽列带下时,onSectionResized槽函数会被调用,然后他们须要在那个函数中把相邻的列头大小进行重新设置。

void RbTableHeaderView::onSectionResized(int logicalIndex, int oldSize, int newSize)for (int i = 0; i < LEVEL_CNT; ++i)QSize cellSize = cellIndex.data(Qt::SizeHintRole).toSize();// set position of cellif (OTN == Qt::Horizontal)sectionRect.setTop(rowSpanSize(logicalIndex, 0, i));cellSize.setWidth(newSize);elsesectionRect.setLeft(columnSpanSize(logicalIndex, 0, i));cellSize.setHeight(newSize);tblModel->setData(cellIndex, cellSize, Qt::SizeHintRole);QRect rToUpdate(sectionRect);rToUpdate.setWidth(viewport()->width() – sectionRect.left());rToUpdate.setHeight(viewport()->height() – sectionRect.top());viewport()->update(rToUpdate.normalized());

大致的同时实现思路就是这种的,由于核心同时实现标识符逻辑较为长,大多数的标识符我只保留了关键的执行步骤。

四、设置属性

上面这一大堆标识符看似很长,其实很好认知,就是调用他们封装好的命令行进行设置

第一段设置了水平字段合并和内容第二段设置了垂直字段合并和内容第三段设置水平字段行高第四段设置了垂直字段列宽和行高第五段设置水平和垂直字段可点击第六段设置水平和垂直字段背景色hHead->setSpan(0, 0, 3, 0);hHead->setSpan(0, 1, 2, 2);hHead->setSpan(1, 3, 2, 0);hModel->setData(hModel->index(0, 0), QStringLiteral(“一级字段”), Qt::DisplayRole);hModel->setData(hModel->index(0, 1), QStringLiteral(“一级字段”), Qt::DisplayRole);hModel->setData(hModel->index(2, 1), QStringLiteral(“二级字段”), Qt::DisplayRole);hModel->setData(hModel->index(2, 2), QStringLiteral(“二级字段”), Qt::DisplayRole);hModel->setData(hModel->index(0, 3), QStringLiteral(“一级字段”), Qt::DisplayRole);hModel->setData(hModel->index(1, 3), QStringLiteral(“二级字段”), Qt::DisplayRole);vHead->setSpan(0, 0, 0, 3);vHead->setSpan(1, 0, 3, 0);vHead->setSpan(1, 1, 2, 0);vModel->setData(vModel->index(0, 0), QStringLiteral(“一级字段”), Qt::DisplayRole);vModel->setData(vModel->index(1, 0), QStringLiteral(“一级字段”), Qt::DisplayRole);vModel->setData(vModel->index(1, 1), QStringLiteral(“二级字段”), Qt::DisplayRole);vModel->setData(vModel->index(3, 1), QStringLiteral(“二级字段”), Qt::DisplayRole);vModel->setData(vModel->index(1, 2), QStringLiteral(“三级字段”), Qt::DisplayRole);vModel->setData(vModel->index(2, 2), QStringLiteral(“三级字段”), Qt::DisplayRole);vModel->setData(vModel->index(3, 2), QStringLiteral(“三级字段”), Qt::DisplayRole);hHead->setRowHeight(0, 30);hHead->setRowHeight(1, 30);hHead->setRowHeight(2, 30);vHead->setRowHeight(0, 30);vHead->setRowHeight(1, 30);vHead->setRowHeight(2, 30);vHead->setColumnWidth(0, 50);vHead->setColumnWidth(1, 50);vHead->setColumnWidth(2, 50);hHead->setSectionsClickable(true);vHead->setSectionsClickable(true);hHead->setCellBackgroundColor(hModel->index(0, 0), 0xcfcfcf);hHead->setCellBackgroundColor(hModel->index(0, 1), 0xcfcfcf);vHead->setCellBackgroundColor(vModel->index(0, 0), Qt::cyan);vHead->setCellBackgroundColor(vModel->index(1, 0), 0xcfcfcf);

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务