Diagramm in Abhängigkeit von Checkbuttons

Fragen zu Tkinter.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Hallo,
ich möchte in Tkinter innerhalb eines Frames einen Subframe haben, indem ein Diagramm geplottet wird, und innerhalb eines zweiten Subframes eine vom Datensatz abhängig lange Liste an Checkbuttons haben. Angenommen der Datensatz beinhaltet die x und y Werte der trigonometrischen Funktionen sin(x), cos(x) sowie sin(2x) und cos(2x), dann sollen in diesem zweiten Subframe vier Checkbuttons mit dem passenden Label erscheinen. Was mir bisher noch nicht gelungen ist, ist die beiden Subframes miteinander zu verknüpfen, d.h. es ändert sich nichts am Diagramm, wenn ich die Checkbuttons ein- und ausschalte.

Bevor ich jetzt mit einem Code-Beispiel komme, möchte ich erstmals allgemein fragen, wie hier vorgegangen werden muss. Definiere ich innerhalb der __init__-Methode nur die beiden Subframes oder sollte dort auch schon etwas geplottet werden bzw. die Checkbutton-Liste erstellt werden?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Man macht dort etwas, wo es sinnvoll ist, es zu tun. Wenn Du also ein Fenster willst, mit einem Plot und einer Checkbutton-Liste, dann ist es sinnvoll, das schon beim Initialisieren des Fensters zu machen.

So ganz allgemein können wir nicht sagen, was Du falsch machst, wenn wir den Code nicht kennen.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Ich werde später oder morgen mal ein Minimalbeispiel erstellen. Das ist mein erstes Tkinter-Projekt, von daher sei mir verziehen, dass ich nicht alles auf Anhieb direkt hinbekomme. Das einfach nur so zur Einordnung. Aber wie gesagt, ich schaue, dass ich später oder spätestens morgen mal ein Code-Beispiel hier poste.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Ich habe mal das folgende Code-Beispiel erstellt.

Code: Alles auswählen

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

## FUNCTIONS
list_csv = []
cb_list = []
cb_list_updated = []

def create_list(parent):
    listbox = tk.Listbox(parent, bg="#DFDFDF", width=25, height=17)
    scrollbar_y = tk.Scrollbar(parent, orient="vertical")
    scrollbar_x = tk.Scrollbar(parent, 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")
    show_data(data=df)

def show_data(data):
        names = list(data.columns.values)
        build_checkbox_list(names=names)

        data_plot = data.to_numpy()
        x = data_plot[:, 0]

        fig = Figure(figsize=(6, 4))
        ax = fig.add_subplot()
        lines = []
        for i in range(1, len(data_plot[0])):
            graph, = ax.plot(x, data_plot[:, i], label=names[i], visible=True)
            lines.append(graph)
        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.01, 1), borderaxespad=0)
        fig.tight_layout()

        canvas = FigureCanvasTkAgg(fig, master=frame_main_plot)
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

        toolbar = NavigationToolbar2Tk(canvas, frame_main_plot)
        toolbar.update()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

def build_checkbox_list(names):
    def select_all():
        for i in cb_list:
            i.select()
        cb_list_updated = cb_list_all
    def deselect_all():
        for i in cb_list:
            i.deselect()
        cb_list_updated = []
    def cb_select():
        print(states[i].get())
        print(cb.cget("text"))
        cb_list_updated.remove(cb.cget("text"))
        print(cb_list_updated)
    cb_list = []
    cb_list_updated = []
    states = [tk.BooleanVar() for i in range(len(names[1:]))]
    for i in range(len(names[1:])):
        states[i].set(True)
        cb = tk.Checkbutton(master=frame_main_check, text=names[1:][i], variable=states[i], height=1,
                       width=15, onvalue=1, offvalue=0, anchor=tk.W, command=cb_select)
        cb.pack()
        cb_list.append(cb)
        cb_list_updated.append(names[1:][i])
    for item in cb_list:
        item.select()
    cb_list_all = cb_list_updated
    #
    btn_select_all = tk.Button(frame_main_diagram, text="Select all", height=1, width=10, padx=2, pady=2, fg="#22252D", bg="#F1F1EB", command=select_all)
    btn_select_all.place(relx=0.875, rely=0.04)
    btn_deselect_all = tk.Button(frame_main_diagram, text="Deselect all", height=1, width=10, padx=2, pady=2, fg="#22252D", bg="#F1F1EB", command=deselect_all)
    btn_deselect_all.place(relx=0.875, rely=0.08)

## GUI
root = tk.Tk()
root.title("tkinter_diagram")

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)

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

# Diagram
frame_main_diagram = tk.Frame(root)
frame_main_diagram.place(relwidth=0.725, relheight=0.9, relx=0.25, rely=0.075)
#
frame_main_plot = tk.Frame(frame_main_diagram)
frame_main_plot.place(relwidth=0.875, relheight=1.0, relx=0.0, rely=0.0)
frame_main_check = tk.Frame(frame_main_diagram)
frame_main_check.place(relwidth=0.1, relheight=0.9, relx=0.875, rely=0.14)

root.mainloop()

Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

So sieht der Beispieldatensatz aus.

Code: Alles auswählen

x;sin(x);cos(x);sin(2x);cos(2x)
0.0;0.0000;1.0000;0.0000;1.0000
5.0;0.0872;0.9962;0.1736;0.9848
10.0;0.1736;0.9848;0.3420;0.9397
15.0;0.2588;0.9659;0.5000;0.8660
20.0;0.3420;0.9397;0.6428;0.7660
25.0;0.4226;0.9063;0.7660;0.6428
30.0;0.5000;0.8660;0.8660;0.5000
35.0;0.5736;0.8192;0.9397;0.3420
40.0;0.6428;0.7660;0.9848;0.1736
45.0;0.7071;0.7071;1.0000;0.0000
50.0;0.7660;0.6428;0.9848;-0.1736
55.0;0.8192;0.5736;0.9397;-0.3420
60.0;0.8660;0.5000;0.8660;-0.5000
65.0;0.9063;0.4226;0.7660;-0.6428
70.0;0.9397;0.3420;0.6428;-0.7660
75.0;0.9659;0.2588;0.5000;-0.8660
80.0;0.9848;0.1736;0.3420;-0.9397
85.0;0.9962;0.0872;0.1736;-0.9848
90.0;1.0000;0.0000;0.0000;-1.0000
95.0;0.9962;-0.0872;-0.1736;-0.9848
100.0;0.9848;-0.1736;-0.3420;-0.9397
105.0;0.9659;-0.2588;-0.5000;-0.8660
110.0;0.9397;-0.3420;-0.6428;-0.7660
115.0;0.9063;-0.4226;-0.7660;-0.6428
120.0;0.8660;-0.5000;-0.8660;-0.5000
125.0;0.8192;-0.5736;-0.9397;-0.3420
130.0;0.7660;-0.6428;-0.9848;-0.1736
135.0;0.7071;-0.7071;-1.0000;0.0000
140.0;0.6428;-0.7660;-0.9848;0.1736
145.0;0.5736;-0.8192;-0.9397;0.3420
150.0;0.5000;-0.8660;-0.8660;0.5000
155.0;0.4226;-0.9063;-0.7660;0.6428
160.0;0.3420;-0.9397;-0.6428;0.7660
165.0;0.2588;-0.9659;-0.5000;0.8660
170.0;0.1736;-0.9848;-0.3420;0.9397
175.0;0.0872;-0.9962;-0.1736;0.9848
180.0;0.0000;-1.0000;0.0000;1.0000
185.0;-0.0872;-0.9962;0.1736;0.9848
190.0;-0.1736;-0.9848;0.3420;0.9397
195.0;-0.2588;-0.9659;0.5000;0.8660
200.0;-0.3420;-0.9397;0.6428;0.7660
205.0;-0.4226;-0.9063;0.7660;0.6428
210.0;-0.5000;-0.8660;0.8660;0.5000
215.0;-0.5736;-0.8192;0.9397;0.3420
220.0;-0.6428;-0.7660;0.9848;0.1736
225.0;-0.7071;-0.7071;1.0000;0.0000
230.0;-0.7660;-0.6428;0.9848;-0.1736
235.0;-0.8192;-0.5736;0.9397;-0.3420
240.0;-0.8660;-0.5000;0.8660;-0.5000
245.0;-0.9063;-0.4226;0.7660;-0.6428
250.0;-0.9397;-0.3420;0.6428;-0.7660
255.0;-0.9659;-0.2588;0.5000;-0.8660
260.0;-0.9848;-0.1736;0.3420;-0.9397
265.0;-0.9962;-0.0872;0.1736;-0.9848
270.0;-1.0000;0.0000;0.0000;-1.0000
275.0;-0.9962;0.0872;-0.1736;-0.9848
280.0;-0.9848;0.1736;-0.3420;-0.9397
285.0;-0.9659;0.2588;-0.5000;-0.8660
290.0;-0.9397;0.3420;-0.6428;-0.7660
295.0;-0.9063;0.4226;-0.7660;-0.6428
300.0;-0.8660;0.5000;-0.8660;-0.5000
305.0;-0.8192;0.5736;-0.9397;-0.3420
310.0;-0.7660;0.6428;-0.9848;-0.1736
315.0;-0.7071;0.7071;-1.0000;0.0000
320.0;-0.6428;0.7660;-0.9848;0.1736
325.0;-0.5736;0.8192;-0.9397;0.3420
330.0;-0.5000;0.8660;-0.8660;0.5000
335.0;-0.4226;0.9063;-0.7660;0.6428
340.0;-0.3420;0.9397;-0.6428;0.7660
345.0;-0.2588;0.9659;-0.5000;0.8660
350.0;-0.1736;0.9848;-0.3420;0.9397
355.0;-0.0872;0.9962;-0.1736;0.9848
360.0;0.0000;1.0000;0.0000;1.0000

Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Also mir ist bewusst, dass die Checkbuttons noch nicht verknüpft sind mit der plot-Ausgabe. Wenn ich den Zustand der Checkbuttons testen möchte, kommt aktuell auch noch Unsinn raus. Das kann ich vermutlich noch relativ "schnell" fixen, aber abgesehen davon, dass der Code sicherlich wesentlich eleganter und "modularer" geschrieben werden kann, ist mir gerade noch nicht bewusst, wie ich erreichen kann, dass immer nur diejenigen Kurven geplottet werden, die mittels Checkbutton aktiviert worden sind.
Ich würde mich freuen und wäre dankbar, wenn man mir bei diesem Problem helfen könnte und bitte auch um etwas Nachsicht, wenn das vom Programmierstil sicherlich noch suboptimal ist und für mich manches, was für andere vielleicht super einfach ist, gerade nicht ganz so offensichtlich ist.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Kann man mehrere mainloops laufen lassen, also einmal für die gesamte Nutzeroberfläche und einmal im Speziellen für ein Teilframe, z.B. in dem etwas geplottet werden soll, was sich aber laufend ändern kann in Abhängigkeit von der Auswahl an aktivierten Checkbuttons?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Du steuerst den Mainloop ja nicht, sondern der steuert nur Deine GUI, in dem er auf Ereignisse reagiert und die passenden Methoden aufruft.
Was Du brauchst, ist ein Ereignis, das auf Änderung eines Checkbuttons reagiert und dann den Plot ändert.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Ich habe den Code nochmal etwas weiter verändert. Die Ausgabe ist jetzt schon mal mit dem Zustand der Variable verknüpft. Was ich noch nicht gemacht habe bzw. mir noch anlesen muss, ist die Frage, wie die Liste, welche den Zustand der Variablen (True oder False) enthält, aktualisiert werden kann. Deswegen hatte ich gedacht, dass man evtl. in dem Subframe auch eine mainloop laufen lassen muss. Naja, ich bin aber guter Dinge, dass ich das noch hinbekommen werde und dann werde ich den Code natürlich noch aufräumen und bin dann natürlich offen für Tipps, wie sich dieser noch weiter vereinfachen lässt. Jetzt muss dieser aber erstmal meinen wirren Gedanken folgen können. :D
Der Code-Schnipsel zeigt nur den Teil, den ich verändert habe.

Code: Alles auswählen

def show_data(data):
        names = list(data.columns.values)
        updated_states = build_checkbox_list(names=names)
        print(updated_states)

        data_plot = data.to_numpy()
        x = data_plot[:, 0]

        fig = Figure()
        ax = fig.add_subplot()
        lines = []
        for i in range(1, len(data_plot[0])):
            if updated_states[i-1][1] == True:
                graph, = ax.plot(x, data_plot[:, i], label=names[i], visible=True)
                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.025, 1.065), borderaxespad=1,
                  bbox_transform=plt.gcf().transFigure, loc="upper right")
        fig.tight_layout()

        canvas = FigureCanvasTkAgg(fig, master=frame_main_plot)
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

        toolbar = NavigationToolbar2Tk(canvas, frame_main_plot)
        toolbar.update()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

def build_checkbox_list(names):
    def select_all():
        for i in cb_list:
            i.select()
        cb_list_updated = cb_list_all
    def deselect_all():
        for i in cb_list:
            i.deselect()
        cb_list_updated = []
    def cb_select(i):
        if states[i].get() == False:
            cb_list_updated.remove(names[1:][i])
    cb_list = []
    cb_list_updated = []
    states = [tk.BooleanVar() for i in range(len(names[1:]))]
    for i in range(len(names[1:])):
        states[i].set(True)
        cb = tk.Checkbutton(master=frame_main_check, text=names[1:][i], variable=states[i], height=1,
                       width=15, onvalue=1, offvalue=0, anchor=tk.W, command=cb_select)
        cb.pack()
        cb_list.append(cb)
        cb_list_updated.append([names[1:][i], states[i].get()])
    for item in cb_list:
        item.select()
    cb_list_all = cb_list_updated
    #
    btn_select_all = tk.Button(frame_main_diagram, text="Select all", height=1, width=10, padx=2, pady=2, fg="#22252D", bg="#F1F1EB", command=select_all)
    btn_select_all.place(relx=0.875, rely=0.04)
    btn_deselect_all = tk.Button(frame_main_diagram, text="Deselect all", height=1, width=10, padx=2, pady=2, fg="#22252D", bg="#F1F1EB", command=deselect_all)
    btn_deselect_all.place(relx=0.875, rely=0.08)
    #
    return cb_list_updated
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

So, ich habe jetzt den Plot mit den Checkbuttons verknüpft, allerdings habe ich nun das Problem, dass sich der Zustand der Checkbuttons nicht verändert, wenn ich an diesen herumklicke. Also innerhalb der cb_select Methode teste ich gerade überhaupt erstmal, ob ich auch mal den Fall hinbekomme, dass der Zustand False ist. Ich würde mich freuen, wenn man mir hier helfen könnte, weil ich das Gefühl habe, dass ich eigentlich nicht mehr weit vom Ziel entfernt bin, aber ich kriege es gerade leider nicht.

Das ist der aktuelle Code:

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

## FUNCTIONS
list_csv = []
cb_list = []
cb_list_updated = []

def create_list(parent):
    listbox = tk.Listbox(parent, bg="#DFDFDF", width=25, height=17)
    scrollbar_y = tk.Scrollbar(parent, orient="vertical")
    scrollbar_x = tk.Scrollbar(parent, 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)

class Plotting:
    #
    def __init__(self, root, data):
        self.color_background = "#DFDFDF"
        self.root = root
        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.data = data
        #
        try:
            self.canvas.get_tk_widget().pack_forget()
            self.canvas = None
        except AttributeError:
            pass
        #
        self.cb_list = []
        self.cb_list_all = []
        self.cb_list_updated = []
        self.names = list(self.data.columns.values)
        self.states = [tk.BooleanVar() for i in range(len(self.names[1:]))]
        self.cb_list_updated, self.states_updated = Plotting.build_checkbox_list(self)
        print("List", self.cb_list_updated)
        #
        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,
                                             updated_states=self.cb_list_updated)
        #
        #self.frame_main.mainloop()
    #
    def show_plot(self, data, fig, ax, updated_states):
        try:
            self.canvas.get_tk_widget().pack_forget()
            self.canvas = None
        except AttributeError:
            pass

        data_plot = data.to_numpy()
        times = data_plot[:, 0]

        lines = []
        for i in range(1, len(data_plot[0])):
            if updated_states[i-1][1] == True:
                graph, = ax.plot(times, data_plot[:, i], label=self.names[i], visible=True)
                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)
    #
    def build_checkbox_list(self):
        for i in range(len(self.names[1:])):
            self.states[i].set(True)
            self.cb = tk.Checkbutton(master=self.frame_main_check,
                                     text=self.names[1:][i], variable=self.states[i],
                                     height=1, width=15, onvalue=1, offvalue=0, anchor=tk.W, command=self.cb_select(i),
                                     bg=self.color_background)
            self.cb.pack()
            self.cb_list.append(self.cb)
            self.cb_list_updated.append([self.names[1:][i], self.states[i].get()])
        self.cb_list_all = self.cb_list
        #
        btn_select_all = tk.Button(self.frame_main, text="Select all", height=1, width=10, padx=2, pady=2,
                                   fg="#22252D", bg="#F1F1EB", command=self.cb_select_all)
        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=self.cb_deselect_all)
        btn_deselect_all.place(relx=0.875, rely=0.08)

        return self.cb_list_updated, self.states
    #
    def cb_select(self, i):
        if self.states[i].get() == True:
            print("Hallo")
        if self.states[i].get() == False:
            print("Tschüss")
            self.cb_list_updated.remove(self.names[1:][i])
    #
    def cb_select_all(self):
        for i in range(len(self.states)):
            self.states[i].set(True)
        for i in self.cb_list:
                i.select()
        self.cb_list_updated = self.cb_list_all
    #
    def cb_deselect_all(self):
        for i in range(len(self.states)):
            self.states[i].set(False)
        for i in self.cb_list:
            i.deselect()
        self.cb_list_updated = []

## GUI
root = tk.Tk()
root.title("tkinter_diagram")

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)

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

root.mainloop()
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Kurtosis hat geschrieben: Freitag 28. Mai 2021, 15:09 So, ich habe jetzt den Plot mit den Checkbuttons verknüpft, allerdings habe ich nun das Problem, dass sich der Zustand der Checkbuttons nicht verändert, wenn ich an diesen herumklicke. Also innerhalb der cb_select Methode teste ich gerade überhaupt erstmal, ob ich auch mal den Fall hinbekomme, dass der Zustand False ist. Ich würde mich freuen, wenn man mir hier helfen könnte, weil ich das Gefühl habe, dass ich eigentlich nicht mehr weit vom Ziel entfernt bin, aber ich kriege es gerade leider nicht.
Nachtrag: Ich habe jetzt noch bisschen was ausprobiert bzw. versucht, im Internet zu dem Thema zu finden. Für mich ist das jetzt der Punkt, an dem ich nur Chinesisch verstehe, d.h. einfach absolut keine Idee habe, wie das Problem zu lösen ist.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da sind ein paar grobe Schnitzer drin. Man schreibt nicht Plotting.build_checkbox_list(self). Man schreibt self.build_checkbox_list(). Man benutzt keine globalen Variablen wie data_listbox. Der ganze Code unten muss in eine main-Funktion. Man vergleicht auch nicht mit True oder False, und wenn man das Gegenteil von etwas machen will, das man vorher geprueft hat, dann schreibt man else:

Code: Alles auswählen

if self.states[i].get():
    ...
else:
   ...
Man benutzt nicht das wirklich unglueckliche range(len(self.names[1:])), sondern einfach direkt

Code: Alles auswählen

for name in names[1:]:
Genauso ist das hier ein anti-pattern:

Code: Alles auswählen

for i in range(len(self.states)):
            self.states[i].set(False)
Auch da arbeitet man *gleich* mit for state in self.states.

Es gibt kein self.canvas beim __init__. Also kann der ganze try-except AttributeError Block weg.

Dann ein typischer Fehler - du benutzt "command=self.cb_select(i)". Du willst damit wahrscheinlich ausdruecken "wenn gedrueckt, mach das". Nur muss das command ein *callable* sein, also etwas aufrufbares. Du schreibst einfach den aufruf hin. Der liefert None zurueck, und nix geht. Darum benutzt man da oft lambda oder functools.partial. Aber man macht das eh so nicht, statt ein command + eine Variable zu benutzen, ueberwacht man einfach direkt die Variable mit trace.

Code: Alles auswählen

import tkinter as tk


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

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


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


if __name__ == '__main__':
    main()
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

__deets__ hat geschrieben: Freitag 28. Mai 2021, 17:06 Da sind ein paar grobe Schnitzer drin. Man schreibt nicht Plotting.build_checkbox_list(self). Man schreibt self.build_checkbox_list(). Man benutzt keine globalen Variablen wie data_listbox. Der ganze Code unten muss in eine main-Funktion. Man vergleicht auch nicht mit True oder False, und wenn man das Gegenteil von etwas machen will, das man vorher geprueft hat, dann schreibt man else:

Code: Alles auswählen

if self.states[i].get():
    ...
else:
   ...
Man benutzt nicht das wirklich unglueckliche range(len(self.names[1:])), sondern einfach direkt

Code: Alles auswählen

for name in names[1:]:
Genauso ist das hier ein anti-pattern:

Code: Alles auswählen

for i in range(len(self.states)):
            self.states[i].set(False)
Auch da arbeitet man *gleich* mit for state in self.states.

Es gibt kein self.canvas beim __init__. Also kann der ganze try-except AttributeError Block weg.

Dann ein typischer Fehler - du benutzt "command=self.cb_select(i)". Du willst damit wahrscheinlich ausdruecken "wenn gedrueckt, mach das". Nur muss das command ein *callable* sein, also etwas aufrufbares. Du schreibst einfach den aufruf hin. Der liefert None zurueck, und nix geht. Darum benutzt man da oft lambda oder functools.partial. Aber man macht das eh so nicht, statt ein command + eine Variable zu benutzen, ueberwacht man einfach direkt die Variable mit trace.

Code: Alles auswählen

import tkinter as tk


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

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


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


if __name__ == '__main__':
    main()
Vielen Dank erstmal für deine Antwort und Hilfestellungen. Ich habe mal versucht, dein Codebeispiel an meins anzupassen, aber hier und da gibt es nun ein paar Unklarheiten. Ich schätze grundsätzlich "learning by doing", aber ich muss auch gerade feststellen, dass meine Frustrationskurve langsam die Lern- und Motivationskurve übersteigt. Ich möchte es gerne verstehen und dann in Zukunft besser machen bzw. letztlich dann hier ein sauberes Minimalbeispiel liefern, aber ich wäre echt dankbar für konkrete Hilfe an meinem Codebeispiel. Ich füge mal nur die Plotting Klasse hinzu.

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}
        i = 0
        for state in self._states.values():
            cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=self.names[i],
                                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()

        Plotting3.show_plot(self, data=self.data, fig=self.fig, ax=self.ax, updated_states=self._states)

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

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

        lines = []
        for i in range(1, len(data_plot[0])):
            if self._states[i].get():
                graph, = ax.plot(times, data_plot[:, i], label=self.names[i], visible=True)
                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)
Diese Klasse soll nur dann aufgerufen werden, wenn in meinem Beispiel auf den jeweiligen Datensatz geklickt wird, d.h. das Klicken ist mit der folgenden Funktion verknüpft.

Code: Alles auswählen

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)
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Man erzeugt nicht nachträglich weitere Frames. Jedesmal wenn Du plot_csv aufrufst, dann wird ein weiterer Frame über den alten gezeichnet. Man erzeugt am Anfang alle nötigen Widgets und ändert dann nur noch ihren Inhalt. `place` benutzt man nicht, weil es je nach System und Bildschirmauflösung unterschiedlich aussieht.
Alles was eine Funktion braucht, muß sie über ihre Argumente bekommen. Bei plot_csv kommen data_listbox, list_csv und root aus dem nichts.
Für GUIs braucht man eigentlich immer Klassendefinitionen, damit man solche Werte sauber in eine Methode bekommt. Bei `Plotting` machst Du das ja schon.
`Plotting` in plot_csv wird gar nicht benutzt, aber wenn Du Dir gar keine Referenz auf Deinen Plot merkst, wie willst Du denn dann nachträglich ändern können?

In `Plotting` wird ein `Plotting3` benutzt, das nicht definiert ist.

Wenn Du frustriert wirst, dann Dein jetziger Wissensstand noch nicht zur Komplexität des Problems, das Du Dir ausgesucht hast. Geh ein paar Schritte zurück und lerne erst, wie man sauber eine einfachere GUI schreibt, z.B. ein Fenster mit eine paar Checkbuttons, die eine einfache Operation, wie z.B. ein Label füllen.
Benutzeravatar
Kurtosis
User
Beiträge: 55
Registriert: Samstag 11. Dezember 2010, 14:32

Sirius3 hat geschrieben: Samstag 29. Mai 2021, 12:05 Man erzeugt nicht nachträglich weitere Frames. Jedesmal wenn Du plot_csv aufrufst, dann wird ein weiterer Frame über den alten gezeichnet. Man erzeugt am Anfang alle nötigen Widgets und ändert dann nur noch ihren Inhalt. `place` benutzt man nicht, weil es je nach System und Bildschirmauflösung unterschiedlich aussieht.
Alles was eine Funktion braucht, muß sie über ihre Argumente bekommen. Bei plot_csv kommen data_listbox, list_csv und root aus dem nichts.
Für GUIs braucht man eigentlich immer Klassendefinitionen, damit man solche Werte sauber in eine Methode bekommt. Bei `Plotting` machst Du das ja schon.
`Plotting` in plot_csv wird gar nicht benutzt, aber wenn Du Dir gar keine Referenz auf Deinen Plot merkst, wie willst Du denn dann nachträglich ändern können?

In `Plotting` wird ein `Plotting3` benutzt, das nicht definiert ist.

Wenn Du frustriert wirst, dann Dein jetziger Wissensstand noch nicht zur Komplexität des Problems, das Du Dir ausgesucht hast. Geh ein paar Schritte zurück und lerne erst, wie man sauber eine einfachere GUI schreibt, z.B. ein Fenster mit eine paar Checkbuttons, die eine einfache Operation, wie z.B. ein Label füllen.
Ich möchte aber halt nicht alle Widgets von Anfang an sehen, d.h. die Checkbuttons, und der Bereich, in dem später der Plot erscheint, sollen am Anfang leer bzw. nicht sichtbar sein. Die Anzahl an Checkbuttons sowie deren Beschriftung soll halt variabel und vom eingelesen Datensatz abhängig sein.
Danke für den Hinweis mit "place". Das ist mir auch schon aufgefallen, wenn man das Fenster größer ziehen möchte.
Wieso kommen bei plot_csv die Variablen aus dem Nichts? Die wurden alle vorher eingeführt und definiert. Ich werde sie aber mal als Argument der übergeben, das wäre sicher sinnvoller fürs Verständnis des Codes.
Naja, einfachere GUIs habe ich schon nachprogrammiert sowie auch schon Tutorials auf YouTube mir dazu angesehen, aber ich sage mal so: wenn ich ewig "Alle meine Entchen" spiele, dann wird das Spielen eines schwierigeren Stücks sicher nicht eher gelingen. Frustration insofern, weil ich glaube, dass es ein Leichtes wäre, mir hier konkret zu helfen, so dass das Programm funktioniert und ich dann in Ruhe lernen kann, was zu meinem Code hinzugefügt oder verändert werden musste und warum. Code-Beispiele, die ignorieren, dass ich einen Datensatz einlesen möchte, dass Checkbuttons die Beschriftung von einer Variablen bekommen sollen, bringen mir halt nichts. Mir ist absolut bewusst, dass mein Code noch verbessert werden kann, also auch grundsätzlich vom Stil, aber wichtiger finde ich erstmal, dass ich verstehe, was wo passiert und dass vor allem am Ende das gewünschte Ergebnis herauskommt.

Code: Alles auswählen

i = 0
for state in self._states.values():
	cb = tk.Checkbutton(master=self.frame_main_check, variable=state, text=self.names[i],
						height=1, width=15, anchor=tk.W, bg=self.color_background)
	i += 1
	cb.pack()
	state.trace_add("write", self._value_changed)
Ich bezweifle, dass sich das jetzt hier an der Stelle so gelohnt hat, dass i als Zähler aus der ursprünglichen for-Schleife herauszunehmen. Da ich aber nicht nur den Zustand der Variablen übergeben möchte sondern auch noch die Beschriftung des Buttons, brauchte ich halt das i. Für mich sieht das jetzt nach im Kreis gedreht aus. "state.trace_add("write", self._value_changed)", keine Ahnung was das jetzt z.B. macht.

Ich möchte nicht hier irgendwie zickig rüberkommen und ich weiß auch grundsätzlich, die bisherigen Hilfestellungen zu schätzen, aber funktionieren tut das Programm immer noch nicht und ich finde bei Google keine vergleichbaren Beispiele. Ich werde die Funktionen mal sauber definieren, inkl. Argumenten und dergleichen, aber Fakt ist für mich leider, dass ich danach nicht weiß, wie ich das Problem gelöst kriege. Das "Hello World" Beispiel für den GUI Einstieg mit Tkinter bringt mir halt wenig.
Ganz ehrlich, ich schicke demjenigen, der/die mir hier konkret helfen kann, gerne bei Paypal ein Bier zu, aber es frustriert mich gerade etwas, das Gefühl zu haben, dass ich sinngemäß alle möglichen Laute miteinander kombinieren muss in der Hoffnung, dass dabei sinnvolle Wörter herauskommen. Das ist für mich kein Lernprozess sondern Zeitverschwendung.
__deets__
User
Beiträge: 14493
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.
Antworten