Dropdown Box mit Tkinter // Listbox Problem

Fragen zu Tkinter.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

Hallo Leute, ich such schon seit einiger Zeit die Möglichkeit mit Tkinter eine Dropdownbox zu erstellen, in etwa so:

Bild

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 (?)
Zuletzt geändert von sorgenlos am Montag 3. Dezember 2007, 15:42, insgesamt 1-mal geändert.
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

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. :wink:
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

okay das mit der Listbox scheint mir zu passen :D
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

Code: Alles auswählen

listbox.place(x=30, y=600, width=80, height=50)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Normalerweise nimmt man da einen Layout-Manager.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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 :D


Der Quelltext sieht dementsprechend aus :lol:

ich habe alle Widgets mit dem place-Manager verwaltet,

würde es mir "Quellcodezeilen" ersparen wenn ich mit "Packer" oder "Gridder" arbeite?
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.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

wie teil ich denn eine gui in mehrere container? :shock:
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.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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?
BlackJack

An Introduction to Tkinter und Thinking in Tkinter sind zwei nützliche Texte zum Thema Tkinter-Programmierung.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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 :?
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

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.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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 :x
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

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)
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

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)
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

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()
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.
Benutzeravatar
sorgenlos
User
Beiträge: 69
Registriert: Donnerstag 15. Februar 2007, 00:52

Gut, das Globlisieren funktioniert jetzt einwandfrei :wink: 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 :D Ich will einfach nur das die Auswahl in den Boxen bleibt und nicht verschwindet.

hier ein flash-Filmchen, damit ihr versteht was ich meine :lol: :

Klick mich zum Anschaun

ich find dazu auch nichts in keinem der offiziellen Tutorials :?
Antworten