【问题标题】:Selection area is not showing up in TkinterTkinter 中未显示选择区域
【发布时间】:2014-09-05 01:56:52
【问题描述】:

我正在开发一个应用程序,它允许用户根据他们的选择缩放图表的一部分。我能够得到初始的 x, y 坐标(x0, y0) 以及最终的 x, y 坐标(x1, y1)。但完全不知道为什么选择区域没有出现。

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()

我从这个问题中得到了帮助Matplotlib: draw a selection area in the shape of a rectangle with the mouse 我得到的输出是

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)

【问题讨论】:

  • 平移时是否离开画布?
  • @MrAlias 不,我只是移动了一下,鼠标光标从原始位置的像素运动会生成None

标签: python matplotlib tkinter


【解决方案1】:

它对我有用:

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()

【讨论】:

  • 真正令人印象深刻的研究!!!你能告诉我为什么没有Rectangle它就不能工作吗?为什么你有#commented out on_release 属性,例如set_width, set_height, set_xy?为您的辛勤工作 +1。
  • 也许它需要新对象Rectangle 来显示它。也许以前它不是矩形,而是背景或其他东西。我在on_release 中不需要set_width, set_height, set_xy,因为on_motion 可以完成所有工作——它总是获得最新的坐标。除了当鼠标在图形之外(在边距上)时我可以释放按钮,然后我不需要这个坐标 - 特别是 xdata,ydata 将是 None。
【解决方案2】:

您需要随着事件的变化更新这些:

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()

他们将在坐标 (2.5, 5) 处不断绘制一个固定大小的矩形,宽度和高度为 1。

与您正在查看的类似问题的问题类似。

    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()

请注意,矩形尺寸取自事件。我添加了警卫以防止事件无法正确解释坐标的事故。


@furas 在 cmets 中提出了一个很好的观点,将坐标初始化为浮点数是个好主意。

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

【讨论】:

  • 感谢您的回复。但我按照你说的方式做了,它产生了一个错误self.rect.set_width(self.x1 - self.x0) TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'
  • None 是哪个值?您是否真的在on_press 函数中设置了self.y0self.x0?您是否在画布内部单击?
  • @Mahasish Shome 你在__init__ 中设置了self.x1=None(以及其他) - 使用self.x1=0
  • @furas:我试过0 现在我得到了类似release Exception in Tkinter callback Traceback (most recent call last): File "C:/Users/VAIO/test.pyw", line 52, in on_release self.rect.set_width(self.x1 - self.x0) TypeError: unsupported operand type(s) for -: 'NoneType' and 'float'的东西
  • 但是event.x, event.yon_motion 中给出正确的值。
猜你喜欢
  • 2016-10-14
  • 2017-12-27
  • 1970-01-01
  • 2014-09-04
  • 2022-09-29
  • 1970-01-01
  • 1970-01-01
  • 2011-10-21
  • 2017-09-22
相关资源
最近更新 更多