找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4300

积分

1

好友

588

主题
发表于 1 小时前 | 查看: 3| 回复: 0

在进行QListWidget自定义绘制时,你是否也遇到过下面这种令人困惑的错误?尤其是在macOS上,它就像一个幽灵,挥之不去。

macOS Qt控件示例程序截图,显示自定义列表控件及报错信息

程序输出中反复出现:

QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setRenderHint: Painter must be active to set rendering hints

第一次看到这几行日志,大部分开发者都会愣住。检查代码,paintEvent 写了,基类调用了,QPainter 也创建了,逻辑看起来天衣无缝。但诡异的是,这段代码在Windows上运行完美,一到macOS就稳定报错

问题出在哪里?这其实不是你的代码写错了,而是 Qt在macOS上的绘制机制与Windows/Linux存在根本性差异

核心结论:大概率是画错了对象

首先需要明确,这类问题99%的可能性并非以下原因:

  • 忘了调用基类 paintEvent
  • QPainter 的用法错误
  • 遇到了Qt的底层Bug

真正的原因往往是:你在macOS上,对一个“不应被直接绘制”的QWidget对象创建了QPainter

从一段“看似正确”的代码开始分析

很多开发者(包括早期的我)会写出类似下面的代码,这非常常见:

void StyledListWidget::paintEvent(QPaintEvent *event)
{
    QListWidget::paintEvent(event); // 先调用基类绘制

    QPainter painter(this); // 问题可能就出在这行!
    painter.setRenderHint(QPainter::Antialiasing);

    drawBackground(&painter);
}

这段代码在Windows或Linux下通常能正常运行。但一旦放到macOS环境,那三行报错信息几乎必定会出现。

解读Qt日志:线索早已给出

1. QWidget::paintEngine: Should no longer be called

这是最关键的一句提示,但容易被忽略。它的潜台词是:

这个QWidget对象当前已经不再适合作为一个绘制设备(Paint Device)来使用了。

换言之,Qt在警告你:你不应该再对它(this)创建QPainter。这不是绘制“姿势”的问题,而是选择绘制“对象”的问题。

2. Paint device returned engine == 0

这是上一条警告的直接后果。engine == 0 意味着:

this->paintEngine() == nullptr

因此,当你执行 QPainter painter(this); 时,QPainter::begin() 操作直接失败了。

3. Painter must be active

这是连锁反应的第三步。由于QPainter未能成功激活(not active),后续所有调用其绘制方法(如setRenderHint)的操作都会继续抛出错误。

关键剖析:为什么macOS上paintEngine会返回nullptr?

根本原因很简单:

在macOS上,QListWidget(或QTableView等)自身常常并不是一个稳定、可用的绘制对象。

macOS下QListWidget的真实内部结构

QListWidget 继承自 QAbstractScrollArea。在macOS上,其内部结构可以简化为:

QListWidget (this)
 ├─ viewport()          ← 真正用于承载和绘制Item的区域
 ├─ verticalScrollBar
 └─ horizontalScrollBar

这里存在一个关键的平台差异:

  • Windows / Linux 上,QListWidget(即this)本身通常可以直接作为绘制目标。
  • macOS 上,很多情况下,只有 viewport() 返回的部件才拥有有效的paintEngine

所以,当你在macOS上写 QPainter painter(this); 时,本质上是在尝试:

对一个没有配备绘制引擎(paintEngine)的QWidget进行绘图。

Qt除了报错,别无他法。

为什么Windows上不出问题?

这并不是你的代码在macOS上“退化”了,而是底层图形系统的差异:

  • Windows:使用GDI或Direct2D,容错性相对较高。
  • macOS:基于Cocoa框架,对“谁能绘图”、“谁不能绘图”有非常严格和清晰的界定。
    Qt只是将这个平台底层的差异如实反映到了应用层。

终极解决方案:跨平台兼容的正确写法

核心原则:放弃直接绘制 this,改为绘制 viewport()

错误写法(macOS高危):

QPainter painter(this); // 在macOS下可能导致paintEngine == 0

正确写法(跨平台兼容):

QPainter painter(viewport()); // 始终对viewport进行绘制

推荐的、健壮的paintEvent实现:

void StyledListWidget::paintEvent(QPaintEvent *event)
{
    // 1. 首先调用基类,让Qt完成Item、选中状态、样式表等标准绘制
    QListWidget::paintEvent(event);

    // 2. macOS下(实际上跨平台都应如此),只对viewport创建QPainter
    QPainter painter(viewport());
    // 3. 安全性检查,确保painter已成功激活
    if (!painter.isActive()) {
        return;
    }

    // 4. 进行你的自定义绘制
    painter.setRenderHint(QPainter::Antialiasing);
    drawBackground(&painter);
    // ... 其他绘制操作
}

这种方法确保了无论是在Windows、Linux还是macOS上,你的自定义绘制代码都作用在正确的、可绘制的部件区域上,从而彻底避免 QWidget::paintEngine: Should no longer be called 及其相关错误。

理解 QListWidgetQTableView 等控件内部 viewport() 的作用,是进行高级 C/C++ GUI自定义绘制的关键一步。这不仅仅是解决一个报错,更是深入理解Qt跨平台绘图模型的好机会。

本文中讨论的示例代码和完整自定义控件项目,可以在 Gitee 上获取:https://gitee.com/liushixiong/QtControDemo.git。如果在开发中遇到更多类似的图形视图或 paintEvent 相关问题,欢迎在 云栈社区 的技术论坛与其他开发者交流探讨。




上一篇:用 Rust + WebAssembly 重写 Shockwave 播放器,在浏览器中复活经典游戏
下一篇:OpenClaw如何重塑AI硬件生态格局:从可穿戴设备到机器人的五大趋势
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-10 09:55 , Processed in 0.416966 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表