Seite 1 von 7

Programm Kontrolle

Verfasst: Freitag 25. Mai 2012, 16:05
von Nobuddy
Hallo zusammen,
wie kann ich ein Python-Programm überprüfen, ob dieses evtl. schon gestartet ist?

Ich habe dazu mit Python, ein kleines Startprogramm erstellt, bei der die Überprüfung über laufende Programme noch fehlt.

Code: Alles auswählen

#!/usr/bin/python3
# -*- coding: utf-8 -*-

### Import Module
from __modul_base__ import *
from __modul_write__ import *
from __modul_files__ import *


from Tkinter import *
import tkSimpleDialog, tkMessageBox

### GUI ###
class DialogFenster(tkSimpleDialog.Dialog):
    ## so leitet man in Python aus einer Klasse ab!
    ## Aufruf: NamenDialog = DialogFenster(root)
    def body(self, master):  ## wird überschrieben
        self.titel = titel
        self.title(self.titel)

        self.auftraginfo = auftraginfo

        self.namen = Label(master, text=self.auftraginfo)
        self.namen.pack(side=LEFT)

    def apply(self):  ## wird überschrieben
        self.result = 1 ## alles ok!

### Start Auftragkontrolle
def start_auftragkontrolle(titel, auftraginfo):
    root = Tk()
    NamenDialog = DialogFenster(root)
    if NamenDialog.result <> None:
        try:
            from __modul_auftragkontrolle__ import auftragkontrolle
            auftragkontrolle()
            info = ('Das Programm für Lieferanten-Bestellungen wurde gestartet!')
            zielordner(export, auftrag, export_path)
        except:
            info = ('Das Programm für Lieferanten-Bestellungen läuft bereits!')
    else:
        info = ('Der Programmstart für Lieferanten-Bestellungen wurde abgebrochen!')

        tkMessageBox.showinfo('Info', info)

auftraginfo = ('Programm für Lieferanten-Bestellungen starten?')
titel = 'Lieferanten-Bestellungen'

start_auftragkontrolle(titel, auftraginfo)
Gibt es da eine einfache Möglichkeit, oder wird das sehr komplex?

Grüße Nobuddy

Re: Programm Kontrolle

Verfasst: Freitag 25. Mai 2012, 16:12
von deets
Hoer mal bitte *SOFORT* mit diesem __modul-*-Mist auf. Les dir das Tutorial ueber packages durch, und dann ist gut.

Und was das laufen von Programmen angeht - da gibt es die verschiedensten Moeglichkeiten, insbesondere Abhaengig vom Betriebssystem. Unter Linux nimmt man ueblicherweise ein PID-File, und prueft auf Existenz, und ob's gelockt ist.

Re: Programm Kontrolle

Verfasst: Freitag 25. Mai 2012, 16:52
von EyDu
Da gibt es noch einiges mehr zu verbessen:
- Entferne die *-Importe, damit müllst du dir nur den ganzen Namensraum zu, überschreibst ausversehen Namen und kennst deren Herkunft nicht mehr
- Deine Kommentare sind alle nichtssagend. Kommentare sollen zusätzliche Information liefern und nicht offensichtlichen Code beschreiben. Außerdem bietet Python Docstrings.
- Du hast einen bunten Sprachmix aus Englisch und Deutsch. Du solltest die für eine Sprache, im Idealfall natürlich Englisch, entscheiden
- Das Label musst du nicht an die Instanz binden, damit machst du später nichts mehr.
- "self.titel" ist auch überflüssig, der liegt in der Basisklasse.
- Schon die Idee deiner apply-Methode ist seltsam, da stimmen mehrere Sachen nicht: ein result-Attribut ist in Python total überflüssig, besonders wenn man wissen will, ob ein Aufruf funktioniert hat. Dazu gibt es return. Hinzu kommt, dass man in Python so keine Fehler verarbeitet. Dazu sollten Exceptions verwendet werden. Und natürlich solltest du nicht 1 und 0 für Wahrheitswerte verwenden, sondern True und False.
- <> ist schon seit Jahren veraltet, du solltest != verwenden. Aus welchen Tutorial auch immer <> gelernt hast, du solltest es sofort wegwerfen, da es hoffnungslos veraltet ist.
- Auf "None" solltest du auch nicht mit != testen, sondern mit "is None" oder "is not None".
- ein Import innerhalb einer Funktion ist nur selten notwendig. In diesem Fall ganz sicher nicht.
- Bei einem except solltest du unbedingt angeben, welche Fehler abgefangen werden. Ansonsten werden wirklich alle Fehler abgefangen, d.h. ggf. auch Programmierfehler. Zum Beispiel ein Name, den du ausversehen falsch geschrieben hast. Damit baust du dir nur Fehler ein, die du nie wieder oder nur sehr schwer finden wirst, da das Verhalten völlig unvorhersagbar ist.
- Die Klammern bei den ganzen Zuweisungen an "info" sind aller überflüssig und gehören dort nicht hin
- Schreibe keinen Code auf Modulebene, dann kannst du die Module nicht mehr importieren. Alles was auf Ebene des Modul steht sollte in eine main-Funktion und mittels

Code: Alles auswählen

if __name__ == "__main__":
    main()
aufgerufen werden.

Sebastian

Re: Programm Kontrolle

Verfasst: Freitag 25. Mai 2012, 21:07
von Hyperion
@EyDu: Die Hinweise zu "<>" und zum "__main__"-Hook haben wir Nobuddy schon gefühlte 100 Mal gegeben :mrgreen: Naja, stetes Wasser... ;-)

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 09:44
von Nobuddy
Ok, werde Eure Kritik annehmen und umsetzen.
Ich hoffe, Ihr gebt mir noch eine Chance, möchte kein Querulant sein.

Mein bunter Sprachmix aus Englisch und Deutsch, kommt daher daß ich 'so gut wie kein Englisch' beherrsche.

Als Erstes werde ich die '__modul_undsoweiter__.py' ändern.
'a_undsoweiter.py' oder 'b_undsoweiter.py' usw. dürfte, hoffe ich für Euch akzeptabel sein, oder?

Danach werde ich mich mit

Code: Alles auswählen

if __name__ == "__main__":
    main()
auseinandersetzen.

Ich hoffe, daß wenn ich noch Fragen dazu habe, Ihr mir helfen werdet!

Grüße Nobuddy

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 10:51
von Nobuddy
Ich habe das mal so umgesetzt

Code: Alles auswählen

#!/usr/bin/python3
# -*- coding: utf-8 -*-

### Import Module
from z_files import *
from Tkinter import *
import tkSimpleDialog, tkMessageBox

### GUI ###
class DialogFenster(tkSimpleDialog.Dialog):
    ## so leitet man in Python aus einer Klasse ab!
    ## Aufruf: NamenDialog = DialogFenster(root)
    def body(self, master):  ## wird überschrieben
        self.titel = titel
        self.title(self.titel)

        self.auftraginfo = auftraginfo

        self.namen = Label(master, text=self.auftraginfo)
        self.namen.pack(side=LEFT)

    def apply(self):  ## wird überschrieben
        return

### Start Auftragkontrolle
def start_auftragkontrolle(titel, auftraginfo):
    root = Tk()
    NamenDialog = DialogFenster(root)
    if NamenDialog.result <> None:
        try:
            from a_auftragkontrolle import auftragkontrolle
            auftragkontrolle()
            info = ('Das Programm für ' + titel + ' wurde gestartet!')
            zielordner(export, auftrag, export_path)
        except:
            info = ('Das Programm für ' + titel + ' läuft bereits!')
    else:
        info = ('Der Programmstart für ' + titel + ' wurde abgebrochen!')

        tkMessageBox.showinfo('Info', info)

auftraginfo = ('Programm für Lieferanten-Bestellungen starten?')
titel = 'Lieferanten-Bestellungen'

if __name__ == "__main__":
    start_auftragkontrolle(titel, auftraginfo)
Bin ich schon an dem näher dran, was Ihr beanstandet habt?

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 11:19
von EyDu
Nicht viel. Die meisten meiner Punkte hast du überhaupt nicht beachtet. Und die neuen Namen der Module sind genau so ungünstig wie die alten. Was sollen diese a_ und z_? Benenne das Modul einfach danach, was darin enthalten ist.

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 14:08
von Nobuddy
EyDu,
ich habe mir mit 'a_name.py oder b_name.py usw.' ein Prinzip für mich überlegt, mit dem eine bestimmte Wertigkeit verbunden ist. Z.B. 'a_name.py' ist für mich das Hauptprogramm und 'b_name.py' sind Unterprogramme. Zu 'z_name.py' gehören Informations- und Hilfsmodule.
Man sich darüber streiten über Sinn oder Unsinn, für mich aber hat es momentan eine bestimmte Ordnung, mit der ich gut arbeiten kann.

Ich habe nochmals die Punkte von Dir durchgearbeitet.
Alle konnte ich noch nicht umsetzen, da würde ich mich über Deine Hilfe freuen.

Aktueller Stand:

Code: Alles auswählen

#!/usr/bin/python3
# -*- coding: utf-8 -*-

### Import Module
from z_files import *
from Tkinter import *
import tkSimpleDialog, tkMessageBox

### GUI ###
class DialogFenster(tkSimpleDialog.Dialog):
    ## so leitet man in Python aus einer Klasse ab!
    ## Aufruf: NamenDialog = DialogFenster(root)
    def body(self, master):  ## wird überschrieben
        self.namen = Label(master, text=auftraginfo)
        self.namen.pack(side=LEFT)

    def apply(self):
        self.result = True
        return

### Start Auftragkontrolle
def start_auftragkontrolle(titel, auftraginfo):
    root = Tk()
    NamenDialog = DialogFenster(root)
    if NamenDialog.result is True:
        try:
            from a_auftragkontrolle import auftragkontrolle
            auftragkontrolle()
            info = 'Das Programm für ' + titel + ' wurde gestartet!'
        except:
            info = 'Das Programm für ' + titel + ' läuft bereits!'
    else:
        info = 'Der Programmstart für ' + titel + ' wurde abgebrochen!'

    tkMessageBox.showinfo('Info', info)

auftraginfo = 'Programm für Lieferanten-Bestellungen starten?'
titel = 'Lieferanten-Bestellungen'

if __name__ == "__main__":
    start_auftragkontrolle(titel, auftraginfo)
PS: Habe noch ein paar kleinere Änderungen vorgenommen!

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 14:58
von Hyperion
Nobuddy hat geschrieben:EyDu,
ich habe mir mit 'a_name.py oder b_name.py usw.' ein Prinzip für mich überlegt, mit dem eine bestimmte Wertigkeit verbunden ist. Z.B. 'a_name.py' ist für mich das Hauptprogramm und 'b_name.py' sind Unterprogramme. Zu 'z_name.py' gehören Informations- und Hilfsmodule.
Man sich darüber streiten über Sinn oder Unsinn, für mich aber hat es momentan eine bestimmte Ordnung, mit der ich gut arbeiten kann.
Hm... Du findest, dass `z_modul` sich sinnvoller liest, als z.B. `helper`? Du scheinst ja Skat oder Doppelkopf zu lieben, denn Du baust Dir hier vollkommen unnötig ein Mapping ein. Du musst nun als Leser des Codes wissen, wofür ein `a_` steht... am besten hinterlegst Du das dann im DocString des Moduls... :twisted: Spaß bei Seite, verstehst Du, worauf ich hinaus will? Das Spiel Uno ist deswegen so erfolgreich, da es gegenüber MauMau Symbole auf die Karten druckt, die man *ohne* ein Mapping im Kopf sofort verstehen und interpretieren kann.

Ein Modul namens `helpers` suggeriert beim Lesen sofort, was man da finden kann: Lauter kleine, unabhängige nützliche Helferlein im Kontext Deiner Gesamtanwendung. Deine Problem spezifischen Hauptkomponenten erwartet darin niemand. Diese würde man dann eher in einem Modul `auftrag` (oder besser `order`) vermuten.

Du schränkst Dich zudem ziemlich ein - das Alphabet hat ja nur 26 Buchstaben ;-)

Zudem ist die Diktion "Programm" und "Unterprogramm" irgend wie unpassend. In Python denkt man in Paketen und Modulen, auf deren Ebene dann in Klassen und Funktionen. Natürlich sollten sich diese *thematisch* aus dem Kontext ergeben, also Du sollst nicht in einem Modul lauter Klassen und im anderen lauter Funktionen haben; es geht dabei um inhaltlich zusammenhängende Dinge. Ich stelle mir vor, dass Du z.B. ein Modul haben könntest, in dem gängige Datenstrukturen für auftragsrelevante definiert sind; dazu kämen dann ggf. noch import- export-Funktionen aus diversen Formaten. Evtl. könnte man diese auch in separate Module packen, die auf spezielle Workflows spezialisiert sind... es gibt da durchaus verschiedene Sichtweisen.

Aber eines geht imho gar nicht, und da schließt sich der Kreis: *Künstliche* Namen wie Du sie Dir ausgedacht hast ;-)

So, nun zu Deinen Fragen bezüglich Deines Codes... ich habe mal Kommentare in den Code geschrieben und hoffe, das hilft Dir weiter:

Code: Alles auswählen

# wieso nicht '/usr/bin/env python3' ?
#!/usr/bin/python3
# Diese Encoding Angabe ist bei Python3 überflüssig, utf-8 hier Standard ist!
# -*- coding: utf-8 -*-

# Sinnloser Kommentar - das ist offensichtlich & üblich im Kopf eines Moduls ;-)
### Import Module
# Sternchen-Import solltest Du vermeiden! Importiere einfach das Modul oder 
# explizit Objekte aus dem Modul, also etwa `from foo import bar`.
from z_files import *
from Tkinter import *
import tkSimpleDialog, tkMessageBox

# imho auch ziemlich sinnfrei
### GUI ###
class DialogFenster(tkSimpleDialog.Dialog):
    # hier fehlt ein DocString:
    """
    Hier schreibst Du Deine Kommentare rein. Allerdings Infos, was dieses Ding
    tut und wofür man es verwenden kann,  nicht wie es das tut oder gar solche
    syntaktischen / semantischen Hilfen wie folgende...
    """
    # brauchst Du diese beiden Infos wirklich?
    ## so leitet man in Python aus einer Klasse ab!
    ## Aufruf: NamenDialog = DialogFenster(root)

                             # Keine Inline-Kommentare! Verwende eigene Zeilen!
    def body(self, master):  ## wird überschrieben
        # Wenn man das überschreiben soll, dann ist es durchaus üblich einfach
        # eine Exception zu werfen, imspeziellen einen `NotImplementedError`
        # Das steht auch in der Doku: 
        # http://docs.python.org/py3k/library/exceptions.html                  
        self.namen = Label(master, text=auftraginfo)
        self.namen.pack(side=LEFT)

# zwischen Klassen, Funktionen usw. solltest Du immer zwei Leerzeilen Platz 
# lassen, zwischen Methoden reicht eine Leerzeile.

# Auch dieser Kommentar ist zweifelhaft - der Name der Funktion sagt doch 
# eigentlich dasselbe aus?!
### Start Auftragkontrolle
def start_auftragkontrolle(titel, auftraginfo):
# hier fehlt wieder ein DocString, der die Funktion erklärt und dazu ggf. die
# Parameter! z.B. so:
    """
    Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen 
    Dialog, in dem dann XYZ passiert.

    :param titel: string mit dem Titel des Dialogfensters
    : param auftraginfo: string mit Informationen zum Auftrag
    """
    root = Tk()
    NamenDialog = DialogFenster(root)
    # wenn Du `root` nicht mehr brauchst, geht auch das:
    # NamenDialog = DialogFenster(Tk())
    if NamenDialog.result is not None:
        try:
            # wieso hier noch ein Import? 
            from a_auftragkontrolle import auftragkontrolle
            # das hier ist "magisch"... Du weisst danl der *-Importe nun nicht,
            # aus welchem Modul das stammt!
            auftragkontrolle()
            # Strings setzt man nicht mit `+` zusammen!
            info = 'Das Programm für ' + titel + ' wurde gestartet!'
            # sondern z.B. so:
            info = 'Das Programm für {} wurde gestartet!'.format(titel)
            # oder auch so:
            info = 'Das Programm für %s wurde gestartet!' % titel
            # woher stammen `auftrag` und `export_path`?
            zielordner(export, auftrag, export_path)
        # Hier sollst Du eine *konkrete* Exception abfangen! Ich weiß nicht,
        # welche, aber grundsätzlich geht das wie folgt:
        # except NotImplementedError:
        # Das wird auch hier beschrieben:
        # http://docs.python.org/py3k/tutorial/errors.html
        # (Ja, ich weiß Du hast Schwierigkeiten mit Englisch, aber da musst
        # Du einfach mal durch ;-) )
        except:
            # s.o.
            info = 'Das Programm für ' + titel + ' läuft bereits!'
    else:
        # s.o.
        info = 'Der Programmstart für ' + titel + ' wurde abgebrochen!'

        tkMessageBox.showinfo('Info', info)

# Modul globale Objekte benennt man idR. groß:
AUFTRAGINFO = '...'
# wozu sind die hier eigentlich gut? Du kannst sie auch direkt unten beim 
# Aufruf von `start_auftragkontrolle` fix setzen.
auftraginfo = 'Programm für Lieferanten-Bestellungen starten?'
titel = 'Lieferanten-Bestellungen'

if __name__ == "__main__":
    start_auftragkontrolle(titel, auftraginfo)

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 17:30
von Nobuddy
Hallo Hyperion,
Danke für Deine ausführliche Erklärung, dies hat mich ein kleines Stückchen weiter gebracht.

Ich habe dies mal so versucht umzusetzen:

Code: Alles auswählen

#!/usr/bin/python3
# -*- coding: utf-8 -*-

### Import Module
from Tkinter import *
import tkSimpleDialog, tkMessageBox


class DialogFenster(tkSimpleDialog.Dialog):
    '''Fenster, das aus den Dialogen "ok" und "cancel" besteht'''
    def body(self, master):
        self.title(TITEL)
        self.namen = Label(master, text=AUFTRAGINFO)
        self.namen.pack(side=LEFT)

    def apply(self):
        self.result = True
        return


def start_bestellorder(TITEL, AUFTRAGINFO):
    '''Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
    Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
    Parameter TITEL: string mit dem Titel des Dialogfensters
    Parameter AUFTRAGINFO: string mit Informationen zum Auftrag'''
    NamenDialog = DialogFenster(Tk())
    if NamenDialog.result is True:
        try:
            from bestellorder_lieferant import auftragkontrolle
            auftragkontrolle()
        except:
            info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % TITEL
    else:
        info = 'Der Programmstart für %s wurde abgebrochen!' % TITEL

        tkMessageBox.showinfo('Info', info)
        NamenDialog.destroy()

TITEL = 'Lieferanten-Bestellungen'
AUFTRAGINFO = 'Programm für %s starten?' % TITEL

if __name__ == "__main__":
    start_bestellorder(TITEL, AUFTRAGINFO)
Ich habe bemerkt, daß ich noch kein Python 3 nutze, da bei Ubuntu 2.7 noch Standard ist.
In Geany habe ich dann mal auf Python 3 umgestellt, da wurde aber gleich reklamiert, daß das Modul Tkinter nicht bekannt ist. Ich habe es dann mal mit tkinter versucht, was dann nicht mehr reklamiert wurde. Jedoch das Sternchen * scheint ein weiteres Problem darzustellen, wo dann eine Fehlermeldung kam.
Python 3 ist zwar bei mir installiert, aber wahrscheinlich fehlen da noch zugehörige weitere Python3-Pakete.
Da muß ich mich wohl mal drum kümmern.
Im Moment habe ich wieder auf 2.7 umgestellt.

Ich hoffe, daß es jetzt schon ein wenig besser aussieht. Bei noch vorhandenen Fehlern bitte ich nochmals um Info.
Das Sternchen bei 'from Tkinter import *' habe ich noch nicht weg bekommen. Habe noch nicht heraus gefunden, was statt dem Sternchen da hin gehört?

Was ich unbedingt da noch einbauen möchte, ist eine Programm-Kontrolle, um ein Mehrfachstarten zu vermeiden.
Da fehlt mir leider noch völlig der Ansatz.

Re: Programm Kontrolle

Verfasst: Samstag 26. Mai 2012, 18:19
von Hyperion
Wenn Du wieder auf Python2.7 umgestiegen bist, wieso postest Du hier Code mit einem Shebang auf Python3 zeigend? :K

Irgend wie arbeitest Du unpräzise...

Nun ja, ich versuche mal das ganze ein wenig zu ordnen...

- Ich würde DocStrings immer so schreiben:

Code: Alles auswählen

def foo():
    """bei kurzen Kommentaren in einer Zeile"""

def foo():
    """
    Bei längeren Kommentaren immer die Anführungszeichen in *einer* Zeile
    stehen lassen, ohne, dass da danach oder davor noch Text steht.
    """
- Die Notation der Parameter stammt aus dem Dokumentationstool Sphinx. Du solltest Dir diese Notation entsprechend angewöhnen und nicht Dein eigenes Süppchen kochen :-) Schau Dir das mal an, da gibt es auch spezielle Notationen für viele andere sinnvolle Angaben, etwa dem Autor uvm.

- Wozu brauchst Du dieses:

Code: Alles auswählen

TITEL = 'Lieferanten-Bestellungen'
AUFTRAGINFO = 'Programm für %s starten?' % TITEL
Es ist zwar gut, wenn man "magic numbers" im Code vermeiden will, aber Dein "main"-Hook ist so kurz - genau genommen rufst Du nur *eine* Funktion auf - dass ich die Parameter dort fix reinschreiben würde. Sie können sich ja auch nicht ändern, außer im Code! (Also man kann diese nicht durch Kommandozeilenparameter o.ä. überschreiben)
Ich würde dafür nicht extra zwei globale "Konstanten" anlegen. Zudem ist es eigentlich gängige Konvention, solche "Konstanten" im Kopf eines Moduls anzulegen, also nach den Imports.

- Die Funktionssignatur `def start_bestellorder(TITEL, AUFTRAGINFO)` ist laut PEP8 nun verschlimmberssert. Wieso schreibst Du `TITEL` nun groß? Das hast Du doch davor auch nicht. Ja, Du hast nun eine globale Variable namens `TITEL` (die ich ja bereits in Frage stellte), aber diese hat doch *nichts* mit der Signatur zu tun!

- Wieso machst Du Dich bei Deinem Dialogfenster nun wieder von den beiden gloablen Variablen abhängig? Übergib diese Werte doch einer `__init__`-Methode!

Code: Alles auswählen

class DialogFenster(tkSimpleDialog.Dialog):
    '''Fenster, das aus den Dialogen "ok" und "cancel" besteht'''

    def __init__(self, title, auftraginfo):
        self.title = title
        self.auftraginfo = auftraginfo

    def body(self, master):
        self.namen = Label(master, text=self.auftraginfo)
        # usw.
Btw: Muss man nicht noch die `__init__`-Methode der Elternklasse `tkSimpleDialog.Dialog` aufrufen?

- "Ok" und "Cancel" sind doch sicherlich Schaltflächen *im* Dialog und nicht der Dialog selber? Dahingehend ist der DocString in der Klasse `DialogFenster` irreführend. Zudem sagt das wenig über dessen "Rolle" bzw. dessen Funktionalität aus... (Ja, ich weiß, gute Kommentare sind schwierig, genau wie das Finden guter Namen!)

- Du hast wieder nur ein allgemeines `except` *ohne*, dass Du eine *spezielle* Ausnahme auffängst! Ist das Schlamperei, Absicht oder Unvermögen? Bis auf ersteres hättest Du das ja zumindest mal ansprechen können, wenn man sich selber schon die Mühe macht, Deinen Code *präzise* zu kommentieren ;-) Ich schrieb das ja schon im Posting zuvor: Ändere das!

- Zu diesem `apply` und der Änderung damit verbundenen Änderung im `if...else` äußere ich mich mal nicht, da ich den Sinn dahinter nicht sehe.

Re: Programm Kontrolle

Verfasst: Sonntag 27. Mai 2012, 13:47
von Nobuddy
Hallo Hyperion,
ich habe alles das versucht umzusetzen, was mir im Moment möglich war, so daß alles noch funktioniert.
Es ist weder Schlamperei, Absicht oder Unvermögen, daß ich das mit `except` noch nicht umgesetzt habe. Ich habe diesen Punkt nicht vergessen und werde es auch noch nachholen. Im Moment gibt es entweder True und es funktioniert, oder None und es gibt einen Fehler und funktioniert nicht. Wie gesagt ich werde mich um diesen Punkt etwas später darum kümmern.

Das mit der '__init__' Methode habe ich versucht umzusetzen, bin da aber noch nicht weiter gekommen, so daß ich vorerst ohne dies umgesezt habe.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

### Import Module
from Tkinter import *
import tkSimpleDialog, tkMessageBox


TITEL = 'Lieferanten-Bestellungen'
AUFTRAGINFO = 'Programm für %s starten?' % TITEL


class DialogFenster(tkSimpleDialog.Dialog):
    def foo():
        '''
        Das Dialog-Fenster besteht aus den Dialogen "ok" und "cancel".
        Die weitere Beschreibung über die Rolle und Funktionalität,
        muß noch erarbeitet werden.
        '''

    def body(self, master):
        self.title(TITEL)
        self.namen = Label(master, text=AUFTRAGINFO)
        self.namen.pack(side=LEFT)

    def apply(self):
        self.result = True
        return


def start_bestellorder(title, auftraginfo):
    def foo():
        '''
        Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
        Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
        :param TITEL: string mit dem Titel des Dialogfensters
        :param AUFTRAGINFO: string mit Informationen zum Auftrag
        '''

    NamenDialog = DialogFenster(Tk())
    if NamenDialog.result is True:
        try:
            from bestellorder_lieferant import auftragkontrolle
            auftragkontrolle()
        except:
            info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % title
    else:
        info = 'Der Programmstart für %s wurde abgebrochen!' % title

    tkMessageBox.showinfo('Info', info)
    Tk().destroy()

if __name__ == "__main__":
    start_bestellorder(TITEL, AUFTRAGINFO)

Re: Programm Kontrolle

Verfasst: Sonntag 27. Mai 2012, 14:32
von Hyperion
Nobuddy hat geschrieben: Es ist weder Schlamperei, Absicht oder Unvermögen, daß ich das mit `except` noch nicht umgesetzt habe. Ich habe diesen Punkt nicht vergessen und werde es auch noch nachholen.
Dann solltest Du solche Sachen aber aus Höflichkeit erwähnen, denn ansonsten wundert man sich, *wieso* Dinge, die man Dir erklärt hat, *wieder* nicht umgesetzt sind. Ich kann dann nicht wissen, ob Du das überlesen oder nicht verstanden hast. Ersteres wäre "Schlamperei", bei letzterem wäre es *die* Chance, das weiter zu verfolgen, um es dann endlich mal zu verstehen!

Ich kapiere ehrlich gesagt nicht, wieso Du hier nicht die Exception hinschreiben kannst:

Code: Alles auswählen

        try:
            from bestellorder_lieferant import auftragkontrolle
            auftragkontrolle()
        except HIER_DIE_EXCEPTION_HINSCHREIBEN:
            info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % title
Was gibt es daran zu "kapieren"? Wenn Du gar nicht weißt, *welche* Exception hier auftreten kann, dann solltest Du das gesamte `try...except` einfach löschen ;-) (Und wenn dann eine Exception auftritt, siehst Du ja, welche es war!)

Ich versuche das mal zu verdeutlichen.

Nehmen wir an, ich habe ein kleines Programm, welches sich bereits vorhandene Daten aus einer Datei einlesen soll; ich verwende hier JSON. Das könnte ich so umsetzen:

Code: Alles auswählen

In [3]: import json

In [4]: def load(filename):
   ...:     with open(filename) as f:
   ...:         return json.load(f)
   ...:     
Wenn ich das nun mal aufrufe, passiert dieses:

Code: Alles auswählen

In [6]: load("data.json")
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
/home/nelson/<ipython-input-6-c90ff8e6ed4c> in <module>()
----> 1 load("data.json")

/home/nelson/<ipython-input-4-9a07b178c4f3> in load(filename)
      1 def load(filename):
----> 2     with open(filename) as f:
      3         return json.load(f)
      4 

IOError: [Errno 2] No such file or directory: 'data.json'
Aha. Logisch, eine solche Datei gibt es ja noch gar nicht, wenn ich noch keine "Datenbank" angelegt habe. Wie kann ich damit umgehen? Ich kann einfach diesen Fehler abfangen (= behandeln) und im Falle dieses Problems einfach eine *leere* Datenstruktur zurück geben. Ich entscheide mich für ein leeres Dictionary, da ich diesen Typen als Rückgabewert erwarte.

Code: Alles auswählen

def load(filename):
    try:
        with open(filename) as f:
            return json.load(f)
    # Hier fange ich *nur* und ausschließlich diese *eine* Exception ab!
    except IOError:
        # Meine "Behandlung" besteht darin, dem Aufrufer einfach eine leere,
        # aber von ihm erwartete Datenstruktur zurück zu gebe.
        return dict()
Schaun mer mal:

Code: Alles auswählen

In [8]: data = load("data.json")

In [9]: data
Out[9]: {}
Aha! Wunderbar, genau das wollte ich doch haben. Die Exception trat auf und ich kann dennoch weiter machen in meinem Programm und bspw. die ersten Datensätze anlegen.

Ich fange nur genau *den* Fehler(typen) ab, der mir oben aufgefallen ist! Dieses ist essenziell wichtig: Ich kann als Entwickler nur auf solche Fehler reagieren, die mir *bekannt* sind! Evtl. gibt es in `json.load` auch noch Passagen, die eine Exception werfen - aber auf diesen Fall kann ich ja noch nicht reagieren! (Im übrigen gibt es die; ich kann in der Doku nachlesen, dass es da zu `ValueError`, `OverflowError` oder auch `TypeError` kommen kann)

Diese Fehler aus dem JSON-Parser sollte ich nicht so behandeln! Denn dann ist ja die bereits vorhandene Datenbank "defekt". Ließe ich das einfach genau so, so würde sich der Benutzer wundern, wieso seine DB "leer" ist! Sinnvoller wäre es in diesem Falle, dem Benutzer mitzuteilen, dass seine JSON-Datei defekt ist und die Stelle zu nennen, so dass er dieses manuell fixen kann.

Ich muss mir immer überlegen, wie ich im Fehlerfalle reagieren will!

Nun überlege Dir mal, was passiert, wenn man *alle* Fehler pauschal abfängt - und genau das machst Du im Moment, wenn Du keinen Typen angibst.

Du "siehst" dann ja nicht, welcher potenziell andere Fehler, als der von Dir erwartete, aufgetreten ist. Insofern behandelst Du dann ggf. Fehler falsch. Das kann im weiteren Verlauf zu Problemen in Deinem Programm führen oder aber zu Debugging-Schwierigkeiten.

Also: *Immer* den Typen angeben, den Du behandeln willst. (Man kann auch mehrere Typen in einem `except`-Ast angeben; lies Dir dazu mal die Doku oder das offizielle Tutorial in dem Abschnitt durch)
Nobuddy hat geschrieben: Das mit der '__init__' Methode habe ich versucht umzusetzen, bin da aber noch nicht weiter gekommen, so daß ich vorerst ohne dies umgesezt
Hu? Ich habe die doch *exakt* so hingeschrieben, dass es laufen sollte!? Was gibt es da noch für "Probleme", so dass Du dieses einfach weggelassen hast?

Und auf die Problematik mit den globalen "Konstanten" und die Nachfrage bezüglich der "OK" und "CANCEL"-Buttons bist Du auch nicht eingegangen! (s.o. mein erster Absatz über "Höflichkeit" ;-) )

Re: Programm Kontrolle

Verfasst: Sonntag 27. Mai 2012, 19:12
von Nobuddy
Hallo Hyperion,
unhöflich möchte und wollte ich auch nicht erscheinen, bitte verzeih, wenn dies den Anschein gehabt hat.

Danke für Deine ausführliche Erläuterung zu 'except', ich habe jetzt die Wichtigkeit daran verstanden.
Das mit 'except' ist eigentlich nicht das Problem gewesen, auch wenn mir die Fehlertypen noch nicht geläufig sind.
In meinem Fall verwende ich den 'ImportError', der sich dann meldet, wenn das Modul nicht geladen werden kann.

Bei der '__init__' Methode, bekomme ich folgende Meldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "start_bestellorder.py", line 57, in <module>
    start_bestellorder('Lieferanten-Bestellungen', 'Programm für Lieferanten-Bestellungen starten?')
  File "start_bestellorder.py", line 42, in start_bestellorder
    NamenDialog = DialogFenster(Tk())
TypeError: __init__() takes exactly 3 arguments (2 given)
Ich poste hier gleich noch dass Konstrukt dazu, daß Du siehst wie ich die '__init__' Methode umgesetzt habe.

Code: Alles auswählen

class DialogFenster(tkSimpleDialog.Dialog):
    def foo():
        '''
        Das Dialog-Fenster besteht aus den Dialogen "ok" und "cancel".
        Die weitere Beschreibung über die Rolle und Funktionalität,
        muß noch erarbeitet werden.
        '''

    def __init__(self, title, auftraginfo):
        self.title = title
        self.auftraginfo = auftraginfo

    def body(self, master):
        self.namen = Label(master, text=self.auftraginfo)

    def apply(self):
        self.result = True
        return


def start_bestellorder(title, auftraginfo):
    def foo():
        '''
        Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
        Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
        :param TITEL: string mit dem Titel des Dialogfensters
        :param AUFTRAGINFO: string mit Informationen zum Auftrag
        '''

    NamenDialog = DialogFenster(Tk())
    if NamenDialog.result is True:
        try:
            from bestellorder_lieferant import auftragkontrolle
            auftragkontrolle()
        except ImportError:
            info = '''Das Programm für %s konnte nicht gestartet werden, da das 
Paket "bestellorder_lieferant" nicht existiert!''' % title
    else:
        info = 'Der Programmstart für %s wurde abgebrochen!' % title

    tkMessageBox.showinfo('Info', info)
    Tk().destroy()

if __name__ == "__main__":
    start_bestellorder('Lieferanten-Bestellungen', 'Programm für Lieferanten-Bestellungen starten?')
Das mit den Konstanten, habe ich jetzt so wie Du geschrieben hast, die Parameter in den Kopf der Funktion hinein geschrieben.
Zu OK und CANCEL, wie Du schon angedeutet hast '(Ja, ich weiß, gute Kommentare sind schwierig, genau wie das Finden guter Namen!)' ... da fehlt mir noch der richtige Kommentar. :wink:

Re: Programm Kontrolle

Verfasst: Sonntag 27. Mai 2012, 20:37
von Hyperion
Du musst dringend noch einiges über Klassen nachlesen ;-) `__init__` wird immer beim Anlegen eines Objektes aufgerufen. Daher musst Du natürlich die entsprechend verlangten Parameter beim Anlegen auch angeben - ganz analog, wie Du das bei Funktionen oder Methoden auch machst:

Code: Alles auswählen

NamenDialog = DialogFenster(Tk(), title, auftraginfo)
Der erste Parameter muss wohl noch zum `__init__` hinzugefügt werden - sofern Du ihn brauchst?! Eigentlich ist das nur sinnvoll, wenn Du den Konstruktor der Elternklasse auch aufrufst:

Code: Alles auswählen

    def __init__(self, tk_obj, title, auftraginfo):
        tkSimpleDialog.Dialog.__init__(tk_obj)
        self.title = title
        self.auftraginfo = auftraginfo
Ich bin aber kein Tk-Kenner, von daher ist das nur "geraten" in Analogie zu Deinem bisherigen Code und Vorgehensweisen von anderen Toolkits.

Re: Programm Kontrolle

Verfasst: Montag 28. Mai 2012, 15:12
von Nobuddy
Hallo Hyperion,
habe Deinen Vorschlag versucht umzusetzen, dabei erhalte ich dann folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "start_bestellorder.py", line 60, in <module>
    start_bestellorder('Lieferanten-Bestellungen', 'Programm für Lieferanten-Bestellungen starten?')
  File "start_bestellorder.py", line 45, in start_bestellorder
    NamenDialog = DialogFenster(Tk(), title, auftraginfo)
  File "start_bestellorder.py", line 22, in __init__
    tkSimpleDialog.Dialog.__init__(tk_obj)
TypeError: unbound method __init__() must be called with Dialog instance as first argument (got Tk instance instead)
Ich habe dann versucht statt

Code: Alles auswählen

tkSimpleDialog.Dialog.__init__(tk_obj)
dies

Code: Alles auswählen

tkSimpleDialog.Dialog(tk_obj)
zu verwenden. Zumindest kommt da keine Fehlermeldung. Ob die richtig ist ... ?

Es öffnet sich ein leeres Fenster, ohne jede Info. Egal ob ich ok oder cancel betätige, kommt dann die Meldung

Code: Alles auswählen

Traceback (most recent call last):
  File "start_bestellorder.py", line 60, in <module>
    start_bestellorder('Lieferanten-Bestellungen', 'Programm für Lieferanten-Bestellungen starten?')
  File "start_bestellorder.py", line 46, in start_bestellorder
    if NamenDialog.result != None:
AttributeError: DialogFenster instance has no attribute 'result'
Wenn ich das richtig interpretiere, wird das

Code: Alles auswählen

    def body(self, master):
        self.title(self.title)
        self.namen = Label(master, text=self.auftraginfo)
        self.namen.pack(side=LEFT)

    def apply(self):
        self.result = True
        return
völlig übergangen.

Re: Programm Kontrolle

Verfasst: Montag 28. Mai 2012, 16:22
von pillmuncher
@Nobuddy:
Bild
Hier klicken.

Re: Programm Kontrolle

Verfasst: Dienstag 29. Mai 2012, 15:26
von Nobuddy
Das eigentliche Thema lautet ja 'Programm Kontrolle'.
Da ich momentan das mit der '__init__' Lösung nicht hin bekomme, belasse ich es vorerst bei diesem Konstrukt, da alle Funktionen laufen.

Code: Alles auswählen

TITEL = 'Lieferanten-Bestellungen'
AUFTRAGINFO = 'Programm für %s starten?' % TITEL


class DialogFenster(tkSimpleDialog.Dialog):
    def foo():
        '''
        Das Dialog-Fenster besteht aus den Dialogen "ok" und "cancel".
        Die weitere Beschreibung über die Rolle und Funktionalität,
        muß noch erarbeitet werden.
        '''

    def body(self, master):
        self.title(TITEL)
        self.namen = Label(master, text=AUFTRAGINFO)
        self.namen.pack(side=LEFT)

    def apply(self):
        self.result = True
        return


def start_bestellorder(title, auftraginfo):
    def foo():
        '''
        Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
        Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
        :param TITEL: string mit dem Titel des Dialogfensters
        :param AUFTRAGINFO: string mit Informationen zum Auftrag
        '''

    NamenDialog = DialogFenster(Tk())
    if NamenDialog.result == True:
        try:
            from bestellorder_lieferant import auftragkontrolle
            auftragkontrolle()
        except ImportError:
            info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % title
    else:
        info = 'Der Programmstart für %s wurde abgebrochen!' % title

    tkMessageBox.showinfo('Info', info)
    Tk().destroy()

if __name__ == "__main__":
    start_bestellorder(TITEL, AUFTRAGINFO)
Zum eigentlichen Thema.
Ich suche nach einer Möglichkeit, mit der ich feststellen kann ob das Paket 'bestellorder_lieferant.py' schon läuft, um ein doppeltes Sarten dieses Paketes zu vermeiden.
Am Anfang des Threades wurde das Stichwort PID eingebracht.
Wenn ich richtig gelesen habe, gibt PID eine Nummer des gestarteten Prozesses wieder.
Nun bin ich mir nicht sicher ob ich mit PID so zu meinem Ziel komme, da mal vorausgesetzt das Paket läuft schon mit der PID 4711 und ich starte das gleiche Paket nochmals, erhalte ich ja eine neue PID z.B. 5413, die ja mit der anderen PID nicht identisch ist und ich so auch nicht feststellen kann, daß das Paket schon läuft.

Liege ich da falsch?
Bitte gebt mir da etwas Input.

Grüße Nobuddy

Re: Programm Kontrolle

Verfasst: Dienstag 29. Mai 2012, 16:02
von Hyperion
Nee, das hast Du schon verstanden! Es geht auch eher darum, dass Du an eine wohldefinierte Stelle in Deinem Verzeichnisbaum eine Datei anlegst, in der die Prozessnummer des laufenden Prozesses eingetragen wird.

Du musst zu Beginn Deines Programmes prüfen, ob eine solche Datei bereits existiert. Wenn ja, dann brichst Du das Programm ab - netter Weise mit dem Hinweis auf das bereits laufende Programm; da kann man z.B. die PID ausgeben! Wenn nein, dann liest Du die PID aus und speicherst sie eben an besagte immer identische Stelle.

Mal in Python-Pseudocode:

Code: Alles auswählen

main():
    if exist(pidpath):
        print "Programm läuft schon mit PID:", pidpath.read()
        sys.exit(1)
    else:
        pidpath.write(get_pid))

    # Dein eigentlicher Code

    remove(pidpath)
Übliche Stellen auf POSIX-Systemen sind wohl "/tmp" oder "/var/run", wobei ich letzteres für einen simplen User-Prozess nicht nutzen würde...

Hier ist jedoch ein Haken dabei: Du musst *irgend wie* sicher stellen, dass die Datei vor dem Beenden auf jeden Fall geschlossen wird! Das gilt natürlich vor allem für einen Fehlerfall...

Re: Programm Kontrolle

Verfasst: Dienstag 29. Mai 2012, 16:04
von deets
@Hyperion

Darum habe ich auch noch zusaetzlich von locking gesprochen - wenn der zuerst gestartete Prozess das Ding lockt, dann koennen nachfolgende das feststellen, und entsprechend reagieren. Dadurch muss man nicht (sollte natuerlich trotzdem, aber wenn's mal nicht klappt...) aufraeumen.