python - matplotlib - 如果有多个事件处理程序,你可以设置优先级吗?
问题描述
我正在使用 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()
解决方案
解决了这个问题。为了给事件处理程序设置一个“优先级”,我只从事件中调用了一个函数。从那时起,当处理程序完成其目的时,它会调用另一个不再连接的处理程序,然后第二个处理程序调用第三个处理程序,依此类推。
我在 DraggableRectangle 类中添加了我的个人画布类“MyFigure”的输入。因此,我现在可以从 DraggableRectangle 调用 MyFigure 的函数。所以,在 On_Click 中,在 'saveInFile' 函数之后,我只是直接调用函数 checkRectangleOnClick 函数,然后调用 checkRectangleOnRelease。我从“button_click_event”断开了这些功能。没有更多的问题,一切都在它应该发生的时候发生。
推荐阅读
- docker - Docker-compose postgres 卷不持久
- javascript - 根据变化反应 Mongoose 项目价格
- python - 如何在 Python 中将多个数组保存为 DataFrame 中的列
- python - 试图让 DeepSpeech 工作。即使我 pip 安装了 SoX 也找不到?
- java - 使用 ArrayList 的 nextPermutation
- google-apps-script - Google Apps 脚本 - 在子文件夹中运行函数
- python - Windows 11 上的 Python IDLE
- c - 为什么可以在结构定义中使用相同的名称?
- python - TypeError: unhashable type: 'list' 在尝试声明一组列表时
- reinforcement-learning - 奖励如何在 Deep Q 网络中发挥作用