Diagramm in Abhängigkeit von Checkbuttons
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?
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?
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.
So ganz allgemein können wir nicht sagen, was Du falsch machst, wenn wir den Code nicht kennen.
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.
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()
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
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.
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.
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?
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.
Was Du brauchst, ist ein Ereignis, das auf Änderung eines Checkbuttons reagiert und dann den Plot ändert.
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.
Der Code-Schnipsel zeigt nur den Teil, den ich verändert habe.
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
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:
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()
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.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.
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:
Man benutzt nicht das wirklich unglueckliche range(len(self.names[1:])), sondern einfach direkt
Genauso ist das hier ein anti-pattern:
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
if self.states[i].get():
...
else:
...
Code: Alles auswählen
for name in names[1:]:
Code: Alles auswählen
for i in range(len(self.states)):
self.states[i].set(False)
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.__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:Man benutzt nicht das wirklich unglueckliche range(len(self.names[1:])), sondern einfach direktCode: Alles auswählen
if self.states[i].get(): ... else: ...
Genauso ist das hier ein anti-pattern:Code: Alles auswählen
for name in names[1:]:
Auch da arbeitet man *gleich* mit for state in self.states.Code: Alles auswählen
for i in range(len(self.states)): self.states[i].set(False)
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()
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)
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)
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.
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.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.
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 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.
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
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?
Code: Alles auswählen
for name, state in self._states.items():
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?
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)
Code: Alles auswählen
def _value_changed(self, name, _, op):
print(name, self._states[name].get())
KeyError: 'sin'
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()
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.
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.
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()
So, die Plotting Klasse habe ich jetzt auch nochmal etwas überarbeitet und zwar die Initialisierung.
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.
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)