Diagramm in Abhängigkeit von Checkbuttons

Fragen zu Tkinter.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Man braucht kein i, weil das Wörterbuch den Namen enthält. Das war nur bei meinem Beispiel nicht notwendig. Wenn du den willst, dann änder die Schleife eben in

Code: Alles auswählen

for name, state in self._states.items():
Look Ma, no i.

Und mein Beispiel tut doch. Hast du das mal ausprobiert? Und danach ist dir ernsthaft nicht klar, wozu das value_changed gut sein könnte? Was suggeriert denn der Name? Und wie denkst du verhält sich das wohl zu deinem Wunsch, auf Änderungen reagieren zu können?

Dann noch zwei Meta-Anmerkungen: Bitte nicht immer den Post davor vollkommen zitieren. Der steht da schon. Und es ist immer etwas befremdlich, wenn jemand, der etwas nicht kann, denjenigen, die das tun, erklären will, wie aufwändig das denn ist. Woher nimmst du den Glauben, das du das beurteilen kannst?
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Code: Alles auswählen

class Plotting:
    def __init__(self, root, data):
        self.color_background = "#DFDFDF"
        self.root = root
        self.data = data

        self.frame_main = tk.Frame(self.root, bg=self.color_background)
        self.frame_main.place(relwidth=0.725, relheight=0.9, relx=0.25, rely=0.075)
        self.frame_main_check = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_check.place(relwidth=0.1, relheight=0.9, relx=0.875, rely=0.14)
        self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)

        self.names = list(self.data.columns.values)
        self.names.pop(0)
        self._states = {name: tk.BooleanVar(name=name) for name in self.names}

        btn_select_all = tk.Button(self.frame_main, text="Select all", height=1, width=10, padx=2, pady=2,
                                   fg="#22252D", bg="#F1F1EB", command= lambda: self.cb_select_all(states=self._states))
        btn_select_all.place(relx=0.875, rely=0.04)
        btn_deselect_all = tk.Button(self.frame_main, text="Deselect all", height=1, width=10, padx=2, pady=2,
                                     fg="#22252D", bg="#F1F1EB", command= lambda: self.cb_deselect_all(states=self._states))
        btn_deselect_all.place(relx=0.875, rely=0.08)

        for name, state in self._states.items():
            state.set(True)
            cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=name,
                                height=1, width=15, anchor=tk.W, bg=self.color_background)
            #i += 1
            cb.pack()
            state.trace_add("write", self._value_changed)

        self.fig = Figure(figsize=(7, 6), facecolor="#FFFFFF")
        self.ax = self.fig.add_subplot()

        Plotting.show_plot(self, data=self.data, fig=self.fig, ax=self.ax, states=self._states)

    def _value_changed(self, name, _, op):
        print(name, self._states[name].get())

    def cb_select_all(self, states):
        for state in states.values():
            state.set(True)
            state.trace_add("write", self._value_changed)
    #
    def cb_deselect_all(self, states):
        for state in states.values():
            state.set(False)
            state.trace_add("write", self._value_changed)

    def show_plot(self, data, fig, ax, states):
        data_plot = data.to_numpy()
        times = data_plot[:, 0]

        lines = []
        for state in states.values():
            print(state.get())
        i = 1
        for name, state in states.items():
            if state.get():
                graph, = ax.plot(times, data_plot[:, i], label=name, visible=True)
                i += 1
                lines.append(graph)
            else:
                continue
        ax.grid(True)
        ax.set_axisbelow(True)
        ax.set_xlabel("x", labelpad=0.5)
        ax.set_ylabel("f(x)", labelpad=0.5)
        ax.legend(fontsize="x-small", framealpha=1.0, bbox_to_anchor=(1.05, 1.205), borderaxespad=0,
                  bbox_transform=plt.gcf().transFigure, loc="upper right")
        fig.tight_layout()
        #
        self.canvas = FigureCanvasTkAgg(fig, master=self.frame_main_plot)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

        toolbar = NavigationToolbar2Tk(self.canvas, self.frame_main_plot)
        toolbar.config(background=self.color_background)
        toolbar._message_label.config(background=self.color_background)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
So, ich habe jetzt mal die Plotting Klasse überarbeitet. Das Diagramm wird nun auch ausgeworfen. Wenn ich an den Checkbuttons herumklicke, bekomme ich folgende Fehlermeldung, z.B. für den Datensatz mit der Sinuskurve:

Code: Alles auswählen

def _value_changed(self, name, _, op):
	print(name, self._states[name].get())
KeyError: 'sin'
Ich habe erst gedacht, dass es vielleicht daran lag, dass nicht die Variable "name" übergeben wurde, aber daran schien es wohl nicht zu liegen. An der Stelle ist mir leider auch nicht klar, was in den Argumenten "_" und "op" bedeuten bzw. welche die Rollen spielen sollen. Zur Erinnerung: in meinem Beispieldatensatz habe ich x-Werte sowie die y-Werte der Funktionen sin(x), cos(x) sowie sin(2x) und cos(2x). Bei den Checkbuttons für sin(x) und cos(x) werden auch diese beiden Funktionen aufgeführt bzw. nur "sin" und "cos". Machen die Klammern hier eventuell ein Problem?
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Nur für die Vollständigkeit und damit man auf einem aktuellen Stand ist, möchte ich hier ein letztes Mal den vollständigen Code posten, weil ich doch einiges hier und da verändert habe. Danach poste ich nur noch Code-Schnipsel, wo sich etwas verändert hat.

Code: Alles auswählen

# MODULES
import tkinter as tk
import tkinter.filedialog as fd
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure

# PROGRAM
class MainApplication(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent

        canvas = tk.Canvas(parent, height=768, width=1024)
        canvas.pack()

        self.list_csv = []

        # Buttons
        btn_add = tk.Button(parent, text="Add", height=1, width=6, padx=2, pady=2, fg="#22252D", bg="#F1F1EB",
                            command=self.open_csv)
        btn_add.place(relx=0.025, rely=0.025)
        btn_delete = tk.Button(parent, text="Delete", height=1, width=6, padx=2, pady=2, fg="#22252D", bg="#F1F1EB",
                               command=self.delete_csv)
        btn_delete.place(relx=0.1, rely=0.025)

        # Listbox
        self.frame_listbox = tk.Frame(parent, bg="#DFDFDF")
        self.frame_listbox.place(relwidth=0.2, relheight=0.9, relx=0.025, rely=0.075)
        self.data_listbox = self.create_list()
        self.data_listbox.bind("<Double-1>", self.plot_csv)

    def create_list(self):
        listbox = tk.Listbox(self.frame_listbox, bg="#DFDFDF", width=25, height=17)
        scrollbar_y = tk.Scrollbar(self.frame_listbox, orient="vertical")
        scrollbar_x = tk.Scrollbar(self.frame_listbox, orient="horizontal")
        listbox.config(yscrollcommand = scrollbar_y.set, xscrollcommand = scrollbar_x.set)
        scrollbar_y.config(command = listbox.yview)
        scrollbar_x.config(command = listbox.xview)
        scrollbar_x.pack(side="bottom", fill="x")
        listbox.pack(side="left", fill="both")
        scrollbar_y.pack(side="right", fill="y")
        return listbox

    def open_csv(self):
        name = fd.askopenfilenames(parent=self.parent, filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
        for i in name:
            if i not in self.list_csv:
                self.list_csv.append(i)
                file_parts = i.split("/")
                self.data_listbox.insert(tk.END, file_parts[-1])
        return name

    def delete_csv(self):
        item = self.data_listbox.curselection()
        self.list_csv.remove(self.list_csv[item[0]])
        self.data_listbox.delete(tk.ANCHOR)
        return list_csv

    def plot_csv(self, event):
        id = self.data_listbox.curselection()
        df = pd.read_csv(self.list_csv[id[0]], sep=";", header=0, skipfooter=0, engine="python")
        Plotting(root=root, data=df)

class Plotting:
    def __init__(self, root, data):
        self.color_background = "#DFDFDF"
        self.root = root
        self.data = data

        self.frame_main = tk.Frame(self.root, bg=self.color_background)
        self.frame_main.place(relwidth=0.775, relheight=0.9, relx=0.225, rely=0.075)
        self.frame_main_check = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_check.place(relwidth=0.1, relheight=0.9, relx=0.875, rely=0.1)
        self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)

        self.names = list(self.data.columns.values)
        self.names.pop(0)
        self._states = {name: tk.BooleanVar(name=name) for name in self.names}

        btn_select_all = tk.Button(self.frame_main, text="Select all", height=1, width=12, padx=1, pady=1,
                                   fg="#22252D", bg="#F1F1EB",
                                   command= lambda: self.cb_select_all(states=self._states))
        btn_select_all.place(relx=0.875, rely=0.0)
        btn_deselect_all = tk.Button(self.frame_main, text="Deselect all", height=1, width=12, padx=1, pady=1,
                                     fg="#22252D", bg="#F1F1EB",
                                     command= lambda: self.cb_deselect_all(states=self._states))
        btn_deselect_all.place(relx=0.875, rely=0.04)

        for name, state in self._states.items():
            state.set(True)
            cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=name,
                                height=1, width=15, anchor=tk.W, bg=self.color_background)
            cb.pack()
            print(name, state.get())
            state.trace_add("write", self._value_changed)

        self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
        self.ax = self.fig.add_subplot()

        Plotting.show_plot(self, data=self.data, fig=self.fig, ax=self.ax, states=self._states)

    def _value_changed(self, name, _, op):
        print(name, self._states[name].get())

    def cb_select_all(self, states):
        for state in states.values():
            state.set(True)
            state.trace_add("write", self._value_changed)
    #
    def cb_deselect_all(self, states):
        for state in states.values():
            state.set(False)
            state.trace_add("write", self._value_changed)

    def show_plot(self, data, fig, ax, states):
        data_plot = data.to_numpy()
        times = data_plot[:, 0]

        i = 1
        for name, state in states.items():
            if state.get():
                ax.plot(times, data_plot[:, i], label=name, visible=True)
                i += 1
            else:
                continue
        ax.grid(True)
        ax.set_axisbelow(True)
        ax.set_xlabel("x", labelpad=0.5)
        ax.set_ylabel("f(x)", labelpad=0.5)
        ax.legend(fontsize="x-small", framealpha=1.0, bbox_to_anchor=(0.125, 0.02), loc=3, borderaxespad=0,
                  bbox_transform=plt.gcf().transFigure, ncol=4)
        fig.tight_layout()
        #
        self.canvas = FigureCanvasTkAgg(fig, master=self.frame_main_plot)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

        toolbar = NavigationToolbar2Tk(self.canvas, self.frame_main_plot)
        toolbar.config(background=self.color_background)
        toolbar._message_label.config(background=self.color_background)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

# EXECUTION
if __name__ == "__main__":
    root = tk.Tk()
    root.title("Tkinter - Plotting csv files")
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Ich habe alternativ zur Klasse MainApplication eine Funktion geschrieben, um da etwas näher am Code-Beispiel von __deets__ zu sein. An der noch bestehenden Fehlermeldung im Zusammenhang mit der Funktion _value_changed hat sich allerdings nichts geändert.

Code: Alles auswählen

def main():

    def create_list():
        # Listbox
        frame_listbox = tk.Frame(root, bg="#DFDFDF")
        frame_listbox.place(relwidth=0.2, relheight=0.9, relx=0.025, rely=0.075)

        listbox = tk.Listbox(frame_listbox, bg="#DFDFDF", width=25, height=17)
        scrollbar_y = tk.Scrollbar(frame_listbox, orient="vertical")
        scrollbar_x = tk.Scrollbar(frame_listbox, orient="horizontal")
        listbox.config(yscrollcommand = scrollbar_y.set, xscrollcommand = scrollbar_x.set)
        scrollbar_y.config(command = listbox.yview)
        scrollbar_x.config(command = listbox.xview)
        scrollbar_x.pack(side="bottom", fill="x")
        listbox.pack(side="left", fill="both")
        scrollbar_y.pack(side="right", fill="y")
        return listbox

    def open_csv():
        name = fd.askopenfilenames(parent=root, filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
        for i in name:
            if i not in list_csv:
                list_csv.append(i)
                file_parts = i.split("/")
                data_listbox.insert(tk.END, file_parts[-1])
        return name

    def delete_csv():
        item = data_listbox.curselection()
        list_csv.remove(list_csv[item[0]])
        data_listbox.delete(tk.ANCHOR)
        return list_csv

    def plot_csv(event):
        id = data_listbox.curselection()
        df = pd.read_csv(list_csv[id[0]], sep=";", header=0, skipfooter=0, engine="python")
        Plotting(root=root, data=df)

    root = tk.Tk()
    root.title("Tkinter - Plotting csv files")
    list_csv = []
    data_listbox = []

    canvas = tk.Canvas(root, height=768, width=1024)
    canvas.pack()

    # Buttons
    btn_add = tk.Button(root, text="Add", height=1, width=6, padx=2, pady=2, fg="#22252D", bg="#F1F1EB",
                        command=open_csv)
    btn_add.place(relx=0.025, rely=0.025)
    btn_delete = tk.Button(root, text="Delete", height=1, width=6, padx=2, pady=2, fg="#22252D", bg="#F1F1EB",
                           command=delete_csv)
    btn_delete.place(relx=0.1, rely=0.025)

    data_listbox = create_list()
    data_listbox.bind("<Double-1>", plot_csv)

    root.mainloop()

Code: Alles auswählen

if __name__ == "__main__":
    main()
Ich habe mich gefragt, ob es evtl. damit zusammenhängt, dass die Plotting Klasse bei mir event-gebunden ist, allerdings hoffe ich doch, dass es auch auf diese Art und Weise möglich ist, die gewünschte Ausgabe in Abhängigkeit von den Checkbuttons zu erzeugen.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

So, die Plotting Klasse habe ich jetzt auch nochmal etwas überarbeitet und zwar die Initialisierung.

Code: Alles auswählen

class Plotting:
    def __init__(self, root, data):
        self.color_background = "#DFDFDF"
        self.root = root
        self.data = data
        self.times = self.data["x"]

        self.frame_main = tk.Frame(self.root, bg=self.color_background)
        self.frame_main.place(relwidth=0.775, relheight=0.9, relx=0.225, rely=0.075)
        self.frame_main_check = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_check.place(relwidth=0.1, relheight=0.9, relx=0.875, rely=0.1)
        self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
        self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)

        self.names = list(self.data.columns.values)
        self.names.pop(0)
        self._states = {name: tk.BooleanVar(name=name) for name in self.names}
        self._values = {name: self.data[name] for name in self.names}

        btn_select_all = tk.Button(self.frame_main, text="Select all", height=1, width=12, padx=1, pady=1,
                                   fg="#22252D", bg="#F1F1EB",
                                   command= lambda: self.cb_select_all(states=self._states))
        btn_select_all.place(relx=0.875, rely=0.0)
        btn_deselect_all = tk.Button(self.frame_main, text="Deselect all", height=1, width=12, padx=1, pady=1,
                                     fg="#22252D", bg="#F1F1EB",
                                     command= lambda: self.cb_deselect_all(states=self._states))
        btn_deselect_all.place(relx=0.875, rely=0.04)

        self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
        self.ax = self.fig.add_subplot()

        for name, state in self._states.items():
            state.set(True)
            cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=name,
                                height=1, width=15, anchor=tk.W, bg=self.color_background)
            cb.pack()
            state.trace_add("write", self._value_changed)
            if state.get():
                self.ax.plot(self.times, self._values[name], label=name, visible=True)
            else:
                continue

        self.ax.grid(True)
        self.ax.set_axisbelow(True)
        self.ax.set_xlabel("x", labelpad=0.5)
        self.ax.set_ylabel("f(x)", labelpad=0.5)
        self.ax.legend(fontsize="x-small", framealpha=1.0, bbox_to_anchor=(0.125, 0.02), loc=3, borderaxespad=0,
                  bbox_transform=plt.gcf().transFigure, ncol=4)
        self.fig.tight_layout()
        #
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame_main_plot)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2Tk(self.canvas, self.frame_main_plot)
        toolbar.config(background=self.color_background)
        toolbar._message_label.config(background=self.color_background)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
Leider weiß ich immer noch nicht, wie ich den letzten Schritt bzw. den nach wie vor existierenden Fehler beheben kann. Ich habe mir mal nur "name" am Anfang der _value_changed Funktion ausgeben lassen. Warum auch immer geht an dieser Stelle ein Teil des Namens verloren, d.h. aus beispielsweise "sin(x)", was zuvor korrekt ausgegeben wurde in der for-Schleife, wird nun nur noch "sin". Wenn ich aus "name" in der for-Schleife mal "self.name" mache, dann gibt es zwar keine Fehlermeldung mehr, allerdings wird mir dann nur noch "cos(2x)" ausgeworfen, also der letzte der vier Datensätze aus dem Datenbeispiel. Der Zustand der Variable ändert sich daher auch nur, wenn ich den Checkbutton zu "cos(2x)" anklicke. Leider wird die Kurve dennoch geplottet.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich vermute, dass einem hier tkinter einen Strich durch die Rechnung macht. Denn dieser Name darf sowas wie (x) augenscheinlich nicht enthalten. Was natuerlich bloed ist. Aber eine Einschraenkung des unterliegenden TCL-Intepreters ist.

Damit man das umgeht, kann man auch einfach so arbeiten:

Code: Alles auswählen

import tkinter as tk
from functools import partial


class Plotting:
    def __init__(self, root, names):
        self._states = {name: tk.BooleanVar(name=name) for name in names}
        for name, state in self._states.items():
            cb = tk.Checkbutton(
                master=root,
                variable=state,
                )
            cb.pack()
            state.trace_add("write", partial(self._value_changed, name))

    def _value_changed(self, real_name, _name, _, op):
        print(real_name, self._states[real_name].get())


def main():
    names = ["Peter", "Maria", "Paul", "Judith", "sin()"]
    root = tk.Tk()
    root.title("tkinter_diagram")
    plotting = Plotting(root, names)
    root.mainloop()


if __name__ == '__main__':
    main()
Und das du mit self.name immer den letzten Namen bekommst - ja, nun. Das ist ja nicht verwunderlich. Was sonst sollte denn deiner Meinung nach passieren? Woher soll Python wissen, dass du magisch den "richtigen" Namen meinst?
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Ich habe in dem Datensatz nun mal die Spalten umbenannt, also z.B. aus sin(x) sinX gemacht. Tatsächlich machen da die Klammern wohl Probleme. Leider wird aber der Plot nicht aktualisiert. Wenn ich vor der if-Bedingung den Zustand abfrage, wird mir richtigerweise False ausgegeben, wenn der Checkbutton nicht ausgewählt ist, aber die Kurve ist dennoch da. Ich vermute mal, dass man über else den Plot wieder löschen müsste?
Naja, "sin()" bringt mir ja nichts, wenn ich eventuell verschiedenen Sinus-Kurven mit unterschliedlichen Argumenten plotten möchte.

Code: Alles auswählen

        for name, state in self._states.items():
            state.set(True)
            cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=name,
                                height=1, width=15, anchor=tk.W, bg=self.color_background)
            cb.pack()
            state.trace_add("write", self._value_changed)
            if state.get():
                self.ax.plot(self.times, self._values[name], label=name, visible=True)
            else:
                self.fig.clear()
                self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
                self.ax = self.fig.add_subplot()
                for state in self._states.values():
                    print("Test", name, state.get())
                    if state.get():
                        self.ax.plot(self.times, self._values[name], label=name, visible=True)
Joa, keine Ahnung, was das Problem ist, dass obwohl mir korrekterweise False ausgegeben wird bei deaktivieren eines Buttons, dennoch nicht die else Bedingung greift.

Nachtrag: Ich habe mal state.get() nach state.trace_add("write", self._value_changed) ausgeben lassen. Der Wert bleibt auf True bzw. er wird nur einmalig ausgegeben. Kann state.trace_add("write", self._value_changed) den neuen Zustand irgendwie zurückgeben oder muss ich den Plotting Kram jetzt in diese Funktion packen?
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Joa, also eine hässliche Lösung des Problems wäre folgendes:

Code: Alles auswählen

    def _value_changed(self, name, _, op):
        if self._states[name].get():
            self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
            self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)
            self.fig.clear()
            self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
            self.ax = self.fig.add_subplot()
            for name, state in self._states.items():
                if state.get():
                    self.ax.plot(self.times, self._values[name], label=name, visible=True)
        else:
            self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
            self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)
            self.fig.clear()
            self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
            self.ax = self.fig.add_subplot()
            for name, state in self._states.items():
                if state.get():
                    self.ax.plot(self.times, self._values[name], label=name, visible=True)
        self.ax.grid(True)
        self.ax.set_axisbelow(True)
        self.ax.set_xlabel("x", labelpad=0.5)
        self.ax.set_ylabel("f(x)", labelpad=0.5)
        self.ax.legend(fontsize="x-small", framealpha=1.0, bbox_to_anchor=(0.125, 0.02), loc=3, borderaxespad=0,
                  bbox_transform=plt.gcf().transFigure, ncol=4)
        self.fig.tight_layout()
        #
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame_main_plot)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2Tk(self.canvas, self.frame_main_plot)
        toolbar.config(background=self.color_background)
        toolbar._message_label.config(background=self.color_background)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
Vom Ergebnis macht es das, was es soll, aber das geht sicher auch kompakter und vor allem effizienter, da mit jedem Klicken das Plotten immer langsamer wird. Ich wäre dankbar, wenn da jemand eine kompaktere Version präsentieren würde. Eigentlich wäre es schön, wenn mit dem deaktivieren eines Checkbuttons einfach nur die Kurve verschwinden würde, anstatt das für eine Millisekunde kurz alles verschwinden würde, weil das komplette Diagramm neu gezeichnet wird.
Zuletzt geändert von Kurtosis am Sonntag 30. Mai 2021, 18:48, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das es langsamer wird, wird daran liegen, dass du einfach immer alles neu machst, und nichts vernuenftig abraeumst.

Und dein Wunsch nach einem partiellen Update wird IMHO nicht realisierbar sein, so funktioniert matplotlib einfach nicht. Das kann nur additiv. Da muesstest du andere Tools benutzen, wie pychart oder D3.js im Browser oder so.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Wie würde der Code denn aussehen, wenn er "vernünftig abgeräumt" wäre?
Ok, schade. Ne, Browser Lösungen kommen für mich in dem Fall nicht in Frage. Es muss in Tkinter gemacht werden. Die Performance geht eigentlich bis zu dem Zeitpunkt, wo ich einen der beiden Buttons "select all" bzw. "deselect all" betätigt habe. Ich vermute mal, dass es daran liegt, dass ich in einer for-Schleife die value_changed Funktion laufen lasse und dabei jedes Mal neu geplottet wird. Das ist noch nicht optimal und wird noch geändert.

Nachtrag: Ich habe jetzt mal die value_changed Funktion aus der for-Schleife rausgeholt. Die Performance ist nun deutlich besser, aber eigentlich ist es immer noch unbefriedigend.

Code: Alles auswählen

    def _value_changed(self, name, _, op):
        if self._states[name].get():
            self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
            self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)
            self.fig.clear()
            self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
            self.ax = self.fig.add_subplot()
            for name, state in self._states.items():
                if state.get():
                    self.ax.plot(self.times, self._values[name], label=name, visible=True)
        else:
            self.frame_main_plot = tk.Frame(self.frame_main, bg=self.color_background)
            self.frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)
            self.fig.clear()
            self.fig = Figure(figsize=(1,2), facecolor="#FFFFFF")
            self.ax = self.fig.add_subplot()
            for name, state in self._states.items():
                if state.get():
                    self.ax.plot(self.times, self._values[name], label=name, visible=True)
        self.ax.grid(True)
        self.ax.set_axisbelow(True)
        self.ax.set_xlabel("x", labelpad=0.5)
        self.ax.set_ylabel("f(x)", labelpad=0.5)
        self.ax.legend(fontsize="x-small", framealpha=1.0, bbox_to_anchor=(0.125, 0.02), loc=3, borderaxespad=0,
                  bbox_transform=plt.gcf().transFigure, ncol=4)
        self.fig.tight_layout()
        #
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame_main_plot)
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2Tk(self.canvas, self.frame_main_plot)
        toolbar.config(background=self.color_background)
        toolbar._message_label.config(background=self.color_background)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    def cb_select_all(self, states):
        for state in states.values():
            state.set(True)
        state.trace_add("write", self._value_changed)
    #
    def cb_deselect_all(self, states):
        for state in states.values():
            state.set(False)
        state.trace_add("write", self._value_changed)
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Hinsichtlich des Performance Aspekts brauchte ich wirklich konkrete Hilfe. Mir ist eben aufgefallen, dass wenn ich mal verschiedene csv-Dateien einlese und dann zwischen diesen munter hin und her wechsle, dass mit jedem Datensatzwechsel das Plotten immer langsamer geht. Ich vermute mal, dass es eine Art Matplotlib Cache gibt, der immer weiter vollgemüllt wird dabei.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Das hatte ich dir doch schon vor Tagen gesagt, dass du immer mehr Frames übereinander zeichnest. Du darfst nur gleich zu Beginn einen Frame erzeugen und diesen immer nur updaten.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Wieso gibt es dann im Internet diverse "Lösungen" und Minimalbeispiele, wo mit Subframes gearbeitet wird? Okay gut, wie wird dieser denn dann geupdatet?
Antworten