Seite 2 von 2

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Sonntag 30. Mai 2021, 17:25
von __deets__
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?

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Sonntag 30. Mai 2021, 17:37
von Kurtosis
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?

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Sonntag 30. Mai 2021, 18:26
von Kurtosis
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.

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Sonntag 30. Mai 2021, 18:45
von __deets__
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.

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Sonntag 30. Mai 2021, 18:54
von Kurtosis
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)

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Montag 31. Mai 2021, 01:16
von Kurtosis
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.

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Montag 31. Mai 2021, 05:38
von Sirius3
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.

Re: Diagramm in Abhängigkeit von Checkbuttons

Verfasst: Montag 31. Mai 2021, 23:43
von Kurtosis
Wieso gibt es dann im Internet diverse "Lösungen" und Minimalbeispiele, wo mit Subframes gearbeitet wird? Okay gut, wie wird dieser denn dann geupdatet?