Reputation: 1261
I am trying to map one widgets pos()
(A) to another widget (B), so that B can do some work based on A's position. I have WidgetA
that gets WidgetB
pos, these widgets can have different parenting hierarchy:
Global
+--- Window
+--- WidgetA
+--- WidgetB
Global
+--- Window
+--- Sub
| +--- Sub
| +--- WidgetB
|
+--- WidgetA
Global
+--- Window
| +--- WidgetB
|
+--- WidgetA
etc.
I have tried to translate B
's position to a relative position on A
' via (Both widgets share 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 cannot seem to wrap it correctly.
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 would expect to get (0, 0)
since that is what WidgetB
is set to.
Upvotes: 4
Views: 10039
Reputation: 244301
The position of a widget is relative to the parent, it is not a position with respect to the screen, so in your example you should obtain (400, 400)
.
According to the docs:
pos : QPoint
This property holds the position of the widget within its parent widget
If 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 it is because you have established the position of the parent widget in (0, 0)
, if you change it, it will fail.
Considering that you want the position of A with respect to B, the solution is as follows:
Obtain the position of Widget B with respect to the screen using the following:
gp = self.WidgetB.mapToGlobal(QtCore.QPoint(0, 0))
it is passed (0, 0)
because mapToGlobal
requires the position with respect to the widget that uses the function, in your case the position of WidgetB
with respect to WidgetB
is (0, 0)
.
Then the global position respect the WidgetA
is mapped:
b_a = self.WidgetA.mapFromGlobal(gp)
So the following example shows the operation of my solution:
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_())
Upvotes: 4