Listbox, die nicht alles anzeigt

Fragen zu Tkinter.
Antworten
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Ich habe eine Frage, die wahrscheinlich eher eine Software-Engineering-Frage ist als eine Tkinter-Frage.

Angenommen ich habe ein Programm mit einer GUI.
Das Programm hat als Variable eine lange Liste von Objekten gespeichert. Sagen wir diese Liste heißt liste.
In der GUI ist eine Listbox. Sagen wir die Listbox heißt listbox.
Jeder Eintrag in der Listbox ist eine Beschreibung von einem der gespeicherten Objekte.
Neben der Listbox ist ein Button. Wenn der Benutzer den Button anklickt, dann soll das Objekt, das gerade in der Listbox ausgewählt ist, irgendwie geändert werden.
Um das zu erreichen, knüpfe ich an den Button eine Funktion die irgendwie sowas macht wie:
liste[listbox.curselection()[0]] = geändertes_objekt

So weit, so einfach. Aber was, wenn die Listbox nicht ALLE Einträge der liste anzeigt, sondern nur die, die ein bestimmtes Kriterium erfüllen? Dann kann ich den Index den mir listbox.curselection()[0] gibt, nicht einfach verwenden.
- Versteht man, was ich meine?

Eine Kopie von liste zu machen in der nur die angezeigten Objekte drinstehen ist auch nicht sinnvoll, denn wenn ich die Einträge der Kopie ändere, ändern sich die Einträge der liste i.A. nicht - aber das sollten sie.
Also bleibt nur, den Index von curselection zu nehmen und dann rumzurechnen, welcher Eintrag von liste dazu wohl gehört hat. Aber dann habe ich gewisse Code-Dopplungen - einmal beim Befüllen der Listbox und dann nochmal beim Rekonstruieren welchen Eintrag der Benutzer wohl gemeint hatte.

Sicherlich bin ich nicht der Erste, der dieses Problem hat. Wie würde das jemand angehen, der Ahnung von der Materie hat? Gibt es dafür irgendwelche Patterns?

Freundliche Grüße
Y.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso denkst du die Liste wäre teuer? Ist sie nicht. Die Objekte selbst werden dabei ja nicht gedoppelt.
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Doch, werden sie. Hier ein Minimalbeispiel, was ich meine:

Code: Alles auswählen

import tkinter as tk
w = tk.Tk()
liste = ["hallo", "quatsch", "mit", "soße", "hussnätter"]
gefilterteliste = [s for s in liste if not s.startswith("h")]
listbox = tk.Listbox()
for s in gefilterteliste:
	listbox.insert(tk.END, s)
listbox.pack()

def helau():
	if len(listbox.curselection()) != 1:
		print("nichts oder zu viel ausgewählt")
		return
	gefilterteliste[listbox.curselection()[0]] += " HELAU"
	print(gefilterteliste)
	print(liste)

tk.Button(w, text="helau anhängen", command=helau).pack()
w.mainloop()

Jetzt stelle man sich noch vor, das "h" wäre nicht hartkodiert sondern würde vom Benutzer in einem Entry eingegeben werden. Und die Listbox müsste nach dem Klick auf den Button die geänderten Strings anzeigen.
PS: Die angebotene Summe ist beachtlich.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein, werden sie nicht. So funktioniert python nunmal nicht. Sofern du keinen aufrufe wie copy.deepcopy oder ähnliches nutzt, werden da nur Referenzen auf das gleiche Objekt gespeichert.
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

__deets__ hat geschrieben: Freitag 8. April 2022, 21:37 Nein, werden sie nicht. So funktioniert python nunmal nicht. Sofern du keinen aufrufe wie copy.deepcopy oder ähnliches nutzt, werden da nur Referenzen auf das gleiche Objekt gespeichert.
Und wieso sehe ich dann in der Ausgabe dass liste sich nicht geändert hat?
PS: Die angebotene Summe ist beachtlich.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

@Üpsilon: Ich sehe auch nicht, wo du ein Objekt in einer Liste veränderst. Ich sehe, dass du `gefilterteliste[listbox.curselection[0]]` etwas _neues_ zuweist.

Hier würdest du auch nicht erwarten, dass der Wert von b plötzlich "ham" wird, oder?

Code: Alles auswählen

>>> a = "spam"
>>> b = a
>>> a = "ham"
>>> print(a)
ham
>>> print(b)
spam
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Ich programmiere seit 10 Jahren in dieser Sprache und weiß sehr wohl wie = funktioniert. Ich hatte mich explizit bemüht, meine Ausgangsfrage klar zu formulieren und bedaure, dass mir das nicht hinreichend gelungen ist.

Nochmal: Meine Frage ist - bezogen auf das kleine Beispiel was ich gepostet habe - wie bekommt man es ohne Code-Dopplungen am besten hin, an den ausgewählten Eintrag "HELAU" anzuhängen?
PS: Die angebotene Summe ist beachtlich.
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Nevermind ich denke ich habe eine gute Lösung gefunden. Ihr könnt den Faden jetzt zumachen.
PS: Die angebotene Summe ist beachtlich.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Strings sind nicht deine Objekte. Sondern immutable, und natürlich hast du dann zwei unterschiedliche.

Steckst du hingegen ein dict statt einem String in deine Liste, und änderst das, dann reflektiert sich das auch in beiden Listen. Und das auch schon seit 10 Jahren. Oder mehr.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Für dem Fall, dass jemand über den Thread stolpert, der noch nicht so viel Erfahrung im Zuweisen hat folgende Session aus dem interaktiven Interpreter. Manchmal sagt das mehr aus als viele Erklärungen.

Code: Alles auswählen

>>> a = "ham"
>>> print(id(a))
2146183766640
>>> l = [a]
>>> x = [l]
>>> print(l)
['ham']
>>> print(x)
[['ham']]
>>> a = "spam"
>>> print(id(a))
2146184025200
>>> print(id(l[0]))
2146183766640
>>> print(l)
['ham']
>>> print(x)
[['ham']]
>>> l[0] = "lemmon"
>>> print(l)
['lemmon']
>>> print(x)
[['lemmon']]
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Um das nochmal klar zu machen (ich war gestern auf dem Mobiltelefon hier unterwegs): dein Ausgangsproblem bezog sich auf die Veraenderung bestehender Objekte, die in zwei Listen - gefiltern und ungefiltert - gefuehrt werden koennen, was dir aber irgendwie nix war. Auf dieses Problem haben sich meine Antworten hier bezogen.

Doch dein Beispiel hier zeigt ein voellig anderes Problem, das *hinzufuegen* eines Objektes an eine eine Liste, mit der Erwartungshaltung, das waere dann auch bei der anderen so. Das ist ein komplett anderes Problem. Niemand hat behauptet, dass da Liste A durch anhaengen an eine andere Liste B implizit mitveraendert wird.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

@__deets__: Ich glaube, er will keinen Eintrag anhängen sondern etwas an die Zeichenkette anhängen. Ersetzt dafür aber das Element der Liste und verändert es nicht.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ah, du hast recht. Er modifiziert das Element in-place. Damit ist mein Beitrag zwei hoeher natuerlich Unfug, das entspricht schon dem urspruenglichen Szenario. Aber mutable vs immutable ist natuelich immer noch das Thema.
Antworten