使用Tkinter选择图中的区域

时间:2016-06-14 08:50:46

标签: python matplotlib tkinter tkinter-canvas

我有一个打开文件的脚本,并通过嵌入在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。我也尝试在课堂外全局定义它们,但这也没有用。

1 个答案:

答案 0 :(得分:-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()