class PlotSelectionTool(PlotTool): """ A tool for selecting a region on a plot. """ #: Selection modes Rect, Lasso = 1, 2 #: Selection was started by the user. selectionStarted = Signal(QPainterPath) #: The current selection has been updated selectionUpdated = Signal(QPainterPath) #: The selection has finished (user has released the mouse button) selectionFinished = Signal(QPainterPath) def __init__(self, parent=None, selectionMode=Rect, **kwargs): super().__init__(parent, **kwargs) self.__mode = selectionMode self.__path = None self.__item = None def setSelectionMode(self, mode): """ Set the selection mode (rectangular or lasso selection). Parameters ---------- mode : int PlotSelectionTool.Rect or PlotSelectionTool.Lasso """ assert mode in {PlotSelectionTool.Rect, PlotSelectionTool.Lasso} if self.__mode != mode: if self.__path is not None: self.selectionFinished.emit() self.__mode = mode self.__path = None def selectionMode(self): """ Return the current selection mode. """ return self.__mode def selectionShape(self): """ Return the current selection shape. This is the area selected/drawn by the user. Returns ------- shape : QPainterPath The selection shape in view coordinates. """ if self.__path is not None: shape = QPainterPath(self.__path) shape.closeSubpath() else: shape = QPainterPath() viewbox = self.viewBox() if viewbox is None: return QPainterPath() return viewbox.childGroup.mapFromParent(shape) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: pos = event.pos() if self.__mode == PlotSelectionTool.Rect: rect = QRectF(pos, pos) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path = QPainterPath() self.__path.moveTo(event.pos()) self.selectionStarted.emit(self.selectionShape()) self.__updategraphics() event.accept() return True else: return False def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: if self.__mode == PlotSelectionTool.Rect: rect = QRectF(event.buttonDownPos(Qt.LeftButton), event.pos()) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path.lineTo(event.pos()) self.selectionUpdated.emit(self.selectionShape()) self.__updategraphics() event.accept() return True else: return False def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.__mode == PlotSelectionTool.Rect: rect = QRectF(event.buttonDownPos(Qt.LeftButton), event.pos()) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path.lineTo(event.pos()) self.selectionFinished.emit(self.selectionShape()) self.__path = QPainterPath() self.__updategraphics() event.accept() return True else: return False def __updategraphics(self): viewbox = self.viewBox() if viewbox is None: return if self.__path.isEmpty(): if self.__item is not None: self.__item.setParentItem(None) viewbox.removeItem(self.__item) if self.__item.scene(): self.__item.scene().removeItem(self.__item) self.__item = None else: if self.__item is None: item = QtGui.QGraphicsPathItem() color = QtGui.QColor(Qt.yellow) item.setPen(QtGui.QPen(color, 0)) color.setAlpha(50) item.setBrush(QtGui.QBrush(color)) self.__item = item viewbox.addItem(item) self.__item.setPath(self.selectionShape())
class GraphicsPathObject(QGraphicsObject): """A QGraphicsObject subclass implementing an interface similar to QGraphicsPathItem, and also adding a positionChanged() signal """ positionChanged = Signal([], ["QPointF"]) def __init__(self, parent=None, **kwargs): QGraphicsObject.__init__(self, parent, **kwargs) self.setFlag(QGraphicsObject.ItemSendsGeometryChanges) self.__path = QPainterPath() self.__brush = QBrush(Qt.NoBrush) self.__pen = QPen() self.__boundingRect = None def setPath(self, path): """Set the items `path` (:class:`QPainterPath`). """ if not isinstance(path, QPainterPath): raise TypeError("%r, 'QPainterPath' expected" % type(path)) if self.__path != path: self.prepareGeometryChange() # Need to store a copy of object so the shape can't be mutated # without properly updating the geometry. self.__path = QPainterPath(path) self.__boundingRect = None self.update() def path(self): """Return the items path. """ return QPainterPath(self.__path) def setBrush(self, brush): """Set the items `brush` (:class:`QBrush`) """ if not isinstance(brush, QBrush): brush = QBrush(brush) if self.__brush != brush: self.__brush = QBrush(brush) self.update() def brush(self): """Return the items brush. """ return QBrush(self.__brush) def setPen(self, pen): """Set the items outline `pen` (:class:`QPen`). """ if not isinstance(pen, QPen): pen = QPen(pen) if self.__pen != pen: self.prepareGeometryChange() self.__pen = QPen(pen) self.__boundingRect = None self.update() def pen(self): """Return the items pen. """ return QPen(self.__pen) def paint(self, painter, option, widget=None): if self.__path.isEmpty(): return painter.save() painter.setPen(self.__pen) painter.setBrush(self.__brush) painter.drawPath(self.__path) painter.restore() def boundingRect(self): if self.__boundingRect is None: br = self.__path.controlPointRect() pen_w = self.__pen.widthF() self.__boundingRect = br.adjusted(-pen_w, -pen_w, pen_w, pen_w) return self.__boundingRect def shape(self): return shapeFromPath(self.__path, self.__pen) def itemChange(self, change, value): if change == QGraphicsObject.ItemPositionHasChanged: self.positionChanged.emit() self.positionChanged[QPointF].emit(value) return QGraphicsObject.itemChange(self, change, value)
class PlotSelectionTool(PlotTool): """ A tool for selecting a region on a plot. """ #: Selection modes Rect, Lasso = 1, 2 #: Selection was started by the user. selectionStarted = Signal(QPainterPath) #: The current selection has been updated selectionUpdated = Signal(QPainterPath) #: The selection has finished (user has released the mouse button) selectionFinished = Signal(QPainterPath) def __init__(self, parent=None, selectionMode=Rect, **kwargs): super().__init__(parent, **kwargs) self.__mode = selectionMode self.__path = None self.__item = None def setSelectionMode(self, mode): """ Set the selection mode (rectangular or lasso selection). Parameters ---------- mode : int PlotSelectionTool.Rect or PlotSelectionTool.Lasso """ assert mode in {PlotSelectionTool.Rect, PlotSelectionTool.Lasso} if self.__mode != mode: if self.__path is not None: self.selectionFinished.emit() self.__mode = mode self.__path = None def selectionMode(self): """ Return the current selection mode. """ return self.__mode def selectionShape(self): """ Return the current selection shape. This is the area selected/drawn by the user. Returns ------- shape : QPainterPath The selection shape in view coordinates. """ if self.__path is not None: shape = QPainterPath(self.__path) shape.closeSubpath() else: shape = QPainterPath() viewbox = self.viewBox() if viewbox is None: return QPainterPath() return viewbox.childGroup.mapFromParent(shape) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: pos = event.pos() if self.__mode == PlotSelectionTool.Rect: rect = QRectF(pos, pos) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path = QPainterPath() self.__path.moveTo(event.pos()) self.selectionStarted.emit(self.selectionShape()) self.__updategraphics() event.accept() return True else: return False def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: if self.__mode == PlotSelectionTool.Rect: rect = QRectF(event.buttonDownPos(Qt.LeftButton), event.pos()) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path.lineTo(event.pos()) self.selectionUpdated.emit(self.selectionShape()) self.__updategraphics() event.accept() return True else: return False def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.__mode == PlotSelectionTool.Rect: rect = QRectF(event.buttonDownPos(Qt.LeftButton), event.pos()) self.__path = QPainterPath() self.__path.addRect(rect) else: self.__path.lineTo(event.pos()) self.selectionFinished.emit(self.selectionShape()) self.__path = QPainterPath() self.__updategraphics() event.accept() return True else: return False def __updategraphics(self): viewbox = self.viewBox() if viewbox is None: return if self.__path.isEmpty(): if self.__item is not None: self.__item.setParentItem(None) viewbox.removeItem(self.__item) if self.__item.scene(): self.__item.scene().removeItem(self.__item) self.__item = None else: if self.__item is None: item = QtGui.QGraphicsPathItem() color = QtGui.QColor(Qt.yellow) item.setPen(QtGui.QPen(color, 0)) color.setAlpha(50) item.setBrush(QtGui.QBrush(color)) self.__item = item viewbox.addItem(item) self.__item.setPath(self.selectionShape())