'_tkinter.tkapp' object has no attribute 'Config'

Fragen zu Tkinter.
Antworten
jbaben
User
Beiträge: 24
Registriert: Dienstag 12. Januar 2016, 16:05

Hallo,
ich bekomme bei meiner Anwendung folgende Ausgabe:

Code: Alles auswählen

Traceback (most recent call last):
  File "J:\MeineProgramme\Python\Dateien\datei_rw_02.py", line 102, in <module>
    root = MyApp()
  File "J:\MeineProgramme\Python\Dateien\datei_rw_02.py", line 35, in __init__
    self.main()
  File "J:\MeineProgramme\Python\Dateien\datei_rw_02.py", line 42, in main
    Name = self.ConfigSectionMap("Zone A")
  File "J:\MeineProgramme\Python\Dateien\datei_rw_02.py", line 84, in ConfigSectionMap
    options = self.Config.options(section)
  File "C:\Program Files\Python35\Editor\WinPython-64bit-3.6.0.1Qt5\python-3.6.0.amd64\Lib\tkinter\__init__.py", line 2095, in __getattr__
    return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'Config'
Ich weiss leider nicht wie hier der Fehler zu beheben ist.
Ich weiss leider auch nicht wie ich die Zeilen-Nr. das Codelisting bekomme.
Meine Anwendung:

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Created on Mon Apr 10 12:46:37 2017

@author: Juergen
"""

import tkinter as tk
import configparser

INI_FILE_out  = "j:\\MeineProgramme\\Python\\Dateien\\config_sz.ini"
INI_FILE_in  = "j:\\MeineProgramme\\Python\\Dateien\\config_in.ini"

ZoneA = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
ZoneB = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
ZoneC = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
ZoneD = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
wert = ""

class MyApp(tk.Tk):
     def __init__(self):
        tk.Tk.__init__(self)
        
        fr = tk.Frame(self)
        fr.pack(side='bottom', fill='x')
        
        self.geometry("600x400")
        self.title('Test Dateien')

        button_ende = tk.Button(self, text = "Beenden", font = 12, command = self.close_fenster)
        button_ende.place(x= 500, y= 350)
        
        self.Lb1 = tk.Listbox(self, width = 50)
        self.Lb1.place(x=25, y=100)
        self.main()
        
     def main(self):
        Config = configparser.ConfigParser()
        Config.read(INI_FILE_out)
        print("Hallo\n")
        # INI-Datei lesen
        Name = self.ConfigSectionMap("Zone A")
        for i in range(len(Name)):
            wert = 'ZoneA' + str(i)
            ZoneA[i] = Config.get('Zone A', wert )
            print("ZoneA%s = %s"  % (str(i) , str(ZoneA[i])))
            if ZoneA[i] == '1':
                print("ZoneA{} = {}".format(i, ZoneA[i]))
        
        # INI-Datei speichern
        cfgfile = open(INI_FILE_in,'w')
        value_zonea = self.ConfigSectionMap("Zone A") 
        value_zoneb = self.ConfigSectionMap("Zone B") 
        value_zonec = self.ConfigSectionMap("Zone C") 
        value_zoned = self.ConfigSectionMap("Zone D") 

         Config.set("PASSWORD","Benutzer","Hallo")
        Config.set("PASSWORD","Kennwort", "2199")
        for i in range(len(value_zonea)):
            wert1 = 'ZoneA' + str(i)
            wert2 = str(ZoneA[i])
            Config.set("Zone A", wert1, wert2)
        for i in range(len(value_zoneb)):
            wert1 = 'ZoneB' + str(i)
            wert2 = str(ZoneB[i])
            Config.set("Zone B", wert1, wert2)
        for i in range(len(value_zonec)):
            wert1 = 'ZoneC' + str(i)
            wert2 = str(ZoneC[i])
            Config.set("Zone C", wert1, wert2)
        for i in range(len(value_zoned)):
            wert1 = 'ZoneD' + str(i)
            wert2 = str(ZoneD[i])
            Config.set("Zone D", wert1, wert2)
   
        Config.write(cfgfile)
        cfgfile.close()


     def ConfigSectionMap(self, section):
         dict1 = {}
         options = self.Config.options(section)
         for option in options:
             try:
                 dict1[option] = self.Config.get(section, option)
                 if dict1[option] == -1:
                     print("skip: %s" % option)
             except:
                 print("exception on %s!" % option)
                 dict1[option] = None
         #return dict1
         return dict(self.Config.items(section)) # von:
         # http://stackoverflow.com/questions/8578 ... -a-section         
        
     def close_fenster(self): 
         quit()

if __name__ == "__main__":
    try:
        root = MyApp()
        root.mainloop()
        root.main()
    except (KeyboardInterrupt, SystemExit):
        print("Programm Ende")
INI-File: config_sz.ini [Pastebin]https://pastebin.com/NFugkMU4[/Pastebin]
MfG
Juergen B.
Zuletzt geändert von Anonymous am Montag 10. April 2017, 14:06, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@jbaben: Nun ja, das Objekt hat ja auch kein `Config`-Attribut. Beheben könntest Du das Problem in dem Du ein solches Attribut vorher an das Objekt bindest.

Attribute sollten alle schon in der `__init__()` erzeugt werden und nicht in irgendwelchen Methoden die irgendwann aufgerufen werden, oder auch nicht. Dann muss man nicht überlegen ob und wann ein Attribut existiert und das gesamte Programm und seinen Ablauf dafür verstehen müssen.

Die Namen halten sich nicht alle an den Style Guide for Python Code.

Globale Variablen die mit Dummywerten vorbelegt sind sollte man nicht verwenden. Durchnummerierte oder in diesem Fall durchbuchstabierte Namen sind ein „code smell“ das man eigentlich keine einzelnen Namen sondern eine Datenstruktur verwenden möchte.

Man sollte weder Grössen noch Positionen von Anzeigeelementen hart und absolut festlegen. Also `geometry()` nur wenn im Layout Elemente sind die sinnvoll mit einer Fenstergrössenänderung wachsen oder schrumpfen können und kein `place()`.

Man sollte keine kryptischen Abkürzungen verwenden. `fr`? `Lb1`? `wert1` und `wert2` sind eher `schluessel` und `wert`. Wobei ich mir das mit deutschsprachigen Bezeichnern gründlich überlegen würde. Zumal Du auch englischsprachige verwendest. Nach welchen Kriterium wurde das entschieden?

In Zeile 57 ist ein `IndentationError` über den schon der Compiler stolpern wird. Also eigentlich sind in dem Block alle anderen Zeilen falsch eingerückt, weil nur drei statt vier Leerzeichen weiter als die ``def``-Anweisung für den Block.

``for index in range(len(sequenze))`` ist in Python ein „anti pattern“. Man kann direkt über Sequenzobjekte wie Listen iterieren. Ich weiss auch nicht ob das INI-Format hier wirklich so passend ist. Also JSON liesse sich das wahrscheinlich deutlich besser abbilden. Oder über Bibliotheken wie `configobj` die auch Listen in INI-Dateien speichern können.

Ab Zeile 59 kommt im Grunde vier mal nahezu der gleiche Code. Das würde man in einer Schleife schreiben statt den Code viermal zu kopieren.

Das `INI_FILE_out` *gelesen* und `INI_FILE_in` *geschriebn* wird, ist ziemlich verwirrend.

Die Ausgabedatei wird geöffnet, und das recht weit von dem Code der dann tatsächlich mal etwas schreibt, aber nicht wieder geschlossen. Hier kommt normalerweise die ``with``-Anweisung zum Einsatz.

Warum die Methode `main()` heisst und sowohl von der `__init__()` als auch *nachdem* die `mainloop()` zurückkehrt aufgerufen wird verstehe ich nicht so ganz.

Wobei sie vielleicht auch gar nicht nach der `mainloop()` aufgerufen wird, denn dazu müsste die normal beendet werden: durch Aufruf der `quit()`-Methode auf einem `Widget`. Die `quit()`-Funktion, deren Existenz ausserhalb einer interaktiven Python-Shell eigentlich gar nicht dokumentiert ist, beendet das Programm ”hart” ohne das am Ende der Code nach der GUI-Hauptschleife eine Chance hat seine Arbeit zu verrichten. `SystemExit` sollte man nämlich nicht behandeln, das ist in aller Regel ein Zeichen für ein Problem mit der Strukturierung des Programmablaufs. Bei GUI-Programmen behandelt man üblicherweise auch keinen `KeyboardInterrupt` weil die GUI ja schon einen Weg bieten sollte das Programm zu beenden.

Ein ”nacktes” ``except:`` ohne konkrete Ausnahe die man behandeln möchte sollte man ebenfalls nicht verwenden. Zumal Du dort auch noch jegliche Information verschluckst welche Ausnahme da behandelt wurde. Letztlich ist der ganze Code für die Tonne weil er beim Rückgabewert gar nicht verwendet wird.
jbaben
User
Beiträge: 24
Registriert: Dienstag 12. Januar 2016, 16:05

Hallo,

ich habe den Fehler gefunden.
In der Funktion "main()" fehlte der Zusatz "self" vor dem "Config".
Also so:

Code: Alles auswählen


def main(self):
        self.Config = configparser.ConfigParser()
        self.Config.read(INI_FILE_out)
        usw.
BlackJack: wie funktioniert denn das mit dem richtigen Listing ?

MfG

Juergen B.
jbaben
User
Beiträge: 24
Registriert: Dienstag 12. Januar 2016, 16:05

Hallo BlackJack,

vielen Dank für Deine ausführlichen Hinweise zu meinem Programm-Code.
Deinen Beitrag habe ich erst nach meiner Erfolgsmeldung gelesen.
Die Hinweise werde ich durcharbeiten und mein Programm entsprechend ändern.
Bei diesem Programm handelt es sich nur um ein Test-Programm um das Modul "ConfigParser" kennen zu lernen.
Das mit dem File In/Out dient auch nur zum Test, damit die Original-Datei nicht zerstört wird.
Später soll es so sein:
Nach dem Programm-Start lesen
Im Programm bei Änderungen: speichern

Zeile 57 ist in meinem Listing Ok (Kopier-Fehler).

Das mit dem Haupt-Prgramm (main()) ist mir noch nicht klar wie das richtig zu Handhaben ist.
Denn mit "def __init__(self):" wird ja nur initialisiert, wenn ich das richtig verstanden habe.

Wie gesagt bin ich noch am üben, und trage mir die Python-Infos über das Internet zusammen.

MfG

Juergen B.
BlackJack

@jbaben: Auch wenn es zum nur zum Testen zwei verschiedene Dateinamen für die INI-Datei sind, sollte man trotzdem die Namen richtig herum wählen und nicht entgegen ihrer Bedeutung.

Ein Hauptprogramm in dem Sinne gibt es in GUI-Programmen nicht wirklich. Das besteht nur aus dem aufsetzen der GUI und der übergabe der Kontrolle an die Hauptschleife des GUI-Rahmenwerks. Also `mainloop()` bei Tk. Wenn das Ziel ist sich mit `ConfigParser` auseinander zu setzen, solltest man eventuell nicht gleichzeitig die GUI-Front aufmachen. Das ist ein eigenes, komplexeres Thema. Und selbst wenn man beides macht, sollte man immer daran denken Programmlogik und GUI-Code zu trennen, damit das einzeln testen kann.
Antworten