Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Using QPainter to paint into offscreen QPixmap fails
Forum Updated to NodeBB v4.3 + New Features

Using QPainter to paint into offscreen QPixmap fails

Scheduled Pinned Locked Moved Solved Qt for Python
pysideqt for python
4 Posts 2 Posters 1.3k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    D-Drum
    wrote on last edited by
    #1

    First of all I am relatively new to Python and to Qt however I'm not new to coding.
    As a means of learning both and to produce something that is of use to me out of the learning process, I am attempting to convert the Qt C++ tablet example to Python. However when I execute my Python version I see the following printed to the console:

    QPainter::begin: Paint device returned engine == 0, type: 2
    QPainter::setRenderHint: Painter must be active to set rendering hints
    QPainter::setPen: Painter not active
    

    In the description of the application it states amongst other things that the example consists of the following:

    The TabletCanvas class inherits QWidget and receives tablet events. It uses the events to paint onto an offscreen pixmap, and then renders it.

    The full C++ code can be found here

    In summary, the two event handling methods of interest are TabletCanvas::tabletEvent and TabletCanvas::paintEvent:

    void TabletCanvas::tabletEvent(QTabletEvent *event)
    {
        switch (event->type()) {
            case QEvent::TabletPress:
                if (!m_deviceDown) {
                    m_deviceDown = true;
                    lastPoint.pos = event->position();
                    lastPoint.pressure = event->pressure();
                    lastPoint.rotation = event->rotation();
                }
                break;
            case QEvent::TabletMove:
    #ifndef Q_OS_IOS
                if (event->pointingDevice() && event->pointingDevice()->capabilities().testFlag(QPointingDevice::Capability::Rotation))
                    updateCursor(event);
    #endif
                if (m_deviceDown) {
                    updateBrush(event);
                    QPainter painter(&m_pixmap);
                    paintPixmap(painter, event);
                    lastPoint.pos = event->position();
                    lastPoint.pressure = event->pressure();
                    lastPoint.rotation = event->rotation();
                }
                break;
            case QEvent::TabletRelease:
                if (m_deviceDown && event->buttons() == Qt::NoButton)
                    m_deviceDown = false;
                update();
                break;
            default:
                break;
        }
        event->accept();
    }
    
    void TabletCanvas::paintEvent(QPaintEvent *event)
    {
        if (m_pixmap.isNull())
            initPixmap();
        QPainter painter(this);
        QRect pixmapPortion = QRect(event->rect().topLeft() * devicePixelRatio(),
                                    event->rect().size() * devicePixelRatio());
        painter.drawPixmap(event->rect().topLeft(), m_pixmap, pixmapPortion);
    }
    

    My Python conversion of tableEvent handler is:

        def tabletEvent(self, event: QTabletEvent) -> None:
            if event.type() == QEvent.TabletPress:
                if not self._device_down:
                    self._device_down = True
                    self._lastPoint["pos"] = event.position()
                    self._lastPoint["pressure"] = event.pressure()
                    self._lastPoint["rotation"] = event.rotation()
            elif event.type() == QEvent.TabletMove:
                if event.pointingDevice() and event.pointingDevice().hasCapability(QInputDevice.Capability.Rotation):
                    self._updateCursor(event)
                if self._device_down:
                    self._updateBrush(event)
                    q_painter: QPainter = QPainter(self._pixmap)
                    self._paintPixMap(q_painter, event)
                    self._lastPoint["pos"] = event.position()
                    self._lastPoint["pressure"] = event.pressure()
                    self._lastPoint["rotation"] = event.rotation()
            elif event.type() == QEvent.TabletRelease:
                if self._device_down and event.button() == Qt.NoButton:
                    self._device_down = False
                self.update()
            elif event.type() == QEvent.TabletTrackingChange:
                print("Tablet tracking change")
            elif event.type() == QEvent.TabletEnterProximity:
                print("Table Enter proximity")
            elif event.type() == QEvent.TabletLeaveProximity:
                print("Tablet leave proximity")
            else:
                print("In the else")
    
            event.accept()
    

    Debugging this the first error to the console (Paint device returned engine == 0) occurs in the tableEvent at the line q_painter: QPainter = QPainter(self._pixmap) and the second and third errors occur at self._paintPixMap(q_painter, event).

    I have read numerous similar issues online and the usual response is to state that QPainter should only be created in the paintEvent method however I'm not able to find that explicitly stated in QPainter's docs. In fact the second paragraph of the detailed description begins (emphasis by me):

    The common use of QPainter is inside a widget’s paint event: Construct and customize (e.g. set the pen or the brush) the painter.

    I can understand the desire to use QPainter in the tabletEvent method to write to the off screen pixmap however in my Python conversion this doesn't seem to be possible. If using a QPainter outside of the paintEvent method is forbidden either in Qt or Pyside6 then I am not sure how the original C++ example functions or is this a difference between QT C++ and Pyside? Is it possible to use a QPainter for off-screen buffering in Pyside and how can this be achieved?

    1 Reply Last reply
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by mrjj
      #2

      Hi
      Using QPainter to paint on a widget outside paintevent is forbidden but
      you can bind it to a pixmap at any time and that should also works with python bindings.

      Are you sure the pixmap is valid/have a size when you assign it ?
      I mean at this line ?
      q_painter: QPainter = QPainter(self._pixmap)

      I asking as paint event says
      if (m_pixmap.isNull())
      initPixmap();

      So I wonder if you try to bind to an invalid pixmap as its
      first created later ? When painEvent fires.

      D 1 Reply Last reply
      1
      • mrjjM mrjj

        Hi
        Using QPainter to paint on a widget outside paintevent is forbidden but
        you can bind it to a pixmap at any time and that should also works with python bindings.

        Are you sure the pixmap is valid/have a size when you assign it ?
        I mean at this line ?
        q_painter: QPainter = QPainter(self._pixmap)

        I asking as paint event says
        if (m_pixmap.isNull())
        initPixmap();

        So I wonder if you try to bind to an invalid pixmap as its
        first created later ? When painEvent fires.

        D Offline
        D Offline
        D-Drum
        wrote on last edited by
        #3

        @mrjj Excellent spot! You are indeed correct.
        In short in my Python port of the original I had incorrectly initialised self._pixmap to a pixmap of no dimension. Fixing this resolved the issue. I now understand how to use a QPainter object outside of the paintEvent handler. Unless I missed it I think this would be useful information to have in the docs.

        mrjjM 1 Reply Last reply
        0
        • D D-Drum

          @mrjj Excellent spot! You are indeed correct.
          In short in my Python port of the original I had incorrectly initialised self._pixmap to a pixmap of no dimension. Fixing this resolved the issue. I now understand how to use a QPainter object outside of the paintEvent handler. Unless I missed it I think this would be useful information to have in the docs.

          mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @D-Drum
          Hi
          super :)
          I agree there could be some more info about this in the docs.

          1 Reply Last reply
          0

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved