0

I have some csv files with data I want to plot. I want to use my arrow keys on my keyboard to overwrite the plot.

My Code:

import os, pandas, glob
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog

counter = 0

class Plotter:
    def __init__(self, filepath):
        self.plotter(filepath)

    def press(self, event):
        print('press', event.key)
        global counter
        if event.key == 'down':
            if (counter + 1) < len(files):
                counter = counter + 1
                self.plotter(files[counter])

        if event.key == 'up':
            if not (counter - 1) < 0:
                counter = counter - 1
                self.plotter(files[counter])

    def plotter(self, filepath):
        data = pandas.read_csv(filepath)
        fig = plt.figure()
        fig.canvas.mpl_connect('key_press_event', self.press)
        ax = fig.add_subplot(111)
        ax.plot(data.x, data.y1, 'r-')

        ax2 = ax.twinx()
        ax2.plot(data.x, data.y2, '--')

        plt.show()


if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()

    dir_path = filedialog.askdirectory()
    files = list(glob.glob(os.path.join(dir_path, 'test*.csv')))
    print(files[counter])
    Plot = Plotter(files[counter])

My issue is: When I press the button a new window opens instead of overwriting the plot.

Any suggestions?

[EDIT](I want to keep the previous code, as the error is different with this approach) My Code after the comment from DavidG looks like:

import os, pandas, glob
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog

counter = 0

class Plotter:
    def __init__(self, filepath):
        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('key_press_event', self.press)
        self.plotter(filepath)

    def press(self, event):
        print('press', event.key)
        global counter
        if event.key == 'down':
            if (counter + 1) < len(files):
                counter = counter + 1
                self.plotter(files[counter])

        if event.key == 'up':
            if not (counter - 1) < 0:
                counter = counter - 1
                self.plotter(files[counter])

    def plotter(self, filepath):
        data = pandas.read_csv(filepath)

        ax = self.fig.add_subplot(111)
        ax.plot(data.x, data.y1, 'r-')

        ax2 = ax.twinx()
        ax2.plot(data.x, data.y2, '--')

        plt.show()


if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()

    dir_path = filedialog.askdirectory()
    files = list(glob.glob(os.path.join(dir_path, 'test*.csv')))
    print(files[counter])
    Plot = Plotter(files[counter])

Here nothing happens when I press down or up.

ChrizZ
  • 47
  • 1
  • 7
  • 1
    On each call to `plotter` you create a new figure with `fig = plt.figure()`. You probably want to create one figure with subplots and then keep updating that figure – DavidG Apr 04 '18 at 18:22
  • I added an other approach, where I create a new figure only once. This time nothing happens, when I press a button. – ChrizZ Apr 04 '18 at 18:39
  • use `plt.savefig('single_image.png')`? – Morse Apr 04 '18 at 19:44

3 Answers3

0

I put together a simplified version of your code that will draw random lines when a button is pressed up or down. You don't need to re-define ax object each time, you need to clear it, it also seems necessary on my build to flush (if you are using a GUI)- (Efficient way to update matplotlib figure in gui? and redraw the canvas upon modifying the axes object (see comments in code).

import matplotlib.pyplot as plt
import Tkinter as tk
import numpy as np

class Plotter:
    def __init__(self, fig, ax):
        self.fig = fig
        self.ax = ax
        self.plotter(ax)
        self.fig.canvas.mpl_connect('key_press_event', self.press)

    def press(self, event):
        print('press', event.key)
        if event.key == 'down':
            self.plotter(self.ax)
            # flush and redraw
            fig.canvas.flush_events()
            fig.canvas.draw()

        if event.key == 'up':
            self.plotter(self.ax)
            # flush and redraw
            fig.canvas.flush_events()
            fig.canvas.draw()

    def plotter(self, ax):

        x1 = np.random.choice(range(0,5), 2)
        y1 = np.random.choice(range(0,5), 2)
        print x1, y1
        # clear the ax and use new data
        ax.clear()
        ax.plot(x1, y1, 'r-')


fig, ax = plt.subplots(1,1)
root = tk.Tk()
root.withdraw()
Plot = Plotter(fig, ax)
djakubosky
  • 907
  • 8
  • 15
0

You cannot call plt.show() more than once. Create your complete plot in the init method and show it. Only update the plot in the plotter method.

class Plotter:
    def __init__(self, filepath):
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)
        self.ax2 = ax.twinx()
        self.fig.canvas.mpl_connect('key_press_event', self.press)
        self.plotter(filepath)
        plt.show()

    def plotter(self, filepath):
        data = pandas.read_csv(filepath)
        self.ax.plot(data.x, data.y1, 'r-')
        self.ax2.plot(data.x, data.y2, '--')
        self.fig.canvas.draw()

Or even better create the plots themselves in the init method as well, only update them with new data.

class Plotter:
    def __init__(self, filepath):
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)
        self.ax2 = ax.twinx()
        self.line1, = self.ax.plot([], [], 'r-')
        self.line2, = self.ax2.plot([], [], '--')
        self.fig.canvas.mpl_connect('key_press_event', self.press)
        self.plotter(filepath)
        plt.show()

    def plotter(self, filepath):
        data = pandas.read_csv(filepath)
        self.line1.set_data(data.x, data.y1)
        self.line2.set_data(data.x, data.y2)
        self.fig.canvas.draw()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
0

I could solve the issue by simply adding a plt.draw() at the end of my plotter function.

So the Code looks like:

import os, pandas, glob
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog

counter = 0

class Plotter:
    def __init__(self, filepath):
        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('key_press_event', self.press)
        self.plotter(filepath)
        plt.show()

    def press(self, event):
        print('press', event.key)
        global counter
        if event.key == 'down':
            if (counter + 1) < len(files):
                counter = counter + 1
                self.plotter(files[counter])

        if event.key == 'up':
            if not (counter - 1) < 0:
                counter = counter - 1
                self.plotter(files[counter])

    def plotter(self, filepath):
        data = pandas.read_csv(filepath)
        print(files[counter])
        plt.clf()
        ax = self.fig.add_subplot(111)
        ax.plot(data.x, data.y1, 'r-')

        ax2 = ax.twinx()
        ax2.plot(data.x, data.y2, '--')
        fname = os.path.split(filepath)[1]
        ax.set_title(fname)
        plt.draw()
        plt.show()

if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()

    dir_path = filedialog.askdirectory()
    files = list(glob.glob(os.path.join(dir_path, 'test*.csv')))
    print(files[counter])
    Plot = Plotter(files[counter])

This should be basically the same code like in my second code snippet. Just with added plt.draw()

ChrizZ
  • 47
  • 1
  • 7