Arbeiten mit mehreren Fenstern

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.
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Hallo allerseits,

mit Phyton gibt es offensichtlich einige Probleme, konkret:
Wenn man neben dem Hauptfenster noch mit weiteren Fenstern arbeitet, dann kann man eine Funktion oder auch nur Daten,
die in weiteren Fenstern untergebracht sind, nicht vom Hauptfenster aus aufrufen.
Umgekehrt allerdings funktioniert es. Man kann Daten wie auch Funktionen im Hauptfenster aufrufen.
Das gleiche gilt auch für eine Kommunikation zwischen den Fenstern. Das geht nicht, habe alles mögliche probiert.

Beispiel:
Folgende Richtung geht:
"root_n" --> "root" geht,
aber
"root" --> "root_n" geht nicht.
Wer weiß Rat?
Theodor
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@Theodor: mit Deinem Programm gibt es offensichtlich einige Probleme. Da wir es aber nicht kennen, kann man dazu auch nichts sagen.

Eines ist aber sicher: an Python liegt es nicht.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das wurde jetzt auch schon wiederholt angemerkt. Ebenso wie der Hinweis, nachvollziehbaren und vollständigen Code zu posten. Der Fortschritt ist bemerkenswert gering.
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Dann mal ein Beispiel aus meinem Programm:
Ich habe über einen externen Timer einen Interrupt mit einem GPIO programmiert,
der sekundenweise "tickt". Mit diesem wird ein GPIO gesteuert.

GPIO.add_event_detect(gpio_3, GPIO.FALLING, callback=UhrInterrupt)

Die Funktion "Uhrinterrupt" liest nun jede Sekunde die Raspi-Uhr aus,
um die Uhrzeit im Hauptfenster anzuzeigen.
Die Funktion "Uhrinterrupt" ist in "root" programmiert, d.h., ich kann die Zeit
auch auf einem Display ausgeben.
Die Funktion für das Display ist natürlich aus "root" programmiert.
Wenn ich aber die Zeit in einem neuen Fenster ausgeben möchte,
beispielsweise unter "root_neu", dann kann in dem neuen Fenster diese Uhrzeit
nicht ausgegeben werden.
Ich kann natürlich auch aus dem neuen Fenster heraus eine Uhrzeit aus dem Raspi lesen,
aber mit dem Uhrinterrupt geht das nicht.

Wäre schön, wenn ich einen Beweis bekäme, dass es an Python nicht liegt.
Habe ich mich etwas verständlicher ausgedrückt?
Theodor
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Theodor: Die Callbacks von GPIO werden von einem Thread aus aufgerufen, das heisst in `UhrInterrupt` (was ein ganz schlechter Name ist, sowohl formal als auch inhaltlich) kannst Du die GUI nicht ändern. Das mag so aussehen als wenn das geht, aber das darf man nicht machen.

Du behauptest es liegt an Python — die Beweislast liegt bei Dir. Die Erfahrung zeigt das das Problem in 99% der Fälle vor dem Rechner sitzt.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Und wieder wäre es viel einfacher, wenn Du statt langer Erklärungen einfach den Code zeigen würdest, der nicht so tut, wie Du es erwartest.
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

@Theodor: Ich sehe auch nicht, wo die eine Zeile Code, die du zeigst, mit dem Problem zu tun hat.
Du gehst doch auch nicht nur mit dem Ersatzreifen in die Werkstatt wenn die dein Auto sehen wollen um festzustellen warum es beim bremsen quietscht.

Zeig uns ein ausführbares Beispiel.
Sag uns was passiert und was du stattdessen erwartest.
Dann kann man dir auch helfen.
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

O.k.,
danke ersteinmal, dass mein Problem offensichtlich interessiert.
Kurze Erläuterung zum Programm:
Mein Programm steuert ein Lichtpult mit DMX-Steuerung. Da sind u.a.
eine Menge Szenen oder Takes abgelegt, denen jeweils eine Zeit unterlegt ist.
Wenn man den "Sequenzer" startet, also bei NULL, wird im Rhytmus von 1 Sekunde,
also wenn ein Interrupt auftritt, abgefragt, ob die Uhrzeit mit der "Sequenz-Zeit"
übereinstimmt. Ist das der Fall, wird die nächste Szene ins Pult geladen.
Ich versuche mal aufzuzeigen, was ich gemacht habe,
1.) In meiner Startdatei <DMX_START> ist der Interruptaufruf entalten:
Diese Datei läuft unter "root".

GPIO.add_event_detect(gpio_3, GPIO.FALLING, callback=UhrInterrupt)

2.) Die Funktion "Uhrinterrupt" ist in der Datei <DMX_Sequenz> enthalten.
Hier wird abgefragt, welche Funktion bedient werden soll, entweder wird die
aktuelle Uhrzeit in einem Display als allgemeine Info ausgegeben,
oder sie wird im Display, allerdings in einem anderen "Fenster", als eine
"Sequenz-Zeit" angezeigt, wobei bei NULL gestartet wird.
Unterhalb des Displays sind Tasten enthalten mit den Funktionen:
Start / Stop,
Wenn der Sequenzer steht, kann man mit weiteren Tasten bestimmte
Zeilen aufrufen:
> = Nächste Zeile
< = Vorhergehende Zeile
>> = Letzte Zeile
<< = Erste Zeile
Mit "Start" kann man jederzeit neu starten.

Hier also die Aufruf-Funktion mit allen Kommentaren:
"""************************** UhrInterrupt *************************************
Funktion wird durch Interrupt GPIO_3 aufgerufen.
Es wird dann die Funktion "UhrStart" mit Parameter (0) aufgerufen.
Bedeutet, dass Sequenz-Programm laufen kann.
===============================================================================

Geändert: 27.12.2019
Erstellt: 16.2.2019
*****************************************************************************"""
def UhrInterrupt(z): # "z" oder irgendein anderer Buchstabe muss vorgegeben
# werden, sonst wird Funktion durch GPIO_3-Interrupt
# nicht aufgerufen.
#===============================================================================
# Abfrage von "progAbfrage"
#-------------------------------------------------------------------------------
if progAbfrage[0] == 0: # Wenn Taste <Prog>
# betätigt wird, wird
# progAbfrage[0] = 1
# in Funktion
# "DisplProgInfo" in
# Datei <DMX_Prog_02>
# gesetzt, damit der
# Uhr-Interrupt nicht
# aktiv sein kann.
# Nach Ende der
# Prog-Funktion wird
# progAbfrage[0] = 0.
#===============================================================================
# Abfrage der Taste <Prog>
# Pro Interruptaufruf = 1 sec wird Uhrzeit neu aus dem MSM-Modul ausgelesen.
#-------------------------------------------------------------------------------
progFrag = Wt[0] # Abfrage der Taste <Prog>
if progFrag == 0x01:
GPIO.output(gpio_2, 1) # Uhr = Lauf
UhrProgInfo() # Funktion bearbeitet das
# Programm "UhrProgInfo".
# Ist das Startbild im Display.
# Uhrzeit wird ständig aktua-
# lisiert = 1 x pro Sekunde.
#===============================================================================
# Abfrage der Tasten <Sequ> und <Start>.
# Wenn "Start" aktiviert ist, beginnt die Sequ-Uhr im Display an zu laufen.
# Uhrzeit wird pro Sekunde mit dem aktiven Sequenz-Take verglichen.
# Wenn Übereinstimmung existiert, wird entsprechender Take ausgeführt.
#===============================================================================
seqFrag = Wt[1] # Abfrage der Tasten "Sequ"
# und "Start".
seqFrag = seqFrag & 0x03
if seqFrag == 0x03: # Funktion "UhrSequenz" wird nur
# aufgerufen, wenn "Sequenzer"
# läuft.
# Bit 0 = "Sequenzer"
# Bit 1 = "Start" / "Stop"
UhrSequenz() # Funktion bearbeitet das
# Programm "Sequenzer"
"""**************************************************************************"""
Das alles funktioniert einwandfrei.

Nun aber habe ich auf dem Desktop aus dem Hauptfenster heraus aus einem
Menü-Punkt "Sequenzer" ein neues Fenster geöffnet, in dem quasi ein Abbild des Displays
zu sehen ist. Leider kann man auf dem Raspi keine Hardcopy erstellen,
oder ich weiß momentan nicht, wie das geht, sodass kurz erklärt sei:
3.) In einer Listbox sind alle Szenen aufgeführt mit Namen und Nummern.
Aus diesem Fenster heraus kann ich ebenfalls den Sequenzer starten
und stoppen.
Wenn er dann läuft, soll jeweils die aktuelle Szene-Zeile "underlined"
markiert werden. Und dazu wird der Klick vom Interrupt benötigt.
>>> Und jetzt das Problem:
Der Uhrinterrupt, bzw. eine Funktion, die von ihm bedient wird,
kann nicht veranlassen, dass in meinem neuen Fenster die aktuelle
Sequenzer-Zeit angezeigt wird und die jeweils aktuelle Zeile
als "underlined" erscheinen kann.

Mehr Programm-Code kann ich leider nicht zeigen, er ist viel zu umfangreich.
Momentan besteht das gesamte Programm aus 9 Programm-Dateien und
4 Daten-Dateien. Jede Programmdatei ist bis zu 4.000 Zeilen groß.

Ich hoffe, ich konnte etwas erklären. Falls nicht, teilt doch bitte mit, was Ihr braucht.
Theodor
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Sorry, meine Antwort ist total entstellt, da alle Zeilen vorn anfengen.
das wußte ich nicht. Ist sicherlich so überhaupt nicht durchschaubar.
Vielleicht klappt es aber.
Theodor
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Das mit dem kaputten Code verhindert man, indem man seinen Code zwische Code-Tags schreibt. Die werden hier automatisch eingefügt, wenn man im "Vollständigen Editor & Vorschau" (kann man unten unter der Schnellantwort auswählen) den </>-Knopf drücken.

Zu deinem Programm: das besteht ja zu 90% aus Kommentaren und behandelt irgendwelchen GPIO-Kram. Ich denke, es geht dir um die Kommunikation zwischen Fenstern? Ich hätte jetzt hier ein minimales, ausführbares Beispiel erwartet, in dem du zeigst, was deiner Meinung das Problem von Python ist. Aber GUI-Code sehe ich in deinem Beitrag nicht.

Also ein kleines Beispiel, ganz ohne GPIO, das zeigt, das ich hier einfach ausführen kann und das zeigt, was deiner Meinung nach nicht geht.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Mit Fenstern hat das der gezeigte Code ja nun nichts zu tun. Was mir neben dem CommentOverflowError auffällt: In deiner Funktion tauchen auf einmal Variablen wie `progAbfrage` oder `Wt` auf die nicht nur vom Namen her nichtssagend sind sondern außerdem anscheinend mal einfach aus irgendeinem äußeren Scope verwendet werden. Alles was eine Funktion braucht sollte sie eigentlich über ihre Parameter übergeben bekommen. So wie das derzeit aussieht habe ich starke Bedenken was die Strukturierung des restlichen Codes angeht, vor allem wenn das tausende Zeilen sind.

Erstell doch mal ein Minimalbesipiel mit zwei Fenstern die sich nicht so verhalten wie du möchtest. Den ganzen GPIO-Kram kannst du für das Beispiel bedenkenlos weglassen.

[Edit: da war sparrow wohl schneller ...]
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso wird denn der Takt per GPIO vorgegeben? Ist das aus Synchronisationsgründen so? Oder weil du es nicht besser wusstest?

Zu deinem Problem: das ist die zu 99% hier diskutierte Frage die jeder hat, der was mit GPIO und tkinter machen will. Entsprechend oft wurde die diskutiert, Beispielcode inklusive. Die Stichworte sind after, GPIO und Queue. after ist darüberhinaus auch die Lösung die man eigentlich will, wenn man regelmässig etwas tun muss. Statt dazu einen GPIO zu verwenden. Außer eben der gibt einen Takt vor, der eine andere (und ggf sogar variable) Uhr darstellt.

Alles andere sind Fragen der GUI Programmierung, und die Antwort auf die so allgemeine Frage “warum geht es nicht” bleibt “du machst es falsch”. WIE es falsch ist - da sind wir wieder beim konkreten Code. Wenn der zu groß ist, schreib ein kleines Beispiel, das das Problem illustriert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hier zb ein Programm das zeigt, wie GPIO Pulse in der GUI ankommen können. viewtopic.php?f=31&t=47234&p=357868&hil ... IO#p357868
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Ja, in meinem Code sind unzählige Kommentare. Ohne die wüßte ich nach einiger Zeit nicht, was alles zu bedeuten hat. Das wird von Programmierern auch wärmstens empfohlen.
Ich will mal versuchen, alles näher zu erläutern:
Aus der Hauptdatei heraus bilde ich mir mehrere Menü-Punkte.
Hier das Beispiel für "Sequenzer" mit 2 Untermenüs:

Code: Alles auswählen

     ============================================================================="""
menubar = Menu(root)
#-------------------------------------------------------------------------------
# Menü-Bar erstellen, in die die Menüs aufgeführt werden.
root.config(menu=menubar)   
#===============================================================================
# Pulldown-Menü: "Sequenzer"
# Funktion "SequenzProg" ist in Datei <DMX_Sequenz> definiert.
# Funktion "SequenzStart" ist in Datei <DMX_Prog_01> definiert.
#-------------------------------------------------------------------------------
seqMenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="Sequenzer", menu=seqMenu)
seqMenu.add_command(label="Liste bearbeiten", command=SequenzProg)
seqMenu.add_command(label="Liste starten", command=SequenzStart)
#===============================================================================
In Datei <DMX_PROG_01> ist die Funktion "SequenzStart" enthalten:

Code: Alles auswählen

"""*************************** SequenzStart ************************************
 Aufruf aus dem Menü "Sequenzer" und "Sequenz starten".
 Ist identisch mit Aufruf der Taste <Sequ> im Display-Feld.
  ===============================================================================
 Geändert: 15.1.2020
 Erstellt: 14.1.2029
*****************************************************************************"""
def SequenzStart():
   SequenzProgDesktop()     # Funktion für den Desktop
   Prog(0x54)                             # Funktion für das Display
"""**************************************************************************"""
Die Funktion "SequenzProgDesktop()" befindet sich in der Datei <DMX_SequenzMenue>,
die so aussieht:

Code: Alles auswählen

"""************************** SequenzProgDesktop ***************************************
 Funktion hat dieselbe Funktion wie die Tasten auf dem Display.
 Arbeiten im neuen Fenster: "root_spd".

 Alle Funktionen enthalten, um eine Sequenz ablaufen zu lassen.
 ===============================================================================
 Geändert: 16.1.2020
 Erstellt: 15.1.2020
*****************************************************************************"""
def SequenzProgDesktop():
   root_spd = Tk()                           # spd = Sequenz Prog Desktop
#-------------------------------------------------------------------------------
# root-Titel
  root_spd.wm_title('Starten der Sequenzer-Tabelle')
#-------------------------------------------------------------------------------
# root-Größe: ("Breite x Höhe")
   root_spd.geometry("1770x810")              
#-------------------------------------------------------------------------------
# root-Position = Gefunden unter:
# https://www.python-forum.de/viewtopic.php?t=774
   xpos  = 5 #125
   ypos  = 92  # 98
   root_spd.wm_geometry("+%d+%d" % (xpos,ypos))
#-------------------------------------------------------------------------------
# Fenster kann in der Größe nicht mehr verändert werden.
   root_spd.resizable(0,0)   
#===============================================================================
In diesem neuen Fenster unter "root_spd" sind nun Funktionen enthalten, die alle in dem neuen
Fenster ausgegeben werden.
u.a. ist dort eine Listbox mit allen Sequenzer-Zeilen:

Code: Alles auswählen

#-------------------------------------------------------------------------------
  seqStartBox = Listbox(root_spd, font=("Courier New", 13))
  seqStartBox.place(x=xStart+3, y=yStart+25, width=455)
#*******************************************************************************
Danach wird die Listbox gefüllt:

Code: Alles auswählen

#===============================================================================
# Laden der Sequenzer-Takes in die Sequenzer-List-Box.
#===============================================================================
  for  i in range(200):                       # Löschen der Sequenzer-Box
     seqStartBox.delete(0, END)
#-------------------------------------------------------------------------------
  n = sequDmx['Stunde'][0]                    # Anzahl der Sequenzer-Datensätze
  n = n+1                                     
  for  i in range(1, n):                      # Laden der Sequenzer-Takes in die Sequenzer-Box
    seqStartBox.insert(END, sequDmx['Sequenz'][i]) # Anzeige-Text: Sequenz-Take
#-------------------------------------------------------------------------------
# Fenster kann in der Größe nicht mehr verändert werden.
  root_spd.resizable(0,0)   
#****************************************************************************"""
Es werden also max. 200 Zeilen angezeigt, jedoch nur so viele, wie tatsächlich an Einträgen existieren.
In dieser Listbox soll nun bei laufender Sequenzer-Uhr die jeweils aktuelle Zeile "underlined" werden.
Ein Befehl müßte sich also aus dem Interrupt heraus ableiten. Dies sollte in der vorhin gezeigten Funktion "UhrInterrupt" passieren.
Man kann aber dort keine Funktion setzen, die dann die aktuelle Zeile "underlined".
Hier nochmals diese Funktion, die wiederum in der Datei <DMX_Sequenz> enthalten ist.

Code: Alles auswählen

*****************************************************************************"""
def UhrInterrupt(z):  # "z" oder irgendein anderer Buchstabe muss vorgegeben
                      # werden, sonst wird Funktion durch GPIO_3-Interrupt
                      # nicht aufgerufen.
#===============================================================================
# Abfrage von "progAbfrage"
#-------------------------------------------------------------------------------
  if progAbfrage[0] == 0:                       # Wenn Taste <Prog>.....
#===============================================================================
# Abfrage der Taste <Prog>
# Pro Interruptaufruf = 1 sec wird Uhrzeit neu aus dem MSM-Modul ausgelesen.
#-------------------------------------------------------------------------------
    progFrag = Wt[0]                            # Abfrage der Taste <Prog>
    if progFrag == 0x01:
      GPIO.output(gpio_2, 1)             # Uhr = Lauf
      UhrProgInfo()                                # Funktion bearbeitet das Programm "UhrProgInfo". 
                                                                   # Ausgabe der Uhrzeit auf einem Display.
#===============================================================================
# Abfrage der Tasten <Sequ> und <Start>.
# Wenn "Start" aktiviert ist, beginnt die Sequ-Uhr im Display an zu laufen.
# Uhrzeit wird pro Sekunde mit dem aktiven Sequenz-Take verglichen.
# Wenn Übereinstimmung existiert, wird entsprechender Take ausgeführt.
#===============================================================================
    seqFrag = Wt[1]                             # Abfrage der Tasten "Sequ" und "Start".
    seqFrag = seqFrag & 0x03
    if seqFrag == 0x03:                     # Funktion "UhrSequenz" wird nur aufgerufen, wenn "Sequenzer" läuft.
      UhrSequenz()                              # Funktion bearbeitet das Programm "Sequenzer". Ausgabe auf einem Display.
"""**************************************************************************"""
Hinter dieser letzten Funktion müßte also eine Funktion stehen, die auf die Datei <DMX_SequenzMenue> zugreift unter "root_spd".
Und das geht leider nicht.
>>> Zur Info: Umgekehrt kann man aus "root_spd" aber auf "root" zugreifen.

Vielleicht jetzt etwas verständlicher?

Noch zur Erklärung: Die Verteilung auf unterschiedliche Dateien ist notwendig, da jede für eine bestimmte Aufgabe da ist und damit eine bessere Übersicht herrscht.
Wie gesagt: Die Dateien sind zwischen 2.000 und 4.000 Zeilen lang, enthalten jedoch eine Unmenge an Kommentarzeilen. Der reine Code ist etwa 2/3 groß.

Noch etwas: Habe das mit der Code-Formatierung umgesetzt.
Theodor
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Erfahrene Programmierer empfehlen Kommentare die Mehrwert bringen, nicht redundant beschreiben was beim Blick auf den Code eh klar ist. Aber schlussendlich musst du dich durch die Zeichenwüste kämpfen.

Die Probleme die du hast sind einfach gelöst: statt mit einem Sack globaler Variablen und Alleinstehender Funktionen arbeitet man bei mehr als trivialen GUI Projekten mit Objektorientierung. Diese Objekte können einander gegenseitig bekannt gemacht werden, und sich gegenseitig durch Methodenaufrufe benachrichtigen.

Zu der Frage wie der “Interrupt” Wirkung auf die GUI entfaltet siehe meine vorherigen Posts.
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Ok, ich bin hier raus. Ich habe jetzt 2 Mal gesagt, dass du in einem einfachen, ausführbaren Beispiel dein Problem zeigen sollst. Stattdessen kommen Wände von Texten. Ein drittes Mal danach zu fragen ist irgendwann Zeitverschwendung.

Ein paar Sachen zum Code:
Kommentare sollten nicht erklären, was Code tut - denn das sieht man ja - sondenr warum er es tut.
Variablennamen sollten erklärend sein. root_spd ist offensichtlich die Wurzel der Sozialdemokraitie in Deutschland. Ich kann mir vorstellen, dass es da Kommunikationsschwierigkeiten mit der root_cdu gibt.
Ist gibt eine Konvention zu den Schreibweisen in Python: Variablennamen klein_mit_unterstrich (und sprechend, Buchstaben fressen kein Brot), Klassennamen schreibt man MixedCase und Konstaten KOMPLETT_GROSS. So weiß der Leser, um was es sich handelt.
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Hallo __deets__,
scheinst ein sehr erfahrener Programmierer zu sein. Ich habe zwar einst C mit dem Borland-Compiler auf DOS gearbeitet, dann aber aus beruflichen Gründen abgeschaltet als Windows kam.
Jetzt habe ich vor vier Jahren beim ILS einen Kurs für C++ mit Qt mit Erfolg absolviert (mit 1,1), muß aber sagen, dass ich so klug als wie zuvor bin. Jedoch haben mir diverse Methoden, auch die Widgets bei der Programmierung mit Phyton sehr geholfen. Ich kannte bis dato Python nicht. Habe mich da immer mehr hinein gearbeitet bis dahin, dass ich eben ein sehr aufwändiges Lichtpult programmiert habe. Alles funktioniert bisher einwandfrei. Die Hardware ist übrigens komplett selbst entwickelt. Und bei Python kriege ich immer wieder die Krise, wenn ich nach einem Problem suche. Ich habe zwar ein tolles Buch: "Raspberry PI das umfassende Handbuch", das mir zwar viele Grundlagen lieferte, aber in der TK-Programmierung nicht das bietet, was ich benötige. Beispielsweise habe ich recht lange gesucht, wie man Dateien speichern und wieder aufrufen kann. Und so gibt es vieles, was man sucht und nicht findet.
Ja, objektorientierte Programmierung war Bestandteil des C++ Kurses, aber auch da klappte nicht immer das, was ich suchte. Und worüber ich sauer war, dass die Fernlehrer über das Lehrwissen hinaus nicht berechtigt waren zu antworten. Aus diesem Grunde habe ich meine Pultprogrammierung ersteinmal nicht mit Objekten programmiert. Und wenn ich das jetzt machen wollte, müßte ich das gesamte Programm neu schreiben. Davor habe ich einen Horror.
Ich habe deshalb erwartet, dass meine Fragen konkret beantwortet würden. Ich habe in dem Forum ziemlich viel gelesen und muß leider feststellen, dass viele Antworten wie von einem Oberlehrer zu kommen scheinen, der grundsätzlich alles weiß und die Nase rümpft, wenn eine dumme Frage kommt. Das finde ich unfair.
Ich glaube, dass Du mir helfen kannst, aber eine Antwort, wie mit der Objektprogrammierung kann mich nicht befriedigen.
Vielleicht werde ich das noch einmal tun, da ich beabsichtige, quasi nur eine Software-Version für mein Lichtpult zu erstellen, die lediglich per Hardware ein DMX-Signal sendet,
Aber momentan noch nicht.
Ja, ich habe in meinem Programm unzählige globale Variablen. Natürlich auch für die immensen Detailaufgaben einzelne Funktionen. Und durch die Aufteilung auf mehrere Dateien habe ich einen recht guten Überblick. Bisher konnte ich jeden Fehler, der während des Arbeitens am fertigen Pult auftrat, recht schnell lokalisieren und beheben.
Gerne würde ich Dir einen Screeshot vom Pult schicken, weiß aber nicht, wie man das beim Raspi macht. Mit "Drucken" geht es nicht.
Sorry, dass ich so ausgeholt habe.
Theodor
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was wäre denn deiner Meinung nach eine befriedigende Antwort?
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

@Theodor: es ist normal, dass man immer wieder Code neu schreibt und sogar, dass man ganz von Vorne anfängt, weil man gelernt hat, wie man es besser machen kann, oder weil man einen anderen Weg ausprobieren will, um zu lernen, wie man es besser machen kann.

Wenn man etwas lernt, fängt man klein an, bis man es beherrscht. Du fängst beim Turnen auch nicht mit dem dreifachen Salto an, wenn Du noch keinen Purzelbaum beherrschst.

Du beherrscht noch nicht die elementarsten Dinge, willst aber ein 20.000 Zeilen Programm geschrieben haben? Ich habe noch nie ein 20.000 Zeilen Programm geschrieben und jede meiner Dateien hat weit unter 1000 Zeilen.

Zu den elementaren Dingen zählen, sich an Konventionen zu halten. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 2 und mal 3. Funktionen und Variablennamen schreibt man klein_mit_unterstrich, sie bestehen nicht aus Abkürzungen und sind aussagekräftig.
Funktionen erhalten alles was sie brauchen, über ihre Argumente. Es gibt keine globalen Variablen.

Programme, die sich nicht an diese Konventionen halten, dürfen nicht mehr als 13 Zeilen haben, sonst endet das im Chaos, wie bei Dir.

Dann suchst Du Dir auch noch komplizierte Themen aus, wo man noch alle Eigenheiten der verwendeten Frameworks kennen muß. Bei GPIO muß man vorsichtig sein, nicht den ganzen Rechner zu blockieren, weil sonst arbeiten sie nicht mehr richtig. Das heißt, innerhalb eines Interrupts nur minimalste Arbeit leisten, meist nicht mehr als einen Wert in eine Queue stecken, die im Hauptprogramm abgearbeitet wird.

Bei GUI-Programmen ist es das selbe, alles was Du so im Internet findest, endet bei 20 Zeilen Code, da kriegt man noch den größten Schrott zum Laufen. Bei allem sinnvollen, muß man sich wieder an die oben genannten Konventionen halten, das führt zu mehr Code, Klassendefinitionen sauberer Umgang mit Ereignissen, etc.

Daher mein Vorschlag: fang von Vorne an, ganz klein. Poste hier einen kleinen vollständigen Code und frag, ob das so korrekt ist, dann können wir Tipps geben, so dass Du Deinen kleinen Code neu schreiben kannst, und so lernst, richtig zu programmieren.
Theodor
User
Beiträge: 17
Registriert: Montag 30. Dezember 2019, 18:53

Hallo, vielen Dank für die diversen Tipps.
verstehe ich ja irgendwie, komme da aber trotzdem nicht mit.
1.) Das Einrücken auf 2 Zeichen ist einfach kompakter. Ich finde, es ist sonst vergeudeter Raum.
2.) Dass es Konventionen gibt, sehe ich ein. Mir ist meine Schreibweise, etwa Funktionen
so zu schreiben: "NeueFunktion(5)", lieber. Mein Programm wollte ich ja nicht unbedingt in die
Welt hinaus posaunen.
3.) Ich weiß, dass man einer Funktion in der Regel Argumente übergeben soll. Das mache ich auch
in der Regel, aber wenn ich in Funktion nun einmal mehrere Werte bearbeitet werden sollen,
dann ging das bisher immer nur mit globalen Variablen. Und bei einer Funktion kann man nur
einmal "return" benutzen.
4.) Die hohe Anzahl an Codezeilen resultiert einfach aus der Größe des Projektes heraus.
Ich zähle mal grob auf, was dieses Programm alles leistet:
a.) Das Hauptfenster meines Lichtpultes stellt ein komplettes Lichtpult dar. Darin enthalten
34 Slider. Jedem Slider sind 2 Spin-Boxen und eine EntryBox zugeordnet. In einer
Spinbox kann man eine Dmx-Adresse zwischen 1 und 512 einstellen. In der anderen
Spinbox wird der %-Wert des Sliders gezeigt (0 - 100%). Der Slider selbst umfaßt den
8-Bit Raum mit 256 Stufen. In der Entrybox kann man einen Namen dem Sliderkanal geben.
Jeder einzelne Slider kann mit einer Farbe versehen werden, siehe Punkt d.
b.) Da auf dem Pult nur insgesamt 28 Kanäle aus Slidern dargestellt werden können, es aber
insgesamt 512 Adressen gibt, ist im rechten Teil des Fenstern eine Listbox, in der sämtliche
Daten eines Sliders gelesen werden können durch Scrollen.
c) In einem weiteren Teil des Fensters besteht die Möglichkeit, eine komplette Pulteinstellung
in einem Take abzuspeichern. Es gibt insgesamt 100 Takes.
d.) Zum Verständnis: Dmx-Geräte besitzen in der Regel mehrere Adressen, wie etwa "Dimmer",
"Farben", "Dreh-Neigfunktionen", etc. Wie unter Punkt a aufgeführt, kann ich nun einem Gerät eine
bestimmte Farbe geben, also den Slidern eines Gerätes. Das ist dann sehr übersichtlich auf dem
Mischpultfenster. Und da man in der Regel bei einem Gerät die "Dimmer"-Funktion bedient, wird
die betreffende Entry-Box auch mit derselben Farbe versehen. Die Farben hole ich mir dabei aus einer
Farbscala, die aus theoretisch 480 Farben auswählen könnte, ich jedoch nur etwa 20 nutze.
Diese Farbzuordnung ist ebenfalls Bestandteil des Hauptfensters.
e.) Und weil man unmöglich alles aus dem Hauptfenster heraus steuern kann, gibt es etliche
Menüpunkte in der Menüleiste. Nach Aufruf tut sich ein neues Fenster auf, das natürlich auch
wieder Steuerfunktikonen übernehmen kann und es auch tut.
f.) Der Hauptmenüpunkt ist der erste, wie bei vielen Programmen: "Datei" genannt.
Mit seinen Untermenü-Punkten wie "Neu", "Speichern", "Speichern unter" oder "Löschen" kann
ich eine komplette Mischpulteinstellung unter einem eigenen Namen speichern oder wie auch
immer. Und der kleine Komfort: Wenn ich das Pultprogramm neu starte, wird jeweils die
zuletzt bearbeitete Datei automatisch geladen.
g.) Und das Lichtpult selbst, also die Hardware, besteht aus 25 Motorfadern, die jeder einzeln von
einem eigenen MC gesteuert werden, dem ATtiny2313A, mit Assembler programmiert. Im
Lichtpult sind weitere Einheiten integriert, wie ein Display mit 20 x 4 Zeilen, der DMX-Generator,
programmiert mit einem ATMega32, insgesamt 4 Karten, AD-Wandler, die die 25 Fader ständig
selbständig abfragen und bei einer Wertänderung einen Interrupt an den Raspi schicken.
Weiter gibt es knapp 64 Tasten für diverse Steuerungen, die ebenfalls bei einer Betätigung
einen Interrupt an den Raspi schicken. Und da ich eine Unmenge an Steuerleitungen benötige,
sind insgesamt 5 ICs mit I²C-Steuerung enthalten, jeder hat 16 Kanäle.
Die gesamte Hardware und Entwicklung ist aus eigener Herstellung. Es gibt dafür halt keine Vorbilder.
Das Assemblerprogramm für die DMX-Steuerung habe ich tatsächlich nicht selbst erstellt, sondern
habe im Internet einen Lösungsweg gefunden.
Gerne würde ich einen Screenshot des Pults zeigen, das ich einst mit Qt entworfen habe, weiß aber nicht,
wie man hier eine Graphik einbringt.

Ich will es dabei belassen. Und all dies ist mit Sicherheit nicht mit 2.000 Codezeilen zu
bewältigen. Und ich bitte doch um etwas Respekt, weil im Gesamtprogramm unterschiedlichste
Funktionen behandelt werden. Sich dafür die Informationen aus dem Internet zusammen zu
klauben und zu klauen, hat mich reichlich graue Haare gekostet. Und aus den unterschiedlichsten
Foren konnte ich bisher am wenigstens profitieren. Wenn man da für ein Problem eine Lösung sucht,
so ist man nicht selten enttäuscht.
Was ich sehr bemängle, dass ich bisher für all die vielen Informationen, die ich zum Erstellen
des Pultes suchte, mühsam im Internet suchen mußte. Grundlagenwissen zu Python habe ich, wie zuvor
geschrieben, aus einem Buch "Raspberry-PI" erworben, hat mir für den Start geholfen.

Da das Pult bis auf wenige Dinge also voll funktionsfähig ist, das Lichtpult alleine nicht ganz so leicht,
plane ich eine reine Software-Funktion, und zwar mit Objektprogrammierung. Und wahrscheinlich nicht mit
Python, sondern mit C++ und Qt. Habe ja mal einen Kurs belegt, sodaß ich da Erfahrung besitze.

Sorry für das weite Ausholen.
Wollte damit nicht angeben, nur aufzeigen, dass ich keinen kleinen Roboter baue oder gebaut habe, sondern
eine sehr komplexe Maschine.
Theodor
Antworten