Abhänige Auswahl mit Listbox

Fragen zu Tkinter.
Antworten
Bibo3000
User
Beiträge: 30
Registriert: Mittwoch 5. Oktober 2022, 14:01

Hallo liebe Community!

Ich zerbreche mir seit einige Tagen den Kopf, wie ich es schaffe eine abhänige Auswahl zu generieren.
Das soll heißen, ich will eine Vorauswahl mit einer Listbox treffen und anschließend, je nach Vorauswahl, weitere Wahlmöglichkeiten bekommen. Meinen Code seht ihr hier drunter:

import tkinter as tk

root = tk.Tk()

Listbox = tk.Listbox(root, height=3)
Listbox.insert ("end", "A", "B", "C")
Listbox.select_set(0)
Listbox.grid(row=0, rowspan=2, column=0)

Anzeige = tk.Label(root, height=5, width=10, relief="sunken")
Anzeige.grid(row=0, rowspan=2, column=1)

Hoch = tk.Button(root, text="Hoch", command=lambda: Aendern.Auswahl_aendern(1))
Hoch.grid(row=0, column=3)
Runter = tk.Button(root, text="Runter", command=lambda: Aendern.Auswahl_aendern(-1))
Runter.grid(row=1, column=3)

Liste_B = ["1", "2", "3", "4", "5"]
Liste_C = ["V", "W", "X", "Y", "Z"]

class Auswahl():
def __init__(self) -> None:
self.zaehler = 0
self.Liste_Auswahl = ""
self.Liste = []
def Auswahl_reset(self):
self.zaehler = 0
self.Liste_Auswahl = Listbox.get(Listbox.curselection())
if self.Liste_Auswahl == "A":
Anzeige.config(text="Nüscht")
elif self.Liste_Auswahl == "B":
self.Liste = Liste_B
Anzeige.config(text=self.Liste[self.zaehler])
elif self.Liste_Auswahl == "C":
self.Liste = Liste_C
Anzeige.config(text=self.Liste[self.zaehler])
def Auswahl_aendern(self, betrag):
if self.Liste:
self.zaehler += betrag
Anzeige.config(text=self.Liste[self.zaehler])

Aendern = Auswahl()

Listbox.bind("<<ListboxSelect>>", Aendern.Auswahl_reset())

root.mainloop()

Vielleicht seht ihr ja schon sofort das Problem und könnt es mir, möglichst für ganz doofe, erklären.
Ansonsten, schaffe ich es mittels Listbox und ListboxSelect die Vorauswahl anzuzeigen, dazu müsste ich allerdings den Listbox.bind folgendermaßen ändern:

Listbox.bind("<<ListboxSelect>>", Auswahl.Auswahl_reset)

Verzeihung, wenn ich total falschen Syntax benutze, aber ich muss anstatt dem Objekt direkt die Klasse ansprechen, damit die Vorauswahl angezeigt wird und das verstehe ich einfach nicht. Kann mir das einer erklären? Ich habe hin und her probiert, bekomme aber mit Aender.Auswahl_reset() keine Vorauswahl mehr angezeigt. Außerdem gibt es aus irgendeinem Grund die self.Liste bei der Auswahl über Auswahl_reset(), aber wenn ich auf die Liste in Auswahl_aendern() zugreifen will, ist diese plötzlich leer. Das verstehe ich auch nicht.

Die einzelenen Elemente der Listen bekomme ich auch angezeigt, ich bekomme nur irgednwie einfach nicht beides verbunden. Könnt Ihr mir da helfen?
Bibo3000
User
Beiträge: 30
Registriert: Mittwoch 5. Oktober 2022, 14:01

Tja... wie es immer so ist. Ich formuliere ne halbe Stunde lang nen Beitrag und finde kurz darauf den Fehler...

Meine Frage hat sich erledigt, ich kann meinen Beitrag nur nicht selbst löschen. ^^
Mein Fehler lag an einer Stelle wo ich vormals schon gefragt habe und hängt mit der für mich noch etwas befremdlichen Eigenart von ListboxSelect zusammen, ein Argument sehen zu wollen. Letztendlich musste ich den Code nur folgendermaßen anpassen:

class Auswahl():
def __init__(self) -> None:
self.zaehler = 0
self.Liste_Auswahl = ""
self.Liste = []
def Auswahl_reset(self, event):
self.zaehler = 0
self.Liste_Auswahl = Listbox.get(Listbox.curselection())
if self.Liste_Auswahl == "A":
Anzeige.config(text="Nüscht")
elif self.Liste_Auswahl == "B":
self.Liste = Liste_B
Anzeige.config(text=self.Liste[self.zaehler])
elif self.Liste_Auswahl == "C":
self.Liste = Liste_C
Anzeige.config(text=self.Liste[self.zaehler])
def Auswahl_aendern(self, betrag):
if self.Liste:
self.zaehler += betrag
Anzeige.config(text=self.Liste[self.zaehler])

Aendern = Auswahl()

Listbox.bind("<<ListboxSelect>>", Aendern.Auswahl_reset)
Benutzeravatar
__blackjack__
User
Beiträge: 13241
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Bibo3000: Auf Modulebene sollte nur Code stehen, der Konstanten, Funktionen, und Klassen definiert. Das Hauptrogramm steht üblicherweise in einer Funktion die `main()` heisst. Damit sind einige Namen/Variablen nicht mehr einfach so global verfügbar und müssen sauber übergeben werden.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassennamen (PascalCase).

Dann muss man auch nicht so komische Sachen machen wie ein Exemplar vom Typ `Auswahl` dann `Aendern` zu nennen, statt einfach `auswahl`.

Es macht keinen Sinn den Klassennamen noch mal in jeder Methode auf der Klasse zu wiederholen. Das ist einfach nur mehr Schreibarbeit bei den Aufrufen ohne dem Leser einen Mehrwert zu liefern.

Wenn es für Zeichenketten mit besonderen Werten als Argumente eine Konstante in `tkinter` gibt, dann sollte man die verwenden.

Die Daten sollten sich nicht an mehreren Stellen wiederholen und die Zuordnung von Buchstabe zu Liste sollte explizit durch eine Datenstruktur ausgedrückt werden. Dann sieht man die Zusammenhänge besser und der Code wird einheitlicher und leichter an *einer* Stelle änder- und erweiterbar.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial


class Auswahl:
    def __init__(self, character_to_values, listbox, anzeige):
        self.character_to_values = character_to_values
        self.listbox = listbox
        self.anzeige = anzeige
        self.index = 0
        self.values = []

    def _update_anzeige(self):
        self.anzeige["text"] = (
            self.values[self.index] if self.values else "Nüscht"
        )

    def reset(self, _event=None):
        self.index = 0
        self.values = self.character_to_values[
            self.listbox.get(self.listbox.curselection())
        ]
        self._update_anzeige()

    def aendern(self, delta):
        #
        # TODO Index auf sinnvolle Werte begrenzen.
        #
        self.index += delta
        self._update_anzeige()


def main():
    character_to_values = {
        "A": [],
        "B": ["1", "2", "3", "4", "5"],
        "C": ["V", "W", "X", "Y", "Z"],
    }
    root = tk.Tk()

    listbox = tk.Listbox(root, height=3)
    listbox.insert(tk.END, *character_to_values.keys())
    listbox.select_set(0)
    listbox.grid(row=0, rowspan=2, column=0)

    anzeige = tk.Label(root, height=5, width=10, relief=tk.SUNKEN)
    anzeige.grid(row=0, rowspan=2, column=1)

    auswahl = Auswahl(character_to_values, listbox, anzeige)

    tk.Button(root, text="Hoch", command=partial(auswahl.aendern, 1)).grid(
        row=0, column=3
    )
    tk.Button(root, text="Runter", command=partial(auswahl.aendern, -1)).grid(
        row=1, column=3
    )
    listbox.bind("<<ListboxSelect>>", auswahl.reset)

    root.mainloop()


if __name__ == "__main__":
    main()
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Bibo3000
User
Beiträge: 30
Registriert: Mittwoch 5. Oktober 2022, 14:01

Wow, das sieht sehr viel besser aus, danke sehr und danke für die Erklärungen.
Leider sind die Beispiele in den zwei Büchern die hier auf dem Schreibtisch liegen nicht derart ausgearbeitet, sondern so simpel gehalten wie das, was ich geschickt habe. Es ist schwer an Beispiele für gute und komplexere Programmierung zu kommen. Deshalb nochmal ein großes "Danke sehr".
Antworten