Qt表格控件蚂蚁线详解

一、蚂蚁线

摘自互动百科:在图像影像软件中表示选区的动态虚线,因为虚线闪烁的样子像是一群蚂蚁在跑,所以俗称蚂蚁线。在Poshop,After Effect等软件中比较常见。

背景:用过excel的同学都知道,当对单元格进行复制时,单元格周围就会出现一个跑动的矩形框,这个矩形框就被称为蚂蚁线。通过设置蚂蚁线的线型和调整控件有效刷新次数我们可以得到不同的跑动效果,这是一个非常有意思的现象。

本文将给大家详细介绍关于Qt表格控件蚂蚁线的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

二、效果展示

  如下图就是蚂蚁线的效果截图,单击单元格时,会绘制一个2个像素宽的外框;当双击某个单元格时,就会产生蚂蚁线,蚂蚁线的线型和跑动速度都可以定制。

 

三、实现

说到Qt绘图,肯定离不开paintEvent函数,而且大多数的功能都可以通过重写paintEvent函数来完成。凡是总有例外,当控件本身就比较复杂,或者只需要重写控件某一部分时,就需要重写一些其他东西来完成需求,比如表格蚂蚁线绘制就属于这个例外,当我们重写表格时就不能重写paintEvent函数,如果重写这个函数那么表格的所有东西就需要我们自己去绘制,一个好的办法就是重写QStyledItemDelegate代理类,通过这个类我们可以定制表格控件的每一个项。下面我们就来仔细的分析下基于重写代理类的实现细节,理解下面4个实现维度后蚂蚁线基本就完成了。

1、绘制区域

蚂蚁线是针对表格项来进行绘制的,因此首先想到的就是刷新表格某一项来进行提高绘制效率,通过阅读Qt源码,找到QTableView::paintEvent函数中对表格项进行了绘制,主要是通过调用QTableViewPrivate::drawCell函数来进行每个单元格的绘制,该函数最后一行是通过QStyledItemDelegate类的paint方法来进行绘制,与第三节第一段的说明对应起来。因此如果想进行局部刷新看来困难比较大,因此最终决定每次刷新蚂蚁线时对整个表格进行刷新。

2、定时器

定时刷新,顾名思义就是我们需要一个定时器,定时刷新表格控件。首先想到的是我们自己维护一个QTimer,通过QTimer::timeout信号来刷新表格;除此之外QObject类已经帮我们提供了一个timerEvent回调函数,我们只需要通过startTime接口来启动一个定时器,timerEvent函数就会被定时调用,当然了这个回调接口同时支持多个定时器,用timeID进行区分每个定时器。

3、绘制策略

当选择一个单元格时(当前单元格发现变化),绘制矩形框;绘制矩形框比较简单,这块需要注意一个地方,就是当绘制第一列的时候矩形框可能会跑出当前项,导致矩形框显示不全。蚂蚁线绘制时也存在这个问题。

void GMPFileItemDelegate::DrawBorderRect( QPainter * painter, const QRect & rect, bool firstColumn ) const
{
 painter->save();
 QPen pen = painter->pen();
 pen.setWidth(2);
 pen.setColor(QColor(0, 132, 255));
 painter->setPen(pen);
 
 QRect tmpRect = rect;
 if (firstColumn)
 {
 tmpRect.adjust(2, 1, -1, -1);
 }
 else
 {
 tmpRect.adjust(1, 1, -1, -1);
 }
 painter->drawRect(tmpRect);
 painter->restore();
}

当双击单元格时绘制蚂蚁线,蚂蚁线绘制是通过定时器进行控制线框奔跑速度,这块有一个需要注意的地方是只有当定时器引起的绘制才会使起蚂蚁线往前跑。

根据蚂蚁线的偏移绘制开始的空白区域,蚂蚁线是由7个像素的蓝色和2个像素的空白循环组成,当偏移10个像素时,重新回到偏移1个像素。

if (startPoint != truthPoint && offset > 2)
{
 QPolygon polygon;
 for (int i = 4; i <= offset; ++i)//绘制前边偏移的像素
 {
 if (polygon.size() >= 7)
 {
  break;
 }
 polygon.append(truthPoint - QPoint(i , 0));
 }
 painter->drawPoints(polygon);
}

4、界面刷新

qt自己又自己的界面刷新策略,平时使用比较多的也不外乎update(建议刷新)、repaint(强制刷新)两个接口,但是这个两个接口调用时也不是说界面肯定会刷新,其实这两个接口都是使用QWidgetBackingStoreTracker类的senUpdateRequest接口类来抛出的刷新界面事件,Qt窗口有一个dirtyWidget的概念,当判定这个窗口为需要刷新的窗口时才会调用sendUpdateRequest接口进行界面刷新,如下代码,update和repaint区别在于调用了switch的不同分支。

void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
{
 if (!widget)
 return;
 
 switch (updateTime) {
 case UpdateLater:
 updateRequestSent = true;
 QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
 break;
 case UpdateNow: {
 QEvent event(QEvent::UpdateRequest);
 QApplication::sendEvent(widget, &event);
 break;
 }
 }
}

对于表格控件当我们单纯调用repaint或者update函数时是不能起到刷新界面的作用,因此我们需要调用其他能直接导致界面刷新的接口,目前我这块想到了直接调用窗口自身style的polish方法,如果大家有其他好的刷新方式可以留言。
通过以上4个小点的说明,蚂蚁线的实现基本就完成了。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个完整的实现表格控件首列显示树形控件代理的C++代码示例: ```cpp #include <QtWidgets/QApplication> #include <QtWidgets/QTableWidget> #include <QtWidgets/QHeaderView> #include <QtWidgets/QTreeView> #include <QtWidgets/QStyledItemDelegate> #include <QtCore/QModelIndex> class TreeDelegate : public QStyledItemDelegate { public: QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override { QTreeView* treeView = new QTreeView(parent); return treeView; } void setEditorData(QWidget* editor, const QModelIndex& index) const override { QTreeView* treeView = static_cast<QTreeView*>(editor); treeView->setModel(index.model()); treeView->setRootIndex(index); treeView->expandAll(); } void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override { QTreeView* treeView = static_cast<QTreeView*>(editor); QModelIndexList selectionList = treeView->selectionModel()->selectedIndexes(); QModelIndex selectionIndex = selectionList.at(0); QVariant data = selectionIndex.data(Qt::DisplayRole); model->setData(index, data); } void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override { editor->setGeometry(option.rect); } }; int main(int argc, char* argv[]) { QApplication app(argc, argv); QTableWidget tableWidget; tableWidget.setColumnCount(2); tableWidget.setRowCount(5); tableWidget.setHorizontalHeaderLabels({ "Name", "Age" }); for (int i = 0; i < tableWidget.rowCount(); ++i) { QTableWidgetItem* nameItem = new QTableWidgetItem(QString("Item %1").arg(i + 1)); tableWidget.setItem(i, 0, nameItem); QTableWidgetItem* ageItem = new QTableWidgetItem(QString::number(i + 10)); tableWidget.setItem(i, 1, ageItem); } TreeDelegate treeDelegate; tableWidget.setItemDelegateForColumn(0, &treeDelegate); tableWidget.header()->setSectionResizeMode(0, QHeaderView::Stretch); tableWidget.show(); return app.exec(); } ``` 在上面的代码中,首先定义了一个TreeDelegate类,继承自QStyledItemDelegate,实现了其createEditor()、setEditorData()、setModelData()和updateEditorGeometry()四个方法。其中,createEditor()方法创建了一个QTreeView控件,并将其作为编辑器返回;setEditorData()方法将数据源中的数据设置到QTreeView控件中;setModelData()方法将QTreeView控件中的数据更新到数据源中;updateEditorGeometry()方法设置编辑器的位置和大小。 然后,在main函数中,创建了一个QTableWidget控件,设置了表格的行列数、水平表头和单元格数据,并将TreeDelegate设置为首列的代理,最后显示QTableWidget控件。 通过以上代码,就可以实现表格控件首列显示树形控件代理的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值