Programmstruktur python + tkinter

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
jake-the-snake

Hallo Leute

Ich habe mal eine Frage zum allgemeinen richtigen Aufbau. Gegeben ist sowas hier (Schema):

Code: Alles auswählen

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

import os
import sys
import time
import pyudev
import subprocess
from tkinter import *

# Variable-Definier-Block
trallala = 0
trullala = 0

class App:

  def 
  def 
  def 

root = Tk()

app = App(root)

Label...
Label...
Label...

root.mainloop()
Gehe ich richtig in der Annahme, dass das tkinter-Fenster bei diesem Schema von Zeile 21-29 reicht?
Das class App Zeile 15 mit Zeile 23 abgeschlossen wird?
Ist das so richtig, dass sich diese beiden Spangen/Klammern überlappen - weil das Programm ja momentan läuft?

Ein paar Gedanken...
Also der aktive Programmteil (wo z.B. was gerechnet wird) kommt immer vor Beginn des tk-Fensters "root = Tk()"?
In der Loop des tkinter kann mit "Fenstercode nicht direkt mehr eingegriffen werden - nur noch über Variablen?

Gruß jts
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@jake-the-snake: wie Dir schon öfter gesagt wurde, werden in Python Blöcke per Einrückung gekennzeichnet. Zeile 21 ist nicht eingerückt, da ist also die Definition der Klasse schon vorbei. Da überlappt auch nichts, weil eingerückt und nicht-eingerückt sich gegenseitig ausschließen. Zeile 21 bis 29 sollten in eine Funktion geschoben werden, Sternchenimporte vermieden werden und Variable-Definier-Block verschwinden. Eingerückt wird immer mit 4 Leerzeichen pro Ebene.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
import os
import sys
import pyudev
import subprocess
import tkinter as tk
 
class App:
    def __init__(self):
        pass

    def xxx(self):
        pass

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()

if __name__ == '__main__':
    main()
jake-the-snake

Abend zusammen :wink:

Diese Einrückungsgeschichte macht mich noch wahnsinnig :) Aber ich fühle es - ich bin schon näher dran, dies zu verstehen... :D

Beim Happy-Birthday Beispielcode stehen zwei Funktionen (Das sind diese def-Dinger / zumindest nach der Python3.3... Doku) direkt untereinander.
Siehe 1.11.3 Lektion http://anh.cs.luc.edu/python/hands-on/3 ... tions.html

Code: Alles auswählen

'''Function definitions and invocation.'''

def happyBirthdayEmily():
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print("Happy Birthday, dear Emily.")
    print("Happy Birthday to you!")

def happyBirthdayAndre():
    print("Happy Birthday to you!")
    print("Happy Birthday to you!")
    print("Happy Birthday, dear Andre.")
    print("Happy Birthday to you!")

happyBirthdayEmily()
happyBirthdayAndre()
Wenn ich aber bei mir diese 4er Einrücktaktik mache, so wie in diesem Ausschnitt:

Code: Alles auswählen

# Einleitung der App-Knoepfe
class App:
  
   def __init__(self, master):
       frame = Frame(master)
       frame.place(width=120, height=320, x=290, y=120)
    
       # Code-Button
       self.codeing = Button(frame, 
                         text="Verschlüsseln ►", fg="red", pady=5, padx=5,
                         command=self.write_codeing)
       self.codeing.pack()

       # Decode-Button    
       self.decoding = Button(frame,
                         text="◄ Entschlüsseln", fg="green", pady=5, padx=5,
                         command=self.write_decoding)
       self.decoding.pack()

       # Export-Button
       self.savetousb = Button(frame,
                         text="USB  - Export ▼", fg="black", pady=5, padx=5,
                         command=self.write_savetousb)
       self.savetousb.pack()

       # Import-Button
       self.loadfromusb = Button(frame,
                         text="USB  - Import ▲", fg="black", pady=5, padx=5,
                         command=self.write_loadfromusb)
       self.loadfromusb.pack()
 
####################
# Codierungs-Block #
####################

    def write_codeing(self):
        print ("Verschlüsselung wird ausgeführt")
        # Linke Schrift Status-Feld 
        threeLabel = Label(root, text="Status: Verschlüsselung wird ausgeführt...", font=("TimesNewRoman", 10), width=36, anchor=("w"))
        threeLabel.place(x=25,y=352)
Bekomme ich wieder diesen berühmten Einrückfehler "IndentationError". Was ist denn nun an meinen beiden def-Funktionen anders, damit diese nicht unter einander fluchten dürfen? Warum soll dieses Einrücken nun falsch sein?

Gruß jts
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Warum soll dieses Einrücken nun falsch sein?
Wie schon mehrmals in diversen Threads gesagt wurde: Python ist es egal, wie weit man einrückt - es muss aber _einheitlich_ !!! sein. Zähl doch mal die Leerzeichen vor dem 1. und dem 2. `def`.... Dann siehst du, was falsch ist.

Absoluter Standard ist aber 4 Leerzeichen pro Ebene als Einrückung. Wurde auch schon gesagt: stell' deinen Editor sein ein, dass ein Druck auf die Tab-Taste vier Leerzeichen erzeugt. Dann sollte das mit dem Einrücken auch klappen.

Und: Einrückungen in Python sind essentiell WICHTIG! WICHTIG! WICHTIG! Solange du da ein Akzeptanzproblem mit hast, kannst du niemals vernünftig in Python coden. Schwierig ist das ja echt nicht...

Gruß, noisefloor
jake-the-snake

Hallo noisefloor

Oh man - ein Leehrzeichen hat gefehlt. Da werde ich wohl mal meine Brille reinigen :idea:
Jetzt funktioniert es.

@Sirius3
Zeile 21 bis 29 sollten in eine Funktion geschoben werden
Du meinst in so ein def-Dingens wie von Dir gezeigt?

Code: Alles auswählen

def main():
    root = tk.Tk()
    app = App(root)
    root.mainloop()
 
if __name__ == '__main__':
    main()
Dazu hätte ich noch eine abschließende Frage: Was bewirkt Zeile 6? Eine If-Abfrage mit init???
Und Zeile 7 ist der loop zu def main(): ?

@noisefloor
Danke nochmals für die vielen Tipps :)

Gruß jts
jake-the-snake

Hallo nochmal

Ich habe das Ausgabematerial für das tkinter-Fenster in ein "def" gepackt:

Code: Alles auswählen

def main():
    # Fenster-Titel
    root = Tk(className=" Fenstertitel")

    # Knoepfe-App-Ende
    app = App(root)

    def center_window(width=300, height=200):
        # get screen width and height
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()

        # calculate position x and y coordinates
        x = (screen_width/2) - (width/2)
        y = (screen_height/2) - (height/2)
        root.geometry('%dx%d+%d+%d' % (width, height, x, y))

    center_window(700, 380)

    # Linke Schrift ueber Klartext-Feld 
    oneLabel = Label(root, text="Klartext-Feld", font=("TimesNewRoman", 18))
    oneLabel.place(x=25,y=15)

    # Rechte Schrift ueber Codetext-Feld
    twoLabel = Label(root, text="Codetext-Feld", font=("TimesNewRoman", 18))
    twoLabel.place(x=535,y=15)

    # Linkes Klartextfeld 
    klartextfeld = Text(root, height=20, width=35)
    klartextfeld.place(x=25,y=60)
    klartextfeld.insert(END, "Ihre Nachricht...")

    # Rechtes Codetextfeld 
    codetextfeld = Text(root, height=20, width=35)
    codetextfeld.place(x=425,y=60)
    codetextfeld.insert(END, "Zugehöriger Cryptocode...")

    # Main-Logo
    photo = PhotoImage(file="independant.gif")
    label = Label(image=photo)
    label.image = photo
    label.place(x=328,y=20)

    if usbid == 123:
        # USB-Photo-Anzeige ON
        photo = PhotoImage(file="usb-logo-on.gif")
        label = Label(image=photo)
        label.image = photo
        label.place(x=330,y=245)
        # Linke Schrift Status-Feld 
        threeLabel = Label(root, text="Status: Betriebsbereit...", font=("TimesNewRoman", 10), width=36, anchor=("w"))
        threeLabel.place(x=25,y=352)

    if usbid == -1:
        # USB-Photo-Anzeige OFF
        photo = PhotoImage(file="usb-logo-off.gif")
        label = Label(image=photo)
        label.image = photo
        label.place(x=330,y=245)
        # Linke Schrift Status-Feld 
        threeLabel = Label(root, text="Status: USB-Stick nicht erkannt / Neustart !", font=("TimesNewRoman", 10), width=36, anchor=("w"))
        threeLabel.place(x=25,y=352)

    root.mainloop()

if __name__ == '__main__':
    main()
Programm läuft. Aber wenn ich auf den ersten Knopf drücke gibt es die Fehlermeldung

[codebox=bash file=Unbenannt.bsh]write_coding
threeLabel = Label(root, text="Status: Verschlüsselung wird ausgeführt...", font=("TimesNewRoman", 10), width=36, anchor=("w"))
NameError: name 'root' is not defined
[/code]

Dieser Fehler tritt erst auf, seit die Fensterausgabe in ein def main(): gepackt worden ist.
Außerdem musste ich die obere Zeile 3 | root = tk.Tk(className=" Fenstertitel")| in |root = Tk(className=" Fenstertitel")| ändern.

Gruß jts
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

sorry, aber das ist "Kraut und Rüben" = total unstrukturierter Code. Warum verschachtelst du jetzt zwei Funktionen? Das lässt Python zwar zu (und funktioniert auch), aber das macht man seeeehr selten, weil seeeeeehr selten sinnvoll.

Sirius3 hat dir doch in diesem Thread schon dir richtige Struktur gezeigt und ich hatte dir mal den Link auf ein gutes Beispiel in der offiziellen Python-Doku gegeben!

Und ich behaupte nach wie vor, dass du die Objektorientierung von Python noch nicht verstanden hast (heißt: wozu sind Klassen, der Instanzen, deren Methoden und Attribute da?), womit es schwer bis unmöglich ist, ein nicht-triviale GUI Anwendung zu programmieren. Was du quasi gerade "live" durchlebst.

Der ganze Code sollte hier _sauber_ und logisch getrennt in der Klasse der GUI stehen. Z.B. eine __init__ Funktion, eine Funktion z.B. Namens `create_widgets` zum Anlegen der Element der GUI, X Funktionen, die die Funktionalität der GUI implementieren. Sonst wird das nichts.

Gruß, noisefloor
jake-the-snake

Hallo nochmal

OK, ist gut. Ich habe die Sicherheitskopie wieder reaktiviert -> Programm läuft!

All eure Verbesserungen konnte ich jedenfalls nicht einbauen (was nicht heisst, dass es ein anderer gekonnt hätte).
Ich klinke mich an der Stelle aus. Den Rest muss ich selber erarbeiten.

Zu dem gesagten:
Die Fenster-Mitten-Berechnung für die Position funktioniert bei mir eben nur in dem Programmbereich unterhalb von root = Tk(className=" Fenstertitel"). Die If-Abfragen, bezüglich der Anzeige ob USB eingesteckt ist, oder nicht (Bildchentausch), ebenfalls nur unter dem root...
Wenn "def main()" für "root-Bereich" verwendet wird, fällt das Programm auseinander, weil dann der root nirgends mehr definiert ist.

Ich weiss, ihr meint es alle gut, aber wegen zwei If-Abfragen und einer Positionsberechnung im Fensterbereich wird wohl niemand sterben.

Ich habe das mit def durchgelesen und auch verstanden. Das mit class und __init__ ist in meiner Doku sehr abstrakt erklärt - da muss ich noch passen!

Allgemein ist ein Programm ohne Sprungmarken einfach nicht meins! Wenn ich als Programmschreiberling nicht meine Klammern und Bereiche selber definieren kann, und nur wegen einer "Einrückung" so "gegängelt" werde, ist das unschön.

Und wegen Struktur:
Ich habe sehr gerne Struktur. Mit einem Variabel-Bereich am Kopf, Subprogrammen, sowie ein und Ausgabe-Funktionen.

Ich habe jedenfalls die 4er-Einrückung soweit es geht umgesetzt. Auf USB schreiben klappt prima, lesen bekomme ich auch noch hin - der Rest ist Kosmetik. Ich hoffe ich darf trotzdem fragen, wenn es mit einer Syntax Schwieriegkeiten geben sollte... 8)

Gruß jts
Antworten