【发布时间】:2019-02-12 15:16:58
【问题描述】:
我正在使用 PyQt5 构建一个工具,该工具将允许用户在 matplotlib_widget 中单击时拖动矩形。问题是我想知道点击了哪个矩形。
由于我的画布和我的矩形是两个不同的类,我很难在它们之间进行正确的交流。为了解决这个问题,我在矩形画布的“button_click_event”上添加了一个事件处理程序,以将其位置写入 .txt 文件。另一方面,我还将“button_click_event”连接到我的画布,这是为了读取 .txt 并在先前存储的列表中验证该位置是否存在。如果是,则表示点击的是这个特定的矩形。
问题:读取 .txt 文件的画布的事件处理程序在写入它的处理程序之前被调用。
是否可以对 figure.canvas 的事件设置优先级?
这是我的具体问题的一个最低限度的工作示例。你会注意到当我想要的功能做某事时我做了打印。如果你双击并创建 2 个矩形,你应该注意到在单击矩形 1 后单击矩形 0,它会说你单击了矩形 1。这是“saveInFilie”功能的演示之后调用文件被读取。
import matplotlib.pyplot as plt
from matplotlib import patches
class DraggableRectangle:
lock = None # only one can be animated at a time
def __init__(self, rect):
self.rect = rect
self.press = None
self.background = None
def connect(self):
'connect to all the events we need'
self.cidpress = self.rect.figure.canvas.mpl_connect(
'button_press_event', self.on_press)
self.cidrelease = self.rect.figure.canvas.mpl_connect(
'button_release_event', self.on_release)
def on_press(self, event):
'on button press we will see if the mouse is over us and store some data'
if event.inaxes != self.rect.axes: return
if DraggableRectangle.lock is not None: return
contains, attrd = self.rect.contains(event)
if not contains: return
print('Held @', self.rect.xy)
x0, y0 = self.rect.xy
self.saveInFile(str(self.rect.xy))
print("click write succeded")
self.press = x0, y0, event.xdata, event.ydata
DraggableRectangle.lock = self
# draw everything but the selected rectangle and store the pixel buffer
canvas = self.rect.figure.canvas
axes = self.rect.axes
self.rect.set_animated(True)
canvas.draw()
self.background = canvas.copy_from_bbox(self.rect.axes.bbox)
# now redraw just the rectangle
axes.draw_artist(self.rect)
# and blit just the redrawn area
canvas.blit(axes.bbox)
def on_release(self, event):
'on release we reset the press data'
if DraggableRectangle.lock is not self:
return
x0, y0 = self.rect.xy
self.press = None
DraggableRectangle.lock = None
# turn off the rect animation property and reset the background
self.rect.set_animated(False)
self.background = None
self.saveInFile(str(self.rect.xy))
print("release write succeded\n")
# redraw the full figure
self.rect.figure.canvas.draw()
#print("Realeased @", x0, y0)
def saveInFile(self, drop):
filename = "pos.txt"
with open(filename, "w") as file:
file.write(drop)
file.close()
class MyFigure:
def __init__(self):
# Figure initialisation
self.fig = plt.figure()
self.axes = self.fig.add_subplot(1, 1, 1)
self.fig.subplots_adjust(0, 0, 1, 1)
self.axes.set_frame_on(False)
self.axes.invert_yaxis()
self.axes.axis('off')
self.axes.xaxis.set_visible(False)
self.axes.yaxis.set_visible(False)
# Connections
self.ciddouble = self.fig.canvas.mpl_connect('button_press_event', self.createRectangle)
self.CheckClick = self.fig.canvas.mpl_connect('button_press_event', self.checkRectangleOnClick)
self.CheckRelease = self.fig.canvas.mpl_connect('button_release_event', self.checkRectangleOnRelease)
# Variables
self.devices = []
self.drs = []
self.sensorPixelSize = [50, 50]
def createRectangle(self, event):
relPosX = event.x / (self.fig.get_size_inches()[0] * self.fig.dpi)
relPosY = 1 - (event.y / (self.fig.get_size_inches()[1] * self.fig.dpi))
relSizeX = self.sensorPixelSize[0] / (self.fig.get_size_inches()[0] * self.fig.dpi)
relSizeY = self.sensorPixelSize[1] / (self.fig.get_size_inches()[1] * self.fig.dpi)
absPoxX = relPosX * (self.fig.get_size_inches()[0] * self.fig.dpi)
absPosY = relPosY * (self.fig.get_size_inches()[1] * self.fig.dpi)
absSizeX = self.sensorPixelSize[0]
absSizeY = self.sensorPixelSize[1]
if event.dblclick and event.button == 1:
rect = self.axes.add_artist(
patches.Rectangle((relPosX, relPosY), relSizeX, relSizeY, edgecolor='black', facecolor='black',
fill=True))
dr = DraggableRectangle(rect)
dr.connect()
print(dr.rect.xy)
self.drs.append(dr)
self.fig.canvas.draw()
local = ["", "", (relPosX, relPosY), relSizeX, relSizeY, (absPoxX, absPosY), absSizeX, absSizeY]
self.devices.append(local)
def checkRectangleOnClick(self, event):
filename = "pos.txt"
with open(filename, "r") as file:
varia = file.read()
file.flush()
for i in range(len(self.devices)):
if str(varia) == str(self.devices[i][2]):
print("Clicked rectangle #%i" % i)
self.clickedIndex = i
else:
self.clickedIndex = None
def checkRectangleOnRelease(self, event):
filename = "pos.txt"
with open(filename, "r") as file:
varia = file.read()
file.flush()
for i in range(len(self.devices)):
if str(varia) == str(self.devices[i][2]):
print("Realeased rectangle #%i" % i)
self.clickedIndex = i
else:
self.clickedIndex = None
fig = MyFigure()
plt.show()
【问题讨论】:
-
也许用不同的处理程序填充优先级队列(或简单的列表),并导致分配给事件的处理程序分派给队列中的处理程序?
-
我将在我的问题中添加一个最小的工作示例。只是代码真的很重,所以我只想快速“是否可能”。所以我会挖掘或改变我解决问题的方法。但我认为这对每个人都有一个更好的例子。
-
所以您还没有研究过文档或其他 SO 问答?
-
当然有。我发现的唯一“使用 mpl 处理事件”是 stackoverflow.com/questions/21066739/matplotlib-button,但这不是我的情况。
-
我不会修改事件的顺序。相反,应该有一个以编程方式定义的明确顺序。我没有深入研究您的代码,因为显示问题似乎太长了。
标签: python matplotlib event-handling