我正在制作一个小部件来访问和修改QGraphicsRectItem 属性(比例,旋转度等)。主要思想是在单击项目时显示一个小部件。当您单击另一个项目时,该小部件应删除并替换为另一个(显示另一个项目的属性。)
我已经使用 QFocusEvent 实现了这一点,很不幸,它给了我一个 SIGSEGV 错误。我完全理解为什么会发生这种情况,但无法找出另一种方法。
这是一个python草图:
from PySide2 import QtWidgets, QtCore, QtGui
import sys, shiboken2
class graphicsView(QtWidgets.QGraphicsView):
def __init__(self, scene, parent=None):
super(graphicsView, self).__init__(parent)
self.scene = scene
self.setScene(self.scene)
def wheelEvent(self, event):
factor = 1.41 ** (-event.delta() / 240)
self.scale(factor, factor)
class boxItem(QtWidgets.QGraphicsRectItem):
def __init__(self, parent):
super(boxItem, self).__init__()
self.setFlags(QtWidgets.QGraphicsItem.ItemIsSelectable
| QtWidgets.QGraphicsItem.ItemIsMovable
| QtWidgets.QGraphicsItem.ItemIsFocusable)
self.rect = QtCore.QRectF(0, 0, 200, 200)
self.setRect(self.rect)
self.parent = parent
def focusInEvent(self, event:QtGui.QFocusEvent):
self.pBox = propBox()
self.parent.layout.addWidget(self.pBox)
self.pBox.r_box.setValue(self.rotation())
self.pBox.r_box.valueChanged.connect(self.setRotationAngle)
def focusOutEvent(self, event:QtGui.QFocusEvent):
# Here's a shiboken(equivalent to sip in PyQt) deletes c++ object and a python wrapper
# It works as expected but because of focusOutEvent it deletes the widget when i click on it
# There's when the error appears.
self.parent.layout.removeWidget(self.pBox)
shiboken2.delete(self.pBox)
def setRotationAngle(self, degrees):
br = self.boundingRect()
self.setTransformOriginPoint(QtCore.QPointF(br.width() / 2, br.height() / 2))
self.setRotation(-degrees)
self.update()
class propBox(QtWidgets.QWidget):
def __init__(self, parent=None):
super(propBox, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout()
self.layout.setAlignment(QtCore.Qt.AlignTop)
self.setLayout(self.layout)
self.r_label = QtWidgets.QLabel("Rotation:", self)
self.r_box = QtWidgets.QSpinBox(self)
self.r_layout = QtWidgets.QHBoxLayout()
self.r_layout.addWidget(self.r_label)
self.r_layout.addWidget(self.r_box)
self.s_label = QtWidgets.QLabel("Scale:")
self.s_box = QtWidgets.QSpinBox(self)
self.s_layout = QtWidgets.QHBoxLayout()
self.s_layout.addWidget(self.s_label)
self.s_layout.addWidget(self.s_box)
self.layout.addLayout(self.r_layout)
self.layout.addLayout(self.s_layout)
class mainWidget(QtWidgets.QWidget):
def __init__(self):
super(mainWidget, self).__init__()
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
box1 = boxItem(self)
box1.setRotation(45)
box2 = boxItem(self)
self.scene = QtWidgets.QGraphicsScene(self)
self.scene.setSceneRect(0, 0, 500, 300)
self.scene.addItem(box1)
self.scene.addItem(box2)
self.view = graphicsView(self.scene)
self.pBox = propBox(self)
self.layout.addWidget(self.view)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = mainWidget()
window.show()
sys.exit(app.exec_())
答案 0 :(得分:1)
我没有分析过,因为您的程序崩溃了,所以我的答案将不再关注它,而是将其关注于潜在的问题。
您只需要在需要时隐藏或显示的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_())