I have a QGraphicsScene where I have QGraphicsItems and enabled rubberBand selection. I want to select these items with rubberband selection but I want them to become selected only when the rubber band is released. Now it selects/deselects items live time. So, items must get selected only when I release the rubberband. I think I might need to completely change the way I add the rubber band but I don't quite know how.
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()
Also I want to make my rubberband area colored and semi-transparent. I've read the docs but can't correctly implement everything that I've read. Any help would be greatly appreciated!
A possible solution is to create a "fake" rubber band, a widget that is child of the view (or, better, the viewport).
While QGraphicsView does it in the paintEvent (with a "virtual" rectangle painted over the view), using a child widget avoids overriding of the paint event and provides more control over its behavior.
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