PyQt5 Get another widget location?
I’m trying to map one widget pos
(A) to another widget (B) so that B can do some work based on where A is. I have WidgetA
get WidgetB
pos, these widgets can have different parent hierarchies:
Global
+--- Window
+--- WidgetA
+--- WidgetB
Global
+--- Window
+--- Sub
| +--- Sub
| +--- WidgetB
|
+--- WidgetA
Global
+--- Window
| +--- WidgetB
|
+--- WidgetA
And so on
I’m trying to convert B's
position to relative position on A
by (two widgets sharing the same parent) :
WidgetB.setGeometry(150, 150, 100, 100)
# expected vs result
WidgetA.mapFromGlobal(WidgetB.mapToGlobal(WidgetB.pos())) # 150, 150 125, 200
WidgetA.mapFrom(WidgetA.parent, WidgetB.pos()) # 150, 150 -25, 50
WidgetA.parent.mapFromGlobal(WidgetB.mapToGlobal(WidgetB.pos())) # 150, 150 300, 300
What is the correct way to do this mapping? I can’t seem to wrap it properly.
Code:
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5 import QtCore
import sys
class Window(QWidget):
def __init__(self, Parent=None):
super().__init__()
self. WidgetA = QWidget(self)
self. WidgetA.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self. WidgetA.setObjectName("WidgetA")
self. WidgetA.setGeometry(400, 400, 100, 100)
self. WidgetB = QWidget(self)
self. WidgetB.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self. WidgetB.setObjectName("WidgetB")
self. WidgetB.setGeometry(0, 0, 100, 100)
self.setGeometry(0, 0, 500, 500)
self.setStyleSheet("""
#WidgetA {
background-color: red;
}
#WidgetB {
background-color: blue;
}
""")
def resizeEvent(self, event):
print(self. WidgetB.pos())
print(self. WidgetA.pos())
print(self. WidgetA.mapFromGlobal(self. WidgetB.mapToGlobal(self. WidgetB.pos())))
print(self. WidgetA.mapFrom(self, self. WidgetB.pos()))
print(self.mapFromGlobal(self. WidgetB.mapToGlobal(self. WidgetB.pos())))
if __name__ == "__main__":
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
Output:
PyQt5.QtCore.QPoint()
PyQt5.QtCore.QPoint(400, 400)
PyQt5.QtCore.QPoint(-400, -400)
PyQt5.QtCore.QPoint(-400, -400)
PyQt5.QtCore.QPoint()
I want to get (0, 0)
because that’s what WidgetB
sets.
Solution
The position of the widget is relative to
the parent, it is not relative to the position of the screen, so in your example you should get (400, 400).
According to docs :
pos : QPoint
This property holds the position of the widget within its parent
widgetIf the widget is a window, the position is that of the widget on the
desktop, including its frame.When changing the position, the widget, if visible, receives a move
event (moveEvent()) immediately. If the widget is not currently
visible, it is guaranteed to receive an event before it is shown.By default, this property contains a position that refers to the
origin.
There are some methods that seem to work, but that’s because you’ve already established the position of the parent widget in (0, 0),
and if you change it, it will fail.
Considering where you want A to be relative to B, the solution is as follows:
Use the following method to get the position of widget B relative to the screen:
gp = self. WidgetB.mapToGlobal(QtCore.QPoint(0, 0))
It is passed (0, 0
) because mapToGlobal
needs to be relative to the position of the widget using the function, in your case the position of WidgetB relative to WidgetB
is (0, 0).
Then map the global location of
WidgetA
:b_a = self. WidgetA.mapFromGlobal(gp)
So the following example shows my solution in action:
import sys
from random import randint
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self. WidgetA = QtWidgets.QWidget(self)
self. WidgetA.resize(100, 100)
self. WidgetA.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self. WidgetA.setObjectName("WidgetA")
self. WidgetB = QtWidgets.QWidget(self)
self. WidgetB.resize(100, 100)
self. WidgetB.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self. WidgetB.setObjectName("WidgetB")
self.posA = QtCore.QPoint(randint(0, self.width()-100), randint(0, self.height()-100))
self.posB = QtCore.QPoint(randint(0, self.width()), randint(0, self.height()))
self. WidgetA.move(self.posA)
self. WidgetB.move(self.posB)
self.setStyleSheet("""
#WidgetA {
background-color: red;
}
#WidgetB {
background-color: blue;
}
""")
self. WidgetA.installEventFilter(self)
self. WidgetB.installEventFilter(self)
self.installEventFilter(self)
def eventFilter(self, obj, event):
if obj in (self. WidgetA, self. WidgetB, self) and event.type() in (QtCore.QEvent.Resize, QtCore.QEvent.Move) :
gp = self. WidgetB.mapToGlobal(QtCore.QPoint(0, 0))
b_a = self. WidgetA.mapFromGlobal(gp)
print(b_a, self.posB -self.posA)
assert(b_a == (self.posB -self.posA))
return QtWidgets.QWidget.eventFilter(self, obj, event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())