一, 蚂蚁线
摘自互动百科: 在图像影像软件中表示选区的动态虚线, 因为虚线闪烁的样子像是一群蚂蚁在跑, 所以俗称蚂蚁线. 在 Poshop,After Effect 等软件中比较常见.
背景: 用过 excel 的同学都知道, 当对单元格进行复制时, 单元格周围就会出现一个跑动的矩形框, 这个矩形框就被称为蚂蚁线. 通过设置蚂蚁线的线型和调整控件有效刷新次数我们可以得到不同的跑动效果, 这是一个非常有意思的现象.
二, 效果展示
如下图就是蚂蚁线的效果截图, 单击单元格时, 会绘制一个 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 个小点的说明, 蚂蚁线的实现基本就完成了. 需要完整源码的去 csdn 下载吧
四, 下载链接
Qt 蚂蚁线 - 表格 https://download.csdn.net/download/qq_30392343/10532988
来源: https://www.cnblogs.com/swarmbees/p/9288902.html