예제 #1
0
class HistoricalViewerApp:
    line_styles = ["solid"]
    colors = [
        (0.0, 0.0, 1.0, 1.0), #Blue
        (1.0, 0.0, 0.0, 1.0), #Red
        (0.0, 1.0, 0.0, 1.0), #Green
        (1.0, 1.0, 0.0, 1.0)
    ]

    def __init__(self, width, height):
        self.path = config.find_options("general.cfg")['database']
        self.descriptors = config.find_graphs("graphs.cfg")

        self.data = {}
        self.collections = {}
        self.nav_collections = {}

        self.color_map = {}

        self.connection = sql.connect(self.path, detect_types=(sql.PARSE_COLNAMES|sql.PARSE_DECLTYPES))

        self.root = Tk.Tk()
        self.root.wm_title("Historical Data Viewer")

        self.intervals = self.connection.execute("SELECT * FROM intervals ORDER BY start;").fetchall()
        self.intervals = list(reversed(self.intervals))
        

        #Grid layout:
        #        Col 0        Col 1
        #       +------------+------+   
        # Row 0 |            | ITVL |
        #       |    ZOOM    | SLCT |
        #       | - -GRAPH- -+------+
        # Row 1 |            | DATA |
        #       |            | SLCT |
        #       +------------+------+
        # Row 2 | NAV GRAPH  | ZOOM |
        #       |            | SLCT |
        #       +------------+------+


        #Setup for the Tk/Agg plotting backend
        self.figure = Figure(figsize = (width+1, height+2), dpi = 100)
        self.figure.subplots_adjust(bottom=0.15)
        self.axes = self.figure.add_subplot(111, autoscale_on=True)
        self.axes.xaxis.labelpad *= 3
        self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
        self.canvas.get_tk_widget().grid(row=0, column=0, rowspan=2)

        #Setup for the mini-navigation figure
        self.nav_figure = Figure(figsize = ((width+1)*1.05, 1.8), dpi = 100)
        self.nav_figure.subplots_adjust(bottom=0.3)
        self.nav_axes = self.nav_figure.add_subplot(111, autoscale_on=False)
        self.nav_axes.xaxis.labelpad *= 2
        self.nav_span = self.nav_axes.axvspan(0.0,1.0, facecolor='g', alpha=0.5)
        self.nav_handle1 = self.nav_axes.axvspan(0.00,0.025, facecolor='black', alpha=0.5)
        self.nav_handle2 = self.nav_axes.axvspan(0.975,1.00, facecolor='black', alpha=0.5)
        self.nav_canvas = FigureCanvasTkAgg(self.nav_figure, master=self.root)
        self.nav_canvas.get_tk_widget().grid(row=2, column=0)

        #Set the z-order to something high so that it works as an overlay
        self.nav_span.zorder = 1000
        self.nav_handle1.zorder = 1001
        self.nav_handle2.zorder = 1001

##        self.nav_axes.xaxis.set_major_locator(ticker.NullLocator())
        self.nav_axes.yaxis.set_major_locator(ticker.NullLocator())

        #Setup for the choosing the interval we want to examine
        self.interval_frame = Tk.Frame(self.root)
        self.interval_frame.grid(row=0, column=1, sticky=N+E)
        picker_label = Tk.Label(self.interval_frame, text="Data Intervals")
        picker_label.grid(row=0, column=0, sticky=N)
        self.interval_picker = Tk.Listbox(self.interval_frame,
                                          selectmode=Tk.BROWSE,
                                          exportselection=0)
        self.interval_picker.grid(row=1, column=0, sticky=N+W+E)
        width = 20
        for (name, start, end) in self.intervals:
            string = format_span(start, end)
            width = max(width, len(string))
            self.interval_picker.insert(Tk.END, string)

        self.interval_picker.config(width=width)

        self.interval_picker.bind("<Double-Button-1>", self.select_interval)
        self.interval_scrollbar = Tk.Scrollbar(self.interval_frame, command=self.interval_picker.yview)
        self.interval_picker.config(yscrollcommand=self.interval_scrollbar.set)
        self.interval_scrollbar.grid(row=1, column=1, sticky=N+S+W)
        picker_button = Tk.Button(self.interval_frame,
                                  text="Select interval",
                                  command=self.select_interval)
        picker_button.grid(row=2, column=0, sticky=N+W)


        #----Variables for picking the table from the database--------------
        self.source_frame = Tk.Frame(self.root)
        self.source_frame.config(relief=Tk.GROOVE,
                                 borderwidth=2)
        self.source_frame.grid(row=1, column=1, sticky=N+W+E+S)

        source_label = Tk.Label(self.source_frame, text="Show/Hide plots")
        source_label.grid(column=0, columnspan=2, sticky=N)
        
        def set_var_callback(cb, data, checkbutton, var=None):
            if var is None:
                var = Tk.IntVar()
            def wrapper_callback():
                var.set(var.get())
                return cb(data, var.get())
            checkbutton.config(variable=var,
                               command=wrapper_callback)

        self.checkbuttons = []
        for i,desc in enumerate(self.descriptors):
            checkbutton = Tk.Checkbutton(self.source_frame,
                                         text=desc.title)
            toggle = Tk.Checkbutton(self.source_frame, text="   ")

            color = self.colors[i % len(self.colors)]
            checkbutton.config(relief=Tk.SUNKEN,
                               indicatoron=False)
            toggle.config(indicatoron=False,
                          background=tk_color(lighten(color)),
                          highlightbackground=tk_color(color),
                          activebackground=tk_color(color),
                          selectcolor=tk_color(color),
                          state=ACTIVE)
            var = Tk.IntVar()
            set_var_callback(self.toggle_source, desc, checkbutton, var)
            set_var_callback(self.toggle_source, desc, toggle, var)
            self.color_map[desc.table_name] = color

            toggle.grid(column=0, row=i+1, sticky=N+W)
            checkbutton.grid(column=1, row=i+1, sticky=N+W)
            self.checkbuttons.append(checkbutton)

        self.style_count = 0
        self.min_y = self.descriptors[0].min_y
        self.max_y = self.descriptors[0].max_y
        self.grabbing_navbar = False
        self.grabbing_handle = False
        self.grab_start = None
        self.handle_grabbed = None
        self.grab_reference = None
        

    def run(self):
        self.nav_figure.canvas.mpl_connect("button_press_event", self.mpl_on_press)
        self.nav_figure.canvas.mpl_connect("button_release_event", self.mpl_on_release)
        self.nav_figure.canvas.mpl_connect("motion_notify_event", self.mpl_on_motion)
        
        self.select_interval(index=0)
        self.interval_picker.selection_set(first=0)

        for desc, checkbutton in zip(self.descriptors, self.checkbuttons):
            checkbutton.select()
            self.toggle_source(desc, True)
        
        self.root.mainloop()

    def update_bounds(self):
        size = to_ordinal(self.absolute_end) - to_ordinal(self.absolute_start)
        x1 = to_ordinal(self.start_date)
        x2 = to_ordinal(self.end_date)
        self.axes.set_xbound(x1, x2)
        self.nav_span.set_xy([[x1, self.min_y],
                              [x1, self.max_y],
                              [x2, self.max_y],
                              [x2, self.min_y],
                              [x1, self.min_y]])
        if x1 > x2:
            x1, x2 = x2, x1
        self.nav_handle1.set_xy([[x1, self.min_y],
                                 [x1, self.max_y],
                                 [x1-size*0.025, self.max_y],
                                 [x1-size*0.025, self.min_y],
                                 [x1, self.min_y]])
        self.nav_handle2.set_xy([[x2, self.min_y],
                                 [x2, self.max_y],
                                 [x2+size*0.025, self.max_y],
                                 [x2+size*0.025, self.min_y],
                                 [x2, self.min_y]])
        locator = KenLocator(7.8)
        self.axes.xaxis.set_major_locator(locator)
        self.axes.xaxis.set_major_formatter(KenFormatter(locator))

        self.axes.set_xlabel(format_span(self.start_date, self.end_date))
        self.nav_axes.set_xlabel(format_timedelta(self.end_date-self.start_date))
        

    def update_data(self):
        for identifier in self.data:
            view = self.data[identifier]
            view.load(self.absolute_start, self.absolute_end)
            self.collections[identifier].set_segments([view.export()])
            self.nav_collections[identifier].set_segments([view.export()])

    def select_interval(self, index=None):
        #Support direct invocation, invocation by event, and as a command
        if not isinstance(index, int):
            if not self.interval_picker.curselection():
                return
            index = int(self.interval_picker.curselection()[0])

        name, start, end = self.intervals[index]
        self.absolute_start = self.start_date = start
        self.absolute_end = self.end_date = end

        size = to_ordinal(start) - to_ordinal(end)
        self.nav_axes.set_xlim(to_ordinal(start)+size*0.025, to_ordinal(end)-size*0.025)

        locator = KenLocator(7.8)
        self.nav_axes.xaxis.set_major_locator(locator)
        self.nav_axes.xaxis.set_major_formatter(KenFormatter(locator))
        
        self.update_data()
        self.update_bounds()

        self.redraw()

    def toggle_source(self, desc, enabled):
        if desc.table_name in self.data:
            if enabled:
                data = self.data[desc.table_name].export()
                self.axes.set_ylabel(desc.units)
            else:
                data = []
            self.collections[desc.table_name].set_segments([data])
            self.nav_collections[desc.table_name].set_segments([data])

            self.redraw()

        elif enabled:
            self.axes.set_ylabel(desc.units)
            self.add_source(desc.table_name, desc.title)
    
            self.min_y = min(self.min_y, desc.min_y)
            self.max_y = max(self.max_y, desc.max_y)
            self.axes.set_ybound(self.min_y,self.max_y)
            self.nav_axes.set_ybound(self.min_y, self.max_y)
##            self.axes.legend(loc=3)
            
            self.redraw()
        
    def redraw(self):
        self.figure.canvas.draw()
        self.nav_figure.canvas.draw()

    def add_source(self, identifier, title):
        view = SQLIntervalView(self.connection, identifier, self.absolute_start, self.absolute_end)
        self.data[identifier] = view

        colors = [self.color_map.get(identifier, self.colors[0])]

        col = DatetimeCollection([view.export()], colors=colors)
        col.set_label(title)
        self.collections[identifier] = col

        col2 = DatetimeCollection([view.export()], colors=colors)
        self.nav_collections[identifier] = col2
        
        self.axes.add_collection(col)
        self.nav_axes.add_collection(col2)

    def mpl_on_press(self, event):
        if event.button != 1 or self.grabbing_navbar or self.grabbing_handle:
            return
        trans = self.nav_axes.transData.inverted()
        xdata = trans.transform((event.x, event.y))[0]
        if self.nav_handle1.contains(event)[0]:
            self.grabbing_handle = True
            self.handle_grabbed = 1
            self.grab_start = xdata
            self.grab_reference = to_ordinal(min(self.start_date, self.end_date))
        elif self.nav_handle2.contains(event)[0]:
            self.grabbing_handle = True
            self.handle_grabbed = 2
            self.grab_start = xdata
            self.grab_reference = to_ordinal(max(self.start_date, self.end_date))
        elif self.nav_span.contains(event)[0]:
            self.grabbing_navbar = True
            self.grab_start = xdata
            self.grab_reference = (to_ordinal(self.start_date),
                                   to_ordinal(self.end_date))

    def move_navbar(self, pos):
        left_end = to_ordinal(self.absolute_start)
        right_end = to_ordinal(self.absolute_end)
        x1, x2 = self.grab_reference

        if x1 > x2:
            x1, x2 = x2, x1
        
        diff = pos - self.grab_start
        if diff < 0 and x1 + diff < left_end:
            self.start_date = self.absolute_start
            self.end_date = from_ordinal(left_end + (x2 - x1))
        elif diff > 0 and x2 + diff > right_end:
            self.end_date = self.absolute_end
            self.start_date = from_ordinal(right_end - (x2 - x1))
        else:
            self.start_date = from_ordinal(x1 + diff)
            self.end_date = from_ordinal(x2 + diff)

        self.update_bounds()
        self.redraw()

    def move_handle(self, pos):
        left_end = to_ordinal(self.absolute_start)
        right_end = to_ordinal(self.absolute_end)
        
        diff = pos - self.grab_start
        new_pos = max(min(self.grab_reference + diff, right_end), left_end)
        new_date = from_ordinal(new_pos)

        if self.handle_grabbed == 1:
            if new_date > self.end_date:
                self.handle_grabbed = 2
                self.start_date = self.end_date
                self.end_date = new_date
            else:
                self.start_date = new_date
        else:
            if new_date < self.start_date:
                self.handle_grabbed = 1
                self.end_date = self.start_date
                self.start_date = new_date
            else:
                self.end_date = new_date
            

        self.update_bounds()
        self.redraw()

    def mpl_on_release(self, event):
        if event.button != 1:
            return

        trans = self.nav_axes.transData.inverted()
        xdata = trans.transform((event.x, event.y))[0]

        if self.grabbing_navbar:
            self.grabbing_navbar = False
            self.move_navbar(xdata)
        elif self.grabbing_handle:
            self.grabbing_handle = False
            self.move_handle(xdata)

    def mpl_on_motion(self, event):
        trans = self.nav_axes.transData.inverted()
        xdata = trans.transform((event.x, event.y))[0]
        if self.grabbing_navbar:
            self.move_navbar(xdata)
        elif self.grabbing_handle:
            self.move_handle(xdata)
예제 #2
0
class HistoricalViewerApp:
    line_styles = ["solid"]
    colors = [
        (0.0, 0.0, 1.0, 1.0), #Blue
        (1.0, 0.0, 0.0, 1.0), #Red
        (0.0, 1.0, 0.0, 1.0), #Green
        (1.0, 1.0, 0.0, 1.0)
    ]

    def __init__(self, width, height):
        self.path = config.find_options("general.cfg")['database']
        self.descriptors = config.find_graphs("graphs.cfg")

        self.data = {}
        self.collections = {}
        self.nav_collections = {}

        self.color_map = {}

        self.connection = sql.connect(self.path, detect_types=(sql.PARSE_COLNAMES|sql.PARSE_DECLTYPES))

        self.root = Tk.Tk()
        self.root.wm_title("Historical Data Viewer")

        self.intervals = self.connection.execute("SELECT * FROM intervals ORDER BY start;").fetchall()
        self.intervals = list(reversed(self.intervals))
        

        #Grid layout:
        #        Col 0        Col 1
        #       +------------+------+   
        # Row 0 |            | ITVL |
        #       |    ZOOM    | SLCT |
        #       | - -GRAPH- -+------+
        # Row 1 |            | DATA |
        #       |            | SLCT |
        #       +------------+------+
        # Row 2 | NAV GRAPH  | ZOOM |
        #       |            | SLCT |
        #       +------------+------+


        #Setup for the Tk/Agg plotting backend
        self.figure = Figure(figsize = (width+1, height+2), dpi = 100)
        self.figure.subplots_adjust(bottom=0.15)
        self.axes = self.figure.add_subplot(111, autoscale_on=True)
        #self.axes.xaxis.labelpad *= 3
        

        self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
        self.canvas.get_tk_widget().grid(row=0, column=0, rowspan=2)

        #Setup for the mini-navigation figure
        self.nav_figure = Figure(figsize = ((width+1)*1.05, 1.8), dpi = 100)
        #self.nav_figure.subplots_adjust(bottom=0.3)
        self.nav_axes = self.nav_figure.add_subplot(111, autoscale_on=False)
        #self.nav_axes.xaxis.labelpad *= 2
                
        self.nav_canvas = FigureCanvasTkAgg(self.nav_figure, master=self.root)
        self.nav_canvas.get_tk_widget().grid(row=2, column=0)
        
        self.nav_axes.yaxis.set_major_locator(ticker.NullLocator())

        #Setup for the slider
        def scale_to_data(left, right, x):
            return from_ordinal(to_ordinal(left) + x * (to_ordinal(right) - to_ordinal(left)))
        def data_to_scale(left, right, x):
            return (to_ordinal(x) - to_ordinal(left)) / (to_ordinal(right) - to_ordinal(left))
        
        self.slider = DoubleSlider(self.root,
                                   left_bound=datetime(1999,1,1),
                                   right_bound=datetime(1999,1,28),
                                   data_to_scale=data_to_scale,
                                   scale_to_data=scale_to_data,
                                   set_on_drag=False,
                                   on_change=self.update_bounds,
                                   on_drag=lambda left, right: self.update_bounds(left, right, True))
        self.slider.grid(row=3, column=0, sticky=W+E)        

        #Setup for the choosing the interval we want to examine
        self.interval_frame = Tk.Frame(self.root)
        self.interval_frame.grid(row=0, column=1, sticky=N+E)
        picker_label = Tk.Label(self.interval_frame, text="Data Intervals")
        picker_label.grid(row=0, column=0, sticky=N)
        self.interval_picker = Tk.Listbox(self.interval_frame,
                                          selectmode=Tk.BROWSE,
                                          exportselection=0)
        self.interval_picker.grid(row=1, column=0, sticky=N+W+E)
        width = 20
        for (name, start, end) in self.intervals:
            string = format_span(start, end)
            width = max(width, len(string))
            self.interval_picker.insert(Tk.END, string)

        self.interval_picker.config(width=width)

        self.interval_picker.bind("<Double-Button-1>", self.select_interval)
        self.interval_scrollbar = Tk.Scrollbar(self.interval_frame, command=self.interval_picker.yview)
        self.interval_picker.config(yscrollcommand=self.interval_scrollbar.set)
        self.interval_scrollbar.grid(row=1, column=1, sticky=N+S+W)
        picker_button = Tk.Button(self.interval_frame,
                                  text="Select interval",
                                  command=self.select_interval)
        picker_button.grid(row=2, column=0, sticky=N+W)


        #----Variables for picking the table from the database--------------
        self.source_frame = Tk.Frame(self.root)
        self.source_frame.config(relief=Tk.GROOVE,
                                 borderwidth=2)
        self.source_frame.grid(row=1, column=1, sticky=N+W+E+S)

        source_label = Tk.Label(self.source_frame, text="Show/Hide plots")
        source_label.grid(column=0, columnspan=2, sticky=N)
        
        def set_var_callback(cb, data, checkbutton, var=None):
            if var is None:
                var = Tk.IntVar()
            def wrapper_callback():
                var.set(var.get())
                return cb(data, var.get())
            checkbutton.config(variable=var,
                               command=wrapper_callback)

        self.checkbuttons = []
        for i,desc in enumerate(self.descriptors):
            checkbutton = Tk.Checkbutton(self.source_frame,
                                         text=desc.title)
            toggle = Tk.Checkbutton(self.source_frame, text="   ")

            color = self.colors[i % len(self.colors)]
            checkbutton.config(relief=Tk.SUNKEN,
                               indicatoron=False)
            toggle.config(indicatoron=False,
                          background=tk_color(lighten(color)),
                          highlightbackground=tk_color(color),
                          activebackground=tk_color(color),
                          selectcolor=tk_color(color),
                          state=ACTIVE)
            var = Tk.IntVar()
            set_var_callback(self.toggle_source, desc, checkbutton, var)
            set_var_callback(self.toggle_source, desc, toggle, var)
            self.color_map[desc.table_name] = color

            toggle.grid(column=0, row=i+1, sticky=N+W)
            checkbutton.grid(column=1, row=i+1, sticky=N+W)
            self.checkbuttons.append(checkbutton)

        self.style_count = 0
        self.min_y = self.descriptors[0].min_y
        self.max_y = self.descriptors[0].max_y


    def run(self):
        left_pad = self.nav_figure.subplotpars.left * self.nav_figure.get_figwidth() * self.nav_figure.dpi
        right_pad = (1-self.nav_figure.subplotpars.right) * self.nav_figure.get_figwidth() * self.nav_figure.dpi
        self.slider.config(left_padding=left_pad,
                           right_padding=right_pad)

        self.figure.canvas.draw()
        self.nav_figure.canvas.draw()

        self.slider.init()
        
        self.select_interval(index=0)
        self.interval_picker.selection_set(first=0)

        for desc, checkbutton in zip(self.descriptors, self.checkbuttons):
            checkbutton.select()
            self.toggle_source(desc, True)

        locator = KenLocator(7.8)
        self.nav_axes.xaxis.set_major_locator(locator)
        self.nav_axes.xaxis.set_major_formatter(KenFormatter(locator))

        locator = KenLocator(7.8)
        self.axes.xaxis.set_major_locator(locator)
        self.axes.xaxis.set_major_formatter(KenFormatter(locator))
        
        self.root.mainloop()

    def update_bounds(self, left, right, dragging=False):
        if not dragging:
            self.axes.set_xbound(to_ordinal(left), to_ordinal(right))

            self.axes.set_xlabel(format_span(left, right)
                                 + " (" + format_timedelta(right - left) + ")")

        agg_canvas = self.nav_canvas.get_tk_widget()
        agg_canvas.delete("OVERLAY")


        #    c1            c3
        #    +---+---------+---+
        #    |...| _/\   _ |...|
        #    |...|/   \_/ \|...|
        #    +---+---------+---+
        #        c2            c4
        cx2, cy2 = self.nav_axes.transData.transform_point((to_ordinal(left), self.max_y)).tolist()
        cx3, cy3 = self.nav_axes.transData.transform_point((to_ordinal(right), self.min_y)).tolist()
        agg_canvas.create_rectangle([cx2, cy2, cx3, cy3], tags="OVERLAY",
                                    stipple = "gray50",
                                    fill = "green")
##        cx1, cy1 = self.nav_axes.transData.transform_point((to_ordinal(self.slider.left_bound), self.min_y)).tolist()
##        cx2, cy2 = self.nav_axes.transData.transform_point((to_ordinal(left), self.max_y)).tolist()
##        agg_canvas.create_rectangle([(cx1+1, cy1+2), (cx2, cy2)],
##                                    tags="OVERLAY",
##                                    stipple="gray50",
##                                    fill="red", outline="")
##
##        cx3, cy3 = self.nav_axes.transData.transform_point((to_ordinal(right), self.min_y)).tolist()
##        cx4, cy4 = self.nav_axes.transData.transform_point((to_ordinal(self.slider.right_bound), self.max_y)).tolist()
##        agg_canvas.create_rectangle([(cx3, cy3+2), (cx4, cy4)],
##                                    tags="OVERLAY",
##                                    stipple="gray50",
##                                    fill="red", outline="")

        if not dragging:
            self.figure.canvas.draw()
        self.nav_figure.canvas.draw()

    def select_interval(self, index=None):
        #Support direct invocation, invocation by event, and as a command
        if not isinstance(index, int):
            if not self.interval_picker.curselection():
                return
            index = int(self.interval_picker.curselection()[0])

        name, start, end = self.intervals[index]
        for identifier in self.data:
            view = self.data[identifier]
            view.load(start, end)
            self.collections[identifier].set_segments([view.export()])
            self.collections[identifier].set_antialiased(False)
            self.nav_collections[identifier].set_segments([view.export()])
        
        self.slider.config(left_bound=start,
                           right_bound=end)
        self.slider.reset()

        self.nav_axes.set_xbound(to_ordinal(start), to_ordinal(end))
        self.nav_axes.set_xlabel("%s (%s)" % (format_span(start, end),
                                              format_timedelta(end - start)))
        self.redraw()

    def toggle_source(self, desc, enabled):
        if desc.table_name in self.data:
            self.nav_collections[desc.table_name].set_visible(enabled)
            self.collections[desc.table_name].set_visible(enabled)
            self.redraw()
        elif enabled:
            self.axes.set_ylabel(desc.units)
            self.add_source(desc.table_name, desc.title)
    
            self.min_y = min(self.min_y, desc.min_y)
            self.max_y = max(self.max_y, desc.max_y)
            self.axes.set_ybound(self.min_y,self.max_y)
            self.nav_axes.set_ybound(self.min_y, self.max_y)
##            self.axes.legend(loc=3)
            self.redraw()
        
    def redraw(self):
        self.figure.canvas.draw()
        self.nav_figure.canvas.draw()

    def add_source(self, identifier, title):
        left, right = sorted([self.slider.left_bound, self.slider.right_bound])
        view = SQLIntervalView(self.connection, identifier, left, right)
        self.data[identifier] = view

        colors = [self.color_map.get(identifier, self.colors[0])]
        col = DatetimeCollection([view.export()], colors=colors)
        col.set_label(title)
        self.collections[identifier] = col

        col2 = DatetimeCollection([view.export()], colors=colors)
        self.nav_collections[identifier] = col2
        
        self.axes.add_collection(col)
        self.nav_axes.add_collection(col2)
예제 #3
0
class DoubleSliderTestApp:
    def __init__(self, width, height):
        self.root = Tk.Tk()
        self.root.wm_title("Double Slider Test")
        
        self.plot = Figure(figsize = (width+1, height+2), dpi=72)
        self.axes = self.plot.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.plot, master=self.root)
        self.canvas.get_tk_widget().grid(row=0)

        self.nav_plot = Figure(figsize = (width+1, 2), dpi=72)
        self.nav_axes = self.nav_plot.add_subplot(111)
        self.nav_canvas = FigureCanvasTkAgg(self.nav_plot, master=self.root)
        self.nav_canvas.get_tk_widget().grid(row=1)
        self.nav_plot.subplots_adjust(bottom=0.2)

        self.agg_canvas = self.nav_canvas.get_tk_widget()

        self.slider = DoubleSlider(self.root, round=lambda x: round(x, 2),
                                   left_bound=2.0, right_bound=3.0)

        self.slider.grid(row=2, sticky=Tk.W+Tk.E+Tk.N+Tk.S)

        data = [(2.0, 0.6), (2.1, 0.9), (2.2, 0.7), (2.3, 0.8), (2.4, 0.5),
                (2.6, 0.2), (2.7, 0.3), (2.8, 0.6), (2.9, 0.4), (3.0, 0.1)]
        
        self.axes.set_xbound(2.0, 3.0)
        self.axes.add_collection(LineCollection([data]))

        self.nav_axes.set_xbound(2.0, 3.0)
        self.nav_axes.add_collection(LineCollection([data]))
        
    def run(self):
        self.plot.canvas.draw()
        self.nav_plot.canvas.draw()
        left_pad = self.nav_plot.subplotpars.left * self.nav_plot.get_figwidth() * self.nav_plot.dpi
        right_pad = (1-self.nav_plot.subplotpars.right) * self.nav_plot.get_figwidth() * self.nav_plot.dpi

        self.slider.config(left_padding=left_pad,
                           right_padding=right_pad)
        

        def update_limits(left, right):
            self.agg_canvas.delete("OVERLAY")
            trans = blended_transform_factory(self.nav_axes.transData,
                                              self.nav_axes.transAxes)

            corner1 = trans.transform_point([left, 1]).tolist()
            corner2 = trans.transform_point([self.slider.left_bound, 0]).tolist()
            self.agg_canvas.create_rectangle([corner1, corner2], stipple="gray25", fill="gray",tags="OVERLAY")

            corner3 = trans.transform_point((right, 1)).tolist()
            corner4 = trans.transform_point((self.slider.right_bound, 0)).tolist()
            self.agg_canvas.create_rectangle([corner3, corner4], stipple="gray25", fill="gray",tags="OVERLAY")
            
            self.axes.set_xbound(left, right)
            self.plot.canvas.draw()

        self.slider.config(on_change=update_limits)
        self.slider.init()
        
        self.root.mainloop()
예제 #4
0
 def create_at(self, fig: plt.Figure):
     fig.subplots_adjust(bottom=0.2)
     next_btn_pos = plt.axes([0.81, 0.05, 0.1, 0.075])
     self._fig = fig
     self._btn_next = Button(next_btn_pos, "next")
     self._btn_next.on_clicked(self._next)
예제 #5
0
def set_figure_properties(fig: plt.Figure, **kwargs) -> None:
    """
    Ease the configuration of a :class:`matplotlib.figure.Figure`.

    Parameters
    ----------
    fig : matplotlib.figure.Figure
        The figure.

    Keyword arguments
    -----------------
    fontsize : int
        Font size to use for the plot titles, and axes ticks and labels.
        Defaults to 12.
    tight_layout : bool
        `True` to fit the whole subplots into the figure area,
        `False` otherwise. Defaults to `True`.
    tight_layout_rect : Sequence[float]
        A rectangle (left, bottom, right, top) in the normalized figure
        coordinate that the whole subplots area (including labels) will
        fit into. Defaults to (0, 0, 1, 1).
    suptitle : str
        The figure title. Defaults to an empty string.
    xlabel : str
        TODO
    ylabel : str
        TODO
    figlegend_on : bool
        TODO
    figlegend_ax : int
        TODO
    figlegend_loc : `str` or `Tuple[float, float]`
        TODO
    figlegend_framealpha : float
        TODO
    figlegend_ncol : int
        TODO
    subplots_adjust_hspace : float
        TODO
    subplots_adjust_vspace : float
        TODO
    """
    fontsize = kwargs.get("fontsize", 12)
    tight_layout = kwargs.get("tight_layout", True)
    tight_layout_rect = kwargs.get("tight_layout_rect", (0, 0, 1, 1))
    suptitle = kwargs.get("suptitle", "")
    x_label = kwargs.get("x_label", "")
    x_labelpad = kwargs.get("x_labelpad", 20)
    y_label = kwargs.get("y_label", "")
    y_labelpad = kwargs.get("y_labelpad", 20)
    figlegend_on = kwargs.get("figlegend_on", False)
    figlegend_ax = kwargs.get("figlegend_ax", 0)
    figlegend_loc = kwargs.get("figlegend_loc", "lower center")
    figlegend_framealpha = kwargs.get("figlegend_framealpha", 1.0)
    figlegend_ncol = kwargs.get("figlegend_ncol", 1)
    wspace = kwargs.get("subplots_adjust_wspace", None)
    hspace = kwargs.get("subplots_adjust_hspace", None)

    rcParams["font.size"] = fontsize

    if suptitle is not None and suptitle != "":
        fig.suptitle(suptitle, fontsize=fontsize + 1)

    if x_label != "" or y_label != "":
        ax = fig.add_subplot(111)
        ax.set_frame_on(False)
        ax.set_xticks([])
        ax.set_xticklabels([], visible=False)
        ax.set_yticks([])
        ax.set_yticklabels([], visible=False)

        if x_label != "":
            ax.set_xlabel(x_label, labelpad=x_labelpad)
        if y_label != "":
            ax.set_ylabel(y_label, labelpad=y_labelpad)

    if tight_layout:
        fig.tight_layout(rect=tight_layout_rect)

    if figlegend_on:
        handles, labels = fig.get_axes()[figlegend_ax].get_legend_handles_labels()
        fig.legend(
            handles,
            labels,
            loc=figlegend_loc,
            framealpha=figlegend_framealpha,
            ncol=figlegend_ncol,
        )

    if wspace is not None:
        fig.subplots_adjust(wspace=wspace)
    if hspace is not None:
        fig.subplots_adjust(hspace=hspace)