mainloop Fehlermeldung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
tom1968
User
Beiträge: 38
Registriert: Sonntag 30. Juni 2013, 09:54

Für mein Tabellenprojekt habe ich nun ein Standard GUI Frontend erstellt, also eines mit dem ich jede zweidimensionale Liste öffnen kann.
Dazu habe ich lediglich mein spezifisches Frontend etwas angepasst und um die Option erweitert dass man beim Programmstart den Namen der Datei eingeben kann die geladen werden soll - dies ohne eine weitere Klasse zu eröffnen, siehe Code unten.

Code: Alles auswählen

......
def dateiladen():
    global tabelle
    global fenster
    dateiname = eingabefeld.get()
    if dateiname != "":
        if dateiname[-3:] != "dmp":
            dateiname = dateiname + ".dmp"
        with open(dateiname, "rb") as inhalt:
            tabelle = pickle.load(inhalt)
        fenster.destroy()
        tabelle = Tabelle(tabelle)
        fenster = Fenster()   # ???? ab hier wird der mainloop von main verlassen, ohne diesen wirklich zu beenden ???? 
        # hier folgt später noch was um Falscheingaben abzufangen        
   
def main():
    global eingabefeld
    global infolabel
    global fenster
    fenster = Tk()
    infolabel=Label(master = fenster, text ="Bitte Dateiname eingeben")
    infolabel.pack()
    eingabefeld= Entry(master = fenster, width=25)
    eingabefeld.pack()
    button =Button(master = fenster, text ="o.k.", width=20, command = dateiladen)
    button.pack()
    beendenbutton=Button(master = fenster, text ="Beenden", width=20, command = beenden)
    beendenbutton.pack()
    mainloop()

if __name__ == '__main__':
    main()
Das funktioniert soweit auch, muss nur zusehen ob und wie ich diese Globalen Variablen noch beseitigen kann, die wurden jetzt mal auf die "Schnelle" deklariert damit das Programm so überhaupt läuft.

Wenn ich das Programm nun unter Win 7 mit starte läuft es tadellos und nach dem Beenden erscheint in der Konsole der übliche Text "None" und "Drücken sie eine beliebige Taste...". Unter Linux ist es dasselbe.
Durch Zufall bemerkte ich aber nun dass ich beim Beenden eine Fehlermeldung bekomme wenn ich das Programm per IDLE 'laufen' lasse (Bisher nur auf dem Win Rechner getestet, aber das wird bei Linux nicht anders sein)

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Thomas\Desktop\dbnorm\trk_standard_frontend.py", line 508, in <module>
    main()
  File "C:\Users\Thomas\Desktop\dbnorm\trk_standard_frontend.py", line 505, in main
    mainloop()
  File "C:\Python30\lib\tkinter\__init__.py", line 323, in mainloop
    _default_root.tk.mainloop(n)
  File "C:\Python30\lib\tkinter\__init__.py", line 1407, in __call__
    raise SystemExit(msg)
SystemExit: None
>>> 
Ich vermute mal dass das damit zusammenhängt dass ich den mainloop() in obigem Listning (4.letzte Zeile) eigentlich nicht so richtig verlasse. Ein zweiter mainloop befindet sich am Ende der __init__ der 'class Fenster' aus der heraus das Programm per quit() beendet wird.
Einfach so ignorieren will ich das eigentlich nicht, auch wenn diese Fehlermeldung nur in der IDLE auftritt.
Wenn mein Verdacht stimmt und es an dem ersten mainloop liegt - wie kann diesen mainloop 'regelgerecht' verlassen ?

Thomas
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

GUIs aus IDLE zu starten ist eine schlechte Idee, das führt gerne zu Problemen. Starte dein Programm daher direkt von der Konsole, wie man es eigentlich mit allen Programmen machen sollte.

Ein ganz schlechtes Zeichen ist es allerdings, dass du die Hauptschleife noch einmal in deiner Fenster-Klasse startest. So funktioniert die Arbeit mit GUIs nicht, du musst dich von streng linearen Programmabläufen verabschieden. In deinem gesamten Programm darfst du mainloop nur genau einmal ausführen. Wenn du das öfter machst, dann deutet das auf einen Fehler hin. Am besten zeigst du mal deinen gesamten Code, dein jetziger sieht bereits stark verbesserungswürdig aus. Da sind sicher einige hilfreiche Hinweise für dich drin.
Das Leben ist wie ein Tennisball.
BlackJack

@tom1968: Mal was ganz anderes: Es gibt fertige Dateiauswahldialoge. Niemand lässt den Benutzer heute noch einen Dateinamen (ausschliesslich) als Text eingeben.
tom1968
User
Beiträge: 38
Registriert: Sonntag 30. Juni 2013, 09:54

@BlackJack
Einen 'Helden' der das noch macht scheints zu geben :lol:

Im Ernst: Kannst Du mir einen Tipp geben wo man sowas findet ?
BlackJack

@tom1968: In der Standardbibliothek: `tkFileDialog` in Python 2.x und `tkinter.filedialog` in Python 3.x.
tom1968
User
Beiträge: 38
Registriert: Sonntag 30. Juni 2013, 09:54

@BlackJack
Ich danke Dir

@EyDu

Den Code ganz zeigen würde hier (in diesem Thread) glaube ich nicht so passen, hab eh immer das Gefühl dass bei mir die Threadtitel mit dem Inhalt nicht so viel gemein haben weil die Diskussionen immer abtrifften ;)
Zudem ist das auch noch nicht ganz fertig, viele Kommentare wo ich noch was prüfen muss, viele Leerzeilen (ändere ich immer erst ganz am Schluß), teils noch nicht ganz fertiggestellte Methdoden, fehlende Funktionen, zu löschende print Funktionen die zu testzwecken werte in der Konsole ausgeben....

Da es hier um diese 'Mainloopsache' geht, zeige ich das Prinzip des Programms:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf - 8 *-

from tkinter import *
from trk_tabelle import Tabelle
import pickle

   
class Fenster(Tk):
    def  __init__(self):
        Tk.__init__(self)
                      
        # Entrys erzeugen 
        self.zeile0=Entry(self, width = 160)
        self.zeile0.grid(row = 0, column = 1, columnspan = 25, sticky = W)      
        self.zeile1=Entry(self, width = 160)
        self.zeile1.grid(row = 1, column = 1, columnspan = 25, sticky = W) 
        # ..... u.s.w. 13 Stück
                
        # Steuerbuttons erstellen
        self.anfang = Button(self, text = "|<<", width = 9, command = self.ersteZeile)
        self.anfang.grid(row = 15, column = 4, columnspan =3, sticky =W)
        #....
        self.suchen = Button(self, text = "Suchen", width =15, command = self.suchen)
        self.suchen.grid(row = 17, column = 6, columnspan =6)        
        #......u.s.w. vor , zurück, zum Anfang, zum Ende, neuer Datensatz, Datensatz löschen, speichern Beeenden....
        
        # Radiobuttons erstellen, jeweils am Ende der Entryfelder zum Sortieren
        
        # Daten laden und ausgeben (erstmalig)
        zeile = tabelle.get_row()
        self.ausgabe(zeile)
        self.spaltentyp = []      #wird benötigt um nach Eingaben zu prüfen ob der Datentyp verändert wurde(w. sortieren)
        self.spaltentyp = zeile[:]   

        # Label für Datentypanzeige hinter jeder Spalte (Zeile im Frontend) str, int oder float         

        self.mainloop() # *************Hier der zweite mainloop******************
    
    #Ab hier folgen die Methoden der Klasse Fenster
    #.....
    
def beenden():
    quit()

def dateiladen():
    global tabelle
    global fenster
    dateiname = eingabefeld.get()
    if dateiname != "":
        if dateiname[-3:] != "dmp":
            dateiname = dateiname + ".dmp"
        #try:
        with open(dateiname, "rb") as inhalt:
            tabelle = pickle.load(inhalt)
        infolabel.config(text="Datei geladen")
        fenster.destroy()
        tabelle = Tabelle(tabelle)
        fenster = Fenster()
        #except:
           # infolabel.config(text="Datei nicht gefunden")
           # eingabefeld.delete(0,END)
           # eingabefeld.insert(END, "")
           # return
  
def main():
    global eingabefeld
    global infolabel
    global fenster
    fenster = Tk()
    infolabel=Label(master = fenster, text ="Bitte Dateiname eingeben")
    infolabel.pack()
    eingabefeld= Entry(master = fenster, width=25)
    eingabefeld.pack()
    button =Button(master = fenster, text ="o.k.", width=20, command = dateiladen)
    button.pack()
    beendenbutton=Button(master = fenster, text ="Beenden", width=20, command = beenden)
    beendenbutton.pack()
    mainloop()    #***************Erster mainloop*****************

if __name__ == '__main__':
    main()
BlackJack

@tom1968: Ich würd an Deiner Stelle erst einmal alle ``global`` beseitigen. Und dann nicht tausend Sachen anfangen und Kommentare und Leerstellen lassen, sondern ein Programm aus vollständig funktionierenden Teilen zusammensetzen. Sonst hat man irgendwann ein „funktionierendes” Programm was auch all den Notlösungen und Basteleien besteht, wo man entweder keine Lust oder Zeit mehr hat etwas sauberes draus zu machen, oder immer wenn man irgendwo etwas richtet geht an anderer Stelle etwas kaputt, was dann wieder auf die Lust geht, weil das frustrierend ist.

Bei den 13 Eingabefeldern in `Fenster`: Wenn man anfängt Namen durchzunummerieren ist das ein „code smell”. Erst recht wenn man dann auch noch für jeden Namen Quelltext schreibt, der aussieht als wäre die beste Art ihn zu „schreiben” die „Kopieren und Einfügen”-Funktion des Texteditors. Statt der nummerierten Namen will man hier eine passende Datenstruktur verwenden — meistens eine Liste — und statt des kopierten Quelltextes eine Schleife.

Das `Fenster` würde ich flexibler aufbauen. Die Zeilen am Anfang gehören in einen `Frame`. Denn überleg mal was Du im Folgenden alles anpassen musst wenn sich die Anzahl der Zeilen mal ändern soll. Man müsste dann bei allen folgenden Widgets die Grid-Positionen per Hand anpassen. Das ist nervig und fehleranfällig.
tom1968
User
Beiträge: 38
Registriert: Sonntag 30. Juni 2013, 09:54

@BlackJack
Da hast Du mich missverstanden, die meisten Kommentarfelder habe ich hier nur eingefügt damit hier verständlich wird, wie ich das grundsätzlich aufgebaut habe.
Aber das Programm ist jetzt schon in einem Zustand dass man damit arbeiten kann, man kann Daten Eingeben, Ausgeben, Löschen, Sortieren, Speichern, Suchen.
Was an Programmfunktionen noch fehlt ist lediglich eine vernünftige Eingabe der Datei die geladen werden soll und eine Funktion mit der ich den Datentyp der Spalten ändern kann, momentan ist es so dass ausgehend vom ersten Datensatz der Datentyp fixiert ist, also bei der Eingabe der Typ geprüft ggf. wenn möglich korrigiert oder nicht aktzeptiert wird - das soll so auch standard bleiben und nur in Notfällen änderbar sein <<< dies aber nur hier in diesem Standard-Frontend, ein anderes fertiges Frontend lädt dann immer nur eine Datei fest- ohne Auswahlmöglichkeit, der Datentyp in den Spalten ist fix und die Ausgabe der jeweiligen Datenstruktur (Spaltenanzahl etc.) angepasst.
Meine Vorgehensweise ist schon die dass ich zuerst die __intit__ mache und dann eben Methode für Methode. Funktioniert dann alles versuche ich es zu optimieren (Schleifen statt copy & past, Prüfprints entfernen, Komentare anpassen, Leerzeichen entfernen etc.)
Antworten