2026, Jan 01 12:01

QPainter «Painter not active» в PyQt6 на Windows: причина в Qt 6.9 и что делать

Почему в PyQt6 на Windows появляются предупреждения QPainter в Qt 6.9 (QTBUG-135844) и как их обойти: дождаться 6.9.2 или временно использовать 6.8.x; код примера верен.

Приложения PyQt6, которые рисуют внутри QWidget, при запуске из терминала в Windows могут выдавать серию предупреждений QPainter, тогда как при запуске из PyCharm этого не видно. Сообщения выглядят так: «QPainter::begin: Paint device returned engine == 0, type: 3», а затем идут строки «Painter not active». В сборках PyQt6 на базе ветки Qt 6.9 эти предупреждения появляются; на 6.8.x — нет. Сам код может быть минимальным и корректным, но после ручного изменения размера или когда окно пристыковывают к краю экрана консоль всё равно заполняется выводом QPainter.

Воспроизводимый минимальный пример

import sys
from PyQt6.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QLabel,
)
from PyQt6.QtGui import QPainter, QColor
from PyQt6.QtCore import Qt
class CanvasPane(QWidget):
    def __init__(self):
        print('----CanvasPane ctor enter')
        super().__init__()
        self.build_ui()
        print('----CanvasPane ctor leave')
    def build_ui(self):
        print('----CanvasPane UI enter')
        self.setGeometry(100, 100, 300, 200)
        column = QVBoxLayout()
        self.setLayout(column)
        caption = QLabel("This is a simple Qt application  ")
        column.addWidget(caption)
        print('----CanvasPane UI leave')
    def paintEvent(self, ev):
        print('----CanvasPane paint enter')
        pen = QPainter(self)
        pen.setPen(Qt.GlobalColor.blue)
        pen.drawLine(0, 0, 200, 100)
        print('----CanvasPane paint leave')
class AppShell(QMainWindow):
    def __init__(self):
        print('--AppShell ctor enter')
        super().__init__()
        self.setup_view()
        print('--AppShell ctor leave')
    def setup_view(self):
        print('--AppShell UI enter')
        self.setWindowTitle("Simple Qt Example  ")
        self.setGeometry(100, 100, 600, 400)
        pane = CanvasPane()
        self.setCentralWidget(pane)
        print('--AppShell UI leave')
if __name__ == "__main__":
    print('Boot application  ')
    qtapp = QApplication([])
    print('Create main window  ')
    main_win = AppShell()
    print('Show main window  ')
    main_win.show()
    print('Execute event loop  ')
    sys.exit(qtapp.exec())

Что на самом деле происходит

Предупреждения вызываются ошибкой в линейке Qt 6.9, зарегистрированной как QTBUG-135844. Согласно публичному отчёту, проблему исправили, и исправление планируется включить в Qt 6.9.2, релиз которой намечен на середину августа 2025 года. Приведённый выше код в порядке; ложные сообщения QPainter не связаны с неправильным использованием QPainter в paintEvent и воспроизводятся на простейшем виджете, рисующем одну линию.

Разница между запуском из терминала Windows и из PyCharm ожидаема. PyCharm обычно показывает только то, что выводит интерпретатор Python. PyQt — это привязка к библиотеке на C++, а строки QPainter — это нативные предупреждения Qt, выводимые в обход обработчика stderr Python. Они возникают в обоих случаях, просто видите их там, где перехватывается нативный вывод. В IDE иногда можно включить показ таких сообщений, но надёжнее дополнительно проверять GUI‑приложения в обычном терминале.

Решение и практические рекомендации

Если предупреждения не выливаются в реальные проблемы для вашего приложения, можно продолжать работать с текущей конфигурацией и дождаться официального релиза 6.9.2. Если они мешают, переключитесь на версию, в которой проблема не проявляется. В отчёте сказано, что баг специфичен для ветки 6.9; предыдущий релиз 6.8.3 теоретически не затронут, как и более ранние версии 6.8.x — при условии, что они подходят под задачи проекта. Учтите, что версии PyQt не всегда один к одному совпадают с патч‑уровнем Qt: третий компонент версии PyQt может отражать собственные исправления PyQt, а не номер патча Qt.

Для минимального примера выше менять код не требуется. Паттерн работы с QPainter в paintEvent корректен: создание QPainter с виджетом автоматически инициирует рисование, установка пера и отрисовка примитива — валидные действия. Источник предупреждений — баг в Qt, а не логика приложения.

Небольшое примечание, чтобы избежать реальных падений в несвязанных ситуациях: обработчикам событий в Qt нужно передавать корректный экземпляр QEvent соответствующего типа. Ручной вызов обработчиков вроде resizeEvent с None неверен и может приводить к сбою для виджетов, где эти обработчики реализованы.

Почему это важно в повседневной разработке

Предупреждения из нативной библиотеки, обходящие Python, могут «прятаться» в сессиях IDE и проявляться только при запуске из терминала. Такая рассинхронизация усложняет диагностику и подталкивает искать проблему в собственном коде. Понимание, что источник — «выше по стеку» (в данном случае Qt), экономит время, помогает выбрать верный обходной путь и предотвращает лишние рефакторинги. Это также подчёркивает ценность фиксации версий и дымовых тестов вне IDE.

Итог

Если вы видите вывод QPainter «Painter not active» в PyQt6 на Windows только при работе через консоль и используете линейку Qt 6.9, скорее всего, вы упираетесь в QTBUG-135844. Либо терпите предупреждения до планируемого релиза 6.9.2, либо временно используйте сборку 6.8.x, которая вам подходит. Следите за обновлениями в публичном отчёте об ошибке, запускайте GUI‑тесты из терминала в дополнение к IDE, не вызывайте обработчики событий вручную и аккуратно переходите на новые минорные версии, только если они действительно нужны прямо сейчас. Минимальный код выше корректен; «шум» идёт с апстрима и носит временный характер.