Seite 1 von 2
Dropdown Box mit Tkinter // Listbox Problem
Verfasst: Sonntag 2. Dezember 2007, 13:12
von sorgenlos
Hallo Leute, ich such schon seit einiger Zeit die Möglichkeit mit Tkinter eine Dropdownbox zu erstellen, in etwa so:
bin aber immer noch auf der suche
zur not steig ich auf wxPython um wenn:
1. sowas dort möglich ist (?)
2. ich die GUI hauptsächlich in Tkinter aufbaue und die Dropdownbox mit wxPython einfach "zwischenquetsche" kann (?)
Verfasst: Sonntag 2. Dezember 2007, 13:21
von schlangenbeschwörer
Tix hat sowas. Ansonsten gibts die Tkinter.Listbox, allerdings müsstes du dir da selbst etwas basteln. Nr. 2 wird nicht möglichsein. Wobei, wenn man in 2 threads ein Tkinter und eine andere GUI laufen lässt, die übernanderlegt, die Fensterränder entfernt, könnte es klappen.

Verfasst: Sonntag 2. Dezember 2007, 14:52
von sorgenlos
okay das mit der Listbox scheint mir zu passen

aber wie definier ich die position der Listbox in der gui?
http://effbot.org/tkinterbook/listbox.htm hilft mir auch nicht mehr weiter leider :/
edit: ich habs hinbekommen
Verfasst: Sonntag 2. Dezember 2007, 16:50
von Leonidas
Normalerweise nimmt man da einen Layout-Manager.
Verfasst: Sonntag 2. Dezember 2007, 17:15
von sorgenlos
Was versteht man unter Layout-Manager?
So ich hab nun ein paar Listboxes erstellt und rumexperimentiert,
aber ich bekomm es nicht hin das wenn ich 2 oder mehr Listboxen habe,
dass dann bei Auswahl in der 2. die Auswahl in der 1. Listbox bleibt,
sie verschwindet immer.
Ich hab auch schon mit selectmodes rumgespielt,
aber das scheint nichts zu nützen da diese nur innerhalb einer Listbox funktionieren.
Wie kann ich diese Problem denn lösen,
das tkinterbook gibt auch keine weiteren Informationen.
Verfasst: Sonntag 2. Dezember 2007, 17:46
von Leonidas
sorgenlos hat geschrieben:Was versteht man unter Layout-Manager?
Guck mal
hier.
Kurz zusammengefasst sind das Helfer die sich darum kümmern, dass die Widgets optimal auf dem Programmfenster plaziert sind, d.h. wenn das Fenster vergrößert wird, dass die Widgets dann immer noch an den richtigen Stellen sind.
Das ist quasi das Gegenteil von dem was Visual Basic immer geboten hat und wenn man mal darüber nachdenkt ist das auch viel angenehmer zu nutzen.
Verfasst: Sonntag 2. Dezember 2007, 18:12
von sorgenlos
Das Programm an dem ich grad schreibe hat an die
40 Entry-Felder
50 Labels
und eine Hand voll Buttons
und dazu werden noch knapp 10 Listboxen hinzukommen wenn ich das
Problem mit dem Fokus nicht hätte
Der Quelltext sieht dementsprechend aus
ich habe alle Widgets mit dem place-Manager verwaltet,
würde es mir "Quellcodezeilen" ersparen wenn ich mit "Packer" oder "Gridder" arbeite?
Verfasst: Sonntag 2. Dezember 2007, 19:04
von BlackJack
Eigentlich ist das erstmal egal weil der Place-Layoutmanager dafür einfach das falsche Werkzeug ist.
Quelltextzeilen sparst Du wenn Du sich wiederholende Code-Abschitte, die nur in "Daten" variieren, in Schleifen steckst.
Und falls das bis jetzt alles direkt in einem Widget steckte, solltest Du Dir überlegen es auf mehrere Container auf zu teilen, um mehr Übersicht im Quelltext und vielleicht sogar in der Anzeige zu schaffen.
Verfasst: Sonntag 2. Dezember 2007, 19:18
von sorgenlos
wie teil ich denn eine gui in mehrere container?

Verfasst: Sonntag 2. Dezember 2007, 20:08
von BlackJack
Indem nicht alle Widgets auf dem Fenster platziert werden, sondern Sachen die zusammengehören in Frames gruppiert werden, und diese dann auf dem Fenster platziert werden.
Zum Beispiel kann man je ein Label plus dazugehöriges Entry in einen Frame stecken. Oder eine Gruppe von Labels und Entries in einen Frame mit Grid-Layout, usw.
Verfasst: Sonntag 2. Dezember 2007, 21:00
von sorgenlos
Wenn ich wüsste wie sowas geht, und wie die Struktur dabei aussieht dann würde ich das auch machen, kannst du mir sagen wo ich da was konkretes anchlesen kann, bzw. Hilfestellung bekommen kann?
Verfasst: Montag 3. Dezember 2007, 08:54
von BlackJack
An Introduction to Tkinter und
Thinking in Tkinter sind zwei nützliche Texte zum Thema Tkinter-Programmierung.
Verfasst: Montag 3. Dezember 2007, 11:10
von sorgenlos
edit: Danke BlackJack, dass sieht sehr gut aus, da werde ich mich mal durchlesen-arbeiten!
Ich will nich für jedes meiner Probleme ein neues Thema eröffnen deswegen kommts hier rein,
hat ja auch was mit Listboxen zu tun, und zwar hab ich eine Listbox mit 2 Einträgen.
Ich kann meine Selection mit einem Button auslesen lassen und den die Selection in der Shell printen lassen.
Code: Alles auswählen
from Tkinter import *
class test_GUI:
def __init__(self):
fenster=Tk()
fenster.geometry("110x75")
#Die Listbox
lb = Listbox(fenster, selectmode=EXTENDED)
lb.pack()
lb.place(x=18, y=10, width=80, height=35)
lb.insert(END, "Peter")
lb.insert(END, "Hans")
#Der Button
test_button=Button(fenster,text="test", command=self.read)
test_button.place(x=6, y=50, width=100, height=20)
#die Methode
def read(event):
text = lb.get(lb.curselection())
print text
__name__ == '__main__'
dasFenster = test_GUI()
mainloop()
Vom Prinzip her ziemlich simpel bis auf den Fehler:
Code: Alles auswählen
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python25\lib\lib-tk\Tkinter.py", line 1403, in __call__
return self.func(*args)
File "C:\123.py", line 19, in read
text = lb.get(lb.curselection())
NameError: global name 'lb' is not defined
ich versteh nicht wieso lb nicht "defined" sein soll, dass ist doch in Zeile 8 passiert...
Vielleicht ist der Fehler zu einfach zu beheben, dass ich nicht drauf komme

Verfasst: Montag 3. Dezember 2007, 11:20
von BlackVivi
Du musst lb an die Klasse binden mit dem Schlüsselwort self...
Also statt lb lieber self.lb, weil du es ja auch außerhalb der __init__ Methode benutzen willst.
Verfasst: Montag 3. Dezember 2007, 11:27
von sorgenlos
Hab ich auch versucht, mit self.* es zu globalisieren aber der Fehler bleibt.
Kannst du es mal bei dir versuchen? Es kann ja nicht sein, dass es bei mir nicht funktioniert

Verfasst: Montag 3. Dezember 2007, 11:32
von BlackVivi
Wenn ich jedes lb mit'n self.lb ersetz und bei read natürlich noch das Argument self hinzukommt
Code: Alles auswählen
def read(self, event):
text = self.lb.get(lb.curselection())
print text
Jedoch gibts dann noch Probleme mit dem Eventbinding und außerdem benutzt du einen außerordentlich bösen Sternimport. Mit self globalisierst du auch nicht, sondern bindest die Variable an die Instanz der Klasse.
(Ich würd' dir ja jetzt'n funktionierendes Beispiel geben, aber von Tkinter hab ich recht wenig Ahnung)
Verfasst: Montag 3. Dezember 2007, 11:41
von sorgenlos
Was ist am Sternimport denn böse?
und deinen Lösungsansatz hatte ich auch schon eingeschlagen, aber:
Code: Alles auswählen
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python25\lib\lib-tk\Tkinter.py", line 1403, in __call__
return self.func(*args)
TypeError: read() takes exactly 2 arguments (1 given)
Verfasst: Montag 3. Dezember 2007, 12:04
von BlackVivi
Man überschreibt Schlüsselwörter, die Übersichtlichkeit geht flöten und der Quellcode wird unübersichtlicher. Hab'n bissel rumgespielt, ist sicherlich total schlechter tkinter Stil, jedoch funktioniert die Version...
(UM NOCHMAL DARAUF HINZUWEISEN!!! Ich bin kein Tkinter Programmierer. Der Quelltext stellt keine Referenz dar!)
Code: Alles auswählen
import Tkinter as tk
class test_GUI:
def __init__(self):
fenster = tk.Tk()
fenster.geometry("110x75")
#Die Listbox
self.lb = tk.Listbox(fenster, selectmode=tk.EXTENDED)
self.lb.pack()
self.lb.place(x=18, y=10, width=80, height=35)
self.lb.insert(tk.END, "Peter")
self.lb.insert(tk.END, "Hans")
#Der Button
test_button = tk.Button(fenster, text="test", command=self.read)
test_button.place(x=6, y=50, width=100, height=20)
#die Methode
def read(self):
for i in self.lb.curselection():
text = self.lb.get(i)
print text
__name__ == '__main__'
dasFenster = test_GUI()
tk.mainloop()
Verfasst: Montag 3. Dezember 2007, 12:08
von BlackJack
Vielleicht solltest Du Dich erst einmal nur mit Klassen, ohne GUI beschäftigen. Das erste Argument bei Methoden ist immer die Instanz auf der die Methode aufgerufen wird. Dein `event` ist also eigentlich `self` und beim Druck auf den den Button wird an die `command`-Funktion gar nichts übergeben.
Code: Alles auswählen
import Tkinter as tk
class TestGUI:
def __init__(self):
fenster = tk.Tk()
self.listbox = tk.Listbox(fenster,
selectmode=tk.EXTENDED,
width=10,
height=2)
for name in ('Peter', 'Hans'):
self.listbox.insert(tk.END, name)
self.listbox.pack()
test_button = tk.Button(fenster, text='test', command=self.read)
test_button.pack()
def read(self):
print self.listbox.get(self.listbox.curselection())
if __name__ == '__main__':
das_fenster = TestGUI()
tk.mainloop()
Da sind so gut wie alle manuelle Grössenangaben herausgenommen, das kann der Layout-Manager im allgemeinen besser als der Programmierer, weil der ja gar nicht alle möglichen Schrifteinstellungen, Schriftgrössen und Windowmanager testen kann.
Die Grösse der `Listbox` sollte man auch dort beim Konstrukturaufruf angeben, weil die Höhe da z.B. in Zeilen/Einträgen angegeben wird. Das passt dann immer, im Gegensatz zu dem `place()` wo bei mir in der Anzeige der untere Rand vom zweiten Eintrag nicht mehr dargestellt wurde.
Verfasst: Montag 3. Dezember 2007, 14:53
von sorgenlos
Gut, das Globlisieren funktioniert jetzt einwandfrei

vielen Dank nochmal an euch, das hat noch paar andere Fehler behoben.
Das einzige was jetzt noch fehlt ist der "Fokus" ich weiß nicht wie das sonst nennen soll

Ich will einfach nur das die Auswahl in den Boxen bleibt und nicht verschwindet.
hier ein flash-Filmchen, damit ihr versteht was ich meine

:
Klick mich zum Anschaun
ich find dazu auch nichts in keinem der offiziellen Tutorials
