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_())