Source code for ncvue.ncvscatter

#!/usr/bin/env python
"""
Scatter/Line panel of ncvue.

The panel allows plotting variables against time or two variables against
each other. A second variable can be plotted in the same graph using the
right-hand-side y-axis.

This module was written by Matthias Cuntz while at Institut National de
Recherche pour l'Agriculture, l'Alimentation et l'Environnement (INRAE), Nancy,
France.

Copyright (c) 2020-2021 Matthias Cuntz - mc (at) macu (dot) de

Released under the MIT License; see LICENSE file for details.

History:

* Written Nov-Dec 2020 by Matthias Cuntz (mc (at) macu (dot) de)
* Open new netcdf file, communicate via top widget, Jan 2021, Matthias Cuntz

.. moduleauthor:: Matthias Cuntz

The following classes are provided:

.. autosummary::
   ncvScatter
"""
from __future__ import absolute_import, division, print_function
import tkinter as tk
try:
    import tkinter.ttk as ttk
except Exception:
    import sys
    print('Using the themed widget set introduced in Tk 8.5.')
    print('Try to use mcview.py, which uses wxpython instead.')
    sys.exit()
from tkinter import filedialog
import numpy as np
import netCDF4 as nc
from .ncvutils   import clone_ncvmain, set_axis_label, vardim2var
from .ncvmethods import analyse_netcdf, get_slice_miss
from .ncvmethods import set_dim_x, set_dim_y, set_dim_y2
from .ncvwidgets import add_checkbutton, add_combobox, add_entry
from .ncvwidgets import add_spinbox, add_tooltip
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
plt.style.use('seaborn-darkgrid')


__all__ = ['ncvScatter']


[docs]class ncvScatter(ttk.Frame): """ Panel for scatter and line plots. Sets up the layout with the figure canvas, variable selectors, dimension spinboxes, and options in __init__. Contains various commands that manage what will be drawn or redrawn if something is selected, changed, checked, etc. Contains three drawing routines. `redraw_y` and `redraw_y2` redraw the y-axes without changing zoom level, etc. `redraw` is called if a new x-variable was selected or the `Redraw`-button was pressed. It resets all axes, resetting zoom, etc. """ # # Setup panel # def __init__(self, master, **kwargs): from functools import partial from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk from matplotlib.figure import Figure super().__init__(master, **kwargs) self.name = 'Scatter/Line' self.master = master self.top = master.top # copy for ease of use self.fi = self.top.fi self.miss = self.top.miss self.dunlim = self.top.dunlim self.time = self.top.time self.tname = self.top.tname self.tvar = self.top.tvar self.dtime = self.top.dtime self.latvar = self.top.latvar self.lonvar = self.top.lonvar self.latdim = self.top.latdim self.londim = self.top.londim self.maxdim = self.top.maxdim self.cols = self.top.cols # new window self.rowwin = ttk.Frame(self) self.rowwin.pack(side=tk.TOP, fill=tk.X) self.newfile = ttk.Button(self.rowwin, text="Open File", command=self.newnetcdf) self.newfile.pack(side=tk.LEFT) self.newfiletip = add_tooltip(self.newfile, 'Open a new netcdf file') self.newwin = ttk.Button( self.rowwin, text="New Window", command=partial(clone_ncvmain, self.master)) self.newwin.pack(side=tk.RIGHT) self.newwintip = add_tooltip( self.newwin, 'Open secondary ncvue window') # plotting canvas self.figure = Figure(facecolor="white", figsize=(1, 1)) self.axes = self.figure.add_subplot(111) self.axes2 = self.axes.twinx() self.canvas = FigureCanvasTkAgg(self.figure, master=self) self.canvas.draw() # pack self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) # grid instead of pack - does not work # self.canvas.get_tk_widget().grid(column=0, row=0, # sticky=(tk.N, tk.S, tk.E, tk.W)) # self.canvas.get_tk_widget().columnconfigure(0, weight=1) # self.canvas.get_tk_widget().rowconfigure(0, weight=1) # matplotlib toolbar self.toolbar = NavigationToolbar2Tk(self.canvas, self) self.toolbar.update() self.toolbar.pack(side=tk.TOP, fill=tk.X) # selections and options columns = [''] + self.cols # colors c = list(plt.rcParams['axes.prop_cycle']) col1 = c[0]['color'] # blue col2 = c[3]['color'] # red # color tooltip ctstr = "- color names: red, green, blue, yellow, ...\n" ctstr += "- single characters: b (blue), g (green), r (red), c (cyan)," ctstr += " m (magenta), y (yellow), k (black), w (white)\n" ctstr += "- hex RGB: #rrggbb such such as #ff9300 (orange)\n" ctstr += "- gray level: float between 0 and 1\n" ctstr += "- RGA (red, green, blue) or RGBA (red, green, blue, alpha)" ctstr += " tuples between 0 and 1, e.g. (1, 0.57, 0) for orange\n" ctstr += "- name from xkcd color survey, e.g. xkcd:sky blue" # marker tooltip mtstr = ". (point), ',' (pixel), o (circle),\n" mtstr += "v (triangle_down), ^ (triangle_up),\n" mtstr += "< (triangle_left), > (triangle_right),\n" mtstr += "1 (tri_down), 2 (tri_up), 3 (tri_left), 4 (tri_right)," mtstr += " 8 (octagon),\n" mtstr += "s (square), p (pentagon), P (plus (filled)),\n" mtstr += "* (star), h (hexagon1), H (hexagon2),\n" mtstr += "+ (plus), x (x), X (x (filled)),\n" mtstr += "D (diamond), d (thin_diamond),\n" mtstr += "| (vline), _ (hline), or None" # 1. row # x- and lhs y-axis selection self.rowxy = ttk.Frame(self) self.rowxy.pack(side=tk.TOP, fill=tk.X) # block x with dimensions self.blockx = ttk.Frame(self.rowxy) self.blockx.pack(side=tk.LEFT) self.rowx = ttk.Frame(self.blockx) self.rowx.pack(side=tk.TOP, fill=tk.X) self.xlbl, self.x, self.xtip = add_combobox( self.rowx, label="x", values=columns, command=self.selected_x, tooltip="Choose variable of x-axis.\nTake index if 'None' (fast).") self.x0 = '' self.line_x = [] self.inv_xlbl, self.inv_x, self.inv_xtip = add_checkbutton( self.rowx, label="invert x", value=False, command=self.checked_x, tooltip="Invert x-axis") self.rowxd = ttk.Frame(self.blockx) self.rowxd.pack(side=tk.TOP, fill=tk.X) self.xdlblval = [] self.xdlbl = [] self.xdval = [] self.xd = [] self.xdtip = [] for i in range(self.maxdim): xdlblval, xdlbl, xdval, xd, xdtip = add_spinbox( self.rowxd, label=str(i), values=(0,), wrap=True, command=self.spinned_x, state=tk.DISABLED, tooltip="None") self.xdlblval.append(xdlblval) self.xdlbl.append(xdlbl) self.xdval.append(xdval) self.xd.append(xd) self.xdtip.append(xdtip) # block y with dimensions spacex = ttk.Label(self.rowxy, text=" "*3) spacex.pack(side=tk.LEFT) self.blocky = ttk.Frame(self.rowxy) self.blocky.pack(side=tk.LEFT) self.rowy = ttk.Frame(self.blocky) self.rowy.pack(side=tk.TOP, fill=tk.X) self.ylbl = tk.StringVar() self.ylbl.set("y") ylab = ttk.Label(self.rowy, textvariable=self.ylbl) ylab.pack(side=tk.LEFT) self.bprev_y = ttk.Button(self.rowy, text="<", width=1, command=self.prev_y) self.bprev_y.pack(side=tk.LEFT) self.bprev_ytip = add_tooltip(self.bprev_y, 'Previous variable') self.bnext_y = ttk.Button(self.rowy, text=">", width=1, command=self.next_y) self.bnext_y.pack(side=tk.LEFT) self.bnext_ytip = add_tooltip(self.bnext_y, 'Next variable') self.y = ttk.Combobox(self.rowy, values=columns, width=25) # long = len(max(columns, key=len)) # self.y.configure(width=(max(20, long//2))) self.y.bind("<<ComboboxSelected>>", self.selected_y) self.y.pack(side=tk.LEFT) self.ytip = add_tooltip(self.y, 'Choose variable of y-axis') self.y0 = '' self.line_y = [] self.inv_ylbl, self.inv_y, self.inv_ytip = add_checkbutton( self.rowy, label="invert y", value=False, command=self.checked_y, tooltip="Inert y-axis") self.rowyd = ttk.Frame(self.blocky) self.rowyd.pack(side=tk.TOP, fill=tk.X) self.ydlblval = [] self.ydlbl = [] self.ydval = [] self.yd = [] self.ydtip = [] for i in range(self.maxdim): ydlblval, ydlbl, ydval, yd, ydtip = add_spinbox( self.rowyd, label=str(i), values=(0,), wrap=True, command=self.spinned_y, state=tk.DISABLED, tooltip="None") self.ydlblval.append(ydlblval) self.ydlbl.append(ydlbl) self.ydval.append(ydval) self.yd.append(yd) self.ydtip.append(ydtip) # redraw button self.bredraw = ttk.Button(self.rowxy, text="Redraw", command=self.redraw) self.bredraw.pack(side=tk.RIGHT) self.bredrawtip = add_tooltip(self.bredraw, 'Redraw, resetting zoom') # 2. row # options for lhs y-axis self.rowxyopt = ttk.Frame(self) self.rowxyopt.pack(side=tk.TOP, fill=tk.X) self.lslbl, self.ls, self.lstip = add_entry( self.rowxyopt, label="ls", text='-', width=4, command=self.entered_y, tooltip="Line style: -, --, -., :, or None") self.lwlbl, self.lw, self.lwtip = add_entry( self.rowxyopt, label="lw", text='1', width=3, command=self.entered_y, tooltip="Line width") self.lclbl, self.lc, self.lctip = add_entry( self.rowxyopt, label="c", text=col1, width=7, command=self.entered_y, tooltip="Line color:\n"+ctstr) self.markerlbl, self.marker, self.markertip = add_entry( self.rowxyopt, label="marker", text='None', width=4, command=self.entered_y, tooltip="Marker symbol:\n"+mtstr) self.mslbl, self.ms, self.mstip = add_entry( self.rowxyopt, label="ms", text='1', width=3, command=self.entered_y, tooltip="Marker size") self.mfclbl, self.mfc, self.mfctip = add_entry( self.rowxyopt, label="mfc", text=col1, width=7, command=self.entered_y, tooltip="Marker fill color:\n"+ctstr) self.meclbl, self.mec, self.mectip = add_entry( self.rowxyopt, label="mec", text=col1, width=7, command=self.entered_y, tooltip="Marker edge color:\n"+ctstr) self.mewlbl, self.mew, self.mewtip = add_entry( self.rowxyopt, label="mew", text='1', width=3, command=self.entered_y, tooltip="Marker edge width") # space self.rowspace = ttk.Frame(self) self.rowspace.pack(side=tk.TOP, fill=tk.X) rowspace = ttk.Label(self.rowspace, text=" ") rowspace.pack(side=tk.LEFT) # 3. row # rhs y-axis 2 selection self.rowyy2 = ttk.Frame(self) self.rowyy2.pack(side=tk.TOP, fill=tk.X) self.blocky2 = ttk.Frame(self.rowyy2) self.blocky2.pack(side=tk.LEFT) self.rowy2 = ttk.Frame(self.blocky2) self.rowy2.pack(side=tk.TOP, fill=tk.X) self.y2lbl, self.y2, self.y2tip = add_combobox( self.rowy2, label="y2", values=columns, command=self.selected_y2, tooltip="Choose variable for right-hand-side y-axis") self.y20 = '' self.line_y2 = [] self.inv_y2lbl, self.inv_y2, self.inv_y2tip = add_checkbutton( self.rowy2, label="invert y2", value=False, command=self.checked_y2, tooltip="Invert right-hand-side y-axis") spacey2 = ttk.Label(self.rowy2, text=" "*1) spacey2.pack(side=tk.LEFT) tstr = "Same limits for left-hand-side and right-hand-side y-axes" self.same_ylbl, self.same_y, self.same_ytip = add_checkbutton( self.rowy2, label="same y-axes", value=False, command=self.checked_yy2, tooltip=tstr) self.rowy2d = ttk.Frame(self.blocky2) self.rowy2d.pack(side=tk.TOP, fill=tk.X) self.y2dlblval = [] self.y2dlbl = [] self.y2dval = [] self.y2d = [] self.y2dtip = [] for i in range(self.maxdim): y2dlblval, y2dlbl, y2dval, y2d, y2dtip = add_spinbox( self.rowy2d, label=str(i), values=(0,), wrap=True, command=self.spinned_y2, state=tk.DISABLED, tooltip="None") self.y2dlblval.append(y2dlblval) self.y2dlbl.append(y2dlbl) self.y2dval.append(y2dval) self.y2d.append(y2d) self.y2dtip.append(y2dtip) # 4. row # options for rhs y-axis 2 self.rowy2opt = ttk.Frame(self) self.rowy2opt.pack(side=tk.TOP, fill=tk.X) self.ls2lbl, self.ls2, self.ls2tip = add_entry( self.rowy2opt, label="ls", text='-', width=4, command=self.entered_y2, tooltip="Line style: -, --, -., :, or None") self.lw2lbl, self.lw2, self.lw2tip = add_entry( self.rowy2opt, label="lw", text='1', width=3, command=self.entered_y2, tooltip="Line width") self.lc2lbl, self.lc2, self.lc2tip = add_entry( self.rowy2opt, label="c", text=col2, width=7, command=self.entered_y2, tooltip="Line color:\n"+ctstr) self.marker2lbl, self.marker2, self.marker2tip = add_entry( self.rowy2opt, label="marker", text='None', width=4, command=self.entered_y2, tooltip="Marker symbol:\n"+mtstr) self.ms2lbl, self.ms2, self.ms2tip = add_entry( self.rowy2opt, label="ms", text='1', width=3, command=self.entered_y2, tooltip="Marker size") self.mfc2lbl, self.mfc2, self.mfc2tip = add_entry( self.rowy2opt, label="mfc", text=col2, width=7, command=self.entered_y2, tooltip="Marker fill color:\n"+ctstr) self.mec2lbl, self.mec2, self.mec2tip = add_entry( self.rowy2opt, label="mec", text=col2, width=7, command=self.entered_y2, tooltip="Marker edge color:\n"+ctstr) self.mew2lbl, self.mew2, self.mew2tip = add_entry( self.rowy2opt, label="mew", text='1', width=3, command=self.entered_y2, tooltip="Marker edge width") # # Event bindings #
[docs] def checked_x(self): """ Command called if any checkbutton for x-axis was checked or unchecked. Redraws left-hand-side and right-hand-side y-axes. """ self.redraw_y() self.redraw_y2()
[docs] def checked_y(self): """ Command called if any checkbutton for left-hand-side y-axis was checked or unchecked. Redraws left-hand-side y-axis. """ self.redraw_y()
[docs] def checked_y2(self): """ Command called if any checkbutton for right-hand-side y-axis was checked or unchecked. Redraws right-hand-side y-axis. """ self.redraw_y2()
[docs] def checked_yy2(self): """ Command called if any checkbutton was checked or unchecked that concerns both, the left-hand-side and right-hand-side y-axes. Redraws left-hand-side and right-hand-side y-axes. """ self.redraw_y() self.redraw_y2()
[docs] def entered_y(self, event): """ Command called if option was entered for left-hand-side y-axis. Redraws left-hand-side y-axis. """ self.redraw_y()
[docs] def entered_y2(self, event): """ Command called if option was entered for right-hand-side y-axis. Redraws right-hand-side y-axis. """ self.redraw_y2()
[docs] def next_y(self): """ Command called if next button for the left-hand-side y-variable was pressed. Resets dimensions of left-hand-side y-variable. Redraws plot. """ y = self.y.get() cols = self.y["values"] idx = cols.index(y) idx += 1 if idx < len(cols): self.y.set(cols[idx]) set_dim_y(self) self.redraw()
[docs] def prev_y(self): """ Command called if previous button for the left-hand-side y-variable was pressed. Resets dimensions of left-hand-side y-variable. Redraws plot. """ y = self.y.get() cols = self.y["values"] idx = cols.index(y) idx -= 1 if idx > 0: self.y.set(cols[idx]) set_dim_y(self) self.redraw()
[docs] def newnetcdf(self): """ Open a new netcdf file and connect it to top. """ # get new netcdf file name ncfile = filedialog.askopenfilename( parent=self, title='Choose netcdf file', multiple=False) if ncfile: # close old netcdf file if self.top.fi: self.top.fi.close() # reset empty defaults of top self.top.dunlim = '' # name of unlimited dimension self.top.time = None # datetime variable self.top.tname = '' # datetime variable name self.top.tvar = '' # datetime variable name in netcdf self.top.dtime = None # decimal year self.top.latvar = '' # name of latitude variable self.top.lonvar = '' # name of longitude variable self.top.latdim = '' # name of latitude dimension self.top.londim = '' # name of longitude dimension self.top.maxdim = 0 # maximum num of dims of all variables self.top.cols = [] # variable list # open new netcdf file self.top.fi = nc.Dataset(ncfile, 'r') analyse_netcdf(self.top) # reset panel self.reinit() self.redraw()
[docs] def selected_x(self, event): """ Command called if x-variable was selected with combobox. Triggering `event` was bound to the combobox. Resets `x` dimensions. Redraws plot. """ set_dim_x(self) self.redraw()
[docs] def selected_y(self, event): """ Command called if left-hand-side y-variable was selected with combobox. Triggering `event` was bound to the combobox. Resets left-hand-side `y` dimensions. Redraws plot. """ set_dim_y(self) self.redraw()
[docs] def selected_y2(self, event): """ Command called if right-hand-side y-variable was selected with combobox. Triggering `event` was bound to the combobox. Resets right-hand-side `y` dimensions. Redraws plot. """ set_dim_y2(self) self.redraw()
[docs] def spinned_x(self, event=None): """ Command called if spinbox of x-dimensions was changed. Triggering `event` was bound to the spinbox. Redraws plot. """ self.redraw()
[docs] def spinned_y(self, event=None): """ Command called if spinbox of any dimension of left-hand-side y-variable was changed. Triggering `event` was bound to the spinbox. Redraws plot. """ self.redraw()
[docs] def spinned_y2(self, event=None): """ Command called if spinbox of any dimension of right-hand-side y-variable was changed. Triggering `event` was bound to the spinbox. Redraws plot. """ self.redraw()
# # Methods #
[docs] def minmax_ylim(self, ylim, ylim2): """ Get minimum of first elements of lists `ylim` and `ylim2` and maximum of second element of the two lists. Returns minimum, maximum. """ if (ylim[0] is not None) and (ylim2[0] is not None): ymin = min(ylim[0], ylim2[0]) else: if (ylim[0] is not None): ymin = ylim[0] else: ymin = ylim2[0] if (ylim[1] is not None) and (ylim2[1] is not None): ymax = max(ylim[1], ylim2[1]) else: if (ylim[1] is not None): ymax = ylim[1] else: ymax = ylim2[1] return ymin, ymax
[docs] def reinit(self): """ Reinitialise the panel from top. """ # reinit from top self.fi = self.top.fi self.miss = self.top.miss self.dunlim = self.top.dunlim self.time = self.top.time self.tname = self.top.tname self.tvar = self.top.tvar self.dtime = self.top.dtime self.latvar = self.top.latvar self.lonvar = self.top.lonvar self.latdim = self.top.latdim self.londim = self.top.londim self.maxdim = self.top.maxdim self.cols = self.top.cols # reset dimensions for ll in self.xdlbl: ll.destroy() for ll in self.xd: ll.destroy() self.xdlblval = [] self.xdlbl = [] self.xdval = [] self.xd = [] self.xdtip = [] for i in range(self.maxdim): xdlblval, xdlbl, xdval, xd, xdtip = add_spinbox( self.rowxd, label=str(i), values=(0,), wrap=True, command=self.spinned_x, state=tk.DISABLED, tooltip="None") self.xdlblval.append(xdlblval) self.xdlbl.append(xdlbl) self.xdval.append(xdval) self.xd.append(xd) self.xdtip.append(xdtip) for ll in self.ydlbl: ll.destroy() for ll in self.yd: ll.destroy() self.ydlblval = [] self.ydlbl = [] self.ydval = [] self.yd = [] self.ydtip = [] for i in range(self.maxdim): ydlblval, ydlbl, ydval, yd, ydtip = add_spinbox( self.rowyd, label=str(i), values=(0,), wrap=True, command=self.spinned_y, state=tk.DISABLED, tooltip="None") self.ydlblval.append(ydlblval) self.ydlbl.append(ydlbl) self.ydval.append(ydval) self.yd.append(yd) self.ydtip.append(ydtip) for ll in self.y2dlbl: ll.destroy() for ll in self.y2d: ll.destroy() self.y2dlblval = [] self.y2dlbl = [] self.y2dval = [] self.y2d = [] self.y2dtip = [] for i in range(self.maxdim): y2dlblval, y2dlbl, y2dval, y2d, y2dtip = add_spinbox( self.rowy2d, label=str(i), values=(0,), wrap=True, command=self.spinned_y2, state=tk.DISABLED, tooltip="None") self.y2dlblval.append(y2dlblval) self.y2dlbl.append(y2dlbl) self.y2dval.append(y2dval) self.y2d.append(y2d) self.y2dtip.append(y2dtip) # set variables columns = [''] + self.cols self.x['values'] = columns self.x.set(columns[0]) self.y['values'] = columns self.y.set(columns[0]) self.y2['values'] = columns self.y2.set(columns[0])
# # Plot #
[docs] def redraw_y(self): """ Redraw the left-hand-side y-axis. Reads left-hand-side `y` variable name, the current settings of its dimension spinboxes, as well as all other plotting options. Then redraws the left-hand-side y-axis. """ # get all states # rowxy y = self.y.get() if y != '': inv_y = self.inv_y.get() # rowxyopt ls = self.ls.get() lw = float(self.lw.get()) c = str(self.lc.get()) try: if isinstance(eval(c), tuple): c = eval(c) except: # several different exceptions possible pass m = self.marker.get() ms = float(self.ms.get()) mfc = self.mfc.get() try: if isinstance(eval(mfc), tuple): mfc = eval(mfc) except: pass mec = self.mec.get() try: if isinstance(eval(mec), tuple): mec = eval(mec) except: pass mew = float(self.mew.get()) # rowy2 y2 = self.y2.get() same_y = self.same_y.get() # y plotting styles pargs = {'linestyle': ls, 'linewidth': lw, 'marker': m, 'markersize': ms, 'markerfacecolor': mfc, 'markeredgecolor': mec, 'markeredgewidth': mew} vy = vardim2var(y) if vy == self.tname: ylab = 'Date' pargs['color'] = c else: ylab = set_axis_label(self.fi.variables[vy]) # ToDo with dimensions if len(self.line_y) == 1: # set color only if single line, # None and 'None' do not work for multiple lines pargs['color'] = c # set style for ll in self.line_y: plt.setp(ll, **pargs) if 'color' in pargs: ic = pargs['color'] if (ic != 'None'): self.axes.spines['left'].set_color(ic) self.axes.tick_params(axis='y', colors=ic) self.axes.yaxis.label.set_color(ic) self.axes.yaxis.set_label_text(ylab) # same y-axes ylim = self.axes.get_ylim() ylim2 = self.axes2.get_ylim() if same_y and (y2 != ''): ymin, ymax = self.minmax_ylim(ylim, ylim2) if (ymin is not None) and (ymax is not None): ylim = [ymin, ymax] ylim2 = [ymin, ymax] self.axes.set_ylim(ylim) self.axes2.set_ylim(ylim2) # invert y-axis if inv_y and (ylim[0] is not None): if ylim[0] < ylim[1]: ylim = ylim[::-1] self.axes.set_ylim(ylim) else: if ylim[1] < ylim[0]: ylim = ylim[::-1] self.axes.set_ylim(ylim) # invert x-axis inv_x = self.inv_x.get() xlim = self.axes.get_xlim() if inv_x and (xlim[0] is not None): if xlim[0] < xlim[1]: xlim = xlim[::-1] self.axes.set_xlim(xlim) else: if xlim[1] < xlim[0]: xlim = xlim[::-1] self.axes.set_xlim(xlim) # redraw self.canvas.draw() self.toolbar.update()
[docs] def redraw_y2(self): """ Redraw the right-hand-side y-axis. Reads right-hand-side `y` variable name, the current settings of its dimension spinboxes, as well as all other plotting options. Then redraws the right-hand-side y-axis. """ # get all states # rowy2 y2 = self.y2.get() if y2 != '': # rowxy y = self.y.get() # rowy2 inv_y2 = self.inv_y2.get() same_y = self.same_y.get() # rowy2opt ls = self.ls2.get() lw = float(self.lw2.get()) c = self.lc2.get() try: if isinstance(eval(c), tuple): c = eval(c) except: pass m = self.marker2.get() ms = float(self.ms2.get()) mfc = self.mfc2.get() try: if isinstance(eval(mfc), tuple): mfc = eval(mfc) except: pass mec = self.mec2.get() try: if isinstance(eval(mec), tuple): mec = eval(mec) except: pass mew = float(self.mew2.get()) # y plotting styles pargs = {'linestyle': ls, 'linewidth': lw, 'marker': m, 'markersize': ms, 'markerfacecolor': mfc, 'markeredgecolor': mec, 'markeredgewidth': mew} vy = vardim2var(y2) if vy == self.tname: ylab = 'Date' pargs['color'] = c else: ylab = set_axis_label(self.fi.variables[vy]) if len(self.line_y2) == 1: # set color only if single line, # None and 'None' do not work for multiple lines pargs['color'] = c # set style for ll in self.line_y2: plt.setp(ll, **pargs) if 'color' in pargs: ic = pargs['color'] if (ic != 'None'): self.axes2.spines['left'].set_color(ic) self.axes2.tick_params(axis='y', colors=ic) self.axes2.yaxis.label.set_color(ic) self.axes2.yaxis.set_label_text(ylab) # same y-axes ylim = self.axes.get_ylim() ylim2 = self.axes2.get_ylim() if same_y and (y2 != ''): ymin, ymax = self.minmax_ylim(ylim, ylim2) if (ymin is not None) and (ymax is not None): ylim = [ymin, ymax] ylim2 = [ymin, ymax] self.axes.set_ylim(ylim) self.axes2.set_ylim(ylim2) # invert y-axis ylim = ylim2 if inv_y2 and (ylim[0] is not None): if ylim[0] < ylim[1]: ylim = ylim[::-1] self.axes2.set_ylim(ylim) else: if ylim[1] < ylim[0]: ylim = ylim[::-1] self.axes2.set_ylim(ylim) # invert x-axis inv_x = self.inv_x.get() xlim = self.axes.get_xlim() if inv_x and (xlim[0] is not None): if xlim[0] < xlim[1]: xlim = xlim[::-1] self.axes.set_xlim(xlim) else: if xlim[1] < xlim[0]: xlim = xlim[::-1] self.axes.set_xlim(xlim) # redraw self.canvas.draw() self.toolbar.update()
[docs] def redraw(self, event=None): """ Redraw the left-hand-side and right-hand-side y-axis. Reads the two `y` variable names, the current settings of their dimension spinboxes, as well as all other plotting options. Then redraws the both y-axes. """ # get all states # rowxy x = self.x.get() y = self.y.get() # rowy2 y2 = self.y2.get() # Clear both axes first, otherwise x-axis only shows # if line2 is chosen. # if (x != self.x0) or (y != self.y0): self.axes.clear() # if (x != self.x0) or (y2 != self.y20): self.axes2.clear() ylim = [None, None] ylim2 = [None, None] # set x, y, axes labels vx = 'None' vy = 'None' vy2 = 'None' if (y != '') or (y2 != ''): # y axis if y != '': vy = vardim2var(y) if vy == self.tname: yy = self.time ylab = 'Date' else: yy = self.fi.variables[vy] ylab = set_axis_label(yy) yy = get_slice_miss(self, self.yd, yy) # y2 axis if y2 != '': vy2 = vardim2var(y2) if vy2 == self.tname: yy2 = self.time ylab2 = 'Date' else: yy2 = self.fi.variables[vy2] ylab2 = set_axis_label(yy2) yy2 = get_slice_miss(self, self.y2d, yy2) if (x != ''): # x axis vx = vardim2var(x) if vx == self.tname: xx = self.time xlab = 'Date' else: xx = self.fi.variables[vx] xlab = set_axis_label(xx) xx = get_slice_miss(self, self.xd, xx) else: # set x to index if not selected if (y != ''): nx = yy.shape[0] elif (y2 != ''): nx = yy2.shape[0] else: nx = 0 xx = np.arange(nx) xlab = '' # set y-axes to nan if not selected if (y == ''): yy = np.ones_like(xx)*np.nan ylab = '' if (y2 == ''): yy2 = np.ones_like(xx)*np.nan ylab2 = '' # plot # y-axis try: self.line_y = self.axes.plot(xx, yy) except Exception: estr = 'Scatter: x (' + vx + ') and y (' + vy + ')' estr += ' shapes do not match for plot:' print(estr, xx.shape, yy.shape) return self.axes.xaxis.set_label_text(xlab) self.axes.yaxis.set_label_text(ylab) # y2-axis try: self.line_y2 = self.axes2.plot(xx, yy2) except Exception: estr = 'Scatter: x (' + vx + ') and y2 (' + vy2 + ')' estr += ' shapes do not match for plot:' print(estr, xx.shape, yy2.shape) return self.axes2.xaxis.set_label_text(xlab) self.axes2.yaxis.set_label_text(ylab2) # styles, invert, same axes, etc. self.redraw_y() self.redraw_y2() # redraw self.x0 = x self.y0 = y self.y20 = y2 self.canvas.draw() self.toolbar.update()