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

解决 QGraphicsDropShadowEffect 导致的 UI 持续刷新

1. 问题描述

        使用 QGraphicsDropShadowEffect 为无边框 QDialog 添加阴影,代码如下:

ShadowDialog::ShadowDialog(QWidget* parent): QDialog(parent), ui(new Ui::ShadowDialog){ui->setupUi(this);setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);setAttribute(Qt::WA_TranslucentBackground, true);      //设置半透明背景// 设置阴影QGraphicsDropShadowEffect* shadow_effect = new QGraphicsDropShadowEffect(this);shadow_effect->setOffset(0, 0);shadow_effect->setColor(QColor(70, 70, 70, 184));shadow_effect->setBlurRadius(10);setGraphicsEffect(shadow_effect);……}

        UI 设计及运行效果如下图所示:

        那么当最上方的文本框获取焦点后,光标每闪烁一次,则对话框重绘一次。

2. 问题原因

        文本框光标闪烁的定时事件触发文本框重绘,进而导致QGraphicsDropShadowEffect重绘,最终引起整个对话框的重绘。具体代码执行流程如下(部分关键流程):

2.1 鼠标光标闪烁定时事件

void QLineControl::timerEvent(QTimerEvent *event)
{if (event->timerId() == m_blinkTimer) {m_blinkStatus = !m_blinkStatus;emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());} ……
}

2.2 文本框更新

void QWidget::update(const QRect &rect)
{……if (hasBackingStoreSupport()) {QTLWExtra *tlwExtra = window()->d_func()->maybeTopData();if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore)tlwExtra->backingStore->markDirty(r, this);} else {d_func()->repaint_sys(r);}
}void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,bool invalidateBuffer)
{
……
#ifndef QT_NO_GRAPHICSEFFECTwidget->d_func()->invalidateGraphicsEffectsRecursively();
#endif //QT_NO_GRAPHICSEFFECT
……
}

2.3 清理阴影设置

void QWidgetPrivate::invalidateGraphicsEffectsRecursively()
{Q_Q(QWidget);QWidget *w = q;do {if (w->graphicsEffect()) {QWidgetEffectSourcePrivate *sourced =static_cast<QWidgetEffectSourcePrivate *>(w->graphicsEffect()->source()->d_func());if (!sourced->updateDueToGraphicsEffect)w->graphicsEffect()->source()->d_func()->invalidateCache();}w = w->parentWidget();} while (w);
}void QGraphicsEffectSourcePrivate::invalidateCache(InvalidateReason reason) const
{……QPixmapCache::remove(m_cacheKey);
}

        ​​​​​​​invalidateGraphicsEffectsRecursively()函数中循环查找父窗口的阴影设置并使阴影缓存无效,此处是导致重绘的关键!

​​​​​​​2.4 发送重绘请求

// qbackingstore.cpp
static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
{if (!widget)return;if (updateImmediately) {QEvent event(QEvent::UpdateRequest);QApplication::sendEvent(widget, &event);} else {QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);}
}

​​​​​​​2.5 QGraphicsDropShadowEffect重绘

void QGraphicsDropShadowEffect::draw(QPainter *painter)
{……// Draw pixmap in device coordinates to avoid pixmap scaling.QPoint offset;const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);if (pixmap.isNull())return;……
}……QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset,QGraphicsEffect::PixmapPadMode mode) const
{……pixmapOffset -= effectRect.topLeft();QPixmap pixmap(effectRect.size());pixmap.fill(Qt::transparent);m_widget->render(&pixmap, pixmapOffset, QRegion(), QWidget::DrawChildren);return pixmap;
}

        上述高亮代码里的 m_widget 为阴影所在窗口,即对话框窗口。

2.6 对话框重绘​​​​​​​

void QWidget::render(QPaintDevice *target, const QPoint &targetOffset,const QRegion &sourceRegion, RenderFlags renderFlags)
{d_func()->render(target, targetOffset, sourceRegion, renderFlags, false);
}void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset,const QRegion &sourceRegion, QWidget::RenderFlags renderFlags,bool readyToRender)
{……
#ifndef Q_WS_MAC// Render via backingstore.drawWidget(target, paintRegion, offset, flags, sharedPainter());// Restore shared painter.if (oldSharedPainter)setSharedPainter(oldSharedPainter);
#else// Render via backingstore (no shared painter).drawWidget(target, paintRegion, offset, flags, 0);
#endif
}

​​​​​​​3. 解决方案

        从代码执行流程可以发现问题的原因在于清理阴影设置引起的子窗口重绘,因此解决方案是让添加阴影的窗口没有子窗口。

        创建一个对话框窗口的子窗口作为阴影窗口,阴影窗口大小与对话框窗口一致,位置与对话框窗口相同,即两个窗口重叠且把子窗口置于对话框窗口下层。代码如下:

class ShadowDialog : public QDialog
{
protected:void resizeEvent(QResizeEvent*);
private:QWidget* m_widgetShadow;
};ShadowDialog::ShadowDialog(QWidget* parent): QDialog(parent), ui(new Ui::ShadowDialog)
{ui->setupUi(this);//设置阴影QGraphicsDropShadowEffect* shadow_effect = new QGraphicsDropShadowEffect(this);shadow_effect->setOffset(0, 0);shadow_effect->setColor(QColor(70, 70, 70, 184));shadow_effect->setBlurRadius(10);m_widgetShadow = new QWidget(this);m_widgetShadow->setObjectName("widget_background");m_widgetShadow->lower();m_widgetShadow->setAttribute(Qt::WA_TransparentForMouseEvents);m_widgetShadow->setStyleSheet("background:white; border: 0px solid #ffffff");m_widgetShadow->setGraphicsEffect(shadow_effect);auto& margins = ui->verticalLayout->contentsMargins();m_widgetShadow->move(margins.left(), margins.top());……
}void ShadowDialog::resizeEvent(QResizeEvent* event)
{QDialog::resizeEvent(event);auto& margins = ui->verticalLayout->contentsMargins();auto size = event->size();size.setWidth(size.width() - margins.left() - margins.right());size.setHeight(size.height() - margins.top() - margins.bottom());m_widgetShadow->resize(size);
}

http://www.dtcms.com/a/410643.html

相关文章:

  • 用 LoRA 微调 Qwen3-0.6B 模型,打造专属宠物商店智能客服
  • 建搜索引擎网站衡东网络推广公司
  • Go test 命令完整指南:从基础到高级用法
  • apifox认证登录自动化
  • 江西网站建设哪家专业女装wordpress
  • IDEA JVM优化配置idea64.vmoptions - 保守兼容版本 兼容IDEA 2023.3.6版本【亲测可用】
  • 网站图片像素多少做视频有赚钱的网站
  • APT攻击:隐蔽战场的威胁与防御之道
  • 小兔鲜项目
  • 黑马点评学习笔记01(手机号校验(正则表达式))
  • 声明式事务7
  • 外贸专业网站制作昆明建设网站哪家好
  • 鸿蒙原生contact.queryContacts通讯录查询实现
  • 根据百度地图做网站太原h5建站
  • 【JAVA】从入门到放弃-02-工具、类型、输入输出
  • 伪静态怎么设置(详细教程)
  • 【leetcode】57. 插入区间
  • 多sheet excel 导出
  • 手机移动端网站是什么用什么软件做网站布局
  • cesium-kit:让 Cesium 开发像写 UI 组件一样简单
  • 电子工程师网站wordpress the ken
  • Nginx HTTPS 深入实战 配置、性能与排查全流程(Nginx https
  • 网站建设和优化的营销话术亚马逊雨林生存游戏手机下载
  • 一场“无感换心”手术:金仓数据库如何让电子证照系统平滑告别MongoDB
  • 【开源】基于STM32的新疆地区棉花智能种植系统
  • 高平市规建设局网站短链接生成器
  • 解决SSL握手失败问题:SSLHandshakeException: Received fatal alert: handshake_failure
  • 降级版本Pillow解决freetypefont has no attribute getsize问题
  • 网站设计实例教程wordpress引用文章
  • 基于蜣螂优化的LSTM深度学习网络模型(DBO-LSTM)的一维时间序列预测算法matlab仿真