Search code examples
qtpositioncontextmenupysideqscrollarea

QAbstractScrollArea introduces offset when using mapToGlobal


I have a slot for customContextMenuRequested and in try to convert point to global point (for context menu). If the class inherits QAbstractScrollArea, the mapToGlobal will have offset (and thus global point does not match cursor point).

Here is the sample code:

#!/usr/bin/python

import sys
from PySide import QtGui, QtCore
from PySide.QtCore import Qt
from PySide.QtGui import QApplication, QMainWindow, QCursor

class Sample(QMainWindow):
    def __init__(self, parent=None):
        super(Sample, self).__init__(parent)
        self.resize(798, 562)
        self.widget = QtGui.QScrollArea(self)
        self.widget.setGeometry(QtCore.QRect(350, 290, 321, 231))
        self.widget.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QtGui.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 315, 225))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.widget.setWidget(self.scrollAreaWidgetContents)
        self.widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.widget.customContextMenuRequested.connect(self.menu)
        self.widget.setViewportMargins(0, 0, 0, 0)

    def menu(self, point):
        print 'Widget {}, {}'.format(point.x(), point.y())
        glob = self.widget.mapToGlobal(point)
        print 'Global {}, {}'.format(glob.x(), glob.y())
        curs = QCursor.pos()
        print 'Cursor {}, {}'.format(curs.x(), curs.y())
        local = self.widget.mapFromGlobal(curs)
        print 'Local {}, {}'.format(local.x(), local.y())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mySW = Sample()
    mySW.show()
    sys.exit(app.exec_())

I get output like this:

Widget 266, 114
Global 616, 427
Cursor 619, 430
Local 269, 117

Am I missing something, or this is a bug in Qt?

I have PySide 1.2.1 and Qt 4.8.5.


Solution

  • See the documentation for QWidget.customContextMenuRequested.

    The position pos is the position of the context menu event that the widget receives. Normally this is in widget coordinates. The exception to this rule is QAbstractScrollArea and its subclasses that map the context menu event to coordinates of the viewport().

    In short, the coordinates in your point variable are not relative to the widget, but to the viewport. As such, you should call self.widget.viewport().mapFromGlobal() and self.widget.viewport().mapToGlobal().