@TheBombTuber: Feste Grössenvorgaben in Pixeln sind nicht gut. Das ist bei mir unbenutzbar weil die hart vorgegebenen 550 Pixel Canvas-Breite nicht ausreichen um die Eingabefelder für die Pfade anzuzeigen. Die Schaltflächen zur Dateiauswahl sind überhaupt nicht mehr sichtbar und damit auch nicht per Maus zu erreichen. Vergrössern des Fensters bringt nichts, weil da nichts mitwächst.
Also sollte man mindestens dafür sorgen, dass das verändern der Fenstergrösse auch einen Einfluss auf den Fensterinhalt hat, so dass man das gross genug ziehen kann damit alles sichtbar wird was man mit der Maus erreichen können soll.
`root`, `umrandung`, und `vsb` muss man nicht an das `App`-Objekt binden.
`frame` ist sinnlos weil das einzige was da rein gesteckt wird ein weiterer Frame ist.
Ein `LabelFrame` bei dem man die Umrandung weg konfiguriert und keinen Text setzt, macht keinen Sinn.
`partial()` mit nur einer Funktion als Argument macht auch keinen Sinn.
Wenn es für Argumente im `tkinter`-Modul Konstanten gibt, sollte man die benutzen, statt den Wert selbst noch mal als Zeichenkette zu schreiben.
Bei `ttk.Combobox()` werden Optionen gleich nach dem erstellen gesetzt, die man auch beim erstellen schon hätte angeben können.
Das Ergebnis von `askopenfilename()` sollte man nur auf ”truthiness” testen und nicht explizit mit der leeren Zeichenkette vergleichen. Es gibt auch Tk/`tkinter`-Versionen und Situationen wo an der Stelle ein leeres Tupel zurückgegeben wird wenn der Benutzer den Dialog abbricht.
Die beiden ``if``\s lassen sich auch sehr einfach zu einem zusammenfassen.
Keine nackten ``except:`` ohne konkrete Ausnahme(n). Da behandelt man *alle* Ausnahmen, auch solche mit denen man gar nicht gerechnet hat. Fehler fallen dann nicht auf und/oder sind nur schwer zu finden.
In `insert_dbc_path()` wird im ``except``-Zweig alles bis auf die erste Zeile im ``try``-Block wiederholt, also eigentlich sollte der ``try``-Block nur diese Zeile umfassen. Und die Ausnahme die da behandelt werden soll, ist vermute ich mal der `ValueError` der auftritt wenn das Element nicht in der Liste ist. (Was ein bisschen komisch wäre, und doch eigentlich auf ein Problem hinweisen würde‽)
Wenn man nichts machen will, aus syntaktischen Gründen aber etwas schreiben muss, dann gibt es die ``pass``-Anweisung. Ein ``print("")`` macht da keinen Sinn.
In `delete_dbc_entries()` wird plötzlich ein Attribut `saved_values` eingeführt, was es vorher nicht gab und das auch nirgends verwendet wird‽
`add_more()` ist nur ein anderer Name für `create_entry()`.
Zwischenstand (ungetestet):
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, filedialog
from functools import partial
class App:
def __init__(self, root):
root.title("Test")
self.siglist = ["CAN", "LIN"]
self.entries = []
self.dbcs = []
umrandung = ttk.LabelFrame(root, text="Paths")
umrandung.pack(expand=True, fill=tk.BOTH)
umrandung.columnconfigure(0, weight=1)
umrandung.rowconfigure(1, weight=1)
button_frame = tk.Frame(umrandung)
button_frame.grid(row=0, column=0)
for i, (text, action) in enumerate(
[
("ADD", self.create_entry),
("Delete", self.loose_some),
("Print", self.print_all),
]
):
ttk.Button(button_frame, text=text, command=action).grid(
row=0, column=i, sticky=tk.NSEW
)
self.dbc_canvas = tk.Canvas(umrandung, borderwidth=0, width=550)
self.dbc_canvas.grid(row=1, column=0, sticky=tk.NSEW)
self.dbc_frame = tk.Frame(self.dbc_canvas)
scrollbar = tk.Scrollbar(
umrandung, orient=tk.VERTICAL, command=self.dbc_canvas.yview
)
scrollbar.grid(row=1, column=1, sticky=tk.NS)
self.dbc_canvas.configure(yscrollcommand=scrollbar.set)
self.dbc_canvas.create_window(
(4, 4), window=self.dbc_frame, anchor=tk.NW
)
self.dbc_frame.bind("<Configure>", self.on_frame_configure)
for _ in range(4):
self.create_entry()
def on_frame_configure(self, _event=None):
"""
Reset the scroll region to encompass the inner frame.
"""
self.dbc_canvas.configure(scrollregion=self.dbc_canvas.bbox(tk.ALL))
def create_entry(self):
combobox = ttk.Combobox(
self.dbc_frame, width=8, values=self.siglist, state="readonly"
)
combobox.current(0)
spinbox = ttk.Spinbox(self.dbc_frame, from_=1, to=48, width=3)
entry = ttk.Entry(self.dbc_frame, width=60)
combobox.unbind("<MouseWheel>")
combobox.bind(
"<Enter>",
partial(self.delete_dbc_entries, combobox, spinbox, entry),
)
combobox.bind(
"<<ComboboxSelected>>",
partial(self.update_dbc_entries, combobox, spinbox, entry),
)
#
# TODO Scenario handeln in dem die COMBOBOX selektiert wird der Inhalt
# allerdings nicht geändert wird.
#
spinbox.bind(
"<Enter>",
partial(self.delete_dbc_entries, combobox, spinbox, entry),
)
spinbox.bind(
"<Leave>",
partial(self.update_dbc_entries, combobox, spinbox, entry),
)
button = ttk.Button(
self.dbc_frame,
text="...",
width=2,
command=partial(self.insert_dbc_path, combobox, spinbox, entry),
)
row = (combobox, spinbox, entry, button)
for column_index, item in enumerate(row):
item.grid(
row=len(self.entries),
column=column_index,
sticky=tk.E,
padx=5,
pady=5,
)
spinbox.set(len(self.entries) + 1)
self.entries.append(row)
return entry
def insert_dbc_path(self, combobox, spinbox, entry):
old_output_path = entry.get()
output_path = filedialog.askopenfilename()
if output_path and output_path != old_output_path:
try:
self.dbcs.remove(
(combobox.get(), spinbox.get(), "=", entry.get())
)
except ValueError:
pass # Intentionally ignored.
entry.delete(0, tk.END)
entry.insert(0, output_path)
self.dbcs.append((combobox.get(), spinbox.get(), "=", entry.get()))
def delete_dbc_entries(self, combobox, spinbox, entry, _event=None):
if entry.get():
try:
self.dbcs.remove(
(combobox.get(), spinbox.get(), "=", entry.get())
)
except ValueError:
pass # Intentionally ignored.
def update_dbc_entries(self, combobox, spinbox, entry, _event=None):
if entry.get():
self.dbcs.append((combobox.get(), spinbox.get(), "=", entry.get()))
def print_all(self):
for data in self.dbcs:
print("".join(data))
def loose_some(self):
if len(self.entries) > 4:
for item in self.entries.pop():
item.grid_forget()
def main():
root = tk.Tk()
_app = App(root)
root.mainloop()
if __name__ == "__main__":
main()
Jetzt ist dieses ganze aktuell halten von `self.dbcs` umständlich. Muss man das überhaupt? Kann man das nicht einfach immer dann erstellen wenn es benötigt wird?
Dann steht ``(combobox.get(), spinbox.get(), "=", entry.get())`` nur noch einmal im Code und das ganze aktualisieren von `self.dbcs` fällt weg. `insert_dbc_path()` braucht dann nur noch `entry` als Argument. `delete_dbc_entries()` und `update_dbc_entries() werden gar nicht mehr benötigt. Dadurch fällt eine Menge Code einfach weg. Auch in `create_entry()`.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, filedialog
from functools import partial
class App:
def __init__(self, root):
root.title("Test")
self.siglist = ["CAN", "LIN"]
self.entries = []
umrandung = ttk.LabelFrame(root, text="Paths")
umrandung.pack(expand=True, fill=tk.BOTH)
umrandung.columnconfigure(0, weight=1)
umrandung.rowconfigure(1, weight=1)
button_frame = tk.Frame(umrandung)
button_frame.grid(row=0, column=0)
for i, (text, action) in enumerate(
[
("ADD", self.create_entry),
("Delete", self.loose_some),
("Print", self.print_all),
]
):
ttk.Button(button_frame, text=text, command=action).grid(
row=0, column=i, sticky=tk.NSEW
)
self.dbc_canvas = tk.Canvas(umrandung, borderwidth=0, width=550)
self.dbc_canvas.grid(row=1, column=0, sticky=tk.NSEW)
self.dbc_frame = tk.Frame(self.dbc_canvas)
scrollbar = tk.Scrollbar(
umrandung, orient=tk.VERTICAL, command=self.dbc_canvas.yview
)
scrollbar.grid(row=1, column=1, sticky=tk.NS)
self.dbc_canvas.configure(yscrollcommand=scrollbar.set)
self.dbc_canvas.create_window(
(4, 4), window=self.dbc_frame, anchor=tk.NW
)
self.dbc_frame.bind("<Configure>", self.on_frame_configure)
for _ in range(4):
self.create_entry()
@property
def dbcs(self):
return (
(combobox.get(), spinbox.get(), "=", entry.get())
for combobox, spinbox, entry in self.entries
if entry.get()
)
def on_frame_configure(self, _event=None):
"""
Reset the scroll region to encompass the inner frame.
"""
self.dbc_canvas.configure(scrollregion=self.dbc_canvas.bbox(tk.ALL))
def create_entry(self):
combobox = ttk.Combobox(
self.dbc_frame, width=8, values=self.siglist, state="readonly"
)
combobox.current(0)
combobox.unbind("<MouseWheel>")
spinbox = ttk.Spinbox(self.dbc_frame, from_=1, to=48, width=3)
entry = ttk.Entry(self.dbc_frame, width=60)
button = ttk.Button(
self.dbc_frame,
text="...",
command=partial(self.set_dbc_path, entry),
)
row = (combobox, spinbox, entry, button)
for column_index, item in enumerate(row):
item.grid(
row=len(self.entries),
column=column_index,
sticky=tk.E,
padx=5,
pady=5,
)
#
# BUG Der Wertebereich von der `spinbox` ist begrenzt, aber hier
# könnte ein grösserer Wert gesetzt werden!
#
spinbox.set(len(self.entries) + 1)
self.entries.append(row)
return entry
def set_dbc_path(self, entry):
output_path = filedialog.askopenfilename()
if output_path:
entry.delete(0, tk.END)
entry.insert(0, output_path)
def print_all(self):
for data in self.dbcs:
print("".join(data))
def loose_some(self):
if len(self.entries) > 4:
for item in self.entries.pop():
item.grid_forget()
def main():
root = tk.Tk()
_app = App(root)
root.mainloop()
if __name__ == "__main__":
main()