Reputation: 45
I am using PyQt5 and PyQtGraph. I have simplified the example code below. Then I want to draw in the plot view a small red rectangle each time the mouse is clicked at the position where the mouse is clicked, thus accumulating several red rectangles in the plot view. The code below has a #??? comment where I need some help with code that will draw the red rectangle(s).
import sys
from PyQt5 import QtWidgets
import numpy as np
import pyqtgraph as pg
from pyqtgraph import PlotWidget, plot
# *********************************************************************************************
# *********************************************************************************************
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("My MainWindow")
self.qPlotWidget = pg.PlotWidget(self)
self.qPlotWidget.setLabel("bottom", "X-Axis")
self.qPlotWidget.setLabel("left", "Y-Axis")
self.qPlotWidget.scene().sigMouseClicked.connect(self.mouseClickedEvent)
data1 = np.zeros((2, 2), float) # create the array to hold the data
data1[0] = np.array((1.0, 10.0))
data1[1] = np.array((2.0, 20.0))
pen1 = pg.mkPen(color=(255,0,0), width=1) # red
self.qPlotWidget.plot(data1, pen=pen1, name="data1")
def mouseClickedEvent(self, event):
print("mouseClickedEvent")
pos = event.scenePos()
if (self.qPlotWidget.sceneBoundingRect().contains(pos)):
mousePoint = self.qPlotWidget.plotItem.vb.mapSceneToView(pos)
print("mousePoint=", mousePoint)
# draw and fill a 2-pixel by 2-pixel red rectangle where
# the mouse was clicked at [mousePoint.x(), mousePoint.y()]
# ??? add code here
def resizeEvent(self, event):
size = self.geometry()
self.qPlotWidget.setGeometry(10, 10, size.width()-20, size.height()-20)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
screen = QtWidgets.QDesktopWidget().screenGeometry()
w.setGeometry(100, 100, screen.width()-200, screen.height()-200) # x, y, Width, Height
w.show()
sys.exit(app.exec_())
Upvotes: 0
Views: 2145
Reputation: 579
I was looking for a similar thing myself, but I needed to be able to size the rectangle myself and extract the coordinates of the rectangle. Eventually I came up with writing a class based on pyqtgraph PlotWidget
, but with the right mouse click being used for drawing a rectangular ROI (instead of zooming). I've shared it here in case someone who stumbles onto this thread is looking for something similar
from PyQt6 import QtCore
from PyQt6.QtGui import QMouseEvent, QKeyEvent
from PyQt6.QtWidgets import QMainWindow
import pyqtgraph as pg
class RectROIPlotWidget(pg.PlotWidget):
"""PlotWidget with a rectangular ROI that can be drawn by right-clicking and dragging the mouse"""
def __init__(self, *args, **kwargs):
"""Initialize method of the RectROIPlotWidget"""
super().__init__(*args, **kwargs)
# disable autorange by default to avoid unexpected behaviour when drawing ROI
self.plotItem.vb.autoRange(False)
# add rectangular ROI
self.roi_rect = None
self.reset_roi_rect()
def reset_roi_rect(self):
"""Reset the rectangular ROI"""
# remove ROI from plot if it exists
if self.roi_rect in self.items():
self.removeItem(self.roi_rect)
# define start point
self.start_point = [0, 0]
# add rectangular ROI
self.roi_rect = pg.RectROI(
pos = [-1, -1],
size = [0, 0],
rotatable = False,
invertible = True,
pen = pg.mkPen([200, 200, 200], width=2, style=QtCore.Qt.PenStyle.DotLine),
hoverPen = pg.mkPen([255, 255, 255], width=2, style=QtCore.Qt.PenStyle.DashLine)
)
self.addItem(self.roi_rect)
self.roi_rect.setZValue(1000)
# set flag for drawing ROI
self.draw_flag = False
def mousePressEvent(self, event: QMouseEvent):
"""Mouse press event handling the start of drawing the ROI
Arguments
---------
- event (QMouseEvent): mouse press event
"""
if event.button().name == 'RightButton':
self.reset_roi_rect()
self.draw_flag = True
mouse_point = self.plotItem.vb.mapSceneToView(event.position())
self.start_point = [mouse_point.x(), mouse_point.y()]
else:
super().mousePressEvent(event)
def mouseMoveEvent(self, event: QMouseEvent):
"""Mouse move event handling the drawing of the ROI
Arguments
---------
- event (QMouseEvent): mouse move event
"""
if self.draw_flag:
mouse_point = self.plotItem.vb.mapSceneToView(event.position())
bottom_left = [min(self.start_point[0], mouse_point.x()), min(self.start_point[1], mouse_point.y())]
top_right = [max(self.start_point[0], mouse_point.x()), max(self.start_point[1], mouse_point.y())]
self.roi_rect.setPos(bottom_left)
self.roi_rect.setSize([top_right[0] - bottom_left[0], top_right[1] - bottom_left[1]])
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event: QMouseEvent):
"""Mouse release event handling the end of drawing the ROI
Arguments
---------
- event (QMouseEvent): mouse release event
"""
if event.button().name == 'RightButton':
self.draw_flag = False
super().mouseReleaseEvent(event)
def keyPressEvent(self, event: QKeyEvent):
"""Key press event handling the removal of the ROI when pressing escape
Arguments
---------
- event (QKeyEvent): key press event
"""
# remove ROI on pressing escape
if event.key() == QtCore.Qt.Key.Key_Escape:
self.roi_rect.setPos([-1, -1])
self.roi_rect.setSize([0, 0])
super().keyPressEvent(event)
def get_roi_rect_coords(self) -> tuple:
"""Get the coordinates of the rectangular ROI
Returns
-------
- tuple: tuple with two lists:
- list: bottom left coordinates of the ROI
- list: top right coordinates of the ROI
"""
# get bottom left and size of ROI
bottom_left = list(self.roi_rect.pos())
size = list(self.roi_rect.size())
# determine top right of ROI
top_right = [bottom_left[0] + size[0], bottom_left[1] + size[1]]
return bottom_left, top_right
app = pg.mkQApp("Plotting Example")
win = QMainWindow()
p = RectROIPlotWidget()
win.setCentralWidget(p)
win.show()
if __name__ == '__main__':
app.exec()
Upvotes: 0
Reputation: 24420
What you could do is to create an empty scatter plot item and add it to self.qPlotWidget
. Then in mousrClickedEvent
you could add the mouse position to the list of points of this scatter plot item, i.e.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
.... as before ....
# add empty scatter plot item with a red brush and a square as the symbol to plot widget
brush = pg.mkBrush(color=(255,0,0))
self.scatterItem = pg.ScatterPlotItem(pen=None, size=10, brush=brush, symbol='s')
self.qPlotWidget.addItem(self.scatterItem)
def mouseClickedEvent(self, event):
pos = event.scenePos()
if (self.qPlotWidget.sceneBoundingRect().contains(pos)):
mousePoint = self.qPlotWidget.plotItem.vb.mapSceneToView(pos)
# add point to scatter item
self.scatterItem.addPoints([mousePoint.x()], [mousePoint.y()])
Upvotes: 2