2

I am writing a program that lets users select items among a bunch of polygons in a QGraphicsScene.

PySide provides a handy rubber-band selection tool but I wanted to be able to select items by drawing a polygon on the scene. To implement this I had to redefined mouse events such as mousePressEvent. This works, great! ...but of course it disabled the rubber-band and the possibility of selecting objects by merely clicking on them.

Now, what I would like to do is to be able to switch between those tools. Either Let users choose one (with, let say, a ctrl modifier) or force them to use any one of them depending on the context.

Has anyone an idea about how this could be achieved? Would be truly grateful for any tips.

Thanks /Romain

Here comes a not-so-minimal working example of what I did:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PySide import QtGui, QtCore

########### Item class creates polygon items
class Geometry(QtGui.QGraphicsItem):

    def __init__(self, pen):
        super(Geometry, self).__init__()

        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable )
        self.brush = None
        self.pen = pen
        self.create_rectangle()

    def create_rectangle(self, w = 0, h= 0, x = 0, z = 0):
        self.xmin = x
        self.xmax = w
        self.ymin = z
        self.ymax = h
        self.polygon = QtGui.QPolygonF([QtCore.QPointF(self.xmax, self.ymax),  QtCore.QPointF(self.xmax, self.ymin), QtCore.QPointF(self.xmin, self.ymin), QtCore.QPointF(self.xmin, self.ymax)])       

    def shape(self):
        path = QtGui.QPainterPath()
        path.addPolygon(self.polygon)
        return path

    def paint(self, painter, option, widget):
        painter.setPen(self.pen)
        if self.brush:
            painter.setBrush(self.brush)

        if self.isSelected ():
            pen = QtGui.QPen(QtCore.Qt.gray, 2, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
            painter.setPen( pen )
            boundRect = self.boundingRect ()
            painter.drawRect( boundRect )
        painter.drawPolygon(self.polygon)

    def setBrush(self, brush):
        self.brush = brush

    def boundingRect(self):
        return self.polygon.boundingRect()
###########################################################################################################

###########  polygon for selection
class Selector(QtGui.QGraphicsItem):

    def __init__(self, point):
        super(Selector, self).__init__()
        self.polygon = QtGui.QPainterPath(point) 

    def add_point(self, point):
        self.polygon.lineTo(point) 

    def boundingRect(self):
        return self.polygon.boundingRect()

    def to_polygon(self):
        self.polygon.closeSubpath()

    def paint(self, painter, option, widget):
        painter.drawPath(self.polygon)

    def path(self):
        return self.polygon
#############################################################################################################


########### View class , creates a view, scene initialized a scene and integrated to view inside this class
class View(QtGui.QGraphicsView):
    default = True
    def __init__(self):
        super(View, self).__init__()

        self.setWindowTitle("Custom item")
        self.setDragMode(QtGui.QGraphicsView.RubberBandDrag)
        self.scene = QtGui.QGraphicsScene()

        self.clickclick = 0
        self.old_selector = None

    def create_domain(self , x = 0 , z = 0):
        pen = QtGui.QPen(QtCore.Qt.black, 0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
        self.item = Geometry(pen)

        self.item.create_rectangle(x,z)
        self.item.setZValue(1)
        self.item.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, False)

        self.scene.addItem(self.item)
        self.scene.mode = "cells"
        self.setScene(self.scene)

        self.line = None

    def discretize_domain(self , nb_x = 1 , nb_z = 1):  
        pen = QtGui.QPen(QtCore.Qt.gray, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)                               
        self.cell_dict = {}
        self.count = 0       
        dx = domain_x / nb_x
        dz = domain_z / nb_z
        for i in range(0,nb_z):
            self.min_z = i*dz
            self.max_z = (i+1)*dz
            for j in range(0,nb_x):
                self.min_x = j*dx
                self.max_x = (j+1)*dx
                self.cell_item = Geometry(pen)
                self.cell_item.create_rectangle(self.max_x, self.max_z, self.min_x, self.min_z)
                self.scene.addItem(self.cell_item)
                self.cell_dict[self.cell_item] = [self.count ,  "button0" ]
                self.count += 1
        global cell_dict
        cell_dict = self.cell_dict
        self.setScene(self.scene)

    def mousePressEvent(self, event):
        self._start = self.mapToScene(event.pos())
        if self.clickclick :
            self.scene.removeItem(self.selector)
            self.selector.add_point(self.mapToScene(event.pos()))
        else :
            self.selector = Selector(self._start)
        self.scene.addItem( self.selector )
        self.clickclick += 1

    def mouseReleaseEvent(self, event):
        if not self.clickclick:
            self.scene.removeItem(self.old_selector)

    def mouseDoubleClickEvent(self , event):
        self.scene.removeItem(self.selector)
        self.selector.to_polygon()
        self.scene.addItem(self.selector)
        self.old_selector = self.selector
        self.clickclick = 0
        self.scene.setSelectionArea(self.selector.path(),QtCore.Qt.ContainsItemShape)
############################################################################################################



############
############   Main window (not in use so far)
class Window(QtGui.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.initUI()

    def initUI(self):
        # Create Buttons
        self.start_bt = QtGui.QPushButton('Start', self)
        self.start_bt.setEnabled(False)

        #Creates a horizontal box with buttons at the start and stretch at the end
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.start_bt)
        hbox.addStretch(1)

        self.view = View()   

        # create a vertical box with hbox at the top and stretch at the bottom
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addLayout(hbox)
        self.vbox.addWidget(self.view)

        # Puts vbox in widget
        self.main_widget = QtGui.QWidget()
        self.main_widget.setLayout(self.vbox)

        # Integrate main widget in window
        self.setCentralWidget(self.main_widget)

        # Defines window position and size
        self.setGeometry(1800, 110, 700, 500)
        self.setWindowTitle('FutureLearn kick-ass software')
        self.setWindowIcon(QtGui.QIcon('glass.jpg'))

        self.firststart = 0

        global domain_x, domain_z 
        domain_x = 500
        domain_z = 200

        self.view.create_domain(domain_x, domain_z)

        global nb_x, nb_z   #, k_matrix, value_matrix
        nb_x = 30
        nb_z = 20

        self.view.discretize_domain(nb_x + 1 , nb_z)

        self.set_scene(self.view)

    def on_key_d(self):
        selectedItems = Self.scene

    def set_scene(self,view): 
        self.view.setParent(None)                
        self.vbox.addWidget(view)
#############################################################################################################


############################################## Mr Main ######################################################
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    main_win = Window()
    main_win.show()
    sys.exit(app.exec_())

1 Answers1

2

To answer my own question, here is the way I solved it:

def mousePressEvent(self, event):
    modifiers = QtGui.QApplication.keyboardModifiers()
    if modifiers == QtCore.Qt.ControlModifier:

        ...
        Do_this()
        do that() 
        ...

    else:
        super(View, self).mousePressEvent(event)

super() allows you to import the original parent method. So you can redefine the event only if a condition is satisfied (in my case ctrl is pressed) and use the inherited method otherwise.

Cheers
\Romain