【问题标题】:Select area in figure using Tkinter使用 Tkinter 选择图中的区域
【发布时间】:2016-10-14 21:39:59
【问题描述】:

我有一个脚本,它打开一个文件并通过嵌入在 Tkinter 中的 matplotlib 图绘制它。 我希望能够在该图中选择一个区域并检索角值。 我的脚本正在打开文件并绘制数据,还有一个工作示例(taken from this question),您可以在其中选择图表上的一个区域,但我正在努力将它们组合起来。

我读取和绘制数据的脚本:

import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from matplotlib.ticker import LogFormatter
from matplotlib import gridspec
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import tkFileDialog 
import tkMessageBox
import Tkinter as tk
from ncdump_file import ncdump
from netCDF4 import Dataset 

class CustomToolbar(NavigationToolbar2TkAgg):
    def __init__(self,canvas_,parent_):
        NavigationToolbar2TkAgg.__init__(self,canvas_,parent_)

class simpleapp_tk(tk.Tk):
    def __init__(self, parent):
        tk.Tk.__init__(self, parent)
        self.parent=parent
        self.initialize()

    def initialize(self):       
        button = tk.Button(self, text="Open file", command=self.onOpen)
        button.pack()
        button = tk.Button(self, text="Plot data", command=self.plot)
        button.pack()   
        button = tk.Button(self, text="Quit", command=self.quit)
        button.pack()   
        self.labelVariable = tk.StringVar()
        label = tk.Label(self, textvariable=self.labelVariable, anchor = "w", fg = 'white', bg = 'blue')
        label.pack()
        self.labelVariable.set("Data Browser")

        self.resizable(True, True) 

    def onOpen(self):
        ftypes = [('Netcdf files', '*.nc'), ('All files', '*.*')]
        dlg = tkFileDialog.Open(filetypes = ftypes)
        fl = dlg.show()
        if fl != '':               # Reading ncfile and dumping data
            nc_file = Dataset(fl, 'r')  
            global time_bnds
            global para_beta
            global perp_beta
            global altitude
            time_bnds = nc_file.variables['time_bnds'][:]
            altitude = nc_file.variables['altitude'][:]
            para_beta = np.swapaxes((nc_file.variables['para_beta'][:]), 0, 1)
            perp_beta = np.swapaxes((nc_file.variables['perp_beta'][:]), 0, 1)
            para_beta[np.isnan(para_beta)]=0
            perp_beta[np.isnan(perp_beta)]=0
            #ncdump(nc_file) 

    def plot (self):
        try:
            fig = plt.figure('Input para+perp ATB')
            gs  = gridspec.GridSpec(4, 6)
            lvls = np.logspace(-5,5,num=20) # sets range and frequency of tick labels 
            l_f = LogFormatter(10, labelOnlyBase=False)
            xaxis_majorLocator    = MultipleLocator(2.0)
            xaxis_majorFormatter  = FormatStrFormatter('%1.1f')
            xaxis_minorLocator    = MultipleLocator(1.0)            
            ax1 = fig.add_subplot(gs[0:4, 0:5])
            ax1.plot([10, 12, 14, 16], [0, 1, 2, 3]) # added so that it can run without netcdf file
            #im  = ax1.imshow(para_beta, extent =(time_bnds.min(), time_bnds.max(), altitude.min(), altitude.max()), aspect = 'auto', cmap = 'jet', interpolation = 'none', origin = 'lowest', norm=LogNorm(vmin=1e-5, vmax = None))
            ax1.set_ylim([0, 10])
            ax1.set_ylabel('Height (km)')
            ax1.xaxis.set_major_locator(xaxis_majorLocator)
            ax1.xaxis.set_major_formatter(plt.NullFormatter())
            ax1.xaxis.set_minor_locator(xaxis_minorLocator)
            plt.title('Parallel')

            #cax = fig.add_subplot(gs[1:3, 5:6])
            #fig.colorbar(im, label = 'Atten Beta [$km^{-1}\,sr^{-1}$]', cax = cax, format=l_f, ticks = lvls)
            #im.set_clim(1e-5, 1e-1) # changes the bounds of the colour bar, irrespective of ticks
            plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.5)

            canvas = FigureCanvasTkAgg(fig, master=self)
            canvas.show()
            canvas.get_tk_widget().pack()

            toolbar = NavigationToolbar2TkAgg(canvas, self)
            toolbar.update()
            canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        except SyntaxError:   
            tkMessageBox.showinfo("No file selected", "Please select a file")

def quit():
    root.destroy()

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('Data Browser')
    app.mainloop()

工作示例:

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

我应该在脚本的哪个部分介绍选择功能以及如何介绍?我曾尝试在我的函数图中定义 on_press 函数等,但我得到了各种 AttributeErrors。我还尝试在课堂之外全局定义它们,但这也没有用。

【问题讨论】:

    标签: python matplotlib tkinter tkinter-canvas


    【解决方案1】:

    为了组合脚本,我将事件管理器(即aid = self.fig.canvas.mpl_connect('button_press_event', self.on_press))添加到我的plot 函数中,然后在主类中定义连接到这些事件管理器的函数(即on_press),就像其他正常的一样职能。 请看下面的完整代码:

    class simpleapp_tk(tk.Tk):
        def __init__(self, parent):
            tk.Tk.__init__(self, parent)
            self.parent=parent
            self.initialize()
    
        def initialize(self):       
            button = tk.Button(self, text="Open file", command=self.onOpen)
            button.pack()
            button = tk.Button(self, text="Plot data", command=self.plot)
            button.pack()   
            button = tk.Button(self, text="Quit", command=self.quit)
            button.pack()   
    
            self.labelVariable = tk.StringVar()
            label = tk.Label(self, textvariable=self.labelVariable, anchor = "w", fg = 'white', bg = 'blue')
            label.pack()
            self.labelVariable.set("Data Browser")
            self.resizable(True, True) 
    
        def onOpen(self):
            ftypes = [('Netcdf files', '*.nc'), ('All files', '*.*')]
            dlg = tkFileDialog.Open(filetypes = ftypes)
            fl = dlg.show()
            if fl != '':               # Reading ncfile and dumping data
                nc_file = Dataset(fl, 'r')  
                global time_bnds
                global para_beta
                global perp_beta
                global altitude
                time_bnds = nc_file.variables['time_bnds'][:]
                altitude = nc_file.variables['altitude'][:]
                para_beta = np.swapaxes((nc_file.variables['para_beta'][:]), 0, 1)
                perp_beta = np.swapaxes((nc_file.variables['perp_beta'][:]), 0, 1)
                para_beta[np.isnan(para_beta)]=0
                perp_beta[np.isnan(perp_beta)]=0
    
        def plot (self):
            try:
                self.fig = plt.figure('Input para+perp ATB')
                gs  = gridspec.GridSpec(4, 6)
                lvls = np.logspace(-5,5,num=20) # sets range and frequency of tick labels 
                l_f = LogFormatter(10, labelOnlyBase=False)         
                self.ax1 = self.fig.add_subplot(gs[0:4, 0:5])
                im  = self.ax1.imshow(para_beta, extent =(time_bnds.min(), time_bnds.max(), altitude.min(), altitude.max()), aspect = 'auto', cmap = 'jet', interpolation = 'none', origin = 'lowest', norm=LogNorm(vmin=1e-5, vmax = None))
                self.rect = Rectangle((0, 0), 0, 0)
                self.ax1.add_patch(self.rect)
    
                cax = self.fig.add_subplot(gs[1:3, 5:6])
                self.fig.colorbar(im, label = 'Atten Beta [$km^{-1}\,sr^{-1}$]', cax = cax, format=l_f, ticks = lvls)
                im.set_clim(1e-5, 1e-1) # changes the bounds of the colour bar, irrespective of ticks
                plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.5)
    
                canvas = FigureCanvasTkAgg(self.fig, master=self)
                canvas.show()
                canvas.get_tk_widget().pack()
    
                toolbar = NavigationToolbar2TkAgg(canvas, self)
                toolbar.update()
                canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
                aid = self.fig.canvas.mpl_connect('button_press_event', self.on_press)
                bid = self.fig.canvas.mpl_connect('button_release_event', self.on_release)
                cid = self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)
            except NameError:   
                tkMessageBox.showinfo("No file selected", "Please select a file")
    
        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
                self.rect.set_width(0)
                self.rect.set_height(0)
                self.rect.set_xy((self.x0, self.y0))
                self.fig.canvas.draw()
                self.rect.set_edgecolor('red')
                self.rect.set_linestyle('solid')
                self.rect.set_facecolor('none')
                self.rect.set_linewidth('5')
    
        def on_motion(self, event):
            try:
                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.fig.canvas.draw()
                        #print 'rect:', self.x0, self.y0, self.x1, self.y1, (self.x1-self.x0), (self.y1-self.y0)
            except AttributeError:
                pass 
    
        def on_release(self, event):
            self.is_pressed = False
            self.x1, self.y1 = event.xdata, event.ydata
            self.rect.set_xy((self.x0, self.y0))
            print 'rect:', self.x0, self.y0, self.x1, self.y1
    
            self.fig.canvas.draw()
    
    def quit():
        root.destroy()
    
    if __name__ == "__main__":
        app = simpleapp_tk(None)
        app.title('Data Browser')
        app.mainloop()
    

    【讨论】:

    • 我们是否应该逐行将您的代码与原始代码进行比较?如果您添加对您所做的事情的解释,这个答案会好很多。
    • 我认为答案和解决方案是直截了当的。谢谢!
    猜你喜欢
    • 2014-09-05
    • 2014-09-04
    • 1970-01-01
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    • 2016-11-12
    相关资源
    最近更新 更多