Unterstützung bei Python-Script

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
entropie
User
Beiträge: 2
Registriert: Samstag 23. Februar 2013, 08:28

Hallo liebe Gemeinschaft,

ich bin ein absoluter Python-Noob und beschäftige mich erst seit kürzlich mit der Programmiersprache. Ich versuche gerade folgenden Code zu erstellen und wollte hier mal fragen, ob man mir dabei etwas unter die Arme greifen kann.

Und zwar geht es darum einen Composite Video Ein- und Ausgang an bzw. ab zu schalten. Der Monitor soll ein Bild ausgeben solange der Benutzer Eingaben tätigt und falls der Benutzer 2 Minuten lang nichts drückt, soll der Monitor ausgehen.

Mein Code ist bisher so weit:

Code: Alles auswählen

#! /usr/bin/env python

from os import fork, chdir, setsid, umask
from sys import exit
from goto import goto, label

def countdown(secs, interval=1):
 while secs > 0:
  yield secs
  secs = secs - 1
  time.sleep(interval) 

  def main():
   while 1:
    #main daemon process loop
    # Dual fork hack to make process run as a daemon

    if __name__ == "__main__":
     try:
      pid = fork()
      if pid > 0:
       exit(0)

       chdir("/")
       setsid()
       umask(0)

       try:
        pid = fork()
        if pid > 0:
         exit(0)

         main()

	 import os
         os.system('"/opt/vc/bin/tvservice -o"')

         import msvcrt
         c = msvcrt.getch()

         os.system('/opt/vc/bin/tvservice -n "PAL 4:3"')
         os.system('fbset -depth 8 && fbset -depth 16')
         os.system('xrefresh')

         for count in countdown(120):
          print count
Leider hängt er sich am Ende auf mit dem Fehler "IdentationError" in der ersten Zeile hinter der letzten Zeile des Codes und zeigt mir ein Leerzeichen oder Tabstopp an, den ich mit dem Linux Editor nano irgendwie nicht wegbekomme. Auch fehlt mir bei diesem Script (welches als Daemon laufen soll) noch die Routine, die nach 120 Sekunden ohne erfolgte Benutzereingabe wieder den Monitor ausschalten soll. Bisher wäre das Skript (wenn es funktionierte) nur so weit, dass es den Monitor ausschaltet, dann auf eine Benutzereingabe wartet, um ihn dann wieder einzuschalten.

Wenn Ihr mir etwas helfen könntet, würde ich mich darüber sehr freuen !
Ich bedanke mich,
Gruß Olli
Zuletzt geändert von Anonymous am Samstag 23. Februar 2013, 09:45, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@entropie: Da hängt sich nichts auf und der Fehler kommt auch nicht am Ende, denn das Programm läuft erst gar nicht. Der `IndentationError` kommt nämlich schon vom Compiler und liegt daran, dass die Einrückung falsch ist.

Du machst da *so* viel falsch oder zumindest sehr gruselig, dass ich schon fast geneigt bin an einen Troll zu glauben. :-)

Bitte arbeite ein Grundlagentutorial durch, zum Beispiel das in der Python-Dokumentation. Danach oder parallel dazu ist der Style Guide for Python Code einen Blick wert.

Dann entwickle Programme nicht, in dem Du erst *alles* runterschreibst und dann erst versuchst ob es läuft, sondern unterteile die Entwicklung in einzelne Abschnitte und teste jeweils ob das Programm bis dahin läuft. Wie gesagt, sind da so viele komische Sachen drin, das hätte sich gar nicht erst unbemerkt soweit anhäufen dürfen.

Vergiss das `goto`-Modul. Das war ein Aprilscherz. Es ”funktioniert” zwar, aber das viele moderne Programmiersprachen kein GOTO haben ist *Absicht*.

Die ``while``-Schleife in `countdown()` könnte man einfacher mit einer ``for``-Schleife ausdrücken.

Python hat einen eigenen Typ für Wahrheitswerte. Endlosschleifen schreibt man verständlicher als ``while True:``.

``try`` kann nicht für sich alleine stehen, da gehört mindestens ein ``except`` dazu. Wenn Du das nicht weisst, wie bist Du dann überhaupt darauf gekommen das dort hinzuschreiben? ``except:`` sollte übrigens nie ohne eine konkrete Ausnahme verwendet werden, denn dann werden *alle* ”behandelt”, also auch solche mit denen man überhaupt nicht rechnet und die bekommt man dann nicht zu Gesicht. Da macht die Fehlersuche dann keinen Spass.

Um einen Daemon aus dem Prozess zu machen würde ich das `daemonize`-Modul verwenden, statt sich selber um diese Details kümmern zu müssen. Debian-Paketname ist `python-daemonize`.

Das ``if __name__ == '__main__':``-Idiom hast Du anscheinend nicht verstanden beziehungsweise wie der Programmablauf aussehen würde, wenn das anders eingerückt wäre. Denn entweder käme das *nach* einer leeren `main()`-Funktion oder *in* der `main()`-Funktion, was entweder ein Syntaxfehler oder nicht sinnvoll ist. Das ist auch etwas was Dir aufgefallen wäre, wenn Du das Programm während der Entwicklung schon einmal ausprobiert hättest.

`os.system()` sollte man nicht verwenden. Alle möglichen Low-Level-Arten externe Programme zu starten sind durch das `subprocess`-Modul abgedeckt. Und da bitte *nicht* ``shell=True`` beim Aufruf benutzen, denn dan handelt man sich die gleichen Probleme wie bei `os.system()` ein. Aus dem ``fbset``-Aufruf müsste man dann zwei separate Aufrufe machen.

Das `msvcrt`-Modul stellt Funktionen aus der Windows-Systembibliothek ``msvcrt.dll`` zur Verfügung. Das gibt es auf Deinem Raspberry Pi also nicht. Das würde so sowieso nicht funktionieren, denn ein Daemon ist ja nicht mehr mit dem Terminal verbunden aus dem er gestartet wurde, also kann man mit dem auch nicht mehr einfach so Tastatureingaben lesen.

Importe stehen übrigens üblicherweise oben im Modul, damit man leichter sieht wovon es abhängt, und man nicht das selbe Modul unnötigerweise mehrfach importiert.
entropie
User
Beiträge: 2
Registriert: Samstag 23. Februar 2013, 08:28

Hallo blackjack,

vielen Dank für deine Rückmeldung. Ich seh schon, ich glaub ich brauch da erstmal ein paar Grundlagen. Für mich wars schon neu, dass eine Programmiersprache auf Einrückungen achtet, kannte ich noch nicht. :)

Ich hab den Code oben nur aus Snippets zusammen gebastelt; deine Erläuterungen bringen aber schonmal Licht ins Dunkle.

Ich meld mich mal wieder wenn ich in den Grundlagen weiter bin :).

Gruß Olli
BlackJack

Hier ist Dein Programm mal in zumindest syntaktisch korrekt mit `daemon` und `subprocess` und ohne Tastenabfrage:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from subprocess import call
from time import sleep
from daemon import DaemonContext

TVSERVICE = '/opt/vc/bin/tvservice'


def fbset(depth):
    return call(['fbset', '-depth', str(depth)]) == 0


def switch_off():
    call([TVSERVICE, '-o'])


def switch_on():
    call([TVSERVICE, '-n', 'PAL 4:3'])
    if not fbset(8):
        fbset(16)
    call(['xrefresh'])


def main():
    with DaemonContext():
        while True:
            switch_off()
            # 
            # TODO Something to get a keypress from the user.
            # 
            switch_on()
            for count in reversed(xrange(1, 121)):
                # print count
                sleep(1)


if __name__ == '__main__':
    main()
Das ist von der Semantik her aber sicher noch nicht das was Du wolltest, denn ich vermute mal, dass am Anfang nicht einfach der Bildschirm ausgeschaltet werden soll und das auch nicht alle 120 Sekunden *bedingungslos* passieren soll. Würde mich als Anwender ziemlich nerven. :-) Ich denke mal eher, dass Du da so etwas wie einen Bildschirmschoner haben willst, wo der Countdown bei jeder Benutzerinteraktion wieder von vorne beginnt!?

Beim `fbset()` habe ich mal Deine Reihenfolge übernommen, die scheint mir aber auch falsch zu sein, denn ich würde beim durchprobieren ja die *höhere* Farbtiefe bevorzugen und nur auf 8 Bit zurückfallen wenn 16 nicht geht!?

Das ``print count`` habe ich auskommentiert, denn wo sollte das den landen nachdem der Prozess sich als Daemon vom Elternprozess abgenabelt hat?

Bleibt die Frage wie man Benutzerinteraktion erkennt. Da beim Anschalten des Bildschirms ``xrefresh`` ausgeführt wird, kann man von einem laufenden X ausgehen? Dann könnte man schauen ob es ein Modul gibt um von X Tastatur und Mausbewegungen „mitzulesen”. Alternativ könnte man versuchen über den Kernel an diese Informationen zu kommen.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Man könnte auch einfach per xset den eingebauten Bildschirmschoner (und sogar DPMS für richtiges Stromsparen) ansteuern. Aber das wäre vieeel zu leicht :mrgreen:
BlackJack

@webspider: Das geht nicht auf dem Raspberry Pi. Das ist recht spezielle Hardware die X (noch?) nicht „kennt” und deshalb per ``xset`` auch nicht steuern kann. So leicht geht das also nicht.

Mit dem ``tvservice -o`` wird das Display ausgeschaltet, also das Gerät nicht nur die Anzeige auf schwarz. Der Monitor/Fernseher geht damit in den Standby-Modus.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Sollte das wahr sein, dann verstehe ich nicht warum derartig viele Leute scharf auf diese Hardware sein sollten. Jedenfalls hab ich angenommen dass es geht, da ich immer wieder auf Threads gestoßen bin in denen gefragt wurde wie man den Standardbildschirmschoner deaktiviert und diese Versuche mal erfolgreich waren oder auch nicht. Einen Versuch ist es ja wert, irgendwie werden ja die Standardwerte beim Start von X gesetzt.
BlackJack

@webspider: Ich verstehe nicht warum *das* ein K.O.-Kriterium sein sollte so ein Gerät nicht haben zu wollen.

Einen normalen X-Bildschirmschoner kann man natürlich verwenden, das ist ja letztendlich auch nur ein Programm das etwas darstellt. Hier geht es aber darum dem Display per HDMI mitzuteilen, dass es sich *ausschalten* soll. Und dazu muss man über die GPU in dem Gerät gehen und das ist nun mal keine Grafikkarte die man in PCs findet, beziehungsweise bräuchte man dafür wie für normale Grafikkarten auch einen Treiber für X. Und den gibt es noch nicht. Die GPU wird vom Kernel als Framebuffer Device unterstützt und darauf setzt momentan der X-Server auf.

Die Leute sind scharf auf das Gerät weil es günstig ist, wenig Strom verbraucht, GPIOs hat, und Linux drauf läuft. Einsatzzwecke bei mir waren bisher XBMC, stromsparender Server, und „Weckerersatz” mit vierstelliger 7-Segment-Anzeige und Piezo-Summer per GPIO angeschlossen. Die XBMC-Version unterstützt die GPU direkt, also kein X, und die kann das Display auch richtig abschalten wenn man länger nichts macht. Der Server hat kein Display. Und beim „Weckerersatz” spielt DPMS-Unterstützung in X auch keine Rolle.
Antworten