Я не проанализировал, потому что ваша программа не работает, поэтому мой ответ не будет сосредоточен на ней, вместо этого я сосредоточусь на основной проблеме.
Вам просто нужно иметь PropertyBox, который вы скрываете или показываете при необходимости. Я вижу проблему, пытаясь использовать методы focusInEvent и focusOutEvent, потому что когда вы нажимаете PropertyBox, элемент теряет фокус, поэтому сам PropertyBox будет удален (возможно, это и является причиной сбоя). Поэтому вместо использования фокуса вы должны использовать mousePressEvent и проверить, есть ли элемент, по которому он был нажат.
Учитывая вышеизложенное, решение:
import random
from PySide2 import QtCore, QtGui, QtWidgets
class BoxItem(QtWidgets.QGraphicsRectItem):
def __init__(self, parent=None):
super(BoxItem, self).__init__(QtCore.QRectF(0, 0, 200, 200), parent)
self.setFlags(
QtWidgets.QGraphicsItem.ItemIsSelectable
| QtWidgets.QGraphicsItem.ItemIsMovable
| QtWidgets.QGraphicsItem.ItemIsFocusable
)
br = self.boundingRect()
self.setTransformOriginPoint(
QtCore.QPointF(br.width() / 2, br.height() / 2)
)
class PropertyBox(QtWidgets.QWidget):
rotationChanged = QtCore.Signal(float)
scaleChanged = QtCore.Signal(float)
def __init__(self, parent=None):
super(PropertyBox, self).__init__(parent)
self.m_rotation_spinbox = QtWidgets.QDoubleSpinBox(
minimum=-360, maximum=360
)
self.m_rotation_spinbox.valueChanged.connect(self.rotationChanged)
self.m_scale_spinbox = QtWidgets.QDoubleSpinBox(
minimum=0, maximum=100, singleStep=0.1
)
self.m_scale_spinbox.valueChanged.connect(self.scaleChanged)
lay = QtWidgets.QFormLayout(self)
lay.addRow("Rotation:", self.m_rotation_spinbox)
lay.addRow("Scale:", self.m_scale_spinbox)
@property
def rotation(self):
return self.m_rotation_spinbox.value()
@rotation.setter
def rotation(self, value):
self.m_rotation_spinbox.setValue(value)
@property
def scale(self):
return self.m_scale_spinbox.value()
@scale.setter
def scale(self, value):
self.m_scale_spinbox.setValue(value)
class GraphicsView(QtWidgets.QGraphicsView):
currentItemChanged = QtCore.Signal(QtWidgets.QGraphicsItem)
def mousePressEvent(self, event):
super(GraphicsView, self).mousePressEvent(event)
it = self.itemAt(event.pos())
self.currentItem = it
@property
def currentItem(self):
if not hasattr(self, "_currentItem"):
self._currentItem = None
return self._currentItem
@currentItem.setter
def currentItem(self, it):
if self.currentItem != it:
self._currentItem = it
self.currentItemChanged.emit(it)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_scene = QtWidgets.QGraphicsScene()
self.m_view = GraphicsView(self.m_scene)
self.m_view.currentItemChanged.connect(self.onCurrentItemChanged)
for i in range(4):
it = BoxItem()
self.m_scene.addItem(it)
it.setPos(QtCore.QPointF(100 * i, 100 * i))
it.setBrush(QtGui.QColor(*random.sample(range(255), 3)))
self.m_property_box = PropertyBox()
self.m_property_box.rotationChanged.connect(self.onRotationChanged)
self.m_property_box.scaleChanged.connect(self.onScaleChanged)
self.m_property_box.hide()
lay = QtWidgets.QHBoxLayout(self)
lay.addWidget(self.m_view)
lay.addWidget(self.m_property_box)
@QtCore.Slot(QtWidgets.QGraphicsItem)
def onCurrentItemChanged(self, item):
self.m_property_box.setVisible(item is not None)
if item is not None:
self.m_property_box.rotation = item.rotation()
self.m_property_box.scale = item.scale()
@QtCore.Slot(float)
def onRotationChanged(self, rotation):
it = self.m_view.currentItem
if it is not None:
it.setRotation(rotation)
@QtCore.Slot(float)
def onScaleChanged(self, scale):
it = self.m_view.currentItem
if it is not None:
it.setScale(scale)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())