Python 按键事件不适用于tkinter中的FigureCanvas

Python 按键事件不适用于tkinter中的FigureCanvas,python,tkinter,events,canvas,Python,Tkinter,Events,Canvas,我想在画布上添加一个按键事件 遗憾的是,我的类ZoomOnWheel中的重置函数只有在连接按钮按下事件时才会被调用。我不知道为什么它不适用于按键事件。。。。 该函数甚至没有被调用 我创建了一个最低限度的示例供您尝试和复制 from tkinter import* import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg imp

我想在画布上添加一个按键事件

遗憾的是,我的类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()
从tkinter导入*
将numpy作为np导入
将matplotlib.pyplot作为plt导入
从matplotlib.backends.backend_tkagg导入图CAVASTKAGG
随机输入
输入数学
类绘图窗口:
定义初始化(自身,帧):
self.master=frame
self.master.title=(“GUI分析”)
self.mainframe=Frame(self.master,padx=“3”,pady=“3”)
self.mainframe.grid(列=0,行=0,粘性=(N,W,E,S))#位置
self.master.columnconfigure(0,权重=1)
self.master.rowconfigure(0,权重=1)
自身图原始=plt图(figsize=(11,11))
self.ax=self.fig\u原始添加子批次(1,1,1)
self.raw\u canvas=figulecavastkagg(self.fig\u raw,self.mainframe)
self.raw\u canvas.get\u tk\u widget().grid()
无球数=25
x=[random.triangal()表示范围内的i(没有球)]
y=[随机高斯(0.5,0.25)表示范围内的i(无球)]
颜色=[random.randint(1,4)表示范围内的i(没有球)]
面积=[math.pi*random.randint(5,15)**2表示范围内的i(无球)]
自最大散射(x,y,s=面积,c=颜色,alpha=0.85)
self.raw_canvas.draw()
self.scroll=ZoomOnWheel(图=self.fig\u原始,比例系数=1.1)
导入日志记录
进口武器
类(对象):
“”“为matplotlib图形提供交互的类的基类。”“”
定义初始化(self,图):
初始值设定项
:param Figure Figure:要将行为附加到的matplotlib图形。
"""
self.\u fig\u ref=weakref.ref(图)
self.\u cids=[]
定义(自我):
self.disconnect()
定义添加连接(自身、事件名称、回调):
“”被调用以将连接添加到图的事件
:param str event_name:要连接到的matplotlib事件名称。
:param callback:要注册到此事件的回调。
"""
cid=self.figure.canvas.mpl\u connect(事件名称,回调)
自我确认附加(cid)
def断开(自):
“”“断开与图形的交互。”“”
如果self.\u fig\u ref不是无:
figure=self.\u fig\u ref()
如果数字不是无:
对于自身中的cid。\u cid:
图.canvas.mpl\u断开连接(cid)
self.\u fig\u ref=无
@财产
def图形(自身):
“”“此交互连接到的图形或
如果未连接,则无
如果self.\u fig\u ref不是None其他None,则返回self.\u fig\u ref()
定义轴到轴更新(自、事件):
“”“返回两组要根据事件更新的轴。
负责多个轴和共享轴。
PARAM-MousEvEnter事件:考虑MaMattLIB事件
:return:要更新xlimit和ylimit的轴
:rtype:2元组集合(xax,yax)
"""
x_轴,y_轴=集(),集()
#遍历所有轴以启用多轴子批次的缩放
对于self.figure.axes中的ax:
如果ax.包含(事件)[0]:
#对于双x轴,确保缩放应用一次
共享_x_轴=设置(ax.get_共享_x_轴().get_同级(ax))
如果x_轴为万向节(共享x_轴):
x_轴。添加(ax)
#对于双y轴,确保缩放应用一次
共享_y_轴=设置(ax.get_共享_y_轴().get_同级(ax))
如果y_轴为万向节(共享y_轴):
y_轴。添加(ax)
返回x_轴,y_轴
def_牵引(自):
“”“重新绘制图形的方便方法”“”
self.figure.canvas.draw()
类ZoomOnWheel(MPLS交互):
“”“类提供对matplotlib图形的滚轮缩放交互。”。
支持子批次、双轴和原木秤。
"""
定义初始值(自,数字=无,比例系数=1.1):
初始值设定项
:param Figure Figure:要将行为附加到的matplotlib图形。
:param float scale_factor:应用于控制盘事件的比例因子。
"""
超级(ZoomOnWheel,self)。\uuuuu初始化\uuuuuuuuuu(图)
self.\u添加连接(“滚动事件”,self.\u在鼠标滚轮上)
self.\u添加连接('按键事件',self.reset)
self.scale\u factor=比例因子
@静力学方法
定义缩放范围(开始、结束、中心、比例因子、比例):
“”“计算围绕中心缩放的1D范围。
:param float begin:范围的开始边界。
:param float end:范围的结束边界。
:param float center:缩放的中心(即不变点)
:param float scale_factor:要应用的比例因子。
:param str scale:轴的比例
:return:缩放范围(最小、最大)
"""
如果开始<结束:
最小值,最大值=开始,结束
其他:
最小值,最大值=结束,开始
如果比例=‘线性’:
旧的最小值,旧的最大值=最小值,最大值_
elif比例==‘日志’:
old_min=np.log10(如果min>0,则为min,否则为np.nextafter(0,1))
中心=np.log10(
如果中心>0,则为中心。否则,np.nextafter(0,1))
如果最大值>0,则旧最大值=np.log10(最大值)。其他0。
其他:
日志记录。警告(
“缩放控制盘未对比例“%s”%scale实施缩放)
返回开始,结束
偏移=(中心-旧的最小值)/(旧的最大值-旧的最小值)
范围=(旧的最大值-旧的最小值)/比例系数
新的最小值=中心-偏移*范围_
新的最大值=中心+(1.-偏移)*范围_
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()