The below code allows me to draw an interactive rectangle and control its size and location by mouse.
In the same way, how do I draw a fixed interactive grid mXn (see the image at the link below) instead of a rectangle?
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
def line_select_callback(eclick, erelease):
'eclick and erelease are the press and release events'
x1, y1 = eclick.xdata, eclick.ydata
x2, y2 = erelease.xdata, erelease.ydata
print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2))
print(" The button you used were: %s %s" % (eclick.button, erelease.button))
def toggle_selector(event):
print(' Key pressed.')
if event.key in ['Q', 'q'] and toggle_selector.RS.active:
print(' RectangleSelector deactivated.')
toggle_selector.RS.set_active(False)
if event.key in ['A', 'a'] and not toggle_selector.RS.active:
print(' RectangleSelector activated.')
toggle_selector.RS.set_active(True)
fig, current_ax = plt.subplots() # make a new plotting range
N = 100000 # If N is large one can see
x = np.linspace(0.0, 10.0, N) # improvement by use blitting!
plt.plot(x, np.sin(.2*np.pi*x), lw=3.5, c='b', alpha=.7) # plot something
plt.plot(x, np.cos(.2*np.pi*x), lw=3.5, c='r', alpha=.5)
plt.plot(x, -np.sin(.2*np.pi*x), lw=3.5, c='g', alpha=.3)
print("\n click --> release")
# drawtype is 'box' or 'line' or 'none'
toggle_selector.RS = RectangleSelector(current_ax, line_select_callback,
drawtype='box', useblit=True,
button=[1, 3], # don't use middle button
minspanx=5, minspany=5,
spancoords='pixels',
interactive=True)
plt.connect('key_press_event', toggle_selector)
plt.show()
CodePudding user response:
If I understood it correctly, m and n are constants. If not, you can set them high enough. Matplotlib is designed for plotting functions, so PyQt5 is probably a much better choice (especially if you extend the functionality further).
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
title = "Grid"
top = 400
left = 400
width = 800
height = 600
self.m = 8
self.n = 6
self.x1 = 0
self.x2 = 0
self.y1 = 0
self.y2 = 0
self.st_x = 0
self.st_y = 0
self.mode = "draw"
self.setWindowTitle(title)
self.setGeometry(top, left, width, height)
self.image = QImage(self.size(), QImage.Format_RGB32) # creating canvas
self.image.fill(Qt.white)
def mousePressEvent(self, event):
if self.x1 < event.x() < self.x2 and self.y1 < event.y() < self.y2:
# mouse pressed in grid area
self.mode = "move"
self.st_x = event.x()
self.st_y = event.y()
else:
self.mode = "draw"
self.x1 = event.x()
self.y1 = event.y()
self.image.fill(Qt.white)
self.update()
def mouseReleaseEvent(self, event):
if self.mode == "draw":
self.x2 = event.x()
self.y2 = event.y()
else:
x_delta = event.x() - self.st_x # 'delta' would be a x, y vector drawn by mouse press and release
y_delta = event.y() - self.st_y
self.x1 = x_delta
self.y1 = y_delta
self.x2 = x_delta
self.y2 = y_delta
self.draw_grid((self.x1, self.y1), (self.x2, self.y2), 50, 50)
def paintEvent(self, event):
canvasPainter = QPainter(self)
canvasPainter.drawImage(
self.rect(), self.image, self.image.rect()
)
def draw_grid(self, start, end, rect_width, rect_height):
painter = QPainter(self.image)
painter.setPen(
QPen(Qt.black, 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
)
for y in range(self.n):
for x in range(self.m):
if start[0] rect_width*x < end[0] and start[1] rect_height*y < end[1]:
painter.drawRect(
QRect(start[0] rect_width*x, start[1] rect_height*y, rect_width, rect_height)
)
self.update()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec()) # may want to remove sys.exit
Update: In your example, a QGraphicsScene is used, unlike here. I don't know if this object provides any advantage but if you want to see the grid moving, while you are holding the mouse button, you can easily override the mouseMoveEvent, like in the example. QMainWindow inherits this event from QWidget.
