Löschen und WIedererstellen von Widgets

Fragen zu Tkinter.
Antworten
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

Also ich bin noch bei meinem Adressbuchprogramm.

Code: Alles auswählen

def search():
    input = entry_suche.get()
    werte = (input, )
    sql = "SELECT * FROM adressen WHERE vorname = (?)"
    cursor.execute(sql, werte)
    search_result = cursor.fetchall()
    try:
        label_vorname.destroy()
        entry_vorname.destroy()
        label_nachname.destroy()
        entry_nachname.destroy()
        label_telefon.destroy()
        entry_telefon.destroy()
    except:
        pass
    listbox_results = Listbox(window, selectmode=SINGLE)
    listbox_results.place(x=400, y=350, anchor="center")
    
def newentry():
    countentry()
    try:
        listbox_results.destroy()
    except:
        pass
    label_vorname.place(x=370, y=250, anchor="e")
    entry_vorname.place(x=370, y=250, anchor="w")
    label_nachname.place(x=370, y=300, anchor="e")
    entry_nachname.place(x=370, y=300, anchor="w")
    label_telefon.place(x=370, y=350, anchor="e")
    entry_telefon.place(x=370, y=350, anchor="w")
    button_entry.place(x=400, y=410, anchor="center")
Wenn ich hier newentry() ausführe erstellt er erstmal alles wunderbar. Auch beim anschließenden ausführen von search() werden die widgets gelöscht. Wenn ich dann allerdings wieder die W. mit newentry() erstellen will kommt folgender Fehler:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1406, in __call__
    return self.func(*args)
  File "addyBs.py", line 53, in newentry
    label_vorname.place(x=370, y=250, anchor="e")
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1819, in place_configure
    + self._options(cnf, kw))
TclError: bad window path name ".136373324"
Was mir jetzt noch einfällt das mit dem Layoutmanager. Ich wurde ja darauf hingewiesen place() nicht zu benutzen. Allerdings finde ich keine günstige Alternative. Mit pack() bekommt man ja kaum Struktur außer das die Widgets an irgendeiner Seite kleben. Auch mit grid() ist es schwierig da man sie nur sehr ungenau platzieren kann... Programme sind doch überall so sauber aufgebaut und alles da wo es hinsoll? Kann mir da jemand ein Tipp geben?

Danke schonmal!
pyStyler
User
Beiträge: 311
Registriert: Montag 12. Juni 2006, 14:24

Hallo secretwz,

zeige mal mehr vom Code bitte! am besten so, dass man die Gui und die Fehlermeldung sehen kann.


Gruss
pyStyler
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

Danke für deine Antwort!

Also das ist mein kompletter Code:

Code: Alles auswählen

#imports
from Tkinter import *
import anydbm
import sqlite3 

if __name__ == "__main__": 
    #Fenstererstellung
    window = Tk()
    window.config(bg="#cde66a")
    window.geometry("800x600")
    
    
    connection = sqlite3.connect("daten.db")
    cursor = connection.cursor()
    
    try:
        cursor.execute("""CREATE TABLE adressen ( 
            vorname TEXT, nachname TEXT, telefon INTEGER)""")
    except:
        pass
    
    #Funktionen
    def search():
        #input = entry_suche.get()
        #werte = (input, )
        #sql = "SELECT * FROM adressen WHERE vorname = (?)"
        #cursor.execute(sql, werte)
        #search_result = cursor.fetchall()
        try:
            label_vorname.destroy()
            entry_vorname.destroy()
            label_nachname.destroy()
            entry_nachname.destroy()
            label_telefon.destroy()
            entry_telefon.destroy()
        except:
            pass
        
        listbox_results = Listbox(window, selectmode=SINGLE)
        listbox_results.place(x=400, y=350, anchor="center")
        #newentry()
        #entry_vorname.insert(0,search_result[0][0])
        #entry_nachname.insert(0,search_result[0][1])
        #entry_telefon.insert(0,search_result[0][2])
        countentry()
    
    def newentry():
        countentry()
        try:
            listbox_results.destroy()
        except:
            pass
        
        label_vorname.place(x=370, y=250, anchor="e")
        entry_vorname.place(x=370, y=250, anchor="w")
        label_nachname.place(x=370, y=300, anchor="e")
        entry_nachname.place(x=370, y=300, anchor="w")
        label_telefon.place(x=370, y=350, anchor="e")
        entry_telefon.place(x=370, y=350, anchor="w")
        button_entry.place(x=400, y=410, anchor="center")
        
    
    def test():
        entry_vorname.place(x=370, y=250, anchor="w")
    
    button_test = Button(window, text="test", command=test)
    button_test.place(x=400, y=500, anchor="center")
    
    
    def entry():
        vorname = entry_vorname.get()
        nachname = entry_nachname.get()
        telefon = entry_telefon.get()
        werte = (vorname, nachname, telefon)
        sql = "INSERT INTO adressen VALUES (?, ?, ?)" 
        cursor.execute(sql, werte)
        connection.commit()
        countentry()
    
    def countentry():
        cursor.execute("""SELECT COUNT(*) FROM adressen""")
        anzahl = cursor.fetchall()
        s = "Die Datenbank enthaellt " + str(anzahl[0][0]) + " Objekte!"
        label_countentry.config(text=s)
        label_countentry.place(x=400, y=450, anchor="center")
        
    #Erstellung von Labels
    label_welcome = Label(window, text="Willkommen bei AddyBs dem Adressbuch!", font=("Arial",18,"bold"), bg="#cde66a")
    label_suche = Label(window, text="Suche", font=("Arial", "12", "underline"), bg="#cde66a")
    label_vorname = Label(window, text="Vorname: ", bg="#cde66a")
    label_nachname = Label(window, text="Nachname: ", bg="#cde66a")
    label_telefon = Label(window, text="Telefon: ", bg="#cde66a")
    label_countentry = Label(window, text="test", bg="#cde66a")
    
    #Erstellung von Buttons
    button_suche = Button(window, text="Suche starten", command=search)
    button_newentry = Button(window, text="Neuer Eintrag", command=newentry)
    button_entry = Button(window, text="Eintragen", command=entry)
    
    #Erstellung von Entrys
    entry_suche = Entry(window)
    entry_vorname = Entry(window)
    entry_nachname = Entry(window)
    entry_telefon = Entry(window)
    
    #Aufbau des Fensters
    label_welcome.place(x=400, y=30, anchor="center")
    label_suche.place(x=400, y=100, anchor="center")
    entry_suche.place(x=395, y=130, anchor="e")
    button_suche.place(x=405, y=130, anchor="w")
    button_newentry.place(x=400, y=180, anchor="center")
    
    
    #Fensterschleife
    window.mainloop()
EDIT: achso^^ also die test-Funktion und alles was dazugehört kann weg... und das wo ich ein # für Kommentar vorgemacht hab kann ignoriert werden^^
pyStyler
User
Beiträge: 311
Registriert: Montag 12. Juni 2006, 14:24

Hallo secretwz,

deine Fehlermeldung kommt daher, dass du versuchst ein Widget
zu löschen oder erstellen, den es noch nicht gibt oder schon mal gab der aber mit destroy() gelöscht worden ist.

Mit destroy löschen heisst auch, das Löschen der Internespeicheradresse der Widgets.

Dass heisst-
führst du dein Script aus und drücktst auf suche starten jetzt werden
die Widgets für suche aufgebaut und dargestellt! Gleichzeitig löschst du aber auch
mit try ( im übrigen ist die try hier total überflüssig da man mit einer -
schleife hier besser bedient ist. ) und label_vorname.destroy().... die Interneadresse aller anderen
widgets die mit neuer eintrag Button dargestellt werden sollen.

In der newentry Funktion ist es genau so. Wobei es hier eigentlich eine Fehlermeldung geben müsste, da
listbox_results.destroy() nicht bekannt ist.

Code: Alles auswählen

def newentry(): 
        countentry()
        listbox_results.destroy() 
        #~ try: 
            #~ listbox_results.destroy() 
        #~ except: 
            #~ print 'NICHTS PASSIERT'

Gruss
pyStyler
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

Danke für deine Antwort!
Wie kann man denn sonst widgets entfernen oder verstecken um sie wieder zu erstellen zu können?
pyStyler
User
Beiträge: 311
Registriert: Montag 12. Juni 2006, 14:24

mit pack_forget(), grid_forget() oder place_forget().
Aber versuch mal place nicht zu verwenden.
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

ok danke ;)
Ja das mit dem place() hat mir Eydu ja auch schon gesagt, wie schon im ersten post erwähnt:

Code: Alles auswählen

Was mir jetzt noch einfällt das mit dem Layoutmanager. Ich wurde ja darauf 
hingewiesen place() nicht zu benutzen. Allerdings finde ich keine günstige Alternative. Mit
 pack() bekommt man ja kaum Struktur außer das die Widgets an irgendeiner Seite kleben.
 Auch mit grid() ist es schwierig da man sie nur sehr ungenau platzieren kann... 
Programme sind doch überall so sauber aufgebaut und alles da wo es hinsoll? Kann mir da 
jemand ein Tipp geben? 
BlackJack

@secretwz: Sauberes und sich anpassendes Layout bekommt man mit `pack()`, `grid()` und `Frame`\s hin. Bei `pack()` ist es wichtig, dass man die Widgets innerhalb eines Container-Widgets immer nur an einer Seite hinzufügt und die nicht mischt. Das kann zu sehr schwer vorhersagbaren Ergebnissen führen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo secretwz

Ich habe dein Skript ein wenig umgestaltet. Habe konsequent den Tk-Pack-Layout-Manager verwendet. Es ist ähnlich wie mit den *Sizers* bei wxPython. Die Gefahr ist gross, dass einem leicht die Übersicht verloren geht. Kann in den Griff bekommen werden indem man den ganzen Fenster-Layout im Detail plant. In deinem Skript habe ich die *search*-Funktion nicht weiter ausprogrammiert das möchte ich lieber dir überlassen. Die Problemlöseung deines Projektes kann natürlich auf viele Arten angegengen werden. Da gib es sicher noch ander Forummitglieder die eigene Ideen und bessere haben. Jetzt kannst du mit vergrössern bzw. verkleinern des Hauptfensters sehen wie die Positionen der einzelnen Widgets proportional verändert werden. Hier das modifizierte Skript zum weiter experimentieren:

http://paste.pocoo.org/show/138307/

Mit den Schaltflächen 'Neue Eingabe' und 'Eintragen' wird der Rahmen mit allen Daten-Eingaberelevanten Widgets Sichtbar gemacht bzw. wieder versteckt.
(Habe das ganze nur unter Linux getestet!)

Gruss wuf :wink:
Take it easy Mates!
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

@BlackJack: danke ;) wuf hat mir ja jetzt eine möglichkeit geschickt ;)

@ wuf: Danke für deine Antwort und deine Mühe die du dir gemacht hast!

Ja das von den *-importen wurde mir ja schonmal abgeraten, wollt ich eig auch noch ändern.
Ich habe inzwischen das Programm eigentlich "fertig" (fertig ist es ja nie richtig *g*)
Ich werde mir aufjedenfall nochmal das mit dem Layout in deiner Version anschaun und übernehmen danke ;)

Hab übrigens sowieso auch nur Linux ;)
Ich melde mich später nochmal für weitere Fragen :P bis dann
secretwz
User
Beiträge: 40
Registriert: Mittwoch 14. Januar 2009, 22:01

Ich habe mich jetzt mal mit dem Layout bei deinem Code beschäftigt und ich denke ich habe das wichtigste verstanden nur habe ich hier zu nochmal eine Frage:

Code: Alles auswählen

data_label_frame.propagate(0)
        data_label_frame.config(height=data_label.winfo_reqheight())
        data_entry_frame.propagate(0)
        data_entry_frame.config(height=data_entry.winfo_reqheight())
Mit dem data_label_frame.config(...) wird doch die Größe des Frames an die Erstellung der Labels angepasst oder?
Und wozu das propagate() ist habe ich auch nichts gefunden was ich verstanden habe. Hoffe jmd kann mir das erklären. Danke!
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo secretwz

Deine Frage ist ein Beweis dafür, dass du mein Skript ganz genau angeschaut hast. Wird der propagate()-Methode des Frame-Widget eine 0 bzw. False übergeben schaltet dies die automatische Layout-Anpassung des Frames auf die Geometrie der ins Frame eingebetteten Widges ab. Das heisst die Geometrie des Frames muss über seine Optionen '.height' oder 'width' gesetzt werden.

Der Grund, dass ich die automatische Geometrie-Steuerung des Frames abgeschaltet habe ist ich wollte eine horizontal symetrische Verteilung der beiden Widgets 'entry_search' und 'button_search' erreichen. Auf diese Art konnte ich mein Willen durchsetzen. Auch in Tkinter gibt es manchmal Momente wo man sich experimentieren durchsetzen muss. Es ist gut möglich, dass es noch andere Wege gibt um dies zu erzielen.

Wenn die automatische Geometrie-Steuerung des Frames abgeschalten ist muss die Geometrie der eingebetteten abgefragt werden. In unserem Fall ist dies die Höhe von 'entry_search' und 'button_search' mittels der Methode-'.winfo_reqheight()' und setzt damit die Option 'height' ihrer' Behälter-Frames.

Ich habe das Skript auf ein Minimum reduziert um einige Beispiele zu zeigen. Hier das das erste Skript mit Abschaltung der automatischen Geometrie-Steuerung der Frames:

Code: Alles auswählen

# coding: UTF-8

#imports
import Tkinter as tk
import anydbm
import sqlite3

MAIN_BG_COLOR = "#cde66a"

if __name__ == "__main__":
    #Fenstererstellung
    window = tk.Tk()
    window.config(bg=MAIN_BG_COLOR)
    window.geometry("800x200")

    # Hauptrahmen
    window_frame = tk.Frame(window, bg=MAIN_BG_COLOR)
    window_frame.pack(side='top', fill='both', expand='yes')

    #--- Erstellung des Fenster-Layouts ---#

    # Label: Begruessung
    label_welcome_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_welcome_frame.pack(side='top', fill='x')
    label_welcome = tk.Label(label_welcome_frame, font=("Arial",18,"bold"),
        bg=MAIN_BG_COLOR, text="Willkommen bei AddyBs dem Adressbuch!")
    label_welcome.pack(pady=10)

    # Label: Suche
    label_suche_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_suche_frame.pack(side='top', fill='x')
    label_suche = tk.Label(label_suche_frame, text="Suche", font=("Arial",
        "12", "underline"), bg=MAIN_BG_COLOR)
    label_suche.pack(anchor='center')

    # Entry: Suche
    # Button: Suche
    search_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    search_frame.pack(side='top', fill='x', pady=10)

    entry_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    entry_search_frame.pack(side='left', fill='x', expand='yes')

    button_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    button_search_frame.pack(side='right', fill='x', expand='yes')

    entry_search = tk.Entry(entry_search_frame, width=20)
    entry_search.pack(side='right', padx=4)

    button_search = tk.Button(button_search_frame,text="Suche starten",
        command=None)
    button_search.pack(side='left', padx=4)

    entry_search_frame.propagate(0)
    entry_search_frame.config(height=entry_search.winfo_reqheight())
    button_search_frame.propagate(0)
    button_search_frame.config(height=button_search.winfo_reqheight())

    # Fensterschleife
    window.mainloop()
Bitte die horizontale Position der beiden Widgets 'entry_search' und 'button_search' beachten!

Hier das Skript ohne Abschaltung der Geometrie-Steuerung:

Code: Alles auswählen

# coding: UTF-8

#imports
import Tkinter as tk
import anydbm
import sqlite3

MAIN_BG_COLOR = "#cde66a"

if __name__ == "__main__":
    #Fenstererstellung
    window = tk.Tk()
    window.config(bg=MAIN_BG_COLOR)
    window.geometry("800x200")

    # Hauptrahmen
    window_frame = tk.Frame(window, bg=MAIN_BG_COLOR)
    window_frame.pack(side='top', fill='both', expand='yes')

    #--- Erstellung des Fenster-Layouts ---#

    # Label: Begruessung
    label_welcome_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_welcome_frame.pack(side='top', fill='x')
    label_welcome = tk.Label(label_welcome_frame, font=("Arial",18,"bold"),
        bg=MAIN_BG_COLOR, text="Willkommen bei AddyBs dem Adressbuch!")
    label_welcome.pack(pady=10)

    # Label: Suche
    label_suche_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_suche_frame.pack(side='top', fill='x')
    label_suche = tk.Label(label_suche_frame, text="Suche", font=("Arial",
        "12", "underline"), bg=MAIN_BG_COLOR)
    label_suche.pack(anchor='center')

    # Entry: Suche
    # Button: Suche
    search_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    search_frame.pack(side='top', fill='x', pady=10)

    entry_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    entry_search_frame.pack(side='left', fill='x', expand='yes')

    button_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    button_search_frame.pack(side='right', fill='x', expand='yes')

    entry_search = tk.Entry(entry_search_frame, width=20)
    entry_search.pack(side='right', padx=4)

    button_search = tk.Button(button_search_frame,text="Suche starten",
        command=None)
    button_search.pack(side='left', padx=4)

    #entry_search_frame.propagate(0)
    #entry_search_frame.config(height=entry_search.winfo_reqheight())
    #button_search_frame.propagate(0)
    #button_search_frame.config(height=button_search.winfo_reqheight())

    # Fensterschleife
    window.mainloop()
Bitte wieder die horizontale Position der beiden Widgets 'entry_search' und 'button_search' beachten!

Hier noch das Skript mit Abschaltung der Geometrie-Steuerung aber ohne das setzen der Option 'height' der Widgets 'entry_search' und 'button_search':

Code: Alles auswählen

# coding: UTF-8

#imports
import Tkinter as tk
import anydbm
import sqlite3

MAIN_BG_COLOR = "#cde66a"

if __name__ == "__main__":
    #Fenstererstellung
    window = tk.Tk()
    window.config(bg=MAIN_BG_COLOR)
    window.geometry("800x200")

    # Hauptrahmen
    window_frame = tk.Frame(window, bg=MAIN_BG_COLOR)
    window_frame.pack(side='top', fill='both', expand='yes')

    #--- Erstellung des Fenster-Layouts ---#

    # Label: Begruessung
    label_welcome_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_welcome_frame.pack(side='top', fill='x')
    label_welcome = tk.Label(label_welcome_frame, font=("Arial",18,"bold"),
        bg=MAIN_BG_COLOR, text="Willkommen bei AddyBs dem Adressbuch!")
    label_welcome.pack(pady=10)

    # Label: Suche
    label_suche_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    label_suche_frame.pack(side='top', fill='x')
    label_suche = tk.Label(label_suche_frame, text="Suche", font=("Arial",
        "12", "underline"), bg=MAIN_BG_COLOR)
    label_suche.pack(anchor='center')

    # Entry: Suche
    # Button: Suche
    search_frame = tk.Frame(window_frame, bg=MAIN_BG_COLOR)
    search_frame.pack(side='top', fill='x', pady=10)

    entry_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    entry_search_frame.pack(side='left', fill='x', expand='yes')

    button_search_frame = tk.Frame(search_frame, bg=MAIN_BG_COLOR)
    button_search_frame.pack(side='right', fill='x', expand='yes')

    entry_search = tk.Entry(entry_search_frame, width=20)
    entry_search.pack(side='right', padx=4)

    button_search = tk.Button(button_search_frame,text="Suche starten",
        command=None)
    button_search.pack(side='left', padx=4)

    entry_search_frame.propagate(0)
#    entry_search_frame.config(height=entry_search.winfo_reqheight())
    button_search_frame.propagate(0)
#    button_search_frame.config(height=button_search.winfo_reqheight())

    # Fensterschleife
    window.mainloop()
Die Höhe der Frames für 'entry_search' und 'button_search': ist nicht gesetzt! Nur als horizontale Striche sichtbar.

Hoffe dir ein wenig geholfen zu haben. Ob mein Weg der einzige ist weis ich nicht.

Gruss wuf :wink:
Take it easy Mates!
Antworten