Select 项仅在释放时带有 RubberBandDrag

Select items with RubberBandDrag only when it's released

我有一个 QGraphicsScene,其中有 QGraphicsItems 并启用了 rubberBand selection。 我想 select 这些物品带有橡皮筋 selection,但我希望它们仅在松开橡皮筋时才变成 selected。现在 selects/deselects 项目的实时时间。因此,只有当我松开橡皮筋时,物品才必须 selected。我想我可能需要彻底改变我添加橡皮筋的方式,但我不太清楚如何。


ui_path = "C:/Users/User/ui/button_test.ui"

class Test(QtWidgets.QWidget):
    def __init__(self):
        super(Test, self).__init__()

        loader = QtUiTools.QUiLoader()
        self.ui = loader.load(ui_path, self)

        self.scene = QtWidgets.QGraphicsScene()
        self.ui.graphics_view.setScene(self.scene)

        self.ui.create_rect.clicked.connect(self.itemAdd) # create_rect is a QPushButton
    
        self.ui.graphics_view.setDragMode(QGraphicsView.RubberBandDrag)

        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint)

    def itemAdd(self, event):
        
        pen = QPen(Qt.GlobalColor.lightGray)
        pen.setWidth(10)

        brush = QBrush(Qt.GlobalColor.lightGray)

        rect = self.scene.addRect(0, 0, 40, 40, pen, brush)
      
        rect.setFlag(QGraphicsItem.ItemIsMovable)
        rect.setFlag(QGraphicsItem.ItemIsFocusable)
        rect.setFlag(QGraphicsItem.ItemIsSelectable)
        


if __name__ == '__main__':
    win = Test()
    win.ui.show()

我还想让我的橡皮筋区域有颜色和半透明。 我已阅读文档,但无法正确实施我已阅读的所有内容。任何帮助将不胜感激!

一个可能的解决方案是创建一个“假”橡皮筋,一个作为视图(或者,更好的,视口)的子窗口小部件。

虽然 QGraphicsView 在 paintEvent 中执行此操作(在视图上绘制了一个“虚拟”矩形),但使用子窗口小部件可避免覆盖绘制事件并提供对其行为的更多控制。

class Test(QWidget):
    def __init__(self):
        # ...
        self.ui.graphics_view.viewport().installEventFilter(self)
        self.rubberBand = None

    def eventFilter(self, obj, event):
        if event.type() == event.MouseButtonPress and event.button() == Qt.LeftButton:
            # if there is no item at the mouse position, create a rubber band
            if not self.ui.graphics_view.itemAt(event.pos()) and not self.rubberBand:
                self.createRubberBand(event.pos())
        elif event.type() == event.MouseMove and self.rubberBand:
            self.updateRubberBand(event.pos())
        elif event.type() == event.MouseButtonRelease and self.rubberBand:
            self.finalizeRubberBand()
        return super().eventFilter(obj, event)

    def createRubberBand(self, pos):
        # create a rubber band as child widget of the *viewport*
        self.rubberBand = QWidget(self.ui.graphics_view.viewport())
        # store the start position to get the proper rectangle when dragging
        self.rubberBand.start = pos

        # use the palette to get the default selection color and
        # make it semi transparent for the background
        background = self.palette().color(QPalette.Highlight)
        background.setAlphaF(.5)
        self.rubberBand.setStyleSheet('''
            border: 1px solid palette(highlight); 
            background: {};
        '''.format(background.name(background.HexArgb)))

        self.rubberBand.setGeometry(pos.x(), pos.y(), 0, 0)
        self.rubberBand.show()

    def updateRubberBand(self, pos):
        # update the rectangle based on start and mouse position, since the result
        # could be a rectangle with negative width or height, we need to "normalize"
        # as widget geometries can only have positive dimensions
        rect = QRect(self.rubberBand.start, pos).normalized()
        self.rubberBand.setGeometry(rect)

    def finalizeRubberBand(self):
        # map the geometry of the rubber band to the scene
        area = self.ui.graphics_view.mapToScene(self.rubberBand.geometry())
        path = QPainterPath()
        path.addPolygon(area)
        self.scene.setSelectionArea(path)
        # remove the rubber band
        self.rubberBand.deleteLater()
        self.rubberBand = None