Modul läuft nur alleine, nicht über Hauptprogramm

Fragen zu Tkinter.
Antworten
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

Hallo zusammen,
leider verstehe ich mal wieder einen Ablauf nicht.
Dieses kleine Programm ist ein Modul, das aus dem Hauptprogramm eine Information erhält, aus einer Datei Adressen erhält und dann ein Schriftstück aufsetzt.
Starte ich nur dieses Modul funktioniert die Adressauswahl wie gewollt (es gibt zwar bestimmt elegantere Wege, die hab ich aber noch nicht gefunden) starte ich das Modul aus dem Hauptprogramm bleibt die Listbox, in der die Namen zur Auswahl aufgelistet werden, leer. Woran kann das liegen?
Die print()-Anweisungen am Ende der Listbox werden in beiden Fällen ausgeführt.
Der Aufruf erfolgt über:

Code: Alles auswählen

def anfordern(self):
	Rezept.medis(self)
	Rezept.tab(self)
Das Programm zur Frage:

Code: Alles auswählen

import tkinter as tk
from tkinter import (Button, RIGHT, LEFT, E, W, N, Frame, RIDGE, SUNKEN, Menu,
                    Label, Entry, Tk, CENTER, Radiobutton, StringVar,
                    messagebox,Text, Listbox, ttk, OptionMenu)
from subprocess import Popen
import os
import webbrowser
import json

BGC = 'darkred'    # Hintergrund
FGC = 'white'      # Schrift
fontA = ('Arial', 12)


class Rezepte:
    def __init__(rezept):
        rezept.medis()
        #rezept.auswahlfeld()
        rezept.tab()
        #rezept.test()
        
        
    def medis(rezept):
        rezept.fenster = Tk()
        rezept.fenster.title("Rezeptanforderung")
        rezept.fenster.config(bg='darkred')

        global daten
        try:
            data = open('arztliste','r')
            daten = json.load(data)
            data.close()
        except:
            rezept.docs = ("leer", " ")

    ### Auslesen der Namen für Optionsfeld
        global namensliste, rezept_docs
        anzahl = len(daten["data"])-1
        zaehler = 0
        i=1     # indices
        namensliste = ["leer"]
        
        while zaehler < anzahl:
            name = daten["data"][i]["Name"]
            i = i + 1
            zaehler = zaehler + 1
            namensliste += [(name)]
        rezept_docs = tk.Variable(value=namensliste)
        
        
        rezept.titel = Label(rezept.fenster, bg=BGC, fg=FGC,
                             text="Rezepte anfordern für:",
                             font=('Arial', 14))
        rezept.titel.grid(column=0, row=0, columnspan=2, padx=30, pady=10)
        rezept.medikamente = Entry(rezept.fenster, width=20,
                                   selectbackground='lime')

        rezept.button_druck = Button(rezept.fenster, command=druck,
                               text="Drucken")
        rezept.button_mail = Button(rezept.fenster, command=mail,
                                    text="Mailen")
        rezept.button_save = Button(rezept.fenster, command=save,
                                    text="Arzt speichern")
        rezept.medikamente.grid(column=0, row=2, columnspan=2, padx=10, pady=10)
        

        rezept.dr_anrede = Label(rezept.fenster, bg=BGC, fg=FGC,
                                text="Anrede/Titel", font=fontA)
        rezept.dr_anredeEntry = Entry(rezept.fenster, width=10)
        rezept.dr_Vname = Label(rezept.fenster, bg=BGC, fg=FGC,
                                text="Vorname", font=fontA)
        rezept.dr_VnameEntry = Entry(rezept.fenster, width=15)
        rezept.dr_Nname = Label(rezept.fenster, bg=BGC, fg=FGC,
                                text="Nachname", font=fontA)
        rezept.dr_NnameEntry = Entry(rezept.fenster, width=20)
        rezept.dr_adr = Label(rezept.fenster, bg=BGC, fg=FGC,
                              text="Adresse", font=fontA)
        rezept.dr_adrEntry = Entry(rezept.fenster, width=40)

        #### Frame für PLZ und Ort
        rezept.dr_codeFrame = Frame(rezept.fenster, bg=BGC)
        rezept.dr_plz = Label(rezept.dr_codeFrame, bg=BGC, fg=FGC,
                              width=10, text="PLZ", font=fontA).pack(side=LEFT)
        rezept.dr_plzEntry = Entry(rezept.dr_codeFrame, width=6)
        rezept.dr_plzEntry.pack(side=LEFT)
        rezept.dr_ort = Label(rezept.dr_codeFrame, bg=BGC, fg=FGC,
                              text="Ort", font=fontA).pack(side=LEFT, padx=5)
        rezept.dr_ortEntry = Entry(rezept.dr_codeFrame, width=20)
        rezept.dr_ortEntry.pack(side=LEFT, padx=5)
        #### /Frame

        rezept.dr_eMail = Label(rezept.fenster, bg=BGC, fg=FGC,
                                text="eMail", font=fontA)
        rezept.dr_eMailEntry = Entry(rezept.fenster, width=30)
        
        rezept.medikamente.grid(column=0, row=2, columnspan=2, padx=10, pady=10)
        rezept.dr_anrede.grid(column=0, row=3, sticky='W')
        rezept.dr_anredeEntry.grid(column=1, row=3, sticky='W')
        rezept.dr_Vname.grid(column=0, row=4)
        rezept.dr_VnameEntry.grid(column=1, row=4, sticky='W')
        rezept.dr_Nname.grid(column=2, row=4)
        rezept.dr_NnameEntry.grid(column=3, row=4, padx=5, pady=5)
        rezept.dr_adr.grid(column=0, row=5)
        rezept.dr_adrEntry.grid(column=1, row=5, columnspan=3, sticky='W')

        rezept.dr_codeFrame.grid(column=0, row=6, columnspan=4, pady=5,
                                 sticky='W')

        rezept.dr_eMail.grid(column=0, row=7)
        rezept.dr_eMailEntry.grid(column=1, row=7, columnspan=3, sticky='W')

        rezept.button_druck.grid(column=0, row=8, pady=10)
        rezept.button_mail.grid(column=1, row=8)
        rezept.button_save.grid(column=3, row=8)

        option_menu = Listbox(
            rezept.fenster,
            listvariable = rezept_docs,
            height = 10,
            selectmode = tk.BROWSE)
        option_menu.grid(column=4, row=1, rowspan=6, padx=5)
        print("namensliste", namensliste)
        print("Vari: ", rezept_docs.get())


        def geklickt(event):
            global arzt_auswahl
            auswahl = option_menu.curselection()
            auswahl = str(auswahl)
            auswahl = auswahl.strip("(")
            auswahl = auswahl.split(",")[0]

            if auswahl not in ")":
                auswahl_1 = int(auswahl)
                arzt_auswahl = namensliste[auswahl_1]
                wahl = namensliste.index(arzt_auswahl)
            rezept.auswahl()
    
        option_menu.bind('<<ListboxSelect>>', geklickt)
        
    def auswahl(rezept, *args):
        '''Zeigt die  Adressen nach Vorgabe des Optionfeldes.'''
        auswahl = arzt_auswahl
        key = namensliste.index(auswahl)
        
        try:
            rezept.dr_anredeEntry.delete(0, 'end')
            rezept.dr_VnameEntry.delete(0, 'end')
            rezept.dr_NnameEntry.delete(0, 'end')
            rezept.dr_adrEntry.delete(0, 'end')
            rezept.dr_plzEntry.delete(0, 'end')
            rezept.dr_ortEntry.delete(0, 'end')
            rezept.dr_eMailEntry.delete(0, 'end')
            rezept.dr_anredeEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["Anrede"])
            rezept.dr_VnameEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["Vorname"])
            rezept.dr_NnameEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["Nachname"])
            rezept.dr_adrEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["adr"])
            rezept.dr_plzEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["PLZ"])
            rezept.dr_ortEntry.insert(0, daten["data"][key]["Anschrift"][
                0]["Ort"])
            eMail = daten["data"][key]["Anschrift"][0]["email"]
            rezept.dr_eMailEntry.insert(0, eMail)
        except: pass

        if key != 0:
            rezept.button_save.config(state='disabled')
        else:
            rezept.button_save.config(state='active')        



    def tab(rezept):
        '''Holt die Medikamente die gekennzeichnet wurden'''
        try:
            if rezept.med1Control.cget('text') == "w":
                med1 = rezept.med1Med.get()
            else: med1 = " "

            if rezept.med2Control.cget('text') == "w":
                med2 = rezept.med2Med.get()
            else: med2 = " "

            if rezept.med3Control.cget('text') == "w":
                med3 = rezept.med3Med.get()
            else: med3 = " "

            if rezept.med4Control.cget('text') == "w":
                med4 = rezept.med4Med.get()
            else: med4 = " "

            if rezept.med5Control.cget('text') == "w":
                med5 = rezept.med5Med.get()
            else: med5 = " "
            
            if rezept.med6Control.cget('text') == "w":
                med6 = rezept.med6Med.get()
            else: med6 = " "

            if rezept.med7Control.cget('text') == "w":
                med7 = rezept.med7Med.get()
            else: med7 = " "

            if rezept.med8Control.cget('text') == "w":
                med8 = rezept.med8Med.get()
            else: med8 = " "
        except: med1 = "irgendwas"

        try:
            if med1 not in " ":
                rezept.medikamente.insert(0, med1 +", ")
            if med2 not in " ":
                rezept.medikamente.insert('end', med2 +", ")
            if med3 not in " ":
                rezept.medikamente.insert('end', med3 +", ")
            if med4 not in " ":
                rezept.medikamente.insert('end', med4 +", ")
            if med5 not in " ":
                rezept.medikamente.insert('end', med5 +", ")
            if med6 not in " ":
                rezept.medikamente.insert('end', med6 +", ")
            if med7 not in " ":
                rezept.medikamente.insert('end', med7 +", ")
            if med8 not in " ":
                rezept.medikamente.insert('end', med8)
        except: pass

        global anforderung
        anforderung = rezept.medikamente.get()
        rezept.fenster.mainloop()

def save():
    '''Speichert die neuen Adressen'''
    print("Speichern gedrückt")
    
def druck():
    '''Erzeugt eine txt-Datei'''
    print("Drucken gedrückt")

def mail():
    '''erzeugt eine eMail'''
    print("eMail gedrück")

def main():
    r=Rezepte()

if __name__ == '__main__':
    print("Modul für Rezeptanforderung."
          "\nTeil von Tabletten ab Version 1.7.")
    main()

Das Hauptprogramm verwaltet meine Medikamente, ist also etwas rein privates,
das ich auf der Suche nach was sinnvollem angefangen habe.
Die def main, save und druck enden nur zur Übersichtlichkeit in print()


Als Arztliste verwende ich eine Datei mit dem Namen: arztliste
Mit diesem Inhalt:

Code: Alles auswählen

{"data":[{}, {"Name": "Eysenbarth", "Anschrift": [{"Anrede": "Dr", "Vorname": "Johan Andreas", "Nachname": "Eysenbarth", "adr": "fahrend", "PLZ": "34346", "Ort": "Hann. Münden", "email": "witte-witt@bum.bum"}]}, {"Name": "Watson", "Anschrift": [{"Anrede": "Dr.", "Vorname": "John", "Nachname": "Watson", "adr": "Baker Street 221B", "PLZ": "NX", "Ort": "London", "email": "keine"}]},{"Name":"Jekyll","Anschrift": [{"Anrede":"Dr","Vorname":"Henry","Nachname":"Jekyll","adr":"","PLZ":"","Ort":"London","email":""}]}]}
Vielen Dank schon mal für eure Unterstützung
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@valentinks: Da wird einiges importiert was nicht benutzt wird, und sowohl `tkinter` als `tk` importieren als auch eine Menge Namen noch mal extra direkt importieren ist verwirrend. Wonach wird denn dann entschieden auf was über welchen Weg zugegriffen wird? Wobei von den einzelnen importieren Namen auch nicht alle verwendet werden.

Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. `BGC` und `FGC` bräuchten beispielsweise keinen Kommentar wenn das eine Wort was dort steht, gleich bestandteil des Namens wäre, statt nur ein einzelner Buchstabe.

Konstanten werden per Konvention KOMPLETT_GROSS geschrieben. Also `FONT_A`. Wobei man da dann auch wieder fragen könnte was dieses A eigentlich bedeuten soll. Da steht nicht einmal ein Kommentar der das erklärt.

Das erste Argument von Methoden heisst `self`, nicht `rezept`. Selbst wenn man es anders nennen dürfte ist `rezept` (Einzahl) falsch. Oder der Klassenname `Rezepte` (Mehrzahl) ist falsch. Beides zusammen geht so nicht.

Eine `__init__()` sollte den Code zum initialisieren des Objekts enthalten, nicht nur zwei Aufrufe zu anderen Methoden die das dann machen. Es gibt bestimmte Entwurfsmuster wo das im Zusammenhang mit Vererbung Sinn machen kann, das liegt hier aber nicht vor.

Das Objekt sollte auch nur initialisiert werden, damit es dann von dem Code der die Erzeugung veranlasst hat, benutzt werden kann. Wenn die `__init__()` den gesamten Programmablauf enthält, stimmt etwas mit der Aufteilung des Codes nicht.

``global`` und globale Variablen haben ja sowieso schon nichts in sauberen Programmen zu suchen, aber wenn man Klassen hat, dann gibt es dafür überhaupt keine Ausrede mehr.

Dateien sollte man wo möglich mit der ``with``-Anweisung verwenden. Bei Textdateien sollte man immer explizit die Kodierung angeben. Und bei JSON-Dateien ist das UTF-8. Alternativ kann man beim Lesen die Datei auch als Binärdatei öffnen, dann kümmert sich das `json`-Modul um die Kodierung.

Man verwendet keine nackten ``except:`` ohne konkrete Ausnahmen die man dann auch so sinnvoll behandeln kann. Die Behandlung beim Einlesen von JSON-Daten ist so beispielsweise weder bei einem Programmierfehler wie einem `NameError` noch wenn zu wenig Speicher vorhanden ist, sinnvoll. Zudem gibt es keinerlei Rückmeldung an den Benutzer, der kann also beispielsweise eine nicht-vorhandene Datei nicht von einer vorhandenen, aber inhaltlich kaputten Datei unterscheiden.

``except: pass`` geht *gar nicht*.

Das was nach ``### Auslesen der Namen für Optionsfeld`` folgt ist sehr umständlich und unpythonisch. Länge einer Sequenz mit `len()` ermitteln um dann mit ``while zaehler < anzahl:`` und manuellem hochzählen von `zaehler` und einem parallelen `i` das sich *immer* genau im *eins* unterscheidet per Index auf die Elemente von der Sequenz zuzugrifen ist falsch. Das ist eine ``for``-Schleife, und auch nicht über einen Index sondern über die Elemente von der Sequenz direkt. ``namensliste += [(name)]`` ist auch falsch. Die runden Klammern sind überflüssig, und man erstellt keine Listen mit immer genau *einem* Element um einzelne Element an eine Liste anzuhängen. Dafür ist die `append()`-Methode da. Grunddatentypen haben nichts in Namen zu suchen.

Also das hier:

Code: Alles auswählen

        anzahl = len(daten["data"])-1
        zaehler = 0
        i=1     # indices
        namensliste = ["leer"]
        
        while zaehler < anzahl:
            name = daten["data"][i]["Name"]
            i = i + 1
            zaehler = zaehler + 1
            namensliste += [(name)]
Sind in *Python* diese drei zeilen:

Code: Alles auswählen

        namen = ["leer"]
        for datensatz in daten["data"]:
            namen.append(datensatz["Name"])
Oder noch ein klein wenig kompakter:

Code: Alles auswählen

        namen = ["leer"]
        namen.extend(datensatz["Name"] for datensatz in daten["data"])
Und wenn man möchte, sogar nur:

Code: Alles auswählen

        namen = ["leer", *(datensatz["Name"] for datensatz in daten["data"])]
`geklickt()` sollte nicht lokal in der Methode definiert werden.

Man wandelt keine Tupel in Zeichenketten um und entfernt dann Klammern und teilt mit Split an Kommas auf um an Werte in einem Tupel zu kommen. Gilt auch für andere Datentypen.

Die `auswahl`-Methode nimmt beliebige Argumente (``*args``) wird aber nie mit solchen aufgerufen und `args` wird in der Methode auch überhaupt nicht benutzt. Was soll das?

Für "end" gibt es im `tkinter`-Modul eine Konstante, die sollte man verwenden. Ebenso für "disabled" und "active".

``daten["data"][key]["Anschrift"][0]`` sollte nicht sieben mal nahezu direkt hintereinander im Code stehen.

`key` ist ein etwas irreführender Name als *Index* in eine Liste.

Die Attribute `med1Control` bis `med8Control` sind nirgends definiert. Man nummeriert auch keine Namen. Entweder will man da bessere Namen oder gar keine Einzelwerte und -namen, sondern eine Datenstruktur. Oft eine Liste.

`cget(…)` ist eher exotisch. Normalerweise verwendet man da den Schlüsselzugriff per ``[…]``.

``if med1 not in " ":`` ist auch sehr schräg und sicher nicht das was da eigentlich ausgedrückt werden soll. Das sollte wohl eher „ungleich“ statt „ist nicht enthalten“ heissen. Aber „kein Medikament“ durch *ein* *Leerzeichen* auszudrücken ist auch schräg. Deutlich naheliegender wäre hier entweder die leere Zeichenkette oder `None`. Bei beiden Werten wäre der Test dann ``if med1:``.

Das eintragen der einzelnen Medikamente immer mit einem ", " angehängt ist umständlich. Statt alle Medikamente mit einem ", " und der `join()`-Methode zu verbinden, und das Ergebnis dann mit *einem* Aufruf in das Textfeld einzutragen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

@__blackjack__: Danke erstmal dafür, einige Sachen konnte ich umsetzten, neue Fragen haben sich dadurch auch ergeben, die Ausgangsfrage bleibt leider offen.
Anmerkung zum folgenden:
Nichts von dem was hier als Antwort von mir steht sollte beleidigend oder als Angriff verstanden werden. Aufgrund einer psychischen Störung bekomme ich das nicht mit wenn ich jemanden mit meinem Subtext angreife. Ich stelle nur Fragen und gebe Antworten.
Da wird einiges importiert was nicht benutzt wird, und sowohl `tkinter` als `tk` importieren als auch eine Menge Namen noch mal extra direkt importieren ist verwirrend. Wonach wird denn dann entschieden auf was über welchen Weg zugegriffen wird? Wobei von den einzelnen importieren Namen auch nicht alle verwendet werden.
Das Problem hierbei war das auf der Suche nach dem aufbau von Optionsfeld oder Listbox alle gefundenen mit "tkinter as tk" arbeiten oder mit "from tkinter import *" und *-Importe sind schlecht, wurde mir hier schon geschrieben. Auch diese Variante wie ich sie hier nutzewird gelegentlich verwendet. Ohne Optionsfeld würde ich dies nicht machen.
Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. `BGC` und `FGC` bräuchten beispielsweise keinen Kommentar wenn das eine Wort was dort steht, gleich bestandteil des Namens wäre, statt nur ein einzelner Buchstabe.
Mir kam es praktischer vor als jedes mal "Backgroundcolor" schreiben zu müssen, was ja auch zu lasten der 80 Spaltenregel geht. Vor allem da dies recht oft geschrieben werden muss.
Eine `__init__()` sollte den Code zum initialisieren des Objekts enthalten, nicht nur zwei Aufrufe zu anderen Methoden die das dann machen. Es gibt bestimmte Entwurfsmuster wo das im Zusammenhang mit Vererbung Sinn machen kann, das liegt hier aber nicht vor.

Das Objekt sollte auch nur initialisiert werden, damit es dann von dem Code der die Erzeugung veranlasst hat, benutzt werden kann. Wenn die `__init__()` den gesamten Programmablauf enthält, stimmt etwas mit der Aufteilung des Codes nicht.
Okay, damit muss ich mich noch intensiver befassen.

``global`` und globale Variablen haben ja sowieso schon nichts in sauberen Programmen zu suchen, aber wenn man Klassen hat, dann gibt es dafür überhaupt keine Ausrede mehr.
Sorry, steht so auch im Lehrbuch. Ist wohl kein gutes Buch.
Ich hab aber auch in diesen Fällen keinen besseren weg gefunden weil jedesmal eine Fehlermeldung kam das das gesucht nicht existiert.

``except: pass`` geht *gar nicht*.
Sorry, steht so auch im Lehrbuch. Ist wohl kein gutes Buch.
Das was nach ``### Auslesen der Namen für Optionsfeld`` folgt ist sehr umständlich und unpythonisch. Länge einer Sequenz mit `len()` ermitteln um dann mit ``while zaehler < anzahl:`` und manuellem hochzählen von `zaehler` und einem parallelen `i` das sich *immer* genau im *eins* unterscheidet per Index auf die Elemente von der Sequenz zuzugrifen ist falsch. Das ist eine ``for``-Schleife, und auch nicht über einen Index sondern über die Elemente von der Sequenz direkt. ``namensliste += [(name)]`` ist auch falsch. Die runden Klammern sind überflüssig, und man erstellt keine Listen mit immer genau *einem* Element um einzelne Element an eine Liste anzuhängen. Dafür ist die `append()`-Methode da. Grunddatentypen haben nichts in Namen zu suchen.

Also das hier:
CODE: ALLES AUSWÄHLEN

anzahl = len(daten["data"])-1
zaehler = 0
i=1 # indices
namensliste = ["leer"]

while zaehler < anzahl:
name = daten["data"]["Name"]
i = i + 1
zaehler = zaehler + 1
namensliste += [(name)]

Sind in *Python* diese drei zeilen:
CODE: ALLES AUSWÄHLEN

namen = ["leer"]
for datensatz in daten["data"]:
namen.append(datensatz["Name"])
Oder noch ein klein wenig kompakter:
CODE: ALLES AUSWÄHLEN

namen = ["leer"]
namen.extend(datensatz["Name"] for datensatz in daten["data"])
Und wenn man möchte, sogar nur:
CODE: ALLES AUSWÄHLEN

namen = ["leer", *(datensatz["Name"] for datensatz in daten["data"])]

Führt leider zu einer Fehlermeldung:
Traceback (most recent call last):
File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
exec(code, self.locals)
File "/mnt/sda5/scripts/python/Pillenplaner/1.9/rezept1_2_1.py", line 327, in <module>
main()
File "/mnt/sda5/scripts/python/Pillenplaner/1.9/rezept1_2_1.py", line 322, in main
r=Rezepte()
File "/mnt/sda5/scripts/python/Pillenplaner/1.9/rezept1_2_1.py", line 42, in __init__
self.medis()
File "/mnt/sda5/scripts/python/Pillenplaner/1.9/rezept1_2_1.py", line 70, in medis
namen.append(datensatz["Name"])
KeyError: 'Name'

`geklickt()` sollte nicht lokal in der Methode definiert werden.

Hab ich befürchtet. Anders hab ich es leider noch nicht geschafft das es überhaupt funktioniert.

Man wandelt keine Tupel in Zeichenketten um und entfernt dann Klammern und teilt mit Split an Kommas auf um an Werte in einem Tupel zu kommen. Gilt auch für andere Datentypen.


Ich weiss das das besser gehen muss, kann ich aber aktuell nicht.
Ein frührerer Versuch ohne die Umwandlung brachte den Fehler:

Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.10/tkinter/__init__.py", line 1921, in __call__
return self.func(*args)
File "/mnt/sda5/scripts/python/Pillenplaner/1.9/rezept1_2_1.py", line 148, in geklickt
auswahl_1 = int(auswahl)
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'tuple'

Deshalb der komplizierte Umbau um die Funktion zu bekommen.

Für "end" gibt es im `tkinter`-Modul eine Konstante, die sollte man verwenden. Ebenso für "disabled" und "active".

Ich hab gesucht wie sich das umsetzten lässt, dies sind die gefunden Wege.
Konstanten dafür sind mir bisher völlig unbekannt.

``daten["data"][key]["Anschrift"][0]`` sollte nicht sieben mal nahezu direkt hintereinander im Code stehen.

Leider habe ich noch keine Ahnung wie ich das Sinnvoll anders machen kann. Das ist eine Frage die ich in ein paar Tagen oder Wochen gestellt hätte.

`key` ist ein etwas irreführender Name als *Index* in eine Liste.

Erschien mir logisch nachdem ich das so auf verschiedenen Seiten im Netz so gefunden habe.

Die Attribute `med1Control` bis `med8Control` sind nirgends definiert. Man nummeriert auch keine Namen. Entweder will man da bessere Namen oder gar keine Einzelwerte und -namen, sondern eine Datenstruktur. Oft eine Liste.

Diese Atribute sind im Hauptprogramm definiert. Dieser Teil funktioniert auch wie erwartet.
Namen fallen mir da beim besten Willen keine besseren ein. Das ist einfach nur ein leeres Feld in das irgendwann, irgendein Wort (Medikament) geschrieben wird.
Das Hauptprogramm hat eine Gitterstruktur:
[Medikament] [Morgens] [Mittags] [Abends] [Tagesdosis] [Neu hinzu] [Anzahl ist] [reicht bis]
Das ganze acht mal. Ist ein definierter Zeitraum erreicht wird "med1Control" mit einem "w" belegt und kann dann von diesem Modul gefunden werden und wird an die Liste angehängt.

`cget(…)` ist eher exotisch. Normalerweise verwendet man da den Schlüsselzugriff per ``[…]``.

Nach langer Suche wie ich an die Daten komme ist dies der einzige Weg, der nicht in einer Fehlermeldung endet das etwas nicht gefunden wurde. Wie geht es anders?

Ein neues Problem:
Das erste Argument von Methoden heisst `self`, nicht `rezept`. Selbst wenn man es anders nennen dürfte ist `rezept` (Einzahl) falsch. Oder der Klassenname `Rezepte` (Mehrzahl) ist falsch. Beides zusammen geht so nicht.

Seitdem ich jetzt wieder "self" statt "rezept" verwende funktioniert speichern nicht mehr.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@valentinks: Keine Sorge ich kann mit sachlichen Antworten und Nachfragen umgehen. Das Leute Kritik an Code manchmal als Kritik an ihrer Person auffassen, verstehe ich manchmal auch nicht so richtig.

Auch mit Optionsfeld gibt es doch keinen Grund zwei verschiedene Varianten zu verwenden um auf Namen im `tkinter`-Modul zuzugreifen. Auf das alles kann man ja auch über den Modulnamen, beziehungsweise dem umbenannten Modulnamen zugreifen. Andererseits könnte man statt über den Modulnamen zuzugreifen auch alles was man braucht explizit importieren. Bei beiden Wegen stellt sich halt die Frage warum nicht ein einheitlicher Weg gewählt wurde, und nach welchem Kriterium entschieden wird was über das Modul angesprochen wird, und was importiert wird.

Farben und Schriftarten und Grössen würde ich eher überhaupt nicht definieren. Das sind Einstellungen die der Benutzer vornimmt, nicht der Programmierer einer Anwendung. Ob da Arial auf dem Rechner vorhanden ist, weiss man ja gar nicht. Und was der Benutzer lesbar findet an Schriftgrössen auch nicht.

Die immer gleichen Optionen für Vorder- und Hintergrundfarbe, und Schriftart könnte man auch in einem Wörterbuch zusammenfassen und das mit ``**``-Syntax bei den Aufrufen übergeben. Dann muss man die Werte selbst nicht mehrfach hinschreiben.

``global`` ist ein ziemlich guter Hinweis auf einen nicht so guten Text, sofern das nicht nur der Vollständigkeit halber erwähnt wird, mit einem Hinweis, dass man keine globalen Variablen haben will in einem Programm.

``except: pass`` sollte nicht mal im Spass vorkommen. Das “behandelt“ *alle* Ausnahmen indem sie einfach ignoriert werden. Also auch solche mit denen man nicht rechnet. Beispielsweise das man sich im ``try``-Block bei einem Namen vertippt hat. Das wird dann einfach ignoriert und der Code im ``try``-Bloch wird nicht komplett ausgeführt. Das macht die Fehlersuche extrem schwer.

Bei einem ``except`` gibt man normalerweise an welche Ausnahmen man erwartet, und dann Code der diese Ausnahmen sinnvoll behandelt.

Der `KeyError` wegen "Name" kommt weil in der Liste aus irgendwelchen Gründen als erstes Element kein sinnvoller Datensatz steht, sondern ein leeres Wörterbuch. Das sollte nicht sein.

Und ein Tupel kann man natürlich nicht in eine Zahl umwandeln. Du musst da halt das Element was umgewandelt werden soll heraus holen. Ist das dann eventuell auch schon eine Zahl? Dann müsste man nicht einmal in eine Zahl umwandeln.

Wenn man den Wert von ``daten["data"][key]["Anschrift"][0]`` einmal an einen Namen bindet, muss man diesen Ausdruck nicht immer wieder schreiben/auswerten lassen. Das Ergebnis ändert sich an der Stelle ja nicht. Wobei die ``[0]`` am Ende merkwürdig ist Warum steckt das Wörterbuch mit der Anschrift in einer Liste? Das müsste dann ja "Anschriften" heissen.

Das `key` als Name für einen Index in eine Liste verwendet wird, ist eher selten, und halt irreführend. Das ist ein typischer generischer Name für den Zugriff auf einen Wert in einem Wörterbuch.

Die Attribute `med*Control` sind im Hauptprogramm also auf einem anderen Objekt von einem anderen Typ definiert, das hier über den Aufruf der “Methode“ auf der Klasse als Argument übergeben wird. Da sind wir wieder bei Klassen und wie die benutzt werden: So nicht. Das erste Argument von Methoden heisst `self` und steht für das Objekt auf dem die Methode aufgerufen wird, nicht für ein Objekt von einem anderen Typ. Und wenn eine Methode nicht auf `self` zugreift, dann ist es in der Regel keine Methode sondern eine Funktion, wo man einen guten Grund haben sollte warum das dann auf einer Klasse definiert ist, und nicht einfach als Funktion.

Bessere Namen sind in diesem Fall keine einzelnamen für die Nummerierung sondern eine Liste. Dann muss man da auch keinen Code 8 mal kopieren und leicht anpassen, sondern kann eine Schleife über die Elemente der Liste schreiben.

Wie man `cget(…)` normalerweise ”vermeidet”, schrob ich doch schon: Schlüsselzugriff: statt ``widget.cget("option")`` schreibt man ``widget["option"]``.

Zum `rezept` → `self`: Das funktioniert hier natürlich nicht überall, weil einige Methoden gar keine Methoden sind und `self` gar nicht ein Objekt vom Typ `Rezepte`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

@ __blackjack__: Nach viel lesen und probieren muss ich erstmal Fragen stellen und Gedanken erklären, was zu Fragen führt.
Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen.
Ich hab das Buch wohl zu genau genommen.
Bei langen Namen sollte man Abkürzungen verwenden, da sonst die Programmzeilen zu
lang und unübersichtlich werden und der Schreibaufwand zu groß wird. Außerdem bergen
lange Namen die Gefahr von Schreibfehlern. Vor allem aber – so sagt die Kognitionspsy-
chologie – kann man lange Namen schlecht im Arbeitsgedächtnis behalten. Des halb ist ein
Programmtext mit kurzen Namen oft besser verständlich.

``global`` ist ein ziemlich guter Hinweis auf einen nicht so guten Text, sofern das nicht nur der Vollständigkeit halber erwähnt wird, mit einem Hinweis, dass man keine globalen Variablen haben will in einem Programm.
Die einzige andere Art die mir bekannt ist, und die ich in anderen Fällen auch anwende, der Sinn leuchtet mir aber leider nicht ein.

Code: Alles auswählen

def oeffnen(self):
    ''' öffnet die Quelle und stellt den Inhalt bereit '''
    with open('arztliste','rb') as data:
    daten = json.load(data)
    data.close()
    return daten
    
und da wo ich »daten« brauche schreibe ich:

Code: Alles auswählen

daten = oeffnen(self)
Was mich daran stört ist das, im Fall von diesem Script, 2 mal gebraucht wird, beim erstellen der Namensliste und beim Füllen der Felder.
Es wird also 2 mal auf eine Datei zugegriffen. Mit global greife ich einmal auf die Datei zu.
Wo liegt mein Fehler das ich global für Sinnvoller halte oder gibt's einen anderen Weg?
Der `KeyError` wegen "Name" kommt weil in der Liste aus irgendwelchen Gründen als erstes Element kein sinnvoller Datensatz steht, sondern ein leeres Wörterbuch. Das sollte nicht sein.
Ich denke wohl zu kompliziert. Der Grund ist das ich eine Namensliste erstelle mit »namensliste = ["leer"]« und »namen.append(datensatz["Name"])«
die an erster stelle das Wort »leer« gefolgt von den Namen aus den Wörterbüchern. Wenn ich jetzt kein leeres Wörterbuch an der ersten Stelle habe ich zwei verschiedene Listen. namensliste mit ›leer‹ auf der [0] und ›Eysenbarth‹ auf der [1] und der Liste in der ich Suche mit ›Eysenbarth‹ auf der [0] und ›Watson‹ auf der [1]. Das heißt also wenn ich auf ›Eysenbarth‹ klicke wird mir ›Watson‹ angezeigt.
Auf dem Weg den ich hier gewählt habe gibt's einen Eintrag der die Felder leert.
Und ein Tupel kann man natürlich nicht in eine Zahl umwandeln. Du musst da halt das Element was umgewandelt werden soll heraus holen. Ist das dann eventuell auch schon eine Zahl? Dann müsste man nicht einmal in eine Zahl umwandeln.
(...) Man wandelt keine Tupel in Zeichenketten um und entfernt dann Klammern und teilt mit Split an Kommas auf um an Werte in einem Tupel zu kommen. Gilt auch für andere Datentypen.
Sorry, »auswahl = option_menu.curselection()[0]« tut's natürlich auch.
Wenn man den Wert von ``daten["data"][key]["Anschrift"][0]`` einmal an einen Namen bindet, muss man diesen Ausdruck nicht immer wieder schreiben/auswerten lassen. Das Ergebnis ändert sich an der Stelle ja nicht. Wobei die ``[0]`` am Ende merkwürdig ist Warum steckt das Wörterbuch mit der Anschrift in einer Liste? Das müsste dann ja "Anschriften" heissen.
Den Einwand verstehe ich beim besten Willen nicht. Das sind 7 verschieden Werte (Anrede, Name, Vorname, usw.). Das zu kürzen hatte ich für viel später in der Planung.
Das `key` als Name für einen Index in eine Liste verwendet wird, ist eher selten, und halt irreführend. Das ist ein typischer generischer Name für den Zugriff auf einen Wert in einem Wörterbuch.
Habe ich so auf verschiedenen Seiten gefunden, und schien mir auch echt Sinnvoll. Mir fällt leider auch nichts ein.
Die Attribute `med*Control` sind im Hauptprogramm also auf einem anderen Objekt von einem anderen Typ definiert, das hier über den Aufruf der “Methode“ auf der Klasse als Argument übergeben wird. Da sind wir wieder bei Klassen und wie die benutzt werden: So nicht. Das erste Argument von Methoden heisst `self` und steht für das Objekt auf dem die Methode aufgerufen wird, nicht für ein Objekt von einem anderen Typ. Und wenn eine Methode nicht auf `self` zugreift, dann ist es in der Regel keine Methode sondern eine Funktion, wo man einen guten Grund haben sollte warum das dann auf einer Klasse definiert ist, und nicht einfach als Funktion.
So steht's halt im Buch. Das Hauptprogramm mit Klasse(self) und die Module haben eine eigen Klasse(sonstwas).
In einer eigenen Klasse ist es damit es auch alleine funktioniert. Wenn ich nur Funktionen habe passiert ja nichts wenn ich es alleine Starte sondern nur beim Start über das Hauptprogramm.
Bessere Namen sind in diesem Fall keine einzelnamen für die Nummerierung sondern eine Liste. Dann muss man da auch keinen Code 8 mal kopieren und leicht anpassen, sondern kann eine Schleife über die Elemente der Liste schreiben.
Verstehe ich nicht. Wie benenne ich ein Feld von dem ich noch nicht weiss was einmal in dem Feld stehen wird? Barbara, Ole, Gustel?
Es wird irgendein Medikament sein.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@valentinks: Man sollte keine zu langen Namen verwenden, aber kryptische Abkürzungen gehen zu weit. Die meisten Namen sind ja auch auf ”natürliche” Weise kurz. Lang wird es meistens bei Namen die aus mehreren Worten zusammengesetzt sind. Das kann dann auch ein Warnzeichen sein, dass man in einem Wert zu viel oder falsches zusammengefasst hat.

Schreibfehler kann man im Grunde ausschliessen, da heute selbst einfache Texteditoren mindestens eine Autovervollständiging auf Basis der offenen Textdatei(en) bieten.

Statt die Daten mehrmals zu laden, lädt man sie einmal und übergibt sie an den relevanten Stellen als Argument an andere Funktionen oder Methoden die sie brauchen. Entweder direkt oder indirekt als Attribut eines Objekts. Bei Methoden zum Beispiel wenn es zum Zustand des Objekts gehört, über `self`.

`oeffnen()` ist hier irreführend weil da nicht geöffnet wird, sondern geöffnet, geladen, und geschlossen. Bei `oeffnen()` erwartet man als Leser das man da dann auch irgendwann etwas wieder schliessen muss.

Du denkst zu kompliziert im Sinne von da gibt es Sonderfälle. Indexwerte fangen bei 0 an. Das mit einem Dummy-Wert künstlich auf 1 zu verschieben, führt dazu dass man ab da überall extra Code haben muss, der berücksichtigt das dort ein Dummy-Wert an erster Stelle steht. Schon so simple Sachen wie über die Elemente iterieren geht schon nicht mehr ohne das zu extra zu berücksichtigen und komplexeren Code zu schreiben als ``for element in elements:``.

Welchen Einwand verstehst Du nicht? Das man nicht sieben mal den gleichen Teilausdruck auswertet, der ja immer das selbe Ergebnis liefert, oder das die zusätzliche Liste komisch bis falsch aussieht? Du hast das ja sicher nicht sieben mal hingeschrieben sondern den Quelltext sechs mal kopiert — *das* ist schon ein deutliches Zeichen das da was falsch läuft. So etwas sollte gar nicht erst entstehen das man später kürzen müsste.

Ich glaube nicht das Du `key` für einen Index auf verschiedenen Seiten gefunden hast. `key` heisst Schlüssel. Und wenn man einen Index hat, für den man nur einen generischen Namen braucht, ist das `index`. Dann weiss der Leser, dass das keine Abbildung von Schlüsseln auf Werte ist, sondern eine Sequenz die Indizes auf Werte abbildet. Wenn man ``container[key]`` liest, weckt das die Erwartung, dass `container` ein Wörterbuch oder etwas in der Art ist. Da wird niemand an eine Liste oder etwas in der Art denken, obwohl es das in diesem Fall ist. Das würde bei ``container[index]`` deutlich.

Wenn in dem Buch tatsächlich steht das im Hauptprogramm die Methoden `self` als erstes Argument bekommen und in Modulen Klassen stehen wo das erste Argument von Methoden nicht `self` heisst, dann taugt das Buch *gar nichts*. Wie ist denn der Titel/Autor/Verlag von dem Buch‽

Weder das Hauptprogramm noch Module brauchen eine Klasse weil nur mit Funktionen nichts passiert. Ich weiss auch nicht so wirklich was genau Du damit ausdrücken willst. Man kann Programme schreiben die aus einem oder mehreren Modulen bestehen, die nur Funktionen enthalten. Klassen sind kein Selbstzweck. Klassen fassen zusammengehörende Daten und Funktionen als Attribute und Methoden zu Objekten zusammen.

Module fassen zusammengehörende Funktionen und Klassen zusammen. Also durchaus auch mehrere. Falls das Buch auch an der Stelle suggerieren sollte das man pro Modul eine Klasse hat, oder pro Klasse ein Modul. *Das* ist *nicht* das Kriterium nach dem man Code auf Module aufteilt.

Wo hast Du Felder von denen Du nicht weisst was da stehen wird *von der Bedeutung her*? Du sagst doch selbst es ist ein Medikament. Und statt `medikament1entry`, medikament2entry`, `medikament3entry`, und so weiter, mit einem nummerierten Namen pro Medikament-Eingabefeld, hat man da nur *einen* Namen `medikamente` und der ist an eine Liste mit den Eingabefeldern gebunden. Statt Code 8 mal hinzuschreiben, der sich jeweils nur durch den nummerierten Namen unterscheidet, schreibt man eine Schleife, die über alle Werte läuft. Und dann zum Beispiel aus den Eingabefeldern eine Liste mit den Inhalten der Eingabefelder erstellt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

@__blackjack__ : Bin jetzt erstmal wieder einen Schritt zurück gegangen und habe in der Version wo alles in einer großen Datei steht angefangen deine Änderungen umzusetzen. Gerade beim Thema global bereitet mir das echt Kopfzerbrechen.
Deine Aussage:
Statt die Daten mehrmals zu laden, lädt man sie einmal und übergibt sie an den relevanten Stellen als Argument an andere Funktionen oder Methoden die sie brauchen. Entweder direkt oder indirekt als Attribut eines Objekts. Bei Methoden zum Beispiel wenn es zum Zustand des Objekts gehört, über `self`.
Kann ich, im Moment, nicht nachvollziehen.
Ich habe hier mal einen Ausschnitt aus dem Hauptprogramm um mein Problem beim Verstehen darzustellen:

Code: Alles auswählen

from tkinter import (Button, RIGHT, Tk, Frame, RIDGE, SUNKEN,
                     Radiobutton, messagebox, Label, StringVar)
from tkinter import messagebox


class Pills():
    def __init__(self):
        self.fenster = Tk()
        self.fenster.title("Auszug aus Pillenplaner")
        self.fenster.config(bg='darkred')
        self.fenster.geometry('300x200')
        self.config()
        self.oberflaeche()

    def config(self):
        ''' oeffnet die Datei mit den Einstellungen '''
        try:
            with open('mod2','rb') as data:
                datenE = json.load(data)
            data.close()
        except:
            datenE = [{'Einstellungen': [{'Hinweis': ' 1 ', 'Warnung': ' 22 '}]}]
            messagebox.showinfo(title="Info",
                                message="Configdatei nicht gefunden. "
                                "Eine neue wird angelegt. "
                                "Standardwerte werden geladen")
        return datenE


    def tasten_text(self, bezeichnung):
        ''' beschriftet die Tasten für Personen '''
        print("ruf Tast:", bezeichnung)
        datenE = self.config()

        if bezeichnung not in datenE[0]["Einstellungen"][0]:
            tasten = bezeichnung
        else:
            tasten = datenE[0]["Einstellungen"][0][bezeichnung]
        
        return tasten    

    def oberflaeche(self):
        ''' difiniert die Gestaltung '''
        self.title = Label(master=self.fenster, width=25,
                           bg='darkred', fg='white',
                           text="Pillenplaner", font=("Courier", 14))

        self.rahmenL = Frame(master=self.fenster, relief=SUNKEN, bd=4,
                             bg='darkred', pady=5)
        self.rahmenL.grid(column=0, row=1, padx=5, pady=10, rowspan=8,
                          sticky='n')
        datei = StringVar()

        self.Taste1 = Radiobutton(self.rahmenL, width=10,
                                  text=self.tasten_text("Taste 1"),
                                  indicatoron=0, variable=datei,
                                  value='db3', command=" ")
        self.Taste1.grid(column=0, row=1, pady=4, padx=5)

        self.Taste2 = Radiobutton(self.rahmenL, width=10,
                                  text=self.tasten_text("Taste 2"),
                                  indicatoron=0, variable=datei,
                                  value='Neu', command=" ")
        self.Taste2.grid(column=0, row=2, pady=4, padx=5)

        self.Taste3 = Radiobutton(self.rahmenL, width=10,
                                  text=self.tasten_text("Taste 3"),
                                  indicatoron=0, variable=datei,
                                  command=" ")
        self.Taste3.grid(column=0, row=3, pady=4, padx=5)

        self.Taste4 = Radiobutton(self.rahmenL, width=10,
                                  text=self.tasten_text("Taste 4"),
                                  indicatoron=0, variable=datei,
                                  command=" ")
        self.Taste4.grid(column=0, row=4, pady=4, padx=5)

        self.Taste5 = Radiobutton(master=self.rahmenL, width=10,
                                  text=self.tasten_text("Taste 5"),
                                  indicatoron=0, variable=datei)
        self.Taste5.grid(column=0, row=5, pady=4, padx=5)

        datei='db3'
        self.Taste1.select()

def main():
    p=Pills()
    p.fenster.mainloop()

if __name__ == "__main__":
    main()
Was soll hier passieren:
Es gibt eine separate Datei namens »mod2« in dieser Datei sind vorgaben gespeichert, die sich vom Nutzer ändern lassen, um den Text
der Tasten anzupassen, sowie wie viele Tage vorher eine Warnung erzeugt werden soll.
Die Datei wird eingelesen und als »datenE« zur Verfügung gestellt, sollte die Datei nicht da oder nicht lesbar sein werden Standardwerte
bereitgestellt.
Starte ich jetzt das Skript muss ich, wenn mod2 fehlt, 6 mal die messagebox bestätigen, statt wie vorher mit global einmal.

Was ist mein Fehler? Wie mache ich das denn jetzt das datenE immer zur Verfügung steht aber nicht jedesmal über »def config():«
die Datei »mod2« wieder und wieder geöffnet wird und ohne global?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Mit der Zeit werden es sehr viele Namen, die man aus tkinter importieren muß. Einfacher wird es, wenn man alle Namen über tkinter (bzw. den Alias tk) anspricht.
Die Größe eines Fensters sollte man nicht fest vorgeben, die ergibts sich aus dem Inhalt.
config wird aufgerufen, aber nichts mit den zurückgegebenen Daten gemacht, die könnte man an ein Attribut binden, und müßte sie nur noch einmal laden.
config braucht nichts von self, also ist das eigentlich eine eigenständige Funktion.
Man benutzt keine nackten `except`, weil die wirklich alles abfangen. Hier sind wohl IOError und ValueError sinnvolle Exceptions.
Variabelnnamen schreibt man klein und benutzt keine kryptischen Abkürzungen, was soll das E bei datenE?
Mit with wird die Datei automatisch geschlossen, explizites close ist unnötig.
Warum gibt es in der Konfigration eine Liste mit einem Element, in dem ein Wörterbuch mit dem einzigen Eintrag "Einstellungen" ist. Die beiden Strukturebenen kann man einfach weglassen.
Die Methode oberflaeche ist überflüssig, weil das gleich in __init__ stehen könnte.
Übrigens, Methoden sollten nach Tätigkeiten benannt sein.
self.title wird nicht angezeigt und nicht benutzt.
Auch StringVar sollte einen `master` haben.
command sollte eine Funktion sein, kein String!
Statt Variablennamen durchzunummerieren sollte man entsprechende Datenstrukturen und Schleifen benutzen.

Ich habe mal die ganzen Formatierungsangaben gelöscht, weil das eigentlich der User mit seinen Einstellungen festlegen sollte.

Code: Alles auswählen

import tkinter as tk
from tkinter import messagebox

CONFIG_FILENAME = "mod2"
DEFAULT_CONFIG = [
    {
        'Einstellungen': [
            {
                'Hinweis': ' 1 ',
                'Warnung': ' 22 '
            }
        ]
    }
]

def read_config():
    try:
        with open('mod2', 'rb') as data:
            return json.load(data)
    except (IOError, ValueError):
        # something went wrong reading the config
        messagebox.showinfo(
            title="Info",
            message="Configdatei nicht gefunden. "
                "Eine neue wird angelegt. "
                "Standardwerte werden geladen"
        )
    return DEFAULT_CONFIG


class Pills():
    def __init__(self):
        self.fenster = tk,Tk()
        self.fenster.title("Auszug aus Pillenplaner")
        self.config = read_config()

        self.rahmen = tk.Frame(
            master=self.fenster,
            pady=5
        )
        self.rahmen.grid(
            column=0, row=1, padx=5, pady=10, rowspan=8,
            sticky=tk.N
        )
        self.datei = tk.StringVar(self.fenster)
        self.tasten = []
        for row, (text, value) in enumerate([
            ("Taste 1", "db3"),
            ("Taste 2", "Neu"),
            ("Taste 3", "???"),
            ("Taste 4", "???"),
            ("Taste 5", "???"),
        ], 1): 
            taste = tk.RadioButton(self.rahmen, width=10,
                text=self.tasten_test(text),
                indicatoron=0, variable=self.datei,
                value=value
            )
            taste.grid(column=0, row=row, pady=4, padx=5)
            self.tasten.append(taste)
        self.Tasten[0].select()

    def tasten_text(self, bezeichnung):
        ''' beschriftet die Tasten für Personen '''
        print("ruf Tast:", bezeichnung)
        if bezeichnung not in self.config[0]["Einstellungen"][0]:
            tasten = bezeichnung
        else:
            tasten = self.config[0]["Einstellungen"][0][bezeichnung]        
        return tasten    


def main():
    pills = Pills()
    pills.fenster.mainloop()

if __name__ == "__main__":
    main()
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

@ Sirius3: Leider hilft das kein Stück. Wenn ich das so versuche zu starten erhalte ich diese Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
    exec(code, self.locals)
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 85, in <module>
    main()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 81, in main
    pills = Pills()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 41, in __init__
    self.fenster = tk,Tk()
NameError: name 'Tk' is not defined. Did you mean: 'tk'?
In meiner Verzweiflung habe ich dann etwas getan das nicht gut ist, nämlich »from tkinter import *« mit eingefügt.
Das brachte eine neue Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
    exec(code, self.locals)
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 86, in <module>
    main()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 82, in main
    pills = Pills()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 43, in __init__
    self.fenster.title("Auszug aus Pillenplaner")
AttributeError: 'tuple' object has no attribute 'title'
Also habe ich die Zeile entfernt die zuvor die Titelleiste beschriftet hat.
Damit war der Weg frei für eine neue Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
    exec(code, self.locals)
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 86, in <module>
    main()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 82, in main
    pills = Pills()
  File "/mnt/sda5/scripts/python/Pillen-planer/Tabletten 1.0/Python 2.py", line 46, in __init__
    self.rahmen = tk.Frame(
  File "/usr/lib/python3.10/tkinter/__init__.py", line 3163, in __init__
    Widget.__init__(self, master, 'frame', cnf, {}, extra)
  File "/usr/lib/python3.10/tkinter/__init__.py", line 2595, in __init__
    self._setup(master, cnf)
  File "/usr/lib/python3.10/tkinter/__init__.py", line 2564, in _setup
    self.tk = master.tk
AttributeError: 'tuple' object has no attribute 'tk'
Was ist hier falsch und warum?
Nachtrag: statt ›import *‹ hab ich es auch nochmal mit ›import Tk‹ probiert, wurde aber auch nicht besser.
Allerdings hab ich auch in meiner Frage imoprt json vergessen.
__deets__
User
Beiträge: 14544
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sirius3 hat sich verschrieben, es sollte tk.Tk heißen. Er hat tk,Tk geschrieben.
valentinks
User
Beiträge: 20
Registriert: Sonntag 27. Juni 2021, 10:31
Wohnort: Kassel

Okay. Danke euch erstmal. Werde mir das jetzt genauer ansehen und dann versuchen im Rest unterzubringen und an das neu gelernte anzupasen.
Antworten