如何使用按钮在 matplotlib 中的两个不同图形之间切换 [英] How do I use a button to switch between two different graphs in matplotlib

查看:31
本文介绍了如何使用按钮在 matplotlib 中的两个不同图形之间切换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个不同的matplotlib图,我想在按下按钮时在它们之间进行切换.当按下按钮时,我拥有的代码会将第二张图添加到第一张图的下面,但是我希望它替换第一张图.这是一个有点类似的 stackoverflow 问题(

I have two different matplotlib graphs that I would like to switch between upon a button press. The code I have will add the second graph below the first graph when the button is pressed but I want it to replace the first graph. This is a somewhat similar stackoverflow question (How to update a matplotlib embedded into tkinter?) but I can't seem to get this to apply to my situation.

The graphs that I have coded, graph_one and graph_two, are two simple graphs that I pulled from the matplotlib documentation. In my actual use I have two much more complicated graphs that are very dissimilar, one is a single plot and the other has an additional subplot. Because the graphs I wish to switch back and forth between are so dissimilar it is important to me that the solution be able to handle the graph inputs as separate definitions. It should also be noted that my graphs are embedded in a tkinter widget and it is important that the solution also account for this embedding. Here is the code I have:

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler



def graph_one():
    t = np.arange(0.0, 2.0, 0.01)
    s = 1 + np.sin(2 * np.pi * t)

    fig, ax = plt.subplots()
    ax.plot(t, s)

    ax.set(xlabel='time (s)', ylabel='voltage (mV)',
           title='Graph One')

    #plt.show()
    return fig


def graph_two():
    t = np.arange(0.0, 2.0, 0.01)
    s = 1 + np.cos(2 * np.pi * t)

    fig, ax = plt.subplots()
    ax.plot(t, s)

    ax.set(xlabel='time (s)', ylabel='voltage (mV)',
           title='Graph Two')

    #plt.show()
    return fig

class matplotlibSwitchGraphs:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.embed_graph_one()
        self.frame.pack(expand=YES, fill=BOTH)

    def embed_graph_one(self):

        fig = graph_one()

        canvas = FigureCanvasTkAgg(fig, self.master)  
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        canvas.draw()

        canvas.mpl_connect("key_press_event", self.on_key_press)

        toolbar = NavigationToolbar2Tk(canvas, self.master)
        toolbar.update()
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)


        self.button = Button(self.master, text="Quit", 
command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)

    def embed_graph_two(self):

        fig = graph_two()

        canvas = FigureCanvasTkAgg(fig, self.master)  
        canvas.draw()
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)

        canvas.mpl_connect("key_press_event", self.on_key_press)

        toolbar = NavigationToolbar2Tk(canvas, self.master)
        toolbar.update()
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)


        self.button = Button(self.master, text="Quit", 
command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)


    def on_key_press(event):
        print("you pressed {}".format(event.key))
        key_press_handler(event, canvas, toolbar)

    def _quit(self):
        self.master.quit()  # stops mainloop

    def switch_graphs(self):
        self.embed_graph_two()

def main():
    root = Tk()
    matplotlibSwitchGraphs(root)
    root.mainloop()



if __name__ == '__main__':
    main()

It seems like I should be able to use a command like

ax.clear()

in the switch_graphs def to clear out the first graph but that doesn't work. Any help would be appreciated.

I'm posting updated code to show some small progress I've made as well as to better represent the different nature of the two graphs I wish to switch between. Both graphs are still simple graphs taken directly from the matplotlib documentation but they better represent that one of my graphs is a single plot while the second graph has two plots positioned directly on top of one another.

In my actual case I am trying to use a button to be able to switch between a candlestick chart with a volume overlay and one without the volume overlay. Posting all the code to show the candlestick charts would make for a very long piece of code so I've simplified it by using these simpler graphs. I've also eliminated matplotlib's navigation toolbar for the sake of simplicity. Here is my revised code:

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler



class matplotlibSwitchGraphs:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.embed_graph_one()
        self.frame.pack(expand=YES, fill=BOTH)

    # the def creates the first matplotlib graph
    def graph_one(self):
        t = np.arange(0.0, 2.0, 0.01)
        s = 1 + np.sin(2 * np.pi * t)

        fig, ax = plt.subplots()
        ax.plot(t, s)

        ax.set(xlabel='time (s)', ylabel='voltage (mV)',
               title='Graph One')

        # plt.show()
        return fig, ax

    # This def creates the second matplotlib graph that uses subplot
    # to place two graphs one on top of the other
    def graph_four(self):
        x1 = np.linspace(0.0, 5.0)
        y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
        fig = plt.figure()
        ax = plt.subplot2grid((5, 4), (0, 0), rowspan=4, colspan=4)
        ax.plot(x1, y1, 'o-')

        means_men = (20, 35, 30, 35, 27)
        std_men = (2, 3, 4, 1, 2)
        ax2 = plt.subplot2grid((5, 4), (4, 0), sharex=ax, rowspan=1, 
colspan=4)
        ax2.bar(std_men, means_men, color='green', width=0.5, 
align='center')

        return fig, ax

    # this def takes graph one and embeds it in a tkinter widget
    def embed_graph_one(self):

        fig, ax = self.graph_one()

        canvas = FigureCanvasTkAgg(fig, self.master)  
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        canvas.draw()

        canvas.mpl_connect("key_press_event", self.on_key_press)


        self.button = Button(self.master, text="Quit", 
command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=lambda: self.switch_graphs(canvas, fig, ax))
        self.button_switch.pack(side=BOTTOM)


    # This def takes the second graph and embeds it in a tkinter 
    # widget
    def embed_graph_two(self):

        fig, ax = self.graph_two()

        canvas = FigureCanvasTkAgg(fig, self.master)  
        canvas.draw()
        canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)

        canvas.mpl_connect("key_press_event", self.on_key_press)


        self.button = Button(self.master, text="Quit", 
command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", 
command=lambda: self.switch_graphs(canvas, fig, ax))
        self.button_switch.pack(side=BOTTOM)

    # the def defines the key press event handler
    def on_key_press(event):
        key_press_handler(event, canvas, toolbar)

    # This def quits the tkinter widget 
    def _quit(self):
        self.master.quit()  # stops mainloop

    # This def switches between the two embedded graphs
    def switch_graphs(self, fig, canvas, ax):
        ax.clear()
        self.embed_graph_two()
        canvas.draw()




def main():
    root = Tk()
    matplotlibSwitchGraphs(root)
    root.mainloop()



if __name__ == '__main__':
    main()

This code still doesn't replace the first graph with second graph but just places the second graph below the first. Any help on getting this code to replace the first graph with the second would be appreciated.

The graphs that I am plotting are an OHLC candlestick chart and an OHLC candlestick chart with a volume overlay. Unfortunately The

self.canvas.draw()

command in the draw_graph defs doesn't seem to apply here. When I try to display the graphs all I get is a blank figure. Here is the code I am using that plots the candlestick chart. This would correspond to draw_graph_one.

def ohlc_daily_date_axis(self, stock_sym):
    mondays = WeekdayLocator(MONDAY)  # major ticks on the mondays
    alldays = DayLocator()  # minor ticks on the days
    weekFormatter = DateFormatter('%b %d %Y')  # e.g., Jan 12 2018
    dayFormatter = DateFormatter('%d')  # e.g., 12

    quotes = get_stock_price_data_list_of_tuples(stock_sym)
    graph_header_text = 'Daily OHLC Candlestick Chart: ' + stock_sym + ' Date Range: ' + str(
        num2date(quotes[0][0]).date()) + ' - ' + str(num2date(quotes[-1][0]).date())

    if len(quotes) == 0:
        raise SystemExit

    self.fig, self.ax = plt.subplots(figsize=(18, 5))
    plt.subplots_adjust(bottom=0.2)
    self.ax.xaxis.set_major_locator(mondays)
    self.ax.xaxis.set_minor_locator(alldays)
    self.ax.xaxis.set_major_formatter(weekFormatter)
    # ax.xaxis.set_minor_formatter(dayFormatter)
    plt.title(graph_header_text)
    self.ax.set_ylabel('Share Price ($)', size=10)

    # plot_day_summary(ax, quotes, ticksize=3)
    candlestick_ohlc(self.ax, quotes, width=0.6)

    self.ax.xaxis_date()
    self.ax.autoscale_view()
    plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right')
    self.ax.format_coord = self.get_ohlc_from_date_xy
    # ax.fmt_xdata = get_ohlc_from_date_x

    #plt.show()
    self.canvas.draw() 

How would I modify this to get it to show the data?

解决方案

New answer to include Tk embedding (significant changes from the other answer, so adding new answer instead of editing that one). I moved graph_one() and graph_two() into the switch graph wrapper class, and renamed them to draw_graph_one() and draw_graph_two(). Those two new class methods replaced the embed_graph_one() and embed_graph_two() methods. Most of the content within embed() methods were duplicates, and were therefore moved to a config_window() method which is called when the class object is instantiated. Created a few class data members to capture the plt variables (e.g. canvas, ax, fig) and created a new data member to keep track of which graph is currently displayed (graphIndex) so that we can properly draw the correct graph when switch_graphs() is called (instead of calling embed_graph_two() every time "switch" is made, as in the original code). Optionally, you can take out t from the draw methods and make it a class data member (if the values for t don't change).

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler

# Seperated out config of plot to just do it once
def config_plot():
    fig, ax = plt.subplots()
    ax.set(xlabel='time (s)', ylabel='voltage (mV)',
           title='Graph One')
    return (fig, ax)

class matplotlibSwitchGraphs:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.fig, self.ax = config_plot()
        self.graphIndex = 0
        self.canvas = FigureCanvasTkAgg(self.fig, self.master)  
        self.config_window()
        self.draw_graph_one()
        self.frame.pack(expand=YES, fill=BOTH)

    def config_window(self):
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        toolbar = NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.button = Button(self.master, text="Quit", command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_switch = Button(self.master, text="Switch Graphs", command=self.switch_graphs)
        self.button_switch.pack(side=BOTTOM)

    def draw_graph_one(self):
        t = np.arange(0.0, 2.0, 0.01)
        s = 1 + np.sin(2 * np.pi * t)
        self.ax.clear() # clear current axes
        self.ax.plot(t, s)
        self.ax.set(title='Graph One')
        self.canvas.draw()

    def draw_graph_two(self):
        t = np.arange(0.0, 2.0, 0.01)
        s = 1 + np.cos(2 * np.pi * t)
        self.ax.clear()
        self.ax.plot(t, s)
        self.ax.set(title='Graph Two')
        self.canvas.draw()

    def on_key_press(event):
        print("you pressed {}".format(event.key))
        key_press_handler(event, self.canvas, toolbar)

    def _quit(self):
        self.master.quit()  # stops mainloop

    def switch_graphs(self):
        # Need to call the correct draw, whether we're on graph one or two
        self.graphIndex = (self.graphIndex + 1 ) % 2
        if self.graphIndex == 0:
            self.draw_graph_one()
        else:
            self.draw_graph_two()

def main():
    root = Tk()
    matplotlibSwitchGraphs(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Output (two windows, alternating whenever switch graph button is clicked):

这篇关于如何使用按钮在 matplotlib 中的两个不同图形之间切换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆