Python 如何使用按钮在matplotlib中的两个不同图形之间切换

Python 如何使用按钮在matplotlib中的两个不同图形之间切换,python,matplotlib,Python,Matplotlib,我有两个不同的matplotlib图,我想在按下按钮时在它们之间切换。当按下按钮时,我的代码将在第一个图形下面添加第二个图形,但我希望它替换第一个图形。这是一个有点类似的stackoverflow问题(),但我似乎无法将其应用于我的情况 我编写的图,graph_one和graph_two,是我从matplotlib文档中提取的两个简单图。在我的实际使用中,我有两个更复杂的图形,它们非常不同,一个是单一的绘图,另一个有额外的子绘图。因为我希望在两个图形之间来回切换的图形非常不同,所以解决方案能够将

我有两个不同的matplotlib图,我想在按下按钮时在它们之间切换。当按下按钮时,我的代码将在第一个图形下面添加第二个图形,但我希望它替换第一个图形。这是一个有点类似的stackoverflow问题(),但我似乎无法将其应用于我的情况

我编写的图,graph_one和graph_two,是我从matplotlib文档中提取的两个简单图。在我的实际使用中,我有两个更复杂的图形,它们非常不同,一个是单一的绘图,另一个有额外的子绘图。因为我希望在两个图形之间来回切换的图形非常不同,所以解决方案能够将图形输入作为单独的定义来处理对我来说非常重要。还应该注意的是,我的图形嵌入在tkinter小部件中,解决方案也要考虑这种嵌入,这一点很重要。 以下是我的代码:

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()
看起来我应该可以使用这样的命令

ax.clear()
在开关中,清除第一个图形,但这不起作用。任何帮助都将不胜感激

我正在发布更新的代码,以显示我取得的一些小进步,并更好地表示我希望在两个图之间切换的不同性质。这两个图仍然是直接从matplotlib文档中获取的简单图,但它们更好地表示我的一个图是一个图,而第二个图有两个图直接位于另一个图之上

在我的实际案例中,我尝试使用一个按钮,以便能够在有音量覆盖的烛台图表和没有音量覆盖的烛台图表之间切换。发布所有的代码来显示烛台图表会产生一段很长的代码,所以我使用了这些简单的图形来简化它。为了简单起见,我还取消了matplotlib的导航工具栏。这是我修改后的代码:

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()
这段代码仍然没有用第二个图替换第一个图,而是将第二个图放在第一个图的下面。如果您有任何帮助,请使用此代码将第一个图形替换为第二个图形,我们将不胜感激

我正在绘制的图形是OHLC烛台图和带有体积覆盖的OHLC烛台图。不幸的是

self.canvas.draw()
draw_graph defs中的命令在这里似乎不适用。当我试图显示图表时,我得到的只是一个空白的数字。下面是我正在使用的绘制烛台图表的代码。这将对应于绘制图1

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() 
我如何修改它以使其显示数据

在matplotlib和的基础上,下面是一些代码,这些代码提供了您的基本意思。很抱歉,我没有使用你的代码,但它的代码块太大了

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

# Button handler object
class Index(object):
    ind = 0

    # This function is called when bswitch is clicked
    def switch(self, event):
        self.ind = (self.ind+1) % len(functions)
        ydata = 1 + functions[self.ind](2 * np.pi * t)
        l.set_ydata(ydata)
        ax.set(title='Graph '+str(self.ind + 1))
        plt.draw()

    # This function is called when bquit is clicked
    def quit(self, event):
        plt.close()

# Store the functions you want to use to plot the two different graphs in a list
functions = [np.sin, np.cos]

# Adjust bottom to make room for Buttons
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

# Get t and s values for Graph 1
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)

# Plot Graph 1 and set axes and title
l, = plt.plot(t, s, lw=2)
ax.set(xlabel='time (s)', ylabel='voltage (mV)',
           title='Graph 1')

# Initialize Button handler object
callback = Index()

# Connect to a "switch" Button, setting its left, top, width, and height
axswitch = plt.axes([0.40, 0.07, 0.2, 0.05])
bswitch = Button(axswitch, 'Switch graph')
bswitch.on_clicked(callback.switch)

# Connect to a "quit" Button, setting its left, top, width, and height
axquit = plt.axes([0.40, 0.01, 0.2, 0.05])
bquit = Button(axquit, 'Quit')
bquit.on_clicked(callback.quit)

# Show
plt.show()
结果:

单击“切换图”,您将获得:


新答案包含Tk嵌入(与其他答案相比有重大变化,因此添加新答案而不是编辑该答案)。我将
graph\u one()
graph\u two()
移动到switch graph包装类中,并将它们重命名为
draw\u graph\u one()
draw\u graph\u two()
。这两个新类方法取代了
embed\u graph\u one()
embed\u graph\u two()
方法。
embed()
方法中的大多数内容都是重复的,因此被移动到
config\u window()
方法中,该方法在实例化类对象时调用。创建了几个类数据成员来捕获plt变量(例如,
canvas
ax
fig
),并创建了一个新的数据成员来跟踪当前显示的图形(
graphIndex
),以便在调用
switch_graphs()
时可以正确绘制图形(而不是像在原始代码中一样,每次进行“切换”时调用
embedded\u graph\u two()
)。或者,您可以从
draw
方法中取出
t
,并将其设为类数据成员(如果
t
的值不变)

输出(两个窗口,每次单击开关图按钮时交替):

您好,谢谢您的输入,但我无法在我的情况下实现这一点。我的图形嵌入到tkinter小部件中,这并不说明嵌入的原因。或者至少如果是这样,我无法确定如何使其工作。再次感谢。好的,我明天将查看Tk嵌入。今天时间不多了:(谢谢,我真的很感激。这几天来我一直在用头撞墙。嗨,谢谢你发布这篇文章。这将花费我几分钟的时间来完成。这是一件美妙的事情。非常感谢。这篇文章写得很好。再次感谢!!没问题!很高兴能提供帮助。我在pos之前没有看到你对你的问题的编辑。)很抱歉,这完全是基于您的原始代码。您的直觉在使用
ax.clear()
以及
ax.plot()时是正确的
简单地在同一个图上绘制新点。我所做的编辑只是为了让我的代码更清晰,更好地帮助我理解如何将ax传递到开关图定义。我的编辑在概念上并没有真正改变任何东西。再次感谢,这真的让我发疯。不幸的是,我的实际图形似乎与此不兼容s设置。我已经发布了与draw_graph_one相对应的代码。我希望您可以查看它,并告诉我需要更改什么以便可以查看数据。
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()