Как использовать кнопку для переключения между двумя разными графиками в matplotlib - PullRequest
0 голосов
/ 10 января 2019

У меня есть два разных графика matplotlib, которые я хотел бы переключать при нажатии кнопки. Код, который я имею, добавит второй график ниже первого графика, когда кнопка нажата, но я хочу, чтобы он заменил первый график. Это в некоторой степени похожий вопрос stackoverflow ( Как обновить matplotlib, встроенный в tkinter? ), но я не могу заставить это примениться к моей ситуации.

Графы, которые я закодировал, 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()

в def_graphs switch, чтобы очистить первый график, но это не работает. Любая помощь будет оценена.

Я публикую обновленный код, чтобы показать небольшой прогресс, которого я достиг, а также чтобы лучше представить различную природу двух графиков, между которыми я хочу переключиться. Оба графика все еще являются простыми графиками, взятыми непосредственно из документации по 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 здесь не применима. Когда я пытаюсь отобразить графики, все, что я получаю, это пустой рисунок. Вот код, который я использую для построения графика свечи. Это будет соответствовать 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() 

Как бы я изменил это, чтобы оно показывало данные?

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Новый ответ, включающий включение Tk (значительные изменения по сравнению с другим ответом, поэтому добавление нового ответа вместо редактирования этого). Я переместил graph_one() и graph_two() в класс оболочки переключающего графа и переименовал их в draw_graph_one() и draw_graph_two(). Эти два новых метода класса заменили методы embed_graph_one() и embed_graph_two(). Большая часть содержимого в методах embed() была дубликатами и поэтому была перемещена в метод config_window(), который вызывается при создании экземпляра объекта класса. Создал несколько элементов данных класса для захвата переменных plt (например, canvas, ax, fig) и создал новый элемент данных, чтобы отслеживать, какой график отображается в данный момент (graphIndex), чтобы мы могли правильно Нарисуйте правильный график, когда вызывается switch_graphs() (вместо вызова embed_graph_two() каждый раз, когда выполняется "переключение", как в исходном коде). При желании вы можете извлечь t из методов draw и сделать его членом класса (если значения для t не меняются).

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()

Вывод (два окна, чередующиеся при каждом нажатии кнопки переключателя графика): enter image description here

0 голосов
/ 10 января 2019

Если не считать документации в matplotlib по кнопкам и вопроса stackoverflow, который вы связали , то вот код, который делает основы того, что вы имеете в виду. Извините, я не использовал ваш код, но он был слишком большим для блока кода.

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()

Результат:

enter image description here

Нажмите «Переключить график», и вы получите:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...