richtiges strukturieren von Programm Code / Konfigurations Datei

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.
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

Hallo Zusammen,

Ich habe eine Allgemeine Frage zum Aufbau von Programm Code..
Ich bin nun bei ca. 28.770 Zeilen Code welches für mein Programm ist und unter Pythonista läuft, das ganze wird nun recht unübersichtlich.. daher wollte ich nun schauen das ich vllt. mehrere Dateien erstellen würde welche immer ein "Hauptprogramm" seinerseits enthält.

Hierbei sind auch Codes doppelt da ich damals für jede Seite eine Eigene Nav. Bar erstellt hatte das wollte ich dann bei dem Überarbeiten anpassen und in eine "Allgemeine" Datei packen worin Code steht der von anderen Funktionen auch genutzt werden sollte.

Zur Zeit habe ich eine Klasse main welche gestartet wird und danach werden die entsprechenden Unterprogramme über Funktionen aufgerufen.

Code: Alles auswählen

if __name__ == '__main__':
    main()
zum etwas verdeutlichen (besseren verstehen):

Code: Alles auswählen

class main(SafeAreaView):
    def __init__(self):
        ####
        EINSTELLUNGEN für Variablen etc.
        ####
        
        self.StartPage()
self.StartPage() ist somit meine Startseite worüber ich alle anderen Seiten aufrufen kann und entsprechend wieder zurück komme.

Meine Frage ist nun wie ich am besten vorgehen soll und ob man das überhaupt macht und machen sollte ??
das ganze würde dann auch für Konfigurationsdateien gelten worin Farben, Texte etc. enthalten sein können sollte ?

Meine Vorstellung würde so für meine main.py aussehen:

Code: Alles auswählen

class main(SafeAreaView):
    def __init__(self):
        ####
        EINSTELLUNGEN für Variablen etc.
        ####
        
        self.StartPage()
		
    def StartPage(self):
        # Nur als Sichtbeispiel
        Button.Action = self.subPage1
        Button.Action = self.subPage2
        Button.Action = self.subPage3

    def subPage1(self, sender):
        # import aus Datei subPage1
        self.subPage1()

    def subPage2(self, sender):
        # import aus Datei subPage2
        self.subPage2()

    def subPage3(self, sender):
        # import aus Datei subPage3
        self.subPage3()
bin für Vorschläge offen wie ich das angehen kann und könnte :D

mfg.
Kalysto
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn man ein Programm hat, das 28770 Zeilen hat, dann ist da sicher was falsch. Aus Deinen Ausführungen werde ich aber nicht schlau.

Das Code-Fragment, den Du zeigst, hält sich an keine Namenskonvention, hat keinerlei Inhalt und besteht nur aus durchnummierierten generischen Wörtern, was ihn nochmals schwieriger zu verstehen macht.

Kannst Du ein Minimalbeispiel posten, das tatsächliche Funktionalität hat und den typischen Aufbau des Programms zeigt.
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

Und wie sind die Zeilen denn auf geteilt, wie lang sind die Methoden, wie viel Kommentare stecken drin und auf wie vielen Dateien sind die verteilt.
28K für ein Python Skript klingt tatsächlich recht viel, als ob man gegen DRY verstößt.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Benutzeravatar
Kebap
User
Beiträge: 687
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

> Hierbei sind auch Codes doppelt da ich damals für jede Seite eine Eigene Nav. Bar erstellt hatte das wollte ich dann bei dem Überarbeiten anpassen und in eine "Allgemeine" Datei packen worin Code steht der von anderen Funktionen auch genutzt werden sollte.

Das bestätigt wohl DRY Verstöße. Gut, wenn du das reparieren willst. Dazu braucht man aber nicht gleichzeitig mehrere Dateien. Mehrere Funktionen könnten schon ausreichen. Nicht zu viel gleichzeitig ändern.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

Sirius3 hat geschrieben: Montag 20. März 2023, 22:39 Wenn man ein Programm hat, das 28770 Zeilen hat, dann ist da sicher was falsch. Aus Deinen Ausführungen werde ich aber nicht schlau.

Das Code-Fragment, den Du zeigst, hält sich an keine Namenskonvention, hat keinerlei Inhalt und besteht nur aus durchnummierierten generischen Wörtern, was ihn nochmals schwieriger zu verstehen macht.

Kannst Du ein Minimalbeispiel posten, das tatsächliche Funktionalität hat und den typischen Aufbau des Programms zeigt.
Hier habe ich nun einmal den verkürzten Auszug der

__init__:

Code: Alles auswählen

def __init__(self):
        # Angabe des Gerätes für debugLog
        UIDevice = ObjCClass('UIDevice')
        device = UIDevice.currentDevice()
        self.DEVICE_VERSION = str(device.systemVersion())
        self.DEVICE_TYPE = str(device.localizedModel())
        self.DEVICE_OS = str(device.systemName())
        self.DEVICE_ID = str(device.identifierForVendor())
        
        # Pfadangabe für die Log Ausgaben...
        self.loggingPath = os.path.join(os.path.dirname(sys.argv[0]), 'Protokollierungen')

        # Angabe ob Abfragen erstellt werden sollten
        self.TABLE_SCHEMA__QUERY = None

        # Auswahl der Datenbank
        self.selectDatabase()
        
        # Benötigt für die Close Funktion
        self.safeModeMateriallist__APPCLOSE = False
        self.safeModeCustomerSetting__APPCLOSE = False
        self.safeModeDatanormEntry__APPCLOSE = False
        self.safeModeManufacturer__APPCLOSE = False
        self.safeModeMateriallistCSV__APPCLOSE = False
        
        # Bildschirmgröße
        self.WIDTH, self.HEIGHT = self.get_screen_size()
        self.DEVICE = self.DEVICE_TYPE

        # Unzulässige Zeichen in Kunden und Projektnamen
        self.forbiddenChars_all = set('<>:"/\|?*!')
        # Unzulässige Zeichen in Projektnamen (als Satz Ende)
        self.forbiddenChars_firstLastChar = set('.')
        
        # Hauptbildschirm Ansicht des Inhaltes
        self.main_content = ui.View(frame=self.bounds, flex='WH')
        self.add_subview(self.main_content)
        
        # Darstellung und Anischt (Begrenzung) der Buttons
        #self.button_area = style(ui.View(name='button_area', bg_color='')) #TODO ## style für Die Umrandung schauen was ich da machen werde ##
        self.button_area = ui.View(name='button_area')
        dock(self.button_area).bottom(self.main_content, At.TIGHT)
        # Ist für die Höhenanpassung der Button Darstellung
        at(self.button_area).height = at(self.button_area).fit_height
        
        # Angaben der Klassen Ansicht
        self.name = 'Barcode Scanner'
        self.background_color = getColor('MAIN')
        self.present('fullscreen', hide_title_bar=True) if self.DEVICE == 'iPad' else self.present('fullscreen', hide_title_bar=True, orientations=['portrait'])
        
        # Anzeige des Contents
        self.content_area = ui.View(name='content_area')
        dock(self.content_area).top(self.main_content, At.TIGHT)
        at(self.content_area).bottom = at(self.button_area).top - At.TIGHT
        
        self.StartPage()
StartPage:

Code: Alles auswählen

def StartPage(self):
        # Button für die Öffnung der sidebar der Kundenprofile
        self.checkCloseButton = size_to_fit(getButton_area(ui.Button(name='checkCloseButton'), 'checkCloseButton', self.DEVICE, 1))
        self.checkCloseButton.action = self.checkCloseButton__ACTION_Button
        self.checkCloseButton.width = self.checkCloseButton.height
        dock(self.checkCloseButton).top_right(self.main_content)
        
        # Button für die Öffnung der sidebar der Kundenprofile
        self.sidebarRegistrationButton = size_to_fit(getButton_area(ui.Button(name='sidebarRegistrationButton'), 'sidebarRegistrationButton', self.DEVICE, 1))
        self.sidebarRegistrationButton.enabled = False
        self.sidebarRegistrationButton.action = self.userSidebar_openClose
        self.sidebarRegistrationButton.width = self.sidebarRegistrationButton.height
        dock(self.sidebarRegistrationButton).bottom_left(self.button_area)
        
        # Anzeige sowie Darstellung der Sidebar (Benutzer)
        self.sidebarUserView = style(ui.View(name='sidebarUserView', width=sidebarSetWidth('sidebarUserView', self.DEVICE, self.WIDTH)))
        self.add_subview(self.sidebarUserView)
        at(self.sidebarUserView).top = at(self.main_content).top
        at(self.sidebarUserView).bottom = at(self.main_content).bottom
        at(self.sidebarUserView).right = at(self.main_content).left
        
        # User Sidebar Schließen
        self.userSidebarCloseButton = size_to_fit(getButton_area(ui.Button(name='userSidebarCloseButton'), 'sidebarCloseButton', self.DEVICE, 1))
        self.userSidebarCloseButton.action = self.userSidebar_openClose
        dock(self.userSidebarCloseButton).bottom_center(self.sidebarUserView)
        
        # Ist für die Höhenanpassung der Button Darstellung
        at(self.button_area).height = at(self.button_area).fit_height
        
        self.regUserLabel = size_to_fit(getLabel_area(ui.Label(name='regUserLabel'), 'regUserLabel', self.DEVICE))
        dock(self.regUserLabel).top_left(self.main_content, -At.TIGHT)
        at(self.regUserLabel).top = at(self.checkCloseButton).bottom
        
        self.showSetting = Action(
            set_menuOptions('Action', 'showSetting', self.DEVICE), self.settingBarcodeScannerAPPSwitcher__ACTION_Action,
            image=SymbolImage('gear'),
        )
        self.hideSetting = Action(
            set_menuOptions('Action', 'hideSetting', self.DEVICE), self.settingBarcodeScannerAPPSwitcher__ACTION_Action,
            image=SymbolImage('gear'),
        )
        self.hideSetting.selected = True
        
        set_menu(self.sidebarRegistrationButton, [
            self.showSetting,
            self.hideSetting,
        ], long_press=True)
        
        self.registration = getView_area('StartPage', get_area('StartPage', self.DEVICE))
        
        fill_with(
            self.registration
        ).from_top(self.content_area)
        
        welcomeLabel = size_to_fit(getLabel_area(ui.Label(name='welcomeLabel'), 'welcomeLabel', self.DEVICE))
        dock(welcomeLabel).top_center(self.registration)
        at(welcomeLabel).top = at(self.regUserLabel).bottom
        
        welcomeTextView = getTextView_area(ui.TextView(name='welcomeTextView'), 'welcomeTextView', self.DEVICE)
        welcomeTextView.width = (self.registration.width - (At.gap * 2))
        welcomeTextView.size_to_fit()
        dock(welcomeTextView).center(self.registration)
        
        serverStatusInfoLabel = size_to_fit(getLabel_area(ui.Label(name='serverStatusInfoLabel'), 'serverStatusInfoLabel', self.DEVICE))
        dock(serverStatusInfoLabel).top_left(self.registration, At.TIGHT)
        at(serverStatusInfoLabel).top = at(welcomeTextView).bottom

        serverStatusLabel = getLabel_area(ui.Label(name='serverStatusLabel'), 'serverStatusLabel', self.DEVICE)
        attach(serverStatusLabel).right_of(serverStatusInfoLabel)
        
        serverUpdateStatusInfoLabel = size_to_fit(getLabel_area(ui.Label(name='serverUpdateStatusInfoLabel'), 'serverUpdateStatusInfoLabel', self.DEVICE))
        dock(serverUpdateStatusInfoLabel).top_left(self.registration, At.TIGHT)
        at(serverUpdateStatusInfoLabel).top = at(serverStatusInfoLabel).bottom

        self.serverUpdateStatusLabel = getLabel_area(ui.Label(name='serverUpdateStatusLabel'), 'serverUpdateStatusLabel', self.DEVICE)
        attach(self.serverUpdateStatusLabel).right_of(serverUpdateStatusInfoLabel)

        appVersionsInfoLabel = size_to_fit(getLabel_area(ui.Label(name='appVersionsInfoLabel'), 'appVersionsInfoLabel', self.DEVICE))
        dock(appVersionsInfoLabel).top_left(self.registration, At.TIGHT)
        at(appVersionsInfoLabel).top = at(serverUpdateStatusInfoLabel).bottom

        self.appVersionsLabel = getLabel_areaContent(ui.Label(name='appVersionsLabel'), f'{appVersion} - {appBuild}', 'appVersionsLabel', self.DEVICE)
        attach(self.appVersionsLabel).right_of(appVersionsInfoLabel)
        
        serverVersionsInfoLabel = size_to_fit(getLabel_area(ui.Label(name='serverVersionsInfoLabel'), 'serverVersionsInfoLabel', self.DEVICE))
        dock(serverVersionsInfoLabel).top_left(self.registration, At.TIGHT)
        at(serverVersionsInfoLabel).top = at(appVersionsInfoLabel).bottom

        self.serverVersionsLabel = getLabel_area(ui.Label(name='serverVersionsLabel'), 'serverVersionsLabel', self.DEVICE)
        attach(self.serverVersionsLabel).right_of(serverVersionsInfoLabel)

        selectedDatabaseInfoLabel = size_to_fit(getLabel_area(ui.Label(name='selectedDatabaseInfoLabel'), 'selectedDatabaseInfoLabel', self.DEVICE))
        dock(selectedDatabaseInfoLabel).top_left(self.registration, At.TIGHT)
        at(selectedDatabaseInfoLabel).top = at(serverVersionsInfoLabel).bottom

        selectedDatabaseLabel = getLabel_areaContent(ui.Label(name='selectedDatabaseLabel'), self.TABLE_SCHEMA, 'selectedDatabaseLabel', self.DEVICE)
        attach(selectedDatabaseLabel).right_of(selectedDatabaseInfoLabel)
        
        self.appVersionsLabel.x = self.serverVersionsLabel.x
        serverStatusLabel.x = self.serverVersionsLabel.x
        selectedDatabaseLabel.x = self.serverVersionsLabel.x
        self.serverUpdateStatusLabel.x = self.serverVersionsLabel.x

        # Angaben der Konfigurationsdateien
        self.configurationsFile__DEFAULT = 'config.default'
        self.configurationsFile__USER = 'config.user'
        self.configurationsFile__FOLDER = 'Konfigurationsdatei'

        # Angaben des Barcode Scanner Ordners
        self.mainDirApplication = '03_Barcode_Application'
        self.subDirApplication = '2021_08_14 -- outVersion'
        
        # Deaktiviert das 2 Finger swipen (Schließen Funktion)
        disable_swipe_to_close(self.superview)
        
        # Sperrung des Ruhemoduses des iPhones
        on_main_thread(console.set_idle_timer_disabled)(True)
Das ganze Projekt hatte ich vor 2 Jahren begonnen und ist natürlich immer wieder gewachsen sodass weitere Seiten dazugekommen sind etc.
Ich bin jetzt nicht der Top Programmierer das war und ist mein erstes Großes Projekt welches immer noch angepasst etc. wird...

meine Frage ist eben wie man das besser gestalten kann bzw. ob man gewissen Code in mehrere Dateien auslagern kann und Darf....
Das Code doppelt ist weis ich jetzt schon ohne nachschauen zu müssen weil ich eben für jede Seite eine eigene Nav. bar erstellt habe welches man natürlich über eine Erledigen könnte... bevor ich das nun aber angehen wollte wollte ich eben wissen worauf ich noch achten sollte was man anpassen Müsste und sollte.

wie gesagt das ganze läuft auf MacOS und IOS über Pythonista.

danke schon einmal für die Hilfen
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

darktrym hat geschrieben: Montag 20. März 2023, 23:17 Und wie sind die Zeilen denn auf geteilt, wie lang sind die Methoden, wie viel Kommentare stecken drin und auf wie vielen Dateien sind die verteilt.
28K für ein Python Skript klingt tatsächlich recht viel, als ob man gegen DRY verstößt.
Kommentare sind vorhanden klar und auch ein Log "System"
Dateien habe ich nur 2 Stk. die "App" an sich und eine Konfigurationsdatei mit Farben und etliche Einstellungen der Button's etc...
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kalysto: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Methoden werden üblicherweise, genau wie Funktionen, nach den Tätigkeiten benannt die sie durchführen, damit man weis was sie tun und um sie leicht von eher passiven Werten unterscheiden zu können. `StartPage` ist keine Tätigkeit und zudem noch so geschrieben als wäre es eine Klasse.

Was liefern denn die Methoden auf dem `device` das man das in Zeichenketten umwandeln muss?

`os.path` & Co sind durch das `pathlib`-Modul abgelöst.

Warum gibt es `DEVICE` und `DEVICE_TYPE` wenn die den gleichen Wert haben?

Das sieht extrem danach aus als wäre das ein Programm das aus Funktionen besteht, die auf globale Variablen zugreifen, nur dass das alles einfach in einer Klasse verschoben wurde. Eine Gott-Klasse die *viel* zu viel weiss und kann.

Attribute werden in der `__init__()` angelegt, nicht in anderen Methoden. Und in der `__init__()` wird hier schon zu viel angelegt wovon sehr wahrscheinlich nicht einmal alles in der Klasse stehen muss.

In der letzten Zeile von dem ``# Angaben der Klassen Ansicht``-Block wird ein bedingter Ausdruck missbraucht um das in eine Zeile zu quetschen, denn mit dem *Ergebnis* von dem Ausdruck wird nichts gemacht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 @Kalysto: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Methoden werden üblicherweise, genau wie Funktionen, nach den Tätigkeiten benannt die sie durchführen, damit man weis was sie tun und um sie leicht von eher passiven Werten unterscheiden zu können. `StartPage` ist keine Tätigkeit und zudem noch so geschrieben als wäre es eine Klasse.
Okay, das kann ich anpassen wenn ich die Datei ändere, Danke.
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 Was liefern denn die Methoden auf dem `device` das man das in Zeichenketten umwandeln muss?
Das weis ch heute leider nicht mehr und müsste es Prüfen. Du möchtest darauf hinaus das man den `str()` part nicht benötigt ?
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 `os.path` & Co sind durch das `pathlib`-Modul abgelöst.
Okay, das wusste ich nicht mit & Co. meinst du noch etwas ?
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 Warum gibt es `DEVICE` und `DEVICE_TYPE` wenn die den gleichen Wert haben?
Ist eine sehr gute Frage .
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 Das sieht extrem danach aus als wäre das ein Programm das aus Funktionen besteht, die auf globale Variablen zugreifen, nur dass das alles einfach in einer Klasse verschoben wurde. Eine Gott-Klasse die *viel* zu viel weiss und kann.
Einfach gesagt ja, denn ich betätige ein Button und das entsprechende wird eben ausgeführt: neue Seite geladen sql Einträge getätigt etc.
Ist das nun aber schlimm alles in eine Klasse zu packen ? denn sonst könnte ich ja nicht auf alle Daten zurückgreifen ?
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 Attribute werden in der `__init__()` angelegt, nicht in anderen Methoden. Und in der `__init__()` wird hier schon zu viel angelegt wovon sehr wahrscheinlich nicht einmal alles in der Klasse stehen muss.
Die Werte brauche ich schon, das steht außer frage...
sollte ich diese Definitionen nicht in der `__init__` erstellen ? dafür ist diese doch aber ?
und was genau meinst du mit Attribute.
__blackjack__ hat geschrieben: Dienstag 21. März 2023, 22:15 In der letzten Zeile von dem ``# Angaben der Klassen Ansicht``-Block wird ein bedingter Ausdruck missbraucht um das in eine Zeile zu quetschen, denn mit dem *Ergebnis* von dem Ausdruck wird nichts gemacht.
Meinst du diese ?

Code: Alles auswählen

self.present('fullscreen', hide_title_bar=True) if self.DEVICE == 'iPad' else self.present('fullscreen', hide_title_bar=True, orientations=['portrait'])
sollte man das eher so schreiben ?

Code: Alles auswählen

if self.DEVICE == 'iPad'
    self.present('fullscreen', hide_title_bar=True)
else
    self.present('fullscreen', hide_title_bar=True, orientations=['portrait'])
Danke für deine Antwort und Tipps!
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kalysto: Ja, ich wollte darauf hinaus ob man die `str()`-Aufrufe überhaupt benötigt.

Mit `os.path` & Co ist alles gemeint was man mit dem `pathlib`-Modul erledigen kann.

Ja es ist schlimm alles in eine Klasse zu packen. Das ist ja 0 Struktur. Dann kann man das auch ohne Klasse machen mit ganz viel ``global``. Das macht dann letztlich keinen Unterschied. Eine Klasse sollte ein ”Ding” modellieren mit einer übersichtlichen Anzahl von Attributen und Methoden und nicht *alles* mit Unmengen an Attributen, die zudem noch nicht mal alle in einer dafür vorgesehenen Methode erstellt werden, sondern über alle Methoden verteilt sind, so dass man gar nicht so leicht sagen kann welche Attribute es insgesamt gibt, und noch weniger *wann* die anfangen zu existieren, also was vorher alles passiert/aufgerufen werden muss. Das kann doch kein Mensch sinnvoll nachvollziehen.

Attribute sind das wo man (üblicherweise) mit dem Punktoperator zugreift. Also `some_object.this_is_the_attribute`. Konkret zum anlegen: ``self.name = ...`` darf nicht ausserhalb einer `__init__()` das erste mal passieren, denn das würde ja bedeuten, dass das Attribut nach der `__init__()` noch nicht existiert. Was bedeutet, dass das Objekt nicht komplett initialisiert ist, was aber die Aufgabe der `__init__()`-Methode ist.

Eine übliche Trennung/Strukturierung ist es die Programmlogik von der GUI zu trennen. Also Funktionen und Klassen zu schreiben mit denen die Aufgaben erledigt werden können, die aber nichts von der GUI wissen. Und dann Code für die GUI, der auf der Programmlogik aufsetzt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

__blackjack__ hat geschrieben: Mittwoch 22. März 2023, 20:08 @Kalysto: Ja, ich wollte darauf hinaus ob man die `str()`-Aufrufe überhaupt benötigt.

Mit `os.path` & Co ist alles gemeint was man mit dem `pathlib`-Modul erledigen kann.
Okay, das werde ich kontrollieren und anpassen.
__blackjack__ hat geschrieben: Mittwoch 22. März 2023, 20:08 Ja es ist schlimm alles in eine Klasse zu packen. Das ist ja 0 Struktur. Dann kann man das auch ohne Klasse machen mit ganz viel ``global``. Das macht dann letztlich keinen Unterschied. Eine Klasse sollte ein ”Ding” modellieren mit einer übersichtlichen Anzahl von Attributen und Methoden und nicht *alles* mit Unmengen an Attributen, die zudem noch nicht mal alle in einer dafür vorgesehenen Methode erstellt werden, sondern über alle Methoden verteilt sind, so dass man gar nicht so leicht sagen kann welche Attribute es insgesamt gibt, und noch weniger *wann* die anfangen zu existieren, also was vorher alles passiert/aufgerufen werden muss. Das kann doch kein Mensch sinnvoll nachvollziehen.
hmm, okay nun weis ich aber nicht genau wie ich das umsetzten kann da ich ja gewisse attribute also self.name etc. auch in anderen Funktionen benötige und wenn ich nun für jede "Seite" eine Klasse erstellen würde wie würde ich so dann meine Attribute auch in andere Klassen "mit nehmen" können ?
__blackjack__ hat geschrieben: Mittwoch 22. März 2023, 20:08 Attribute sind das wo man (üblicherweise) mit dem Punktoperator zugreift. Also `some_object.this_is_the_attribute`. Konkret zum anlegen: ``self.name = ...`` darf nicht ausserhalb einer `__init__()` das erste mal passieren, denn das würde ja bedeuten, dass das Attribut nach der `__init__()` noch nicht existiert. Was bedeutet, dass das Objekt nicht komplett initialisiert ist, was aber die Aufgabe der `__init__()`-Methode ist.
Heißt das nun das ich alle Attribute self.xyz welche ich benötige in der __init__() erstellen muss ?
__blackjack__ hat geschrieben: Mittwoch 22. März 2023, 20:08 Eine übliche Trennung/Strukturierung ist es die Programmlogik von der GUI zu trennen. Also Funktionen und Klassen zu schreiben mit denen die Aufgaben erledigt werden können, die aber nichts von der GUI wissen. Und dann Code für die GUI, der auf der Programmlogik aufsetzt.
Also müsste ich meine Darstellung (Visuell) von meinen SQL Einträgen / Abfragen Trennen ?
Wäre das somit eine eigene Datei ?
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Kalysto hat geschrieben: Freitag 24. März 2023, 20:07 hmm, okay nun weis ich aber nicht genau wie ich das umsetzten kann da ich ja gewisse attribute also self.name etc. auch in anderen Funktionen benötige und wenn ich nun für jede "Seite" eine Klasse erstellen würde wie würde ich so dann meine Attribute auch in andere Klassen "mit nehmen" können ?
Hier mal ein Beispielcode, der macht eigentlich nichts, aber man sieht wie man zwischen Seiten navigieren kann:

Code: Alles auswählen

import tkinter as tk
from functools import partial


class CalculationApp(tk.Frame):
    def __init__(self, master, calculator):
        tk.Frame.__init__(self, master)
        self.calculator = calculator
        self.frames = {}
        home_page = Home(self)
        home_page.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
        entry_page = Entry(self, calculator)
        entry_page.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
        result_page = Result(self)
        result_page.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
        self.frames['Home'] = home_page
        self.frames['Entry'] = entry_page
        self.frames['Result'] = result_page
        self.show_page("Home")

    def show_page(self, page_name):
        self.frames[page_name].tkraise()

    def get_page(self, page_name):
        return self.frames[page_name]


class Home(tk.Frame):
    def __init__(self, controller):
        tk.Frame.__init__(self)
        self.controller = controller
        headline = tk.Label(self, text="Hier gehts zur Berechnung")
        headline.grid(row=0, column=1)
        go_to_entry_page = tk.Button(
            self, text=">>", command=partial(self.controller.show_page, "Entry")
        )
        go_to_entry_page.grid(row=1, column=3)


class Entry(tk.Frame):
    def __init__(self, controller, calculator):
        tk.Frame.__init__(self)
        self.controller = controller
        self.calculator = calculator
        values_for_calculation = [10, 30]
        description = tk.Label(self, text="Bitte Werte auswählen")
        description.grid(row=0, column=1)
        self.selected_button = tk.StringVar()
        for row_index, value in enumerate(values_for_calculation, 1):
            radio_button = tk.Radiobutton(
                self, text=value, value=value, variable=self.selected_button
            )
            radio_button.grid(row=row_index, column=0)
        tk.Button(
            self, text="Berechne", command=self.start_calculation
        ).grid(row=3, column=3)
        tk.Button(self, text="<<", command=partial(self.controller.show_page, "Home")).grid(
            row=3, column=0
        )

    def start_calculation(self):
        self.calculator.calculate(int(self.selected_button.get()))
        self.controller.get_page('Result').show_result.config(text=self.calculator.result)
        self.controller.show_page('Result')


class Result(tk.Frame):
    def __init__(self, controller):
        tk.Frame.__init__(self)
        self.controller = controller
        self.show_result = tk.Label(self, text='')
        self.show_result.grid(row=0, column=1)
        button = tk.Button(
            self, text="Startseite", command=partial(controller.show_page, "Home")
        )
        button.grid(row=1, column=1)


class Calculator:
    def __init__(self):
        self.result = None
        self.database = [2, 3, 5, 6]

    def calculate(self, user_choice):
        self.result = sum(number * user_choice for number in self.database)


def main():
    calculator = Calculator()
    root = tk.Tk()
    root.title("Berechnungsprogramm")
    app = CalculationApp(root, calculator)
    app.mainloop()


if __name__ == "__main__":
    main()
Kalysto hat geschrieben: Freitag 24. März 2023, 20:07 Heißt das nun das ich alle Attribute self.xyz welche ich benötige in der __init__() erstellen muss ?
Ja
Kalysto hat geschrieben: Freitag 24. März 2023, 20:07 Also müsste ich meine Darstellung (Visuell) von meinen SQL Einträgen / Abfragen Trennen ?
Wäre das somit eine eigene Datei ?
Trenne ja, eine eigene Datei muss es nicht sein. Du kannst das auch in einer Datei voneinader trennen. Du musst dir dabei "nur" überlegen, ob die Logik funktionieren würde, wenn es kein GUI gibt und du das ganze zum Beispiel über das Terminal steuern willst.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

Dennis89 hat geschrieben: Freitag 24. März 2023, 21:14
Kalysto hat geschrieben: Freitag 24. März 2023, 20:07 Heißt das nun das ich alle Attribute self.xyz welche ich benötige in der __init__() erstellen muss ?
Ja
auch wenn ich erst später diese definieren werde ?
muss man dann leere Attribute erstellen ?

kann man dann z.b. mit:

Code: Alles auswählen

self.controller.show_page.name
auf attribute zurückgreifen ?
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kalysto: Man könnte jede Seite mit einer eigenen Klasse modellieren. Und das was die Seite(n) benötigen ebenfalls in einer (oder mehreren) Klassen, wo das oder die Objekte beim erstellen der Seite übergeben werden, damit der GUI-Code auf den entsprechenden Teil der Programmlogik zugreifen kann.

Die Trennung von Programmlogik und GUI muss nicht zwingend auch@Kalysto: Man könnte jede Seite mit einer eigenen Klasse modellieren. Und das was die Seite(n) benötigen ebenfalls in einer (oder mehreren) Klassen, wo das oder die Objekte beim erstellen der Seite übergeben werden, damit der GUI-Code auf den entsprechenden Teil der Programmlogik zugreifen kann.

Die Trennung von Programmlogik und GUI muss nicht zwingend auch eine Trennung in Module bedeuten. Kann es aber. Und natürlich kann man sowohl Programmlogik als auch GUI auf mehrere Module verteilen. Das lässt sich so pauschal nicht sagen was da sinnvoll ist. Spontan würde ich ja sagen 28K Zeilen sind zu viel für ein Modul, andererseits wissen wir nicht wie viel mal davon durch Schleifen, Funktionen, und Klassen einsparen kann.

Leere Attribute ja, wenn man das machen muss, es ist aber ein „code smell“ wenn man das dauernd und/oder viele Attribute hat die in der `__init__()` nicht mit sinnvollen Werten belegt werden können. Nach der `__init__()` sollte ein Objekt normalerweise komplett und in einem gebrauchsfertigen Zustand sein. eine Trennung in Module bedeuten. Kann es aber. Und natürlich kann man sowohl Programmlogik als auch GUI auf mehrere Module verteilen. Das lässt sich so pauschal nicht sagen was da sinnvoll ist. Spontan würde ich ja sagen 28K Zeilen sind zu viel für ein Modul, andererseits wissen wir nicht wie viel mal davon durch Schleifen, Funktionen, und Klassen einsparen kann.

Leere Attribute ja, wenn man das machen muss, es ist aber ein „code smell“ wenn man das dauernd und/oder viele Attribute hat die in der `__init__()` nicht mit sinnvollen Werten belegt werden können. Nach der `__init__()` sollte ein Objekt normalerweise komplett und in einem gebrauchsfertigen Zustand sein.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

__blackjack__ hat geschrieben: Samstag 25. März 2023, 11:57 @Kalysto: Man könnte jede Seite mit einer eigenen Klasse modellieren. Und das was die Seite(n) benötigen ebenfalls in einer (oder mehreren) Klassen, wo das oder die Objekte beim erstellen der Seite übergeben werden, damit der GUI-Code auf den entsprechenden Teil der Programmlogik zugreifen kann.

Die Trennung von Programmlogik und GUI muss nicht zwingend auch@Kalysto: Man könnte jede Seite mit einer eigenen Klasse modellieren. Und das was die Seite(n) benötigen ebenfalls in einer (oder mehreren) Klassen, wo das oder die Objekte beim erstellen der Seite übergeben werden, damit der GUI-Code auf den entsprechenden Teil der Programmlogik zugreifen kann.

Die Trennung von Programmlogik und GUI muss nicht zwingend auch eine Trennung in Module bedeuten. Kann es aber. Und natürlich kann man sowohl Programmlogik als auch GUI auf mehrere Module verteilen. Das lässt sich so pauschal nicht sagen was da sinnvoll ist. Spontan würde ich ja sagen 28K Zeilen sind zu viel für ein Modul, andererseits wissen wir nicht wie viel mal davon durch Schleifen, Funktionen, und Klassen einsparen kann.

Leere Attribute ja, wenn man das machen muss, es ist aber ein „code smell“ wenn man das dauernd und/oder viele Attribute hat die in der `__init__()` nicht mit sinnvollen Werten belegt werden können. Nach der `__init__()` sollte ein Objekt normalerweise komplett und in einem gebrauchsfertigen Zustand sein. eine Trennung in Module bedeuten. Kann es aber. Und natürlich kann man sowohl Programmlogik als auch GUI auf mehrere Module verteilen. Das lässt sich so pauschal nicht sagen was da sinnvoll ist. Spontan würde ich ja sagen 28K Zeilen sind zu viel für ein Modul, andererseits wissen wir nicht wie viel mal davon durch Schleifen, Funktionen, und Klassen einsparen kann.

Leere Attribute ja, wenn man das machen muss, es ist aber ein „code smell“ wenn man das dauernd und/oder viele Attribute hat die in der `__init__()` nicht mit sinnvollen Werten belegt werden können. Nach der `__init__()` sollte ein Objekt normalerweise komplett und in einem gebrauchsfertigen Zustand sein.
Sry, ich glaube das ist was schief gelaufen, kann das nicht so richtig deuten und lesen die Texte sind verschoben...
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Naja da ist der Text mitten im Text noch einmal eingefügt. Das lässt sich leicht rekonstruieren — einfach den Mittelteil entfernen:

@Kalysto: Man könnte jede Seite mit einer eigenen Klasse modellieren. Und das was die Seite(n) benötigen ebenfalls in einer (oder mehreren) Klassen, wo das oder die Objekte beim erstellen der Seite übergeben werden, damit der GUI-Code auf den entsprechenden Teil der Programmlogik zugreifen kann.

Die Trennung von Programmlogik und GUI muss nicht zwingend auch eine Trennung in Module bedeuten. Kann es aber. Und natürlich kann man sowohl Programmlogik als auch GUI auf mehrere Module verteilen. Das lässt sich so pauschal nicht sagen was da sinnvoll ist. Spontan würde ich ja sagen 28K Zeilen sind zu viel für ein Modul, andererseits wissen wir nicht wie viel mal davon durch Schleifen, Funktionen, und Klassen einsparen kann.

Leere Attribute ja, wenn man das machen muss, es ist aber ein „code smell“ wenn man das dauernd und/oder viele Attribute hat die in der `__init__()` nicht mit sinnvollen Werten belegt werden können. Nach der `__init__()` sollte ein Objekt normalerweise komplett und in einem gebrauchsfertigen Zustand sein.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

@__blackjack__: Ich werde das so machen das ich je Seite ein eigens Modul erstellen werde mit den entsprechenden Funktionen.
Ich habe hier einmal ein Test gemacht und ist es korrekt das die jeweilige Datei auch die Importe benötigt wie z.b. sys wenn dies verwendet wird und nicht über die "main" "versorgt" werden würde ?

des Weiteren ist der Aufruf so korrekt in dem "kleinen" Beispiel wie unten dargestellt:

main.py

Code: Alles auswählen

import Startseite

class main():
    def __init__(self):
    	# hier steht nun ein wenig code
    	
    	Startseite.Startseite.start_der_funktion(self)
Startseite.py

Code: Alles auswählen

# und hier nochmal alle Importe welche diese Datei benötigt ?
class Startseite():
     def __init__(self):
         # hier steht nun ein wenig code
         
     def start_der_funktion(self):
         # Hier ist nun der Code
         # welcher in der Main ausgeführt werden sollte
         ...
         ...
         ..
         .
Wäre das vom Prinzip her so korrekt mit dem Aufruf ?
`Startseite.Startseite.start_der_funktion(self)`
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

'main' ist üblicherweise die Funktion, aus der das Programm gesteuert wird. Dass ist also die Funktion die zum Programmstart aufgerufen wird, darin kannst du dann eine Insatz von 'Startseite' anlagen.

Code: Alles auswählen

from Startseite import Startseite

def main():
    startseite = Startseite()
    startseite.start_der_funktion()


if _name__ == '__main__':
    main()
Klassen sind nicht dazu da, dass da Code drin steht und das vielleicht optisch aufgeräumt aussieht. Ich würde an deiner Stelle erst mal die unterschiedlichen Seiten vergessen und einen Code schreiben der eine Seite darstellt und eine einfache Aufgabe hat. Dann, wenn der Code ordentlich ist und die GUI von der Logik getrennt ist, dann würde ich mich an die zweite Seite wagen und so den Code von Anfang an ordentlich (und mit Nachfragen an die Profis hier) aufbauen. Achja und lass mal alles in einer Datei, das ist für ne Anfang übersichtlicher (so ging es mir zumindest).

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

Dennis89 hat geschrieben: Dienstag 28. März 2023, 22:04 Hallo,

'main' ist üblicherweise die Funktion, aus der das Programm gesteuert wird. Dass ist also die Funktion die zum Programmstart aufgerufen wird, darin kannst du dann eine Insatz von 'Startseite' anlagen.

Code: Alles auswählen

from Startseite import Startseite

def main():
    startseite = Startseite()
    startseite.start_der_funktion()


if _name__ == '__main__':
    main()
Klassen sind nicht dazu da, dass da Code drin steht und das vielleicht optisch aufgeräumt aussieht. Ich würde an deiner Stelle erst mal die unterschiedlichen Seiten vergessen und einen Code schreiben der eine Seite darstellt und eine einfache Aufgabe hat. Dann, wenn der Code ordentlich ist und die GUI von der Logik getrennt ist, dann würde ich mich an die zweite Seite wagen und so den Code von Anfang an ordentlich (und mit Nachfragen an die Profis hier) aufbauen. Achja und lass mal alles in einer Datei, das ist für ne Anfang übersichtlicher (so ging es mir zumindest).

Grüße
Dennis
Ich werde morgen einmal die Zeit haben und den Code mal erstellen wie ich es machen würde und Poste ihn dann einmal
Danke dir für deine Hilfe.
Kalysto
User
Beiträge: 117
Registriert: Freitag 14. April 2017, 15:28

@Dennis89
Wenn ich das nun machen würde wie in deinem Beispiel:

Code: Alles auswählen

from Startseite import Startseite

def main():
    startseite = Startseite()
    startseite.start_der_funktion()


if _name__ == '__main__':
    main()
wie kann ich nun das "self" von main an das "self" von start_der_funktion übergeben ?
Ich brauche die Möglichkeit das ich hier auf Variablen zurückgreifen müsste als Konkretes Beispiel:

Code: Alles auswählen

from Startseite import Startseite

class main(SafeAreaView):
    def __init__(self):
        self.device = 'iPhone' # 'iPad', 'Mac' etc...
        
        startseite = Startseite()
        startseite.Anmeldung()

if _name__ == '__main__':
    main()
Startseite.py:

Code: Alles auswählen

class Startseite():
    def Anmeldung():
        # Hier müsste ich nun mit dem self.device weiter arbeiten können.. (und ja das self.device muss in der main stehen da ich das öfter verwenden muss)
wie kann man das Problem lösen ? habe schon versucht das irgend wie zu übergeben aber leider alles ohne erfolg.

des Weiteren ist es Korrekt das ich in meiner Startseite.py alle import's angeben muss und diese nicht von der main.py übergeben werden ?
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich weis nicht ob ich dich richtig verstehe. Das was 'Startseite' bekommen soll wird in der '__init__' entgegen genommen. Wieso fehlt die im letzten Post?

Vielleicht wäre es hilfreicher wenn du etwas mehr Code zeigst, damit man das Prolem besser versteht und das man versteht was du vor hast.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten