我在容器小部件中有多个由 QGraphicLayout 管理的 QGraphicItem。
QGraphicItems 位于 QGraphicsItemGroup 内
我想实现当一个项目悬停在其他项目上时移动该项目时,使它们的边框更厚。有点强调它。
这是我当前状态的视频:https://streamable.com/104bdy
例如在随附的视频中:
首先拖动
setNetworkOffline
时,awaitMapPage
的上边框可能是粗体。
当该方法被向下拖动时,一旦到达放下的位置,它就会最终位于:
awaitMapPage
/clickOnLevelPin
之间,它们之间的边框会变成粗体。
当
setNetworkOffline
进一步拖动到下方时,clickOnLevelPin
/ sleepMillis
之间的边框可以变为粗体等。
我的容器类
创建的所有新项目都会自动生成唯一的node_id
class Container(QGraphicsWidget):
def __init__(self, node_id, parent=None):
super(Container, self).__init__(parent)
self._id = node_id
self.layout = QGraphicsGridLayout()
self.setLayout(self.layout)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.prev = None
self.setHandlesChildEvents(False)
self.setFlag(QGraphicsItem.ItemIsMovable, False)
self.setFlag(QGraphicsWidget.ItemIsMovable, True)
self.setFlag(QGraphicsWidget.ItemIsSelectable, True)
self.acceptTouchEvents()
self.setZValue(0)
我的物品类别
class CustomItem(GraphNodeItem):
def __init__(self, nodeId, parent, width):
super(CustomItem, self).__init__(nodeId, parent)
self.width = width
self.prev = None
self.parent = parent
def mousePressEvent(self, mouse_event):
if mouse_event.button() == Qt.LeftButton:
self.prev = self.pos()
color = QGraphicsColorizeEffect()
color.setColor(Qt.darkGreen)
if type(self) is CustomItem:
self.setGraphicsEffect(color)
super().mousePressEvent(mouse_event)
def mouseReleaseEvent(self, event):
self.graphicsEffect().setEnabled(False)
super().mouseReleaseEvent(event)
if self.prev is not None and event.button() == Qt.LeftButton:
colliding = self.collidingItems()
hit = False
for item in colliding:
if type(item) is CustomItem:
print("Switch methods")
container = self.parent.get_container()
container.switch(self, item)
hit = True
break
if not hit:
self.setPos(self.prev)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange and self.scene():
br = self.boundingRect().translated(value)
sceneRect = self.boundingRect()
if not sceneRect.contains(br):
if br.right() > sceneRect.right():
br.moveRight(sceneRect.right())
if br.x() < sceneRect.x():
br.moveLeft(sceneRect.x())
if br.y() < sceneRect.y():
br.moveTop(sceneRect.top())
return br.topLeft()
return super().itemChange(change, value)
请问如何实现这个效果?有任何想法吗?预先感谢。
您需要做的事情最可接受的选择至少需要两个方面:
有很多可能的方法来实现你想要的,这深深地取决于对象结构;不幸的是,您提供的代码不足以理解这些要求,因此我将提供一个通用实现,它显示了一种“可能”的方法。 对象关系和交互是通过自定义信号来实现的,这些信号“告诉”父级
如果他们将被移动,何时他们被移动,以及之后他们的移动可能已经结束。然后他们的容器将决定如何处理上述信号:
如果要移动托管项目,它将从布局中删除该项目并为其创建相关占位符;from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Container(QGraphicsWidget):
_placeholder = _movingItem = None
def __init__(self):
super().__init__()
layout = QGraphicsLinearLayout(Qt.Vertical, self)
layout.setSpacing(1)
for i in range(5):
item = CustomItem('item {}'.format(i + 1))
layout.addItem(item)
item.moveStarted.connect(self.moveStarted)
item.moving.connect(self.childIsMoving)
item.moveFinished.connect(self.moveFinished)
def _indexOf(self, item):
layout = self.layout()
for i in range(layout.count()):
if layout.itemAt(i) == item:
return i
return -1
def moveStarted(self, item):
index = self._indexOf(item)
if index < 0:
return
self._movingItem = item
self.layout().removeItem(item)
item.setParentItem(self)
item.setProperty('_oldZValue', item.zValue())
item.setZValue(1)
self._placeholder = Placeholder(item.size())
self.layout().insertItem(index, self._placeholder)
def childIsMoving(self, item, pos):
if not self._placeholder:
return
targetPos = item.boundingRect().translated(pos).center()
targetIndex = 0
layout = self.layout()
for i in range(layout.count()):
geo = layout.itemAt(i).geometry()
if targetPos.y() > geo.y():
targetIndex = i
elif targetPos.y() < geo.y():
break
layout.insertItem(targetIndex, self._placeholder)
self.update()
def moveFinished(self, item):
if self._movingItem != item:
return
if self._placeholder:
index = self._indexOf(self._placeholder)
self.layout().removeItem(self._placeholder)
self.scene().removeItem(self._placeholder)
self.layout().insertItem(index, item)
z = item.property('_oldZValue')
if isinstance(z, float):
item.setZValue(z)
item.setProperty('_oldZValue', None)
self._placeholder = None
self._movingItem = None
self.update()
def _dragItemLines(self, child):
top = bottom = False
if self._placeholder:
index = self._indexOf(child)
placeholderIndex = self._indexOf(self._placeholder)
if index >= 0 and placeholderIndex >= 0:
if index == placeholderIndex - 1:
bottom = True
if index == placeholderIndex + 1:
top = True
return top, bottom
def paint(self, qp, opt, widget=None):
qp.drawRect(self.boundingRect())
class Placeholder(QGraphicsWidget):
def __init__(self, size):
self._size = size
super().__init__()
def sizeHint(self, *args):
return self._size
def paint(self, qp, opt, widget=None):
qp.save()
qp.setPen(Qt.red)
qp.drawRect(self.boundingRect())
qp.restore()
class TextItem(QGraphicsLayoutItem):
def __init__(self, text):
super().__init__()
self.textItem = QGraphicsSimpleTextItem(text)
self.setGraphicsItem(self.textItem)
def setGeometry(self, *args):
super().setGeometry(*args)
br = self.textItem.boundingRect()
br.moveCenter(self.geometry().center())
self.textItem.setPos(br.topLeft())
def sizeHint(self, which, constraint):
sz = self.textItem.boundingRect().size()
if which == Qt.MaximumSize:
sz.setWidth(2000)
return sz
class CustomItem(QGraphicsWidget):
moving = pyqtSignal(object, QPointF)
moveStarted = pyqtSignal(object)
moveFinished = pyqtSignal(object)
basePen = QPen(Qt.blue)
dragPen = QPen(Qt.green, 4)
_background = QColor(127, 127, 127, 127)
_isMoving = False
def __init__(self, text):
super().__init__()
self.setFlag(self.ItemIsMovable)
self.setFlag(self.ItemSendsGeometryChanges)
layout = QGraphicsLinearLayout(Qt.Vertical, self)
self.textItem = TextItem(text)
layout.addItem(self.textItem)
def itemChange(self, change, value):
if change == self.ItemPositionChange and self._isMoving:
value.setX(self.x())
self.moving.emit(self, value)
return super().itemChange(change, value)
def mousePressEvent(self, event):
super().mousePressEvent(event)
if event.button() == Qt.LeftButton:
self._isMoving = True
self.moveStarted.emit(self)
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
if event.button() == Qt.LeftButton and self._isMoving:
self._isMoving = False
self.moveFinished.emit(self)
def paint(self, qp, opt, widget=None):
qp.setPen(self.basePen)
qp.fillRect(self.boundingRect(), self._background)
qp.drawRect(self.boundingRect())
parent = self.parentItem()
if isinstance(parent, Container):
drawTop, drawBottom = parent._dragItemLines(self)
if drawTop:
br = self.boundingRect()
qp.setPen(self.dragPen)
qp.drawLine(br.topLeft(), br.topRight())
if drawBottom:
br = self.boundingRect()
qp.setPen(self.dragPen)
qp.drawLine(br.bottomLeft(), br.bottomRight())
if __name__=='__main__':
import sys
app = QApplication(sys.argv)
scene = QGraphicsScene()
scene.addItem(Container())
view = QGraphicsView(scene)
view.resize(app.primaryScreen().size() / 2)
view.setRenderHint(QPainter.Antialiasing)
view.show()
sys.exit(app.exec_())
如前所述,这只是“一个”可能的实现。例如,您可以在不同级别实现子项目(或其“拖动线”)的绘制。没有绝对好的方法来实现这一切,这一切都取决于很多方面(程序要求、对象结构、项目标准等),这些方面甚至可能从根本上改变这一切的实现方式。
此外,我强烈建议您耐心地花时间研究上面的代码
原样,然后再尝试将其应用到您的程序中。