Plotting to matplotlib from a function

Hey guys,

I am new to Python programming and want to built a simple tool do analyze measuring data.
For this I have written some code which in most parts works as I expect.

But the plotting of the data does not work. When I press the “plot” button to call the plot_data() function my data is not showing up although the code is running (i.e. print to console works).
I have tested the code for plotting previously stand-alone and it works fine. Even if I print the data from terz_pd and the x & y values to console it seems fine.

I think there is an issue where either: the figure / subplot2 needs to be updated or the function “plot_data” can not “access/refer to” subplot2? But there are now issues / errors shown in the consol.

Thanks for your help!

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import os
from tkinter import filedialog
import numpy as np
import pandas as pd
import re

# initialise global lists
channel_name = []
channel_unit = []

def import_proj():
    proj_path = filedialog.askopenfilename(title='Mars Projekt-Datei auswählen', filetypes=[('project', '*.proj'), ('All Files', '*')])
    global dir_path
    dir_path = os.path.dirname(proj_path)
    print(dir_path)

    # read bin files from directory
    bin_files = []
    if len(bin_files) > 0:                                                   # empty list with bin files and listbox_bin if it was used previously
        bin_files.clear()
        listbox_bin.delete(0,"end")
    for file in os.listdir(dir_path):
        if file.endswith('.bin'):
            bin_files.append(file)
    for i in bin_files:
        listbox_bin.insert("end", i)
    root.title("MarsMaster5000"+"---"+dir_path)
    pass

def plot_data():
    ## import Terz.txt-file
    terz_pd = pd.read_csv(terz_path, header=1, delimiter=';', engine='python', decimal=",")
    num_datasets = int((len(terz_pd) - 2) / 26)
    lines_per_dataset = 26
    x = terz_pd.iloc[1:26, 0].astype(float)  # Terzbänder
    for j in range(num_datasets):
        subplot2.plot(x, terz_pd.iloc[1 + j * lines_per_dataset:26 + j * lines_per_dataset, channel_to_plot+1].astype(float), label="1")
    pass


def on_select_bin(event):
    selected_item = listbox_bin.get(listbox_bin.curselection()).split(".")      # generate path to header
    global hdr_path
    global bin_path
    global terz_path
    hdr_path = dir_path + "/" + selected_item[0][0:19] + ".hdr"                 # read header and split into list
    bin_path = dir_path + "/" + selected_item[0] + "." + selected_item[1]
    terz_path = dir_path + "/Terzen/" + selected_item[0] + "_Terzen.txt"
    with open(hdr_path, 'r') as file:
        line = file.readline()
        line = re.split(r';|:', line)
        channel_number = int(line[3])
    global channel_name
    if len(channel_name) > 0:                                                   # empty list with channel names + channel_units and listbox_channel if it was used previously
        channel_name.clear()
        channel_unit.clear()
        listbox_channel.delete(0,"end")
    i = 0
    while i <= channel_number-1:                                                # get channel names
        channel_name.insert(i,line[5+4*i])
        channel_unit.insert(i,line[6+4*i])
        i = i+1
    i = 0
    for i in channel_name:
        listbox_channel.insert("end", i)
    pass

def on_select_channel(event):
    selected_item = listbox_channel.get(listbox_channel.curselection())
    global channel_to_plot
    channel_to_plot = channel_name.index(selected_item)
    print(channel_name[channel_to_plot])
    pass

root = tk.Tk()
root.title("MarsMaster5000")

import_button = tk.Button(root, text="Import Mars-Projekt", command=import_proj)
import_button.grid(row=0, column=0, padx=5, pady=5)

listbox_bin = tk.Listbox(root, width=40, height=5, exportselection = False)
listbox_bin.grid(row=0, column=1, padx=5, pady=5)
listbox_bin.bind("<<ListboxSelect>>", on_select_bin)

scrollbar_bin = tk.Scrollbar(root, orient="vertical", command=listbox_bin.yview)
scrollbar_bin.grid(row=0, column=2, sticky="ns")
listbox_bin.config(yscrollcommand=scrollbar_bin.set)

listbox_channel = tk.Listbox(root, width=20, height=5, exportselection = False)
listbox_channel.grid(row=0, column=3, padx=5, pady=5)
listbox_channel.bind("<<ListboxSelect>>", on_select_channel)

scrollbar_channel = tk.Scrollbar(root, orient="vertical", command=listbox_channel.yview)
scrollbar_channel.grid(row=0, column=4, sticky="ns")
listbox_channel.config(yscrollcommand=scrollbar_channel.set)

plot_button = tk.Button(root, text="Plot", command=plot_data)
plot_button.grid(row=0, column=5, padx=5, pady=5)

figure1 = plt.Figure(figsize=(6, 2), dpi=100)
subplot1 = figure1.add_subplot(111)
canvas1 = FigureCanvasTkAgg(figure1, root)
canvas1.get_tk_widget().grid(row=1, column=0, columnspan=6, padx=5, pady=5)
subplot1.set_title("Zeitschrieb")

figure2 = plt.Figure(figsize=(6, 4), dpi=100)
subplot2 = figure2.add_subplot(111)
canvas2 = FigureCanvasTkAgg(figure2, root)
canvas2.get_tk_widget().grid(row=2, column=0, columnspan=6, padx=5, pady=5)
subplot2.set_title("Terzspektrum")
# subplot2.xlabel('Frequenz')
# subplot2.ylabel('Schwinggeschwindigkeit')
# subplot2.xscale('log')
# subplot2.yscale('log')

root.mainloop()

You say that there are errors, but not what those errors are, and I’m not able to test it myself because I don’t have any data to plot, although I did find that if I click the “Plot” button without importing, it complains that terz_path isn’t defined. (I’d suggest that you disable the plot button until it has imported some data.)

Please post the complete traceback wrapped in backticks:

```
(traceback lines)
```

That was a typo on my side - I wanted to say “there is no error”.

I have provided sample data here:
Data from GoogleDrive

Here is the - for now without plotting - code that works (I think the code I provided in the original post may have an error from tinkering around…)

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import os
from tkinter import filedialog
import numpy as np
import pandas as pd
import re

# initialise global lists
channel_name = []
channel_unit = []

def import_proj():
    proj_path = filedialog.askopenfilename(title='Mars Projekt-Datei auswählen', filetypes=[('project', '*.proj'), ('All Files', '*')])
    global dir_path
    dir_path = os.path.dirname(proj_path)
    print(dir_path)

    # read bin files from directory
    bin_files = []
    if len(bin_files) > 0:                                                   # empty list with bin files and listbox_bin if it was used previously
        bin_files.clear()
        listbox_bin.delete(0,"end")
    for file in os.listdir(dir_path):
        if file.endswith('.bin'):
            bin_files.append(file)
    for i in bin_files:
        listbox_bin.insert("end", i)
    root.title("MarsMaster5000"+"---"+dir_path)
    pass

def plot_data():
    terz_pd = pd.read_csv(terz_path, header=1, delimiter=';', engine='python', decimal=",")
    print(terz_pd)
    num_datasets = int((len(terz_pd) - 2) / 26)
    lines_per_dataset = 26
    x = terz_pd.iloc[1:26, 0].astype(float)
    for j in range(num_datasets):
        subplot2.plot(x, terz_pd.iloc[1 + j * lines_per_dataset:26 + j * lines_per_dataset, channel_to_plot+1].astype(float), label="1")
    pass


def on_select_bin(event):
    selected_item = listbox_bin.get(listbox_bin.curselection()).split(".")      # generate path to header
    global hdr_path
    global bin_path
    global terz_path
    hdr_path = dir_path + "/" + selected_item[0][0:19] + ".hdr"                 # read header and split into list
    bin_path = dir_path + "/" + selected_item[0] + "." + selected_item[1]
    terz_path = dir_path + "/Terzen/" + selected_item[0] + "_Terzen.txt"
    with open(hdr_path, 'r') as file:
        line = file.readline()
        line = re.split(r';|:', line)
        channel_number = int(line[3])
    global channel_name
    if len(channel_name) > 0:                                                   # empty list with channel names + channel_units and listbox_channel if it was used previously
        channel_name.clear()
        channel_unit.clear()
        listbox_channel.delete(0,"end")
    i = 0
    while i <= channel_number-1:                                                # get channel names
        channel_name.insert(i,line[5+4*i])
        channel_unit.insert(i,line[6+4*i])
        i = i+1
    i = 0
    for i in channel_name:
        listbox_channel.insert("end", i)
    pass

def on_select_channel(event):
    selected_item = listbox_channel.get(listbox_channel.curselection())
    global channel_to_plot
    channel_to_plot = channel_name.index(selected_item)
    print(channel_name[channel_to_plot])
    pass

root = tk.Tk()
root.title("MarsMaster5000")

import_button = tk.Button(root, text="Import Mars-Projekt", command=import_proj)
import_button.grid(row=0, column=0, padx=5, pady=5)

listbox_bin = tk.Listbox(root, width=40, height=5, exportselection = False)
listbox_bin.grid(row=0, column=1, padx=5, pady=5)
listbox_bin.bind("<<ListboxSelect>>", on_select_bin)

scrollbar_bin = tk.Scrollbar(root, orient="vertical", command=listbox_bin.yview)
scrollbar_bin.grid(row=0, column=2, sticky="ns")
listbox_bin.config(yscrollcommand=scrollbar_bin.set)

listbox_channel = tk.Listbox(root, width=20, height=5, exportselection = False)
listbox_channel.grid(row=0, column=3, padx=5, pady=5)
listbox_channel.bind("<<ListboxSelect>>", on_select_channel)

scrollbar_channel = tk.Scrollbar(root, orient="vertical", command=listbox_channel.yview)
scrollbar_channel.grid(row=0, column=4, sticky="ns")
listbox_channel.config(yscrollcommand=scrollbar_channel.set)

plot_button = tk.Button(root, text="Plot", command=plot_data)
plot_button.grid(row=0, column=5, padx=5, pady=5)

figure1 = plt.Figure(figsize=(6, 2), dpi=100)
subplot1 = figure1.add_subplot(111)
canvas1 = FigureCanvasTkAgg(figure1, root)
canvas1.get_tk_widget().grid(row=1, column=0, columnspan=6, padx=5, pady=5)
subplot1.set_title("Zeitschrieb")

figure2 = plt.Figure(figsize=(6, 4), dpi=100)
subplot2 = figure2.add_subplot(111)
canvas2 = FigureCanvasTkAgg(figure2, root)
canvas2.get_tk_widget().grid(row=2, column=0, columnspan=6, padx=5, pady=5)
subplot2.set_title("Terzspektrum")

# subplot2.xlabel('Frequenz')
# subplot2.ylabel('Schwinggeschwindigkeit')
# subplot2.xscale('log')
# subplot2.yscale('log')

root.mainloop()

Well, that was interesting… :slight_smile:

Basically, you need to recreate the canvas when you plot the data.

It’s strange that it appears you can plot just fine right after creating the plot (if you see what I mean), but not after you’ve called root.mainloop().

Oh, and it does leak memory each time, but I’ve read that tkinter does that anyway when you try to destroy widgets.

def plot_data():
    terz_pd = pd.read_csv(terz_path, header=1, delimiter=';', engine='python', decimal=",")
    print(terz_pd)
    num_datasets = int((len(terz_pd) - 2) / 26)
    lines_per_dataset = 26
    x = terz_pd.iloc[1:26, 0].astype(float)

    for j in range(num_datasets):
        subplot2.plot(x, terz_pd.iloc[1 + j * lines_per_dataset:26 + j * lines_per_dataset, channel_to_plot+1].astype(float), label="1")

    global canvas2
    canvas2.get_tk_widget().destroy()
    canvas2 = FigureCanvasTkAgg(figure2, root)
    canvas2.get_tk_widget().grid(row=2, column=0, columnspan=6, padx=5, pady=5)

Thank you! That was exactly what I was looking for - works perfectly now!