Umsteiger such nach Lösungansätzen

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
De Rand Ere
User
Beiträge: 3
Registriert: Donnerstag 22. Juni 2017, 20:46

Hallo wehrte Gemeinschaft,

ich habe bisher in AutoIt gecodet. Da bin ich jetzt aber irgendwie an eine Grenze gestoßen, die ich, auch mit Hilfe der Gemeinschaft nicht lösen konnte und so hab ich mich letztes WE entschlossen mal was Neues zu lernen.
Somit setze ich momentan mein Frontend in Python um und stoße da jetzt auf eine kleine Hürde, bei der ich Euch um Hilfe bitten möchte.

Mein Frontend beinhaltet 3 Tabellen, in denen immer nur Daten im ca. 15sec Takt angezeigt werden. In AutoIt konnte ich mit der Funktion "ListView" diese Tabellen sehr einfach generieren. Da wird also eine Tabelle mit einem Kopf (grafisch abgesetzt) erstellt und ich kann den Tabellenkörper später füllen, ändern und jedes Element einzeln ansprechen. Das ging sehr gut, hatte dennoch den kleinen Fehler, das ich diese Tabellen nur zur Anzeige brauche. Also ein Editieren bzw. Auswählen ist gar nicht notwendig. Ein entsprechen gestaltetes Label würde es auch machen.
Nun hab ich gesehen, daß es in Python "nur" die Funktion ListBox gibt (Standardinstallation Python 3.x). Das ist ein Ansatz... ich könnte mehrere ListBoxen nebeneinander stellen. Dennoch bleibt der programmtechnische Verwaltungsaufwand im Hintergrund, der aber nicht notwendig wäre - ich brauch ja nur die "Anzeigeoptionen". Ich denke ein Konstrukt mit "Label" wäre die bessere Richtung. Die angezeigten Daten halte ich im Hintergrund in Arrays bzw. hier in Listen.
Ich hab nun ca. 10h nach etwas passendem gesucht - ohne Erfolg. Die Suche ist auch schwierig, da ich die Begrifflichkeiten von Python noch zu wenig kenne. Sicher ich könnte so'ne Tabelle auch selber bastelln, denke aber sowas gibt es schon. Ich weiß nur nicht, wie es tituliert wird.
Für konstruktive Vorschläge wäre ich sehr dankbar. :D Für Eure Mühe vorab Danke....
BlackJack

@De Rand Ere: In `tkinter` gäbe es `ttk.Treeview` mit dem man eine Tabelle anzeigen könnte, oder man bastelt sich eine Tabelle mit einem `Frame`, `Label`\s und einem `grid()`-Layout.

Alternativ könnte man auch ein moderneres GUI-Rahmenwerk verwenden, das ein Tabellen-Anzeigeelement schon von Haus aus mitbringt.
De Rand Ere
User
Beiträge: 3
Registriert: Donnerstag 22. Juni 2017, 20:46

Hallo zusammen,

es hat ein paar Tage gedauert. Vielen Dank an @BlackJack für die Hinweise bzw. Tipps. Ich wollte zum Anfang schon bei den hauseigen Mitteln von Python bleiben und meine Aufgabenstellung damit lösen. Somit habe ich begonnen mein GUI zusammen zu stellen. Nun schon das zweite mal, da ich das Fenster nun als Klasse programmieren will(ich weiß nicht ob das sinnvoll bzw. notwendig ist). Wie bereits weiter oben erwähnt soll das Fenster ständig aktualisiert werden, also aktuelle Daten anzeigen. Ich möchte oben links, da wo jetzt das weiße Feld angezeigt wird die Zeit des Abrufes aus dem INet in "ms" ... zB: "Ping: 123.56ms" ausgeben.Ich habe mit OOP wenig, genauer gar keine Erfahrung und hänge seit zwei Tagen an diese Ausgabe fest. Hier erst mal das Script:

Code: Alles auswählen

import tkinter as win

# Ein Windoof erstellen
class Fenster:
	def __init__(self):
		self.Fenster = win.Tk()
		self.name=win.Tk.title(self.Fenster,"Hight Pro Gugg")
		self.groesse=win.Tk.geometry(self.Fenster,"800x600")
		# Start Button
		self.start_button = win.Button(text="Start")
		self.start_button.place(x=10, y=570, width=90, height=25)
		#self.start_button.bind("<Button-1>", Start_Button)
		# Stop Button
		self.stop_button = win.Button(text="Stop")
		self.stop_button.place(x=120, y=570, width=90, height=25)
		self.stop_button.config(state="disabled")
		#self.stop_button.bind("<Button-1>", Stop_Button)
		# Exit Button
		self.exit_button = win.Button(text="Exit", command=exit)
		self.exit_button.place(x=700, y=570, width=90, height=25)

		# Rahmen 1 oben links mit Elenten erstellen
		X=20
		Y=55
		self.Rahmen_KeditSuche = win.LabelFrame(text=" Suche ")
		self.Rahmen_KeditSuche.place(x=10, y=5, width=350, height=300)
		self.PingL = win.Label(textvariable=win.StringVar(), bg="white", justify="left")
		self.PingL.place(x=17, y=24, width=170, height=18)

		self.Liste_Suche0 = win.Label(text="Prozente", justify="center", relief="ridge", bg="white")
		self.Liste_Suche0.place(x=X, y=Y + 20, width=60, height=20)
		self.Liste_Suche1 = win.Label(text="Menge", justify="center", relief="ridge", bg="white")
		self.Liste_Suche1.place(x=X + 60, y=Y + 20, width=100, height=20)
		self.Liste_Suche2 = win.Label(text="Gewicht in Kg", justify="center", relief="ridge", bg="white")
		self.Liste_Suche2.place(x=X + 160, y=Y, width=170, height=20)
		self.Liste_Suche21 = win.Label(text="min", justify="center", relief="ridge", bg="white")
		self.Liste_Suche21.place(x=X + 160, y=Y + 20, width=85, height=20)
		self.Liste_Suche22 = win.Label(text="max", justify="center", relief="ridge", bg="white")
		self.Liste_Suche22.place(x=X + 245, y=Y + 20, width=85, height=20)

		self.Fenster.mainloop()
def main():
	Paul = Fenster()
	for i in range(123):
		win.StringVar("Ping: "+i+"ms")
		#sleep(Pause) soll ca. aller 15sec abgefragt werden 

if __name__ == '__main__':
	main()
Problembeschreibung: 2 Probleme
1. Ich weiß nicht so recht, wo ich die veränderbare Methode "PingL" (PingLabel) platzieren bzw richtig definieren soll. Muß ich das Label "PingL" extra definieren, damit ich dann den Wert "textvariable" verändern kann ?
2. Mit "justify="left"" bei "self.PingL" dachte ich, daß der String dann linksbündig ausgegeben wird. Ich habe "alles mögliche" getestet. Egal, was ich der win.Label(...) mitgebe ... der String steht immer mittig (center)...

Ich hoffe auf Eure Hilfe ...
BlackJack

@De Rand Ere: Die Klasse ist semantisch keine Klasse. Mach aus dem ``class`` ein ``def``, Klammern hinter `Fenster`, entferne Zeile 5 und alle ``self.`` und Du hast eine Funktion die genau das gleiche macht.

Warum benennst Du `tkinter` in `win` um? Das hat mich total verwirrt.

`place()` ist keine gute Idee. Das ist arbeitsaufwändig, fehleranfällig wenn man da mal etwas ändern muss, und es sieht auch nur auf dem System wo Du das gemacht hast und sehr ähnlich eingestellten so aus wie es aussieht. Woanders kann das komisch aussehen, oder sogar unbenutzbar werden. Es gibt `pack()` und `grid()` um die konkrete Anordnung Tk zu überlassen, so dass es nicht von den Einstellungen und der Bildschirmgrösse und -auflösung abhängt ob die GUI richtig aussieht.

Für Aktionen zu Schaltflächen hat `Button` das `command`-Argument. Mit `bind()` verhält sich die Schaltfläche nicht so wie der Benutzer das erwartet.

Namen ausser Konstanten und Klassennamen werden `klein_mit_unterstrichen` geschrieben und sollten weder Abkürzungen enthalten, noch irgendwie durchnummeriert werden. Und `Paul` ist kein sinnvoller Name nur weil das ein ”Name” ist. Siehe auch den Style Guide for Python Code.

Um mal die Abkürzungen oder kryptischen Namenszusätze zu demonstrieren: Bei `Rahmen_KeditSuche` habe ich mich gefragt warum das `r` bei `Kredit` fehlt, und dann was der Rest des Programms mit Krediten zu tun hat. Namen sollen dem Leser vermitteln was die Werte dahinter im Kontext des Programms bedeuten. Und zwar möglichst klar und verständlich. Den `Liste_`-Präfix habe ich übrigens auch so gar nicht verstanden.

`time.sleep()` geht in einer GUI nicht. Die Hauptschleife ist die `mainloop()` und die muss laufen wenn die GUI nicht einfrieren soll. Das heisst Dein Code darf nur von der GUI-Hauptschleife aus aufgerufen werden und nur sehr kurz etwas tun und dann sofort zur Hauptschleife zurückkehren. Wenn man regelmässig etwas machen möchte, was nur kurz läuft, dann gibt es dafür die `after()`-Methode auf allen Widgets. Mit der kann man der Hauptschleife eine Verzögerung und ein aufrufbares Objekt (Funktion oder Methode) übergeben die dann nach dieser Zeit aufgerufen wird. Und am Ende kann man dann wieder `after()` für den nächsten Aufruf programmieren.

Wenn das was man machen will nicht ganz schnell läuft, sondern beispielsweise eine möglicherweise blockierende Netzkommunikation ist, dann braucht man Threads (`threading`-Modul). Das bringt dann weitere Komplikationen mit sich, weil man die GUI nur aus dem Thread manipulieren darf in dem die Hauptschleife läuft, weil GUI-Rahmenwerke in der Regel nicht „threadsicher“ sind. Das heisst dann braucht man auch wieder die `after()`-Methode und eine `Queue.Queue` (Python 2) bzw. `queue.Queue` (Python 3) um zwischen den Threads zu kommunizieren.
De Rand Ere
User
Beiträge: 3
Registriert: Donnerstag 22. Juni 2017, 20:46

@BlackJack: Vielen Dank für Deine Gedanken. Die "class, def Zeile 5"-Thematik probiere ich morgen .. wenn Zeit.
place()` ist keine gute Idee.
Nun ich bin seit ca. 25 Jahren ein Kind von Windoof ... da wird alles mit x & y beschrieben. mit grid() & pack() war mir ..unheimlich.. ich werde es testen. Dazu muß ich dem Fenster doch ein Raster vorgeben. Dazu hab ich nix gelesen... oder woher weiß der Button, wie breit er sein soll... ich hab nicht nachgelesen, ich hab x, y gesucht.
Für Aktionen zu Schaltflächen hat `Button` das `command`-Argument. Mit `bind()` verhält sich die Schaltfläche nicht so wie der Benutzer das erwartet.
Auch wenn es zwei verschiedene Wege sind, dachte ich, es macht das selbe ... scheinbar nicht. Das Problem hat ich schon...
Und ja.. zu meinem Style: Die Konventionen ... ich hab einfach angefangen und erste später von diesen gelesen ... ich gelobe Besserung :D Du hast es sehr gut und ganz kurz zusammengefasst.
Den Konflikt mit sleep() & mainloop() hab ich gesehen ... klick ...klick ... und nix geht los bzw hört auf.
D.h. ich muß also meine "def request.get(url)" in eine after()-Anweisung stecken und das im main() laufen lassen. Ich komm immer noch nicht so richtig klar mit dem, wo was platziert wird.
Die Threads find ich geil ... probier es erst mal ohne...
Achso: sollte auch im Script stehen ... ich verwende Python 3.6 & PyCham .. oder sagt man das hier nicht :?
Kurze Zusammenfassung: Danke für die Hilfe & ich muß noch viel lerne ... lesen ... dafür bin ich angetreten ... :D
BlackJack

@De Rand Ere: Auch unter Windows platziert schon eine Weile niemand mehr ernsthaft an absoluten Positionen, denn auch Windows läuft mittlerweile auf einer Bandbreite von Monitoren wo das nicht mehr funktioniert, weil nicht mehr alle annähernd die gleiche Grösse/Auflösung haben. Eine Schaltfläche weiss wie gross sie ist, weil sie weiss wie viel Platz der Text darin benötigt. Ein Raster muss man deshalb nicht vorgeben.

Etwas aus dem Web herunterladen kannst Du nicht innerhalb der `mainloop()` machen, denn selbst wenn es normalerweise schnell genug gehen würde, so kann das doch nahezu beliebig lange blockieren und damit Deine GUI einfrieren. Und es gibt auch Betriebssysteme die fragen dann den Benutzer ob er die Anwendung die da nicht mehr reagiert eventuell beenden möchte. So etwas finden Benutzer in der Regel nicht vertrauenserweckend. :-) Also würde man das in einem Thread erledigen.
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

De Rand Ere hat geschrieben:ich habe bisher in AutoIt gecodet.
Als früherer AutoIt-Programmierer möchte ich noch auf den Weg bringen, dass sich AutoIt - im Vergleich zu Python und so ziemlich den meisten anderen Programmiersprachen, die ich kenne - in Sachen "Grafische Benutzeroberfläche" schon SEHR anders verhält. Mir ist keine Sprache bekannt, in der man so schnell eine Oberfläche "zusammenhacken" kann wie mit AutoIt (außer, man nutzt direkt einen GUI-Builder).

Ich erwähne hier das explizit, weil AutoIt auch die Möglichkeit bietet, innerhalb(!) einer "GUI-While-Schleife" IF-Abfragen einzubauen, wann welches Bedienelement ausgelöst wurde (z.B. Druck auf einen Button). Und da kann ich gleich vorausschicken: Das wird (zurecht) nirgendwo sonst so gemacht. Es mag auf den ersten Blick einfacher sein, aber wenn man hinterher die Oberfläche anpassen muss, hat man viel "Spaß"(/Ärger) dabei... Für die Timing-Geschichte muss man dann andere Lösungen finden (die aber auch einfach umzusetzen sind, sobald man das Prinzip einmal verstanden hat).

Dasselbe gilt auch für Layoutmanager, die es so unter AutoIt garnicht gibt. Auf den ersten Blick ist es aufwändiger, sich mit .pack() und .grid() rumschlagen zu müssen anstatt direkt die Breite und Höhe der GUI-Elemente einzugeben. Stellt man hinterher fest, dass das Programm z.B. auf einem 4K-Monitor laufen muss, dankt man dem Programmier-Gott auf Knien mit .pack() oder .grid() gearbeitet zu haben^^.

AutoIt bietet meines Wissens auch keine Objektorientierte Programmierung an. Wenn du öfters GUI-Anwendungen in Python schreiben willst, musst du dich darauf einstellen, dich etwas länger mit OOP zu befassen. Es gibt kaum GUI-Toolkits (neben Tkinter gibts ja auch noch z.B. Qt und GTK), die sich anständig ohne OOP steuern lassen (außer man verwendet "global" - und das will keiner bei Projekten sehen; am Besten gleich wieder vergessen, dass ich es erwähnt habe^^).
Antworten