EnMAP-Box main window drag move access violation of

Issue #1344 closed
Benjamin Jakimow created an issue
  1. Open EnMAP-Box
  2. Open & close a Map View
  3. Move EnMAP-Box to second screen (move fast between 1st and 2nd screen in both directions)

Exception is raised in qgsmapcanvas.cpp (sipPySelf QgsMapCanvas instance) in line:

mScreenDpiChangedConnection = connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
      updateDevicePixelFromScreen();

Line Context in qgsmapcanvas.cpp:

void QgsMapCanvas::showEvent( QShowEvent *event )
{
  Q_UNUSED( event )
  updateDevicePixelFromScreen();
  // keep device pixel ratio up to date on screen or resolution change
  if ( window()->windowHandle() )
  {
    connect( window()->windowHandle(), &QWindow::screenChanged, this, [ = ]( QScreen * )
    {
      disconnect( mScreenDpiChangedConnection );
      mScreenDpiChangedConnection = connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
      updateDevicePixelFromScreen();
    } );

    mScreenDpiChangedConnection = connect( window()->windowHandle()->screen(), &QScreen::physicalDotsPerInchChanged, this, &QgsMapCanvas::updateDevicePixelFromScreen );
  }
}

Call Stack:

Update: Python example to reproduce the error. Can be run from QGIS python shell or from PyCharm:

import gc
import sys

from qgis.PyQt.QtGui import QScreen
from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QPushButton
from qgis.core import QgsApplication
from qgis.gui import QgsMapCanvas
from qgis.testing import start_app

app = QgsApplication.instance()
if not isinstance(app, QgsApplication):
    app = start_app()
    call_exec = True
else:
    call_exec = False

assert len(app.screens()) > 1, 'This test requires at least 2 screens'
canvas = QgsMapCanvas()


class MyWidget(QWidget):

    def __init__(self, *args, **kwds):
        super(MyWidget, self).__init__(*args, **kwds)
        self.btn = QPushButton('Remove canvas')
        self.btn.clicked.connect(self.onClicked)
        self.canvas = canvas
        layout = QVBoxLayout()
        layout.addWidget(self.btn)
        layout.addWidget(self.canvas)
        self.setLayout(layout)

    def onClicked(self):
        if isinstance(self.canvas, QgsMapCanvas):
            # self.layout().takeAt(self.layout().indexOf(self.canvas))
            self.layout().removeWidget(self.canvas)
            self.canvas.setParent(None)
            self.canvas = None

        app.processEvents()
        gc.collect()
        print(f'REFCOUNT={sys.getrefcount(canvas)}')


# 1. create widget with QgsMapCanvas on default screen
w = MyWidget()
w.show()
app.processEvents()

# 2. Remove QgsMapCanvas, but keep a python reference on, so that is still exists
w.btn.click()
app.processEvents()

# 3. Move widget to 2nd screen. This raises an access violation on windows systems
nextScreen: QScreen = [s for s in app.screens() if s != w.screen()][0]
nextScreenCenter = nextScreen.geometry().center()
w.move(nextScreenCenter)
app.processEvents()

if call_exec is True:
    app.exec()

Comments (5)

  1. Benjamin Jakimow reporter

    Script to reproduce the exception (move the created DockArea between screens):

    from enmapbox.qgispluginsupport.qps.pyqtgraph.pyqtgraph.dockarea import DockArea, Dock
    from qgis.gui import QgsMapCanvas
    from qgis.core import QgsApplication
    from qgis.testing import start_app
    
    
    class MyDock(Dock):
    
        def __init__(self, *args, **kwds):
            super().__init__('mydock', *args, closable=True, **kwds)
    
            self.mCanvas = QgsMapCanvas(self)
            self.layout.addWidget(self.mCanvas)
    
    
    app = None
    if not isinstance(QgsApplication.instance(), QgsApplication):
        app = start_app()
    
    area = DockArea()
    mydock = MyDock()
    area.addDock(mydock)
    area.show()
    
    if app:
        app.exec_()
    

  2. Log in to comment