我想在画布上添加一个按键事件。
遗憾的是,我的类 ZoomOnWheel 中的重置函数只有在我连接按钮按下事件时才会被调用。我不知道为什么它不适用于按键事件.... 该函数甚至没有被调用。
我创建了一个最小示例供您试用和复制。
from tkinter import*
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import random
import math
class PlotWindow:
def __init__(self, frame):
self.master=frame
self.master.title=("GUI Analysis")
self.mainframe=Frame(self.master,padx="3",pady='3')
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) #Position der widgets
self.master.columnconfigure(0, weight=1)
self.master.rowconfigure(0, weight=1)
self.fig_raw=plt.figure(figsize=(11,11))
self.ax=self.fig_raw.add_subplot(1,1,1)
self.raw_canvas = FigureCanvasTkAgg(self.fig_raw,self.mainframe)
self.raw_canvas.get_tk_widget().grid()
no_of_balls=25
x = [random.triangular() for i in range(no_of_balls)]
y = [random.gauss(0.5, 0.25) for i in range(no_of_balls)]
colors = [random.randint(1, 4) for i in range(no_of_balls)]
areas = [math.pi * random.randint(5, 15)**2 for i in range(no_of_balls)]
self.ax.scatter(x, y, s=areas, c=colors, alpha=0.85)
self.raw_canvas.draw()
self.scroll=ZoomOnWheel(figure=self.fig_raw, scale_factor=1.1)
import logging
import weakref
class MplInteraction(object):
"""Base class for class providing interaction to a matplotlib Figure."""
def __init__(self, figure):
"""Initializer
:param Figure figure: The matplotlib figure to attach the behavior to.
"""
self._fig_ref = weakref.ref(figure)
self._cids = []
def __del__(self):
self.disconnect()
def _add_connection(self, event_name, callback):
"""Called to add a connection to an event of the figure
:param str event_name: The matplotlib event name to connect to.
:param callback: The callback to register to this event.
"""
cid = self.figure.canvas.mpl_connect(event_name, callback)
self._cids.append(cid)
def disconnect(self):
"""Disconnect interaction from Figure."""
if self._fig_ref is not None:
figure = self._fig_ref()
if figure is not None:
for cid in self._cids:
figure.canvas.mpl_disconnect(cid)
self._fig_ref = None
@property
def figure(self):
"""The Figure this interaction is connected to or
None if not connected."""
return self._fig_ref() if self._fig_ref is not None else None
def _axes_to_update(self, event):
"""Returns two sets of Axes to update according to event.
Takes care of multiple axes and shared axes.
:param MouseEvent event: Matplotlib event to consider
:return: Axes for which to update xlimits and ylimits
:rtype: 2-tuple of set (xaxes, yaxes)
"""
x_axes, y_axes = set(), set()
# Go through all axes to enable zoom for multiple axes subplots
for ax in self.figure.axes:
if ax.contains(event)[0]:
# For twin x axes, makes sure the zoom is applied once
shared_x_axes = set(ax.get_shared_x_axes().get_siblings(ax))
if x_axes.isdisjoint(shared_x_axes):
x_axes.add(ax)
# For twin y axes, makes sure the zoom is applied once
shared_y_axes = set(ax.get_shared_y_axes().get_siblings(ax))
if y_axes.isdisjoint(shared_y_axes):
y_axes.add(ax)
return x_axes, y_axes
def _draw(self):
"""Conveninent method to redraw the figure"""
self.figure.canvas.draw()
class ZoomOnWheel(MplInteraction):
"""Class providing zoom on wheel interaction to a matplotlib Figure.
Supports subplots, twin Axes and log scales.
"""
def __init__(self, figure=None, scale_factor=1.1):
"""Initializer
:param Figure figure: The matplotlib figure to attach the behavior to.
:param float scale_factor: The scale factor to apply on wheel event.
"""
super(ZoomOnWheel, self).__init__(figure)
self._add_connection('scroll_event', self._on_mouse_wheel)
self._add_connection('key_press_event', self.reset)
self.scale_factor = scale_factor
@staticmethod
def _zoom_range(begin, end, center, scale_factor, scale):
"""Compute a 1D range zoomed around center.
:param float begin: The begin bound of the range.
:param float end: The end bound of the range.
:param float center: The center of the zoom (i.e., invariant point)
:param float scale_factor: The scale factor to apply.
:param str scale: The scale of the axis
:return: The zoomed range (min, max)
"""
if begin < end:
min_, max_ = begin, end
else:
min_, max_ = end, begin
if scale == 'linear':
old_min, old_max = min_, max_
elif scale == 'log':
old_min = np.log10(min_ if min_ > 0. else np.nextafter(0, 1))
center = np.log10(
center if center > 0. else np.nextafter(0, 1))
old_max = np.log10(max_) if max_ > 0. else 0.
else:
logging.warning(
'Zoom on wheel not implemented for scale "%s"' % scale)
return begin, end
offset = (center - old_min) / (old_max - old_min)
range_ = (old_max - old_min) / scale_factor
new_min = center - offset * range_
new_max = center + (1. - offset) * range_
if scale == 'log':
try:
new_min, new_max = 10. ** float(new_min), 10. ** float(new_max)
except OverflowError: # Limit case
new_min, new_max = min_, max_
if new_min <= 0. or new_max <= 0.: # Limit case
new_min, new_max = min_, max_
if begin < end:
return new_min, new_max
else:
return new_max, new_min
def _on_mouse_wheel(self, event):
if event.step > 0:
scale_factor = self.scale_factor
else:
scale_factor = 1. / self.scale_factor
# Go through all axes to enable zoom for multiple axes subplots
x_axes, y_axes = self._axes_to_update(event)
for ax in x_axes:
transform = ax.transData.inverted()
xdata, ydata = transform.transform_point((event.x, event.y))
xlim = ax.get_xlim()
xlim = self._zoom_range(xlim[0], xlim[1],
xdata, scale_factor,
ax.get_xscale())
ax.set_xlim(xlim)
for ax in y_axes:
ylim = ax.get_ylim()
ylim = self._zoom_range(ylim[0], ylim[1],
ydata, scale_factor,
ax.get_yscale())
ax.set_ylim(ylim)
if x_axes or y_axes:
self._draw()
def reset(self, event):
print(event.key)
x_axes, y_axes = self._axes_to_update(event)
for ax in y_axes:
ax.set_ylim(0,1)
print("")
for ax in x_axes:
ax.set_xlim(0,1)
self._draw()
root=Tk()
root.title('CARS Analyser')
root.geometry("1920x1080")
Var=PlotWindow(root)
root.mainloop()
答案 0 :(得分:0)
我没有与 matplotlib FigureCanvasTkAgg
合作过,所以这可能有点偏离,因为我只是在看画布,假设它们是相关的。
通常,即使点击画布或画布小部件也不会获得焦点,这意味着如果您将它们绑定到 <Key>
或 <KeyPress>
,它们将不会做出反应。
那么,你想做什么?在画布上选择一个项目,然后让它响应 <KeyPress>
?我包括了一个如何做到这一点的例子:
from tkinter import *
root = Tk()
root.geometry('300x200')
canvas = Canvas(root, bg='white')
canvas.pack(padx=10, pady=10, expand=True, fill='both')
widgets = []
widgets.append(canvas.create_rectangle(120, 70, 160, 110, fill='black'))
widgets.append(canvas.create_rectangle(20, 20, 60, 60, fill='black'))
selected = None
def canvas_button(event=None):
global selected
try:
current = event.widget.find_withtag("current")[0]
if current == selected:
canvas.itemconfig(current, fill='black')
selected = None
else:
canvas.itemconfig(selected, fill='black')
canvas.itemconfig(current, fill='red')
selected = current
except IndexError:
# Clicked outside all canvas widgets
pass
def canvas_key(event=None):
if selected != None:
canvas.itemconfig(selected, fill='blue')
root.bind('<Key>', canvas_key)
canvas.bind('<Button-1>', canvas_button)
root.mainloop()
这对你有用吗?