El área de selección no se muestra en Tkinter

Estoy desarrollando una aplicación que le permite al usuario ampliar una parte del gráfico en función de su selección. Puedo obtener las coordenadas iniciales x, y (x0, y0) y también las coordenadas finales x, y (x1, y1) . Pero no tengo ni idea de por qué el área de selección no aparece.

 from Tkinter import * matplotlib.use('TkAgg') from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg root = Tk() graph = Figure(figsize=(5,4), dpi=100) ax = graph.add_subplot(111) plot = ax.plot([1,2,3,4],[5,6,2,8]) canvas = FigureCanvasTkAgg(graph, master=root) canvas.show() canvas.get_tk_widget().grid(column=2, row=1, rowspan=2, sticky=(N, S, E, W)) class Zoom(object): def __init__(self): self.graph = Figure(figsize=(5,4), dpi=100) self.ax = graph.add_subplot(111) self.rect = ax.patch self.rect.set_facecolor('green') self.ax.plot([1,2,3,4],[5,6,2,8]) self.is_pressed = False self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.aid = graph.canvas.mpl_connect('button_press_event', self.on_press) self.bid = graph.canvas.mpl_connect('button_release_event', self.on_release) self.cid = graph.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): print 'press' self.is_pressed = True self.x0 = event.xdata self.y0 = event.ydata print(self.x1, self.x0) print(self.y1, self.y0) def on_motion(self, event): if self.is_pressed is True: print 'panning' self.x1 = event.xdata self.y1 = event.ydata print(self.x1, self.x0) print(self.y1, self.y0) self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.rect.set_linestyle('dashed') self.ax.figure.canvas.draw() def on_release(self, event): print 'release' self.is_pressed = False self.x1 = event.xdata self.y1 = event.ydata print(self.x1, self.x0) print(self.y1, self.y0) self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.rect.set_linestyle('solid') self.ax.figure.canvas.draw() my_object = Zoom() root.mainloop() 

He tomado la ayuda de esta pregunta Matplotlib: dibujar un área de selección con la forma de un rectángulo con el mouse La salida que obtengo es

 press (0.0, 1.4007056451612905) (0.0, 6.9296116504854366) panning (1.4007056451612905, 1.4007056451612905) (6.8932038834951452, 6.9296116504854366) panning (None, 1.4007056451612905) (None, 6.9296116504854366) panning (None, 1.4007056451612905) (None, 6.9296116504854366) release (None, 1.4007056451612905) (None, 6.9296116504854366) 

Esto funciona para mi:

 from Tkinter import * from matplotlib.figure import * import matplotlib matplotlib.use('TkAgg') from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg root = Tk() graph = Figure(figsize=(5,4), dpi=100) ax = graph.add_subplot(111) plot = ax.plot([1,2,3,4],[5,6,2,8]) canvas = FigureCanvasTkAgg(graph, master=root) canvas.show() canvas.get_tk_widget().grid(column=2, row=1, rowspan=2, sticky=(N, S, E, W)) class Zoom(object): def __init__(self): self.graph = Figure(figsize=(5,4), dpi=100) self.ax = graph.add_subplot(111) # should be Rectangle((0,0),0,0) self.rect = Rectangle((10,10),100,100) self.ax.add_patch(self.rect) self.ax.plot([1,2,3,4],[5,6,2,8]) self.is_pressed = False self.x0 = 0.0 self.y0 = 0.0 self.x1 = 0.0 self.y1 = 0.0 self.aid = graph.canvas.mpl_connect('button_press_event', self.on_press) self.bid = graph.canvas.mpl_connect('button_release_event', self.on_release) self.cid = graph.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): self.is_pressed = True if event.xdata is not None and event.ydata is not None: self.x0, self.y0 = event.xdata, event.ydata print 'press:', self.x0, self.y0 # only remove old rectangle self.rect.set_width(0) self.rect.set_height(0) self.rect.set_xy((self.x0, self.y0)) self.ax.figure.canvas.draw() # color and linestyle for future motion self.rect.set_facecolor('red') self.rect.set_linestyle('dashed') def on_motion(self, event): if self.is_pressed: if event.xdata is not None and event.ydata is not None: self.x1, self.y1 = event.xdata, event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.ax.figure.canvas.draw() print 'rect:', self.x0, self.y0, self.x1, self.y1, (self.x1-self.x0), (self.y1-self.y0) def on_release(self, event): self.is_pressed = False print 'release:', event.xdata, event.ydata # change only color and linestyle #self.rect.set_width(self.x1 - self.x0) #self.rect.set_height(self.y1 - self.y0) #self.rect.set_xy((self.x0, self.y0)) self.rect.set_facecolor('blue') self.rect.set_linestyle('solid') self.ax.figure.canvas.draw() my_object = Zoom() root.mainloop() 

Necesita actualizar estos a medida que el evento cambie:

 def on_motion(self, event): if self.is_pressed is True: self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(1) self.rect.set_height(1) self.rect.set_xy((2.5, 5)) self.rect.set_linestyle('dashed') self.ax.figure.canvas.draw() def on_release(self, event): print 'release' self.is_pressed = False self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(1) self.rect.set_height(1) self.rect.set_xy((2.5, 5)) self.rect.set_linestyle('solid') self.ax.figure.canvas.draw() 

Tal como están, dibujarán continuamente un rectángulo de tamaño fijo en las coordenadas (2.5, 5) con un ancho y alto de 1.

Similar a la pregunta que estabas viendo en algo como esto funciona.

  def on_press(self, event): print('press') self.is_pressed = True self.x0 = event.xdata self.y0 = event.ydata def on_motion(self, event): self.x1, self.y1 = event.xdata, event.ydata if (self.is_pressed is True and self.x1 is not None and self.y1 is not None): self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.rect.set_linestyle('dashed') self.ax.figure.canvas.draw() def on_release(self, event): print('release') self.is_pressed = False self.x1, self.y1 = event.xdata, event.ydata try: self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) except TypeError: if (self.x1 is None or self.y1 is None): return else: raise self.rect.set_linestyle('solid') self.ax.figure.canvas.draw() 

Observe que las dimensiones del rectángulo se toman del evento. Agregué la protección para evitar contratiempos en los que el evento no interpreta correctamente la coordenada.


@furas hace buenos puntos en los comentarios, es una buena idea inicializar sus coordenadas a flotadores.

  self.x0 = 0.0 self.y0 = 0.0 self.x1 = 0.0 self.y1 = 0.0