Einmal Code verkleinern bitte

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Hallo zusammen,

ich hab schon viel mit Powershell gemacht und versuche nebenbei immer mal wieder was in Python zu machen.
Aktuell ein kleines Tool welches in regelmäßigen Abständen (30 Sekunden - 5 Min) eine Website (Gaming-Server) abfragt und die Daten auswerten soll.
Der Webserver liefert dann so was wie:
b'{"gametime":{"days":908,"hours":8,"minutes":14},"players":4,"hostiles":10,"animals":12}'
Und das Programm bringt es dann kurzerhand in eine ansehnliche Form. Natürlich kann man auch die Website aufrufen, aber darum gehts ja nicht, ich möchte ja was lernen.

Hier der Quellcode:

Code: Alles auswählen

# Server Info - 7 Days to die - Hauptcode
#import ctypes #Für beehebung des Unschärfeeffektes
#ctypes.windll.shcore.SetProcessDpiAwareness(1)

#import qtmodern.styles   # Nur für Dark - Mode
#import qtmodern.windows  # Nur für Dark - Mode

from time import sleep  # Für Sleep Befehl
#import requests # für Webabfragen
from threading import Thread # Für Hintergrund Schleife der Websiteabfragen
import urllib.request # Benötigt zur Abfrage von Websites
#import requests # Nur bei alternativer Prüfung Onlinestatus ! Ist aktuell auskommentiert
import re # Regex

#from PySide6.QtWidgets import (QApplication, QMainWindow, QMenuBar, QAction, QStatusBar)
from PySide6.QtWidgets import (QApplication, QMainWindow, QToolBar, QMenu, QMenuBar, QStatusBar, QSystemTrayIcon)
from PySide6.QtGui import (QAction, QIcon)
from PySide6 import QtCore # Für QTimer (Loop)

#from PyQt6.QtWidgets import QAction
# GUI Import
from gui.ui_Hauptfenster import Ui_Hauptfenster                    # Hauptfenster ist eine Python Datei - Die "Klasse" darin wird geladen (sprich das User Interface, die Benutzeroberfläche)
from gui.ui_Einstellungen import Ui_Einstellungen                  # Das UserInterface kann mit dem PySide6-Designer sauber modeliert werden
from gui.ui_Einstellungen_Server import Ui_Einstellungen_Server    # GUI für Servereinstellungen


# Globale VARIABLEN
SERVER = {  'Name'    : 'Nachtwache 1',
            'IP'      : '176.57.174.233',
            'Port'    : '8102',
            'Status'  : 'Unbekannt'
}
SERVER['Adresse'] = SERVER['IP'] + ":" + SERVER['Port']

#serveradresse="0.0.0.0:0000"
temp = 1

app = QApplication()

##### H A U P T F E N S T E R ##########################################
class Hauptfenster(QMainWindow, Ui_Hauptfenster, QSystemTrayIcon, QIcon):
    def __init__(self):
        super().__init__()

        global testvariable, SERVER

        self.setupUi(self)

        self.lb_servername.setText("Nachtwache 1")
        #self.lb_serveradresse.setText("176.57.169.88:8082")
        self.lb_serveradresse.setText(SERVER['Adresse'])



        self.v_tag.setText("100")
        self.v_uhrzeit
        self.v_rebootzeit
        self.v_spieler
        self.v_freie_slots
        self.v_eventzeit.setText("00:30 Uhr")
        self.v_eventzeit.setStyleSheet("color:red")


        #menuTest = self.menubar.addMenu("Test")
    
    
        self.actionEinstellungen.triggered.connect(self.einstellungen)
        self.actionInformationen.triggered.connect(self.informationen)
        self.actionBeenden.triggered.connect(self.close)

        # Test
        #self.menuEinstellungen.addAction(self.einstellungen_oeffnen)

        
        self.v_freie_slots = temp + 1
        #self.schleife()

    def schleife(self):
        self.timer = QtCore.QTimer()
        self.timer.setSingleShot(False)
        #self.timer.timeout.connect(self.do_stuff_timer)
        self.timer.start(5000)
        print("5 Sekunden")

    def statusbar(self):
        statusBar.showMessage(tr("Server ONLINE"))   

    def einstellungen_server(self):
        einstellungen_server.show()
        print("Servereinstellungen wird eingeblendet...")
       
    def einstellungen(self):
        einstellungen.show()
        print("Einstellungensfenster wird eingeblendet")

    def informationen(self):
        print("Informationsfenster wird angezeigt")



##### H A U P T F E N S T E R - ENDE #####################################


##### E I N S T E L L U N G E N - S E R V E R #############################
class Einstellungen_Server(QMainWindow, Ui_Einstellungen_Server):
    def __init__(self):
        # Einstellungsfenster wird definiert
        super().__init__()
        self.setupUi(self)
        
        #self.bt_uebernehmen.clicked.connect(self.uebernehmen)
        self.bt_uebernehmen.clicked.connect(self.uebernehmen)

    def uebernehmen(self):
        # Button Übernehmen wird gedrückt !
        print("Servereinstellung werden übernommen...")
        # Eingebefelder werden übernommen
        hauptfenster.lb_servername.setText(self.in_servername.text())
        hauptfenster.lb_serveradresse.setText(self.in_serveripport.text())
        
        #hauptfenster.statusbar.setToolTip(einstellungen_server.in_servername.text())
        # ENDE
        self.close()
###########################################################################

##### E I N S T E L L U N G E N ##########################################
class Einstellungen(QMainWindow, Ui_Einstellungen):
    def __init__(self):
        # Einstellungsfenster wird definiert
        super().__init__()
        self.setupUi(self)
        #self.bt_uebernehmen.clicked.connect(self.uebernehmen)
        self.bt_uebernehmen.clicked.connect(self.uebernehmen)

    def uebernehmen(self):
        # Button Übernehmen wird gedrückt !
        print("Einstellung werden übernommen")
        # Eingebefelder werden übernommen
        #hauptfenster.lb_serveradresse.setText(self.in_serveripport.text())
        # ENDE
        self.close()
###########################################################################




###### TEST SCHLEIFE ###########
def Get_Server(Serveradresse, Return):
    print(Serveradresse)

    url = "http://" + Serveradresse + "/api/getstats"

    if Return == "Status":
        print("Serverstatus wird abgerufen... ")

        try:
            urllib.request.urlopen(urllib.request.Request(url))

        except urllib.error.HTTPError as error:
            # Return code error (e.g. 404, 501, ...)
            status = "Offline" 
            print("FEHLER - HTTPError: " + error.code + "\n")
            return status
            
        except urllib.error.URLError as error:
            # Not an HTTP-specific error (e.g. connection refused)
            status = "Offline"
            print("\nFEHLER (URLError)\tGrund: " + str(error.reason) + "\nFEHLER\t"+url+" Offline.\n")
            return status
        else:
            # 200
            status = "Online"
            return status


        """
        try : 
            code = requests.get(url).status_code
        except requests.exceptions.RequestException:
            code = 0
            print(url + " ist OFFLINE")

        print("Status-Code: " + str(code))
        if code == 200:
            print("Server ist ONLINE !")
            return "ONLINE"
        elif code == 0:
            print("Server ist OFFLINE !")
            return "OFFLINE"
        else:            
            print("Serverstatus: Unbekannt")
            return "UNBEKANNT"
        """

    elif Return in ["Tag","Zeit","Spieler","Feinde","Tiere"]:
        inhalt = urllib.request.urlopen(url,timeout=1)
        daten = str(inhalt.read())
        print("Daten: " + daten)
        if Return == "Tag":
            print("Tag")
            #match = re.match("(?<=time:{).+?(?=})",daten)
            regex = r"gametime.\:{(.*)},"
            # gametime.:{\"days\":(.*),\"hours.*},
            suche = re.search(r"days\":(.*),\"hours\".*},",daten)
            tage = suche.group(1)
            print("Tage:" + tage)
            return tage
        elif Return == "Zeit":
            suche = re.search(r"hours\":(.*),\"minutes\":(.*)},",daten)
            zeit = suche.group(1) + ":" + suche.group(2)
            print("Zeit: " + zeit)
            return zeit

        elif Return == "Spieler":
            suche = re.search(r"players\":(.*),\"hostiles.*",daten)
            spieler = suche.group(1)
            print("Spieler:" + spieler)    
            return spieler

        elif Return == "Feinde":
            suche = re.search(r"hostiles\":(.*),\"animals.*",daten)
            feinde = suche.group(1)
            print("Feinde:" + feinde)    
            return feinde

        elif Return == "Tiere":
            suche = re.search(r"animals\":(.*)}'",daten)
            tiere = suche.group(1)
            print("Tiere:" + tiere)    
            return tiere


    elif Return == "Namen":
        print(Return)
    else:
        print("Abfrage falsch ! Return: " + Return)
###

    # http://176.57.174.233:8102/api/getstats

        # Prüfe ob Hauptfenster geschlossen wurde
##############################################


#       hauptfenster.quit()
#       Hauptfenster.destroy()
#        print("Warte 10 Sekunden bis erneut abgefragt wird...")
#        sleep(10)
#    hauptfenster.after(1000,update)    
#        # .. collect ur things



hauptfenster = Hauptfenster()
# Dark Mode - Benötigt pip install qtmodern
#qtmodern.styles.dark(app)
#hauptfenster = qtmodern.windows.ModernWindow(Hauptfenster())


# Trayicon #################################################################
class Trayicon(QSystemTrayIcon):

    icon = QIcon('Grafiken\Serverinfo-7Dtd.ico')
    tray = QSystemTrayIcon(icon, hauptfenster)
    #tray.setVisible(True)
    tray.setToolTip("Ich bin ein Tooltipp")
    # Traymenü
    traymenu = QMenu()

    # Tray - Zeige Hauptfenster

    # Tray - Einstellungen Server
    traym_Einstellungen_Server = traymenu.addAction("Einstellungen Server")
    traym_Einstellungen_Server.triggered.connect(hauptfenster.einstellungen_server)

    # Tray - Einstellungen     
    traym_Einstellungen = traymenu.addAction("Einstellungen")
    traym_Einstellungen.triggered.connect(hauptfenster.einstellungen)
    
    traymenu.addAction("") # Leerzeile

    # Tray - Exit
    traym_exit = traymenu.addAction("Exit")
    traym_exit.triggered.connect(app.quit)
    
    # Tray - ContextMenu    
    tray.setContextMenu(traymenu)  # Fügt Menu dem Trayicon hinzu
    tray.showMessage("7Days to die", "Popupnachricht") # Wenn in Hauptcode ausgeführt Tray.show() ploppt es auf 
        
    # TrayIcon anzeigen
    tray.show()
    # Tayicon - ENDE
##############################################################################



Tray = Trayicon()
Tray.show()

#Tray.setToolTip("Tooltipp")
#Tray.showMessage("Supermesage", "Massage") # 


einstellungen        = Einstellungen()
einstellungen_server = Einstellungen_Server()

#hole_Daten("176.57.169.88:8082")

SERVER['Status'] = Get_Server(SERVER["Adresse"], "Status")
print("SERVER['Status']: " + SERVER['Status'])
if SERVER['Status'] == "Offline":
    print("Server ist Offline")
    hauptfenster.lb_servername.setStyleSheet("color:red")
else:
    hauptfenster.lb_servername.setStyleSheet("color:green")
    SERVER['Tag']     = Get_Server(SERVER["Adresse"], "Tag")
    SERVER['Zeit']    = Get_Server(SERVER["Adresse"], "Zeit")
    SERVER['Spieler'] = Get_Server(SERVER["Adresse"], "Spieler")
    hauptfenster.v_tag.setText(SERVER['Tag'])
    hauptfenster.v_uhrzeit.setText(SERVER['Zeit'])
    hauptfenster.v_spieler.setText(SERVER['Spieler'])


hauptfenster.show() # Standardmäßig werden Fenster versteckt
app.exec()         # Startet die Ereigniskette/schleife

#hauptfenster.schleife.timer.stop()

#VARIABLEN


#DEFINITIONEN
STD = { 'Schriftart'       : 'Calibri',
        'Schriftfarbe'     : '#FFFFFF',
        'Hintergrundfarbe' : '#404452'
}

# Ende

Aktuell funktioniert das Programm schon sehr gut, ich habe allerdings zwei Probleme.
1. Der Code sollte sauberer aussehen
Vom Aufbau her hätte ich Gerne Hauptfenster, Funktionen, App starten
Aktuell sieht das so aus:
  1. Import
  • app = QApplication()
  • Klasse Hauptfenster
  • Klasse Einstellungen_Server (Fenster Servereinstellungen)
  • Klasse Einstellungen (Fenster Einstellungen)
  • Funktion Get_Server (Ruft Informationen vom Server ab)
  • hauptfenster = Hauptfenster()
  • Klasse Trayicon
  • Trayicon.show()
  • Serverabfrage (hier fehlt mir noch ne saubere SChleife)
  • hauptfenster.show()
  • app.exec()


Das sollte aber eher so aussehen:
  1. Import
  • Klasse Hauptfenster
  • Klasse Einstellungen_Server (Fenster Servereinstellungen)
  • Klasse Einstellungen (Fenster Einstellungen)
  • Klasse Trayicon
  • Funktion Get_Server (Ruft Informationen vom Server ab)
  • Serverabfrage (hier fehlt mir noch ne saubere SChleife)
  • hauptfenster = Hauptfenster()
  • hauptfenster.show()
  • Trayicon.show()
  • Starte Schleife
  • app.exec()


Also es sollten erst sauber alle Klassen und Funktionen definiert werden und dann alles geladen werden.
Aber irgendwie krieg ich das nicht hin.

2. Ich bekomme den Code für das Trayicon nicht separiert (als Klasse der Funktion).
- Ich würde das Trayicon gerne in das Hauptfenster bringen (und später dann in eine eigene Datei) allerdings meckert Python dann das er das Trayicon erst nach dem Hauptfenster laden kann.

Ich denke mal wenn man Problem 1 in den Griff bekommt ist Problem 2 auch mit behoben... ...aber wo fange ich an - möchte ungern weiter Programmieren so lange das so "unsauber" ist.
Ich hoffe Ihr versteht was ich meine...
:roll:
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Man erzeugt nicht aus ui-Dateien Pythoncode, sondern lädt die ui-Datei direkt in der jeweiligen GUI-Klasse mit loadui.
Globale Variablen benutzt man nicht, vor allem nicht, wenn sie so sprechende Namen wie `temp` haben. Der app-Aufruf gehört auch gut 300 Zeilen tiefer in eine Funktion, die man typischerweise `main` nennen.
Vergiss auch gleich, dass es `global` überhaupt gibt, testvariable wird nicht benutzt und SERVER ist eine Konstante.
In `Einstellungen_Server` benutzt Du Label, um Informationen an das Hauptfenster zu übermitteln? Macht man nicht. Solche Fenster sind auch üblicherweise Dialoge, die vom Hauptfenster aus gesteuert werden, so dass man dort einen sauberen Weg zum Übergeben von Werten hat. Solch ein Fenster schon auf Vorrat zu erzeugen, um es dann irgendwann anzuzeigen ist unüblich, das Dialogfenster wird erst dann erzeugt, wenn es auch gebraucht wird.
`Get_Server` muß man komplett klein schreiben, weil es eine Funktion ist. Variablennamen schreibt man nach Konvention auch komplett klein.
Die Funktion hat aber auch einen falschen Namen, denn es wird gar kein Server geholt, sondern Informationen von einem Server.
Strings setzt man nicht per + zusammen, sondern benutzt Formatstrings.
Die Funktion besteht aus drei großen if-Blöcken, die jeweils etwas anderes machen, je nachdem was als `Return`-Wert übergeben worden ist. Das sollten aber drei getrennte Funktionen sein, dann braucht man das `Return` auch nicht (Bzw. ist es unsinnig die Funktionalität von draußen getrennt aufzurufen).
Die Variable `status` ist überflüssig, weil man den lateralen String auch direkt in return angeben könnte.
"Tag","Zeit","Spieler","Feinde","Tiere" kommen alle über einen Web-Aufruf, darfür dann vielemale die selbe Information abzufragen ist Verschwendung!
Was Du zurückbekommst ist JSON, das parst man mit dem json-Modul und nicht per regulärem Ausdruck.
Die ganze Funktion wird also zu:

Code: Alles auswählen

def get_information_from_server(serveradresse):
    url = f"http://{serveradresse}/api/getstats"
    inhalt = urllib.request.urlopen(url, timeout=1)
    daten = json.load(inhalt)
    return {
        "Tag": daten["gametime"]["days"],
        "Zeit": f'{daten["gametime"]["hours"]}:{daten["gametime"]["minutes"]}',
        "Spieler": daten["players"],
        "Feinde": daten["hostiles"],
        "Tiere": daten["animals"],
    }
Trayicon ist keine Klasse, sondern Du hast einfach nur Code, der sofort ausgeführt, in einen class-Block gepackt, was so keinen Sinn macht. Der Code gehört ebenso in die main-Funktion, wo ja auch das app-Objekt erzeugt wird, das Du für die Aktionen im TrayIcon brauchst.


Der Code zum Abfragen des Servers gehört ins Hauptfenster, weil dort ja auch die Information angezeigt wird.
Die main würde dann exemplarisch so aussehen:

Code: Alles auswählen

def main():
    app = QApplication()
    hauptfenster = Hauptfenster()
    create_tray(app, hauptfenster)
    hauptfenster.show() # Standardmäßig werden Fenster versteckt
    app.exec()         # Startet die Ereigniskette/schleife

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jeypee: Das was der Server da sendet ist JSON, da geht man um Himmels willen nicht mit regulären Ausdrücken dran, sondern mit einem JSON-Parser. In der Standardbibliothek gibt es dafür das `json`-Modul:

Code: Alles auswählen

In [404]: import json

In [405]: json.loads(b'{"gametime":{"days":908,"hours":8,"minutes":14},"players":4,"hostiles":10,"animals":12}')
Out[405]: 
{'gametime': {'days': 908, 'hours': 8, 'minutes': 14},
 'players': 4,
 'hostiles': 10,
 'animals': 12}

In [406]: data = json.loads(b'{"gametime":{"days":908,"hours":8,"minutes":14},"p
     ...: layers":4,"hostiles":10,"animals":12}')

In [407]: type(data)
Out[407]: dict

In [408]: data["gametime"]
Out[408]: {'days': 908, 'hours': 8, 'minutes': 14}

In [409]: type(data["gametime"])
Out[409]: dict

In [410]: data["gametime"]["days"]
Out[410]: 908

In [411]: type(data["gametime"]["days"])
Out[411]: int
Bei den Importen sollte man mal aufräumen, da wird nicht alles gebraucht was importiert wird. Und diese ganzen Kommentare sollten da weg. Und wenn Kommentare dann nicht irgendwo rechts vom Code wenn das längerer Text ist, sondern über dem Code der damit kommentiert wird.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Die dekorativen Trennlinienkommentare sollten auch weg.

Literale Zeichenketten sind kein Ersatz für Kommentare. Damit sollte man keinen Code auskommentieren. An bestimmten Stellen haben Zeichenketten die Bedeutung von DocStrings, und für einige Werkzeuge an mehr Stellen als Python selbst.

Es sieht so aus als wurde aus dem Qt-Designer Quelltext aus *.ui-Dateien generiert. Das macht man eigentlich nicht mehr, sondern lädt die UI-Dateien zur Laufzeit im Programm. Damit erspart man sich einen Zwischenschritt, und generierten Quelltext den man nicht anfassen darf.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Die Qt-Namenskonventionen sehen da ein bisschen anders aus, es ist also okay wenn man die zumindest für den GUI-Kram übernimmt. Man sollte dann aber nicht anfangen auch noch Schreibweisen einzuführen die weder Python noch Qt Konventionen folgen, wie PascalCase mit zusätzlichen Unterstrichen zwischen den Worten wie `Einstellungen_Server` oder `Get_Server()` für eine Funktion.

Namen sollten keine kryptischen Abkürzungen enthalten. Es gibt beispielsweise eine Reihe Attribute die mit `v_` anfangen, wo ich nicht erraten konnte wofür das wohl stehen mag. Aber auch `bt_`, `lb_`, und Co sollte man a) ausschreiben, und b) auch besser in der richtigen Reihenfolge, denn `button_uebernehmen` wäre eine Funktion die einen Button übernimmt, während `uebernehmen_button` ein Button ist.
Oder `Einstellungen_Server` — das sollte eher `Servereinstellungen` heissen. Man sollte Quelltext vorlesen können, ohne das die Zuhörer denken man wäre Yoda. 😉

Funktionen und Methoden werden üblicherweise nach Tätigkeiten benannt, damit der Leser weiss was sie tun, und um sie leichter von eher passiven Werten unterscheiden zu können.

``# Globale VARIABLEN`` ist ein Problem, denn globale Variablen sollte man gar nicht haben. Also weder Variablen auf Modulebene, noch auf Klassenebene anlegen, noch ``global`` verwenden. Vergiss am besten das es ``global`` gibt. Alles was Funktionen oder Methode ausser Konstanten brauchen, wird als Argument(e) übergeben.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Vererbung ist eine „ist ein(e)“-Beziehung. Dass Dein `Hauptfenster` ein `QMainWindow` ist und da die `Ui_Hauptfenster`-Methoden als Mixin dazu kommen, okay, aber das Hauptfenster ist weder ein Tray-Icon, noch ein Icon. Das Hauptfenster *hat* vielleicht ein Icon, und kümmert sich auch um die Darstellung mittels eines Tray-Icons — das ist aber keine Vererbung, sondern Komposition.

Die Klasse `TrayIcon` ist keine Klasse, das ist eine Funktion wo der Funktionskörper auf Klassenebene geschrieben wurde und damit alle lokalen Variablen als Attribute erhalten bleiben. Das ist total schräg.

Sind die Einstellungsfenster wirklich Hauptfenster? Und nicht vielleicht eher Dialoge?

`Get_Server()` ist eine Funktion die eigentlich ganz viele Funktionen — oder vielleicht Methoden — sein sollte. Eine pro möglichem `Return`-Wert, denn die macht da ja andere Sachen je nach `Return`-Wert.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

So, ich denke damit hast Du erst einmal was zu tun. 🙂
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Ja "heiligs Blechle" - da hab ich einiges vor mir... ...aber das ist ja das schöne am Programmieren, der Code wird eigentlich nur besser wenn er erst mal steht...
Und ich programmiere auch nicht, ich bezeichne mich eher als "Scriptkiddy". Bei allem was größer als 3 Seiten ist verlier ich den Überblick.
Und alles was da oben steht wurde mehr oder weniger nur mit copy&Paste aus der "Duckduckgo-Suche" herausgesaugt. Also ein Flickenteppich aus vielen, eigentlich guten, Antworten...
Genug entschuldigt. Danke erstmal und los gehts.
Ich hab sowieso gerade den Code vom " Get_Server()" heute Vormittag aufgespittet und eine eigene Datei gepackt.
Es gibt jetzt "server_status(ip,port)" und "server_info(adresse)" als eigene "Funktionen".
Was mich gerade noch etwas irritiert hatte ist dieser "JSON", der hat sich mir nicht vorgestellt insofern hatte ich ihn mit regex zerlegt.
Ich verstehe natürlich warum es Sinn macht es mit JSON zu zerlegen.

get_server.py (unterordner "module")

Code: Alles auswählen

import urllib.request   # Benötigt zur Abfrage von Websites
import json             # json Modul

def server_status(ip,port):
    print("server_status")

    print(f"Serveradresse (IP+Port): {ip}:{port}")
    status = "unbekannt"
    # url    = "http://" + ip + ":" + port + "/api/getstats"
    url    = f"http://{ip}:{port}/api/getstats"

    try:
        urllib.request.urlopen(urllib.request.Request(url))

    except urllib.error.HTTPError as error:
        # Return code error (e.g. 404, 501, ...)
        status = "Offline" 
        # print("FEHLER - HTTPError: " + error.code + "\n")
        
    except urllib.error.URLError as error:
        # Not an HTTP-specific error (e.g. connection refused)
        status = "Offline"
        # print("\nFEHLER (URLError)\tGrund: " + str(error.reason) + "\nFEHLER\t"+url+" Offline.\n")
    else:
        # 200
        status = "Online"

    return status

def server_info(serveradresse):
    url = f"http://{serveradresse}/api/getstats"
    daten = json.load(urllib.request.urlopen(url, timeout=1))
    return {
        "Tag": daten["gametime"]["days"],
        "Zeit": "{:02d}:{:02d}".format(daten["gametime"]["hours"],daten["gametime"]["minutes"]),
        "Spieler": daten["players"],
        "Feinde": daten["hostiles"],
        "Tiere": daten["animals"],
    }
    


# TEST
"""
SERVER = {  'Name'    : 'Nachtwache 1',
            'IP'      : '176.57.174.233',
            'Port'    : '8102',
            'Status'  : 'Unbekannt'
}
SERVER['Adresse'] = SERVER['IP'] + ":" + SERVER['Port']

INFO = server_info(SERVER['Adresse'])

print(INFO)
print(f"Info - Tag:     {INFO['Tag']}"     )
print(f"Info - Zeit:    {INFO['Zeit']}"    )
print(f"Info - Spieler: {INFO['Spieler']}" )
print(f"Info - Feinde:  {INFO['Feinde']}"  )
"""

# ENDE
Sofern ich das demnächst sauber in das Hauptprogramm hole kann ich mit:

SERVER['status'] = server_status(SERVER['Adresse']
INFO = server_info(SERVER['Adresse'])

kann ich die Daten vom Server schon mal sauber ziehen :)

Kommentare sind nur für mich, da es vorkommen kann das ich mal n halbes Jahr nicht dran schreibe und dann tu ich mich sonst schwer wieder in Python rein zu finden.
Für jemanden der ständig nur Python schreibt mag das selbsterklärend sein, für mich ist es eine Hilfe als Anfänger wenn ich so viele Kommentare wie möglich mache.
Die Globalen Variablen sind auch nur Anfängerhilfen - ich hab so schon lange gebastelt bis mal ein Wert von einem Gui im anderen Gui-Fenster war... ..bin froh das es aktuell irgendwie geht... ...das behandeln wir mal später...

Ich schieb dann mal weiter den Code umher... ...soll nur ein Zwischenstand sein...
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich muss doch noch mal die Kommentare ansprechen: Selbst wenn man nicht in die Dokumentation schauen möchte, warum auch immer, ein ``# json Modul`` hinter ``import json`` macht einfach keinen Sinn.

Auch auskommentierter Code sollte nicht im Quelltext stehen. Entweder man braucht den, dann ist er nicht auskommentiert, oder man braucht den nicht, dann sollte er nicht den Code unübersichtlicher machen. Denn die `server_status()`-Funktion ist unnötig komplex weil da Sachen drin stehen, die man brauchen *würde* wenn der auskommentierte Code nicht auskommentiert wäre. Die beiden ``except``-Zweige unterscheiden sich nämlich gar nicht (mehr) und `url` wird ausserhalb vom ``try``-Block nicht (mehr) verwendet.

``status = "unbekannt"`` wird nirgends verwendet, weil in jedem Zweig `status` ein Wert zugewiesen wird. Eigentlich bräuchte man `status` als Variable gar nicht.

Netzverbindungen die man öffnet, sollte man auch wieder schliessen, und sich nicht darauf verlassen, dass das irgendwann schon automatisch passieren wird. `urlopen()` liefert einen Kontextmanager, kann also mit der ``with``-Anweisung verwendet werden.

Ich sehe bei der simplen URL keinen Grund ein `Request`-Objekt selber zu erstellen.

Bei der Namensgebung sollte man konsistenter sein, und den gleichen Wert nicht mal `ip` und mal `serveradresse` nennen.

"Tag" und "Zeit" ist falsch. Die drei Werte sind *eine* Zeitdauer und gehören zusammen. Wenn man damit irgendwann mal etwas machen will, zum Beispiel rechnen, dann würde sich da ein `datetime.timedelta`-Objekt anbieten. Oder man rechnet das in *einen* Zahlwert um.

Wörterbücher mit immer dem gleichen, festen Satz an Schlüsseln sind eigentlich Objekte und sollten als Datentyp represäntiert werden. Am einfachsten, falls es okay ist, das man die Attribute nicht mehr ändert, kann man `collections.namedtuple()` dafür verwenden. Ansonsten eine eigene Klasse. Wobei ich da dann sehr gerne auf das externe `attr`-Modul zurückgreife um mir das Leben einfacher zu machen. Und wenn ich das sowieso als Abhängigkeit habe, dann auch für unveränderliche Sachen statt `namedtuple()`.

Könnte dann so aussehen (ungetestet):

Code: Alles auswählen

import json
import urllib.error
from collections import namedtuple
from datetime import timedelta as TimeDelta
from urllib.request import urlopen

GameStatistics = namedtuple(
    "GameStatistics", "duration player_count hostile_count animal_count"
)


def get_status(ip, port):
    try:
        with urlopen(f"http://{ip}:{port}/api/getstats"):
            pass

    except (urllib.error.HTTPError, urllib.error.URLError):
        return "Offline"
    else:
        return "Online"


def get_info(ip, port):
    with urlopen(f"http://{ip}:{port}/api/getstats", timeout=1) as response:
        data = json.load(response)

    duration_data = data["gametime"]

    return GameStatistics(
        TimeDelta(
            days=duration_data["days"],
            hours=duration_data["hours"],
            minutes=duration_data["minutes"],
        ),
        data["players"],
        data["hostiles"],
        data["animals"],
    )
Die `print()`-Anweisungen habe ich rausgeworfen. Das ist eher ein Fall für Logging. Entweder mit dem `logging`-Modul aus der Standardbibliothek, oder etwas externem wie `loguru`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Also ich hab jetzt schon etwas umgestrickt und verstehe auch einige Sachen die Ihr gesagt habt.
Allerdings bin ich kein Programmierer und strauchel bei einigen Sachen.

Wir machen am Besten mal mit dem TrayIcon weiter.
Ich hab also die Main() eingefügt wie gepostet.
Trayicon ist keine Klasse, sondern Du hast einfach nur Code, der sofort ausgeführt, in einen class-Block gepackt, was so keinen Sinn macht.
Der Code gehört ebenso in die main-Funktion, wo ja auch das app-Objekt erzeugt wird, das Du für die Aktionen im TrayIcon brauchst.
Als Leihe hab ich verstanden "Pack die Traysachen in die Main() !" ERLEDIGT
Die Klasse `TrayIcon` ist keine Klasse, das ist eine Funktion wo der Funktionskörper auf Klassenebene geschrieben wurde und damit alle lokalen Variablen als Attribute erhalten bleiben. Das ist total schräg.
Wir kümmern uns ja jetzt drum.
Jetzt wurde gepostet in der Main()

Code: Alles auswählen

create_tray(app, hauptfenster)
Das sieht mir so aus als würde eine Funktion aufgerufen die "app" und "hauptfenster" übergibt.
Akuell gibt es die nicht... ich poste mal was ich hab, aber bitte nicht gleich wieder prügeln...
So geht es auf, zeigt aber kein Icon mehr.

Code: Alles auswählen

def main():
    app = QApplication()
    hauptfenster = Hauptfenster()


    # Trayicon #################################################################
    icon = QIcon('Grafiken\Serverinfo-7Dtd.ico')
    tray = QSystemTrayIcon(icon, hauptfenster)
    #tray.setVisible(True)
    tray.setToolTip("Ich bin ein Tooltipp")
    # Traymenü
    traymenu = QMenu()

    # Tray - Zeige Hauptfenster

    # Tray - Einstellungen Server
    traym_Einstellungen_Server = traymenu.addAction("Einstellungen Server")
    traym_Einstellungen_Server.triggered.connect(hauptfenster.einstellungen_server)

    # Tray - Einstellungen     
    traym_Einstellungen = traymenu.addAction("Einstellungen")
    traym_Einstellungen.triggered.connect(hauptfenster.einstellungen)
    
    traymenu.addAction("") # Leerzeile

    # Tray - Exit
    traym_exit = traymenu.addAction("Exit")
    traym_exit.triggered.connect(app.quit)
    
    # Tray - ContextMenu    
    tray.setContextMenu(traymenu)  # Fügt Menu dem Trayicon hinzu
    tray.showMessage("7Days to die", "Popupnachricht") # Wenn in Hauptcode ausgeführt Tray.show() ploppt es auf 
        
    # TrayIcon anzeigen
    tray.show()
    # Trayicon - ENDE

    #create_tray(app, hauptfenster)
    hauptfenster.show() # Standardmäßig werden Fenster versteckt
    app.exec()         # Startet die Ereigniskette/schleife

if __name__ == "__main__":
    main()
Ich hab also das Ganze gleich wieder verworfen und es in eine Funktion oberhalb der main() gepackt:

Code: Alles auswählen

def erzeuge_trayicon(app, fenster):
    icon = QIcon('Grafiken\Serverinfo-7Dtd.ico')
    tray = QSystemTrayIcon(icon, fenster)
    tray.setVisible(True)
    tray.setToolTip("Ich bin ein Tooltipp")
    
    # Traymenü
    traymenu = QMenu()

    # Tray - Zeige Hauptfenster

    # Tray - Einstellungen Server
    traym_Einstellungen_Server = traymenu.addAction("Einstellungen Server")
    traym_Einstellungen_Server.triggered.connect(fenster.einstellungen_server)

    # Tray - Einstellungen     
    traym_Einstellungen = traymenu.addAction("Einstellungen")
    traym_Einstellungen.triggered.connect(fenster.einstellungen)
    
    traymenu.addAction("") # Leerzeile

    # Tray - Exit
    traym_exit = traymenu.addAction("Exit")
    traym_exit.triggered.connect(app.quit)
    
    # Tray - ContextMenu    
    tray.setContextMenu(traymenu)  # Fügt Menu dem Trayicon hinzu
    tray.showMessage("7Days to die", "Popupnachricht") # Wenn in Hauptcode ausgeführt Tray.show() ploppt es auf 
    
    tray.show()




def main():
    app = QApplication()
    hauptfenster = Hauptfenster()

    erzeuge_trayicon(app, hauptfenster)
    hauptfenster.show() # Standardmäßig werden Fenster versteckt
    app.exec()         # Startet die Ereigniskette/schleife

if __name__ == "__main__":
    main()
Leider zeigt er weiterhin kein Trayicon an. Aber auch keinen Fehler im Ablauf.

Außerdem bräuchte ich noch genauere Infos bezüglich dem Aufrufen der ui Dateien mit "loadui".
Ein Beispiel wäre schön. Danke.
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Also an einem anderne Rechner erscheint komischerweise das Trayicon. Also erst mal soweit i.O.
Ich hab mich mal an dem LoadUI probiert, und das hat für das Hauptfenster auch funtkioniert.

Ich bräuchte jetzt aber Unterstützung für das "Servereistellungen" - Fenster.
Das soll jetzt bei Bedarf eingeblendet werden. Es ist also kein Hauptfenster sondern offenbar ein Dialog. Ich habe mich mit "Dialogen" allerdings noch gar nicht beschäftigt.
Fakt ist erstmal das nach der Kürzung im Code bis auf das Hauptfenster und das Trayicon nix mehr geht.

Code: Alles auswählen

# Server Info - 7 Days to die - Hauptcode
#import ctypes #Für beehebung des Unschärfeeffektes
#ctypes.windll.shcore.SetProcessDpiAwareness(1)

#import qtmodern.styles   # Nur für Dark - Mode
#import qtmodern.windows  # Nur für Dark - Mode

from time import sleep  # Für Sleep Befehl
#import requests # für Webabfragen
from threading import Thread # Für Hintergrund Schleife der Websiteabfragen
import urllib.request # Benötigt zur Abfrage von Websites
#import requests # Nur bei alternativer Prüfung Onlinestatus ! Ist aktuell auskommentiert
import re # Regex

#from PySide6.QtWidgets import (QApplication, QMainWindow, QMenuBar, QAction, QStatusBar)
from PySide6.QtWidgets import (QApplication, QMainWindow, QToolBar, QMenu, QMenuBar, QStatusBar, QSystemTrayIcon)
from PySide6.QtGui import (QAction, QIcon)
from PySide6.QtUiTools import QUiLoader                             # Zum dierekten laden der im Designer erstellen ui-Dateien (Erspart das convertieren in py-dateien)
from PySide6 import QtCore # Für QTimer (Loop)

#from PyQt6.QtWidgets import QAction
# GUI Import
from gui.ui_Hauptfenster import Ui_Hauptfenster                    # Hauptfenster ist eine Python Datei - Die "Klasse" darin wird geladen (sprich das User Interface, die Benutzeroberfläche)
from gui.ui_Einstellungen import Ui_Einstellungen                  # Das UserInterface kann mit dem PySide6-Designer sauber modeliert werden
from gui.ui_Einstellungen_Server import Ui_Einstellungen_Server    # GUI für Servereinstellungen

from module.get_server import server_status,server_info            # Eigenes Python Modul (Definitionen aus dem Unterordner "Module")



# Globale VARIABLEN
SERVER = {  'Name'    : 'Nachtwache 1',
            'IP'      : '176.57.174.233',
            'Port'    : '8102',
            'Status'  : 'unbekannt'
}
SERVER['Adresse'] = SERVER['IP'] + ":" + SERVER['Port']



##### H A U P T F E N S T E R ##########################################


def hauptfenster_setup(w):

    global SERVER

    w.lb_servername.setText("Nachtwache 1")
    #self.lb_serveradresse.setText("176.57.169.88:8082")
    w.lb_serveradresse.setText(SERVER['Adresse'])

    w.v_tag.setText("100")
    w.v_uhrzeit
    w.v_rebootzeit
    w.v_spieler
    w.v_freie_slots
    w.v_eventzeit.setText("00:30 Uhr")
    w.v_eventzeit.setStyleSheet("color:red")


    w.actionEinstellungen.triggered.connect(einstellungen)
    w.actionInformationen.triggered.connect(informationen)
    w.actionBeenden.triggered.connect(w.close)

    # Test
    #self.menuEinstellungen.addAction(self.einstellungen_oeffnen)
    
    w.v_freie_slots = 0
    #self.schleife()

def schleife(self):
    timer = QtCore.QTimer()
    timer.setSingleShot(False)
    #self.timer.timeout.connect(self.do_stuff_timer)
    timer.start(5000)
    print("5 Sekunden")

def statusbar(w):
    statusBar.showMessage(tr("Server ONLINE"))   

def einstellungen_server(w):
    einstellungen_server.show()
    print("Servereinstellungen wird eingeblendet...")
    
def einstellungen(w):
    einstellungen.show()
    print("Einstellungensfenster wird eingeblendet")

def informationen(w):
    print("Informationsfenster wird angezeigt")



##### H A U P T F E N S T E R - ENDE #####################################


##### S E R V E R E I N S T E L L U N G E N ##############################
def servereinstellungen_setup(w):
    #self.bt_uebernehmen.clicked.connect(self.uebernehmen)
    def uebernehmen(self):
        # Button Übernehmen wird gedrückt !
        print("Servereinstellung werden übernommen...")
        # Eingebefelder werden übernommen
        hauptfenster.lb_servername.setText(w.in_servername.text())
        hauptfenster.lb_serveradresse.setText(w.in_serveripport.text())
        
        #hauptfenster.statusbar.setToolTip(einstellungen_server.in_servername.text())
        # ENDE
        self.close()
    w.bt_uebernehmen.clicked.connect(uebernehmen)
###########################################################################


"""
SERVER['Status'] = server_status(SERVER["IP"],SERVER["Port"])

print("SERVER['Status']: " + SERVER['Status'])
if SERVER['Status'] == "Offline":
    print("Server ist Offline")
    hauptfenster.lb_servername.setStyleSheet("color:red")
elif SERVER['Status'] == "Online":
    INFO = server_info(SERVER['Adresse'])
    print(f"INFO: {INFO}")
    hauptfenster.lb_servername.setStyleSheet("color:green")
    hauptfenster.v_tag.setText(str(INFO['Tag']))
    hauptfenster.v_uhrzeit.setText(INFO['Zeit'])
    hauptfenster.v_spieler.setText(str(INFO['Spieler']))
else:
    print("Serverstatus unbekannt !")

"""



#hauptfenster.schleife.timer.stop()

def erzeuge_trayicon(anwendung, gui):
    icon = QIcon('Grafiken\Serverinfo-7Dtd.ico')
    tray = QSystemTrayIcon(icon, gui)
    
    # Tray - Kontextmenü
    traymenu = QMenu()

    # Tray - Kontextmenü - Servereinstellungen
    traym_Servereinstellungen = traymenu.addAction("Servereinstellungen")
    #traym_Servereinstellungen.triggered.connect(servereinstellungen)

    # Tray - Kontextmenü -  Einstellungen     
    # traym_Einstellungen = traymenu.addAction("Einstellungen")
    # traym_Einstellungen.triggered.connect(gui.einstellungen)
    
    traymenu.addAction("")                              # Leerzeile

    # Tray - Exit
    traym_exit = traymenu.addAction("Exit")
    traym_exit.triggered.connect(anwendung.quit)
    
    # Tray - KontextMenu    
    tray.setContextMenu(traymenu)                      # Fügt das definierte Kontextmenü dem Trayicon hinzu
    
    tray.show()

    tray.showMessage("7Days to die", "Popupnachricht") # Muss nach tray.show() ausgeführt werden ! 
    tray.setToolTip("Ich bin ein Tooltipp")

    # Trayicon - ENDE



def main():

    loader = QUiLoader()                            # Erzeugt ein Objekt mit dem ich die ui-Dateien laden kann
    app = QApplication()
    
    
    hauptfenster = loader.load("gui\Hauptfenster.ui", None)
    hauptfenster_setup(hauptfenster)

    servereinstellungen = loader.load("gui\Servereinstellungen.ui",None)
    servereinstellungen_setup(servereinstellungen)

    erzeuge_trayicon(app, hauptfenster)
    hauptfenster.show()                             # Standardmäßig werden Fenster versteckt
    
    app.exec()                                      # Startet die Ereigniskette/schleife

if __name__ == "__main__":
    main()


# Ende
1. Per rechter Mausklick auf das Trayicon "Servereinstellungen" soll der entsprechende "Dialog" aufgehen
2. Ich benötige ja noch eine Schleife für die Serverabfrage alle 50 Sekunden - wo kommt die rein (main () oder als Funktion seperat ? )
3. Die Konstante "SERVER" - wo sollte ich die am Besten platzieren (bei "hauptfenster_setup" oder in der main () ?)
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jeypee: Ich weiss nicht ob das so viel Sinn macht da weiter zu basteln. Für GUI-Programme braucht man ein halbwegs solides Verständnis von objektorientierter Programmierung (OOP), also wie man Klassen schreibt und verwendet. Und dafür muss man den Umgang mit Funktionen beherrschen, und zwar da schon ohne ``global`` und globale Variablen.

GUIs funktionieren ereignisbasiert, dass heisst man schreibt nicht mehr Code der linear abgearbeitet wird, sondern baut die GUI auf und registriert Rückruffunktionen/-methoden für bestimmte Ereignisse (Benutzer klickt Schaltfläche an, eine gegebene Zeitspanne ist vergangen, …). Diese Rückrufe werden dann vom GUI-Rahmenwerk gemacht, wenn die Ereignisse eintreten. Und falls man sich über solche Aufrufe hinweg Zustand merken muss, passiert das über Klassen/Objekte, Attribute, und Methoden.

`w` ist ein superschlechter Name. Gewöhn Dir so etwas gar nicht erst an. Nicht nur das man raten muss was der wohl bedeutet, es stehen hier auch noch „window“ und „widget“ zur Auswahl beim raten. Und da `QWidget` auch als Fenster, also „window“ verwendbar ist, kann man das noch nicht mal so wirklich an der Verwendung der Methoden auf dem `w`-Objekt festmachen was gemeint ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Das Problem ist das Python Gui auf jeder Seite anders beschrieben wird, mal mit Klasse mal mit Funktion. Dann gibts noch verschiede Sachen mit denen man das machten kann (Tkinker, QT, Pyside). Wie soll da ein Anfänger durchblicken.
Nachdem nix kam hab ich mich selbst umgeschaut wie das mit dem loadui funkioniert und mich an das Tutorial gehalten:
https://www.pythonguis.com/tutorials/py ... -designer/
Und dort wird "w" verwendet !

Code: Alles auswählen

import sys
from PySide6 import QtWidgets
from PySide6.QtUiTools import QUiLoader

loader = QUiLoader()

def mainwindow_setup(w):
    w.setWindowTitle("MainWindow Title")

app = QtWidgets.QApplication(sys.argv)

window = loader.load("mainwindow.ui", None)
mainwindow_setup(window)
window.show()
app.exec_()
Grundsätzlich braucht es ja denk ich nicht viel, dem Programm fehlt ja nur etwas Struktur.
Wenn das Grundgerüst steht komm ich dann schon klar damit und sehe dann auch die Zusammenhänge...
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

"Grundsätzlich" ist die Strukur.
Deshalb auch die Bemerkung von __blackjack__, dass es sinnvoll ist, erst einmal die Grundlagen zu lernen.
Es ist leider so, dass GUI-Programmierung ein komplexes und oft unterschätztes Thema ist. Und in der Regel beginnt man nich mit komplexen Themen sondern lernt die Grundlagen und baut auf denen auf.
Und es ist völlig normal, dass man Code einfach weg schmeißt und neu macht, weil man bemerkt hat, dass das Konzept nicht passt.

Den Weg sind vor dir schon viele gegangen. Und keiner ohne aufgeschürfte Knie. Und weil den schon so viele gegangen sind, sind die Aussichtspunkte auch bekannt. Da kommt erst das Tal der Funktionen, dann der Objektpass und wenn man an den Oberflächen vorbei kommt überrascht einen all zu oft der große Bär namens "Nebenläufigkeiten".
Die Wanderkarte hat __blackjack__ dir bereits in die Hand gedrückt.
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sparrow: Der Nebenläufigkeits-🐻 ist schon da:

Code: Alles auswählen

...
from threading import Thread # Für Hintergrund Schleife der Websiteabfragen
...
@Jeypee: Im Internet steht sogar das die Erde eine Scheibe ist und Echsenmenschen unter der Oberfläche leben. Jeder kann alles mögliche im Internet schreiben, ob das stimmt oder nicht und egal wie viel oder wenig Ahnung er hat. Wie soll ein Anfänger da durchblicken? 🤔 Keine Ahnung. Schaffen manchmal nicht mal Leute die schon länger dabei sind. 😜

Ja in dem Tutorial wird `w` als Name verwendet. Und das Hauptprogramm steht einfach so auf Modulebene. Und ich sehe den Sinn der Funktion nicht die `w` verwendet. Das wäre einfach:

Code: Alles auswählen

#!/usr/bin/env python3
import sys

from PySide6 import QtWidgets
from PySide6.QtUiTools import QUiLoader


def main():
    app = QtWidgets.QApplication(sys.argv)
    loader = QUiLoader()
    window = loader.load("mainwindow.ui")
    window.setWindowTitle("MainWindow Title")
    window.show()
    app.exec()


if __name__ == "__main__":
    main()
Ein weiteres Problem mit Tutorials, auch den besseren oder guten: Programmieren funktioniert nicht durch abschreiben von Code aus dem Internet. Man muss schon verstehen was da gemacht wird und warum das so gemacht wird, um das Wissen aus dem Tutorial dann im eigenen Programm anwenden zu können.

Das `QUiLoader`-Beispiel braucht zum Beispiel keine Klasse (und keine extra Funktion), aber das zeigt ja auch nur wie man `QUiLoader` verwendet, um ein einfaches Fenster ohne Funktion anzuzeigen. Die meisten Anwendungen werden etwas mehr erfordern als das.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Jeypee
User
Beiträge: 6
Registriert: Montag 12. September 2022, 18:53

Das Programm war, wo ich es gepostet habe schon fast fertig. Es wurden bereits Daten abgerufen und im Hauptfenster platziert. Ich konnte die Serveradresse festlegen und diese wurde ins Hauptprogramm übernommen.
Ja, für einen Anfänger fertig. Es hat gemacht was es soll !
Jetzt ist es professionell halbgar und man bekommt nur Sprüche das man nix kann und kein Profi ist, obwohl ich das am Anfang gleich ganz klar gesagt hab.
Ich danke euch für die anfängliche Hilfe.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jeypee: hier macht Dir niemand Vorwürfe, weil Du ein Anfänger bist. Jeder hat mal angefangen. Aber um vom Anfängertum wegzukommen, wurden Dir hier Tipps gegeben, aber es zeigt sich, dass Du die Tipps noch nicht umsetzen kannst, weil Dir noch Grundlagen fehlen. Das ist nichts schlimmes, sondern nur der Hinweis, dass Dir noch grundlegendere Grundlagen fehlen, um sauberen GUI-Code zu schreiben.
Der Weg ist weit und beschwerlich. Du bist losgelaufen und hast Dir einen Weg gebahnt, stehst jetzt aber vor einem Fluß, den Du nicht so einfach überqueren kannst. Wir sagen Dir nur, dass ein paar Kilometer aufwärts eine Brücke ist, und dass Du ein ganzes Stück zurückgehen mußt, weil Du dort falsch abgebogen bist.
Antworten