Anzeige von Meldungen bei laufenden Prozess

Fragen zu Tkinter.
Antworten
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo,

ich habe ein etwas umfangreiches Programm und möchte in einem Textfeld verschiedene Meldungen (z.B. welche Datei gerade verarbeitet wird) während des Prozesses ausgeben. Dazu verwende ich immer self.widget.insert('end','text'). Leider werden die Meldungen erst nach Beendigung des Prozesses angezeigt.
Gibt es eine Möglichkeit, die Meldungen während des Prozesses anzuzeigen?

Hier im Forum habe ich mal einen Thread gelesen, wie man die Python-Fehlermeldungen in einem Widget anzeigen kann. Leider finde ich den Thread nicht mehr. Hat jemand das Thema des Threads noch in Erinnerung?

Stephan
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Ich hab den Thread leider nicht mehr in Erinnerung, aber mach nach dem insert einfach root.update.

Gruß, mawe
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo mawe,

das root.update() ist genau was ich gesucht habe. Manchmal sieht man einfach den Wald vor lauter Bäumen nicht :D

Danke
Stephan
1-1-1

Ich hatte das gleiche Problem und bin hier im Forum fündig geworden. Leider war die vorgeschlagene Lösung auch nicht so erfolgreich. Nun blieb mir nur noch die Handarbeit.
Das Ergebnis ist nun ein kleines Progrämmchen, was die nötigen(!) Funktionen realisieren sollte. Trotzdem ist der Quelltext zu umfangreich, um ihn hier reinzustellen. Quelltext und Beschreibung mit Kommentaren zum Quelltext kannst Du hier abholen:

http://www.boe-se.org/python/pypro/protokoll.py
http://www.boe-se.org/python/pypro/protokoll.txt

Den Quelltext stelle ich zur freien Verfügung, würde mich aber immer über Anregungen und Erweiterungen freuen (GPL-Erklärung wäre umfangreicher als das eigentliche Programm).
Auf Wunsch kann ich auch verbesserte Varianten auf meiner Website parken!

email an "pypro @ boe - se . org" genügt!

Gruß Michael
1-1-1
User
Beiträge: 12
Registriert: Freitag 9. September 2005, 08:42
Wohnort: München

Wegen Anfragen (und auf mehrfachen Wunsch einer einzelnen Dame) stelle ich die Quelltexte und Beschreibung hier in das Forum!

Das "Progrämmchen":

Code: Alles auswählen

# Beschreibung und Kommentare siehe Datei "protokoll.txt"

def Protokoll(puffer,t): #10

    warteschreibe=600 #11
    hoch=33
    breit=65
    
    def schreibe(): #12
        while not puffer.empty():
            h=puffer.get()
            if h == None: #13
                text.see(END)
                return
            if h != '': text.insert(END,h) #14
        log.tk.createtimerhandler(warteschreibe, schreibe) #15
    def ein(): #16
        datei=tkFileDialog.askopenfile()
        z=None
        if datei: z=datei.read()
        return z
    def oeffnen():
        z=ein()
        if z:
            text.delete(1.0,END)
            text.insert(1.0,z)
    def einfuegen():
        text.insert(INSERT,ein())
    def speichern():
        datei=tkFileDialog.asksaveasfile()
        if datei: datei.write(text.get(1.0,END))
    def leeren():
        text.delete(1.0,END)
    def kopieren():
        text.clipboard_clear()
        text.clipboard_append(text.get("0.0", END))
    def ausis(event=None): #17
        log.destroy(); log.quit() #18
        puffer.put(None) #19
    def hilfe():
        text.delete(1.0,END)
        text.insert(1.0,"""               K u r z e  B e s c h r e i b u n g
               ----------------------------------
Dieses "Progr\xe4mmchen" sollte wirklich nur zur Anzeige und Bear-
beitung von Protokollen genutzt werden. Zum Editieren gibt es
wirklich besseres! Au\xdferdem sind bei Ver\xe4nderungen an DOS/
Windows-Dateien Probleme zu erwarten!

Das Protokoll kann f\xfcr sp\xe4tere Verwendung auch ein wenig bear-
beitet und gespeichert werden. Alle Men\xfcs werden sofort ausge-
f\xfchrt(ohne weitere Sicherheitsabfragen)! Weitere Untermen\xfcs gibt
es nicht und sind f\xfcr diesen Zweck auch nicht erforderlich.

<Protokoll \xf6ffnen>
    Ein Protokoll \xf6ffnen (beliebige Texdateien ohne Umwandlung)
    Protokollfenster wird ohne R\xfcckfrage gel\xf6scht!

<Fenster Leeren>
Protokollfenster wird ohne R\xfcckfrage gel\xf6scht

<Alles Kopieren>
Gesamten Fensterinhalt in die Zwischenablage kopieren

<Textdatei Einf\xfcgen>
Textdatei wird an der aktuellen Cursorposition eingef\xfcgt

<Fenster Speichern>
Das gesamte Protokollfenster als Textdatei speichern

<Hilfe>
    Hier befinden wir uns gerade

<Quit>
    "Und Tsch\xfc\xdf!" """)

# Hauptroutine    
    log=Tk()
    log.title(t)
    yscroll=Scrollbar(log)
    yscroll.pack(side=RIGHT,fill=Y)
    xscroll=Scrollbar(log,orient=HORIZONTAL)
    xscroll.pack(side=BOTTOM,fill=X)
    text=Text(log,yscrollcommand=yscroll.set,xscrollcommand=xscroll.set,\
              font=('Courier',10),width=breit,height=hoch,padx=5,wrap='none')
    yscroll.config(command=text.yview)
    xscroll.config(command=text.xview)
    text.pack(side=LEFT, fill=BOTH, expand=1)
    menue=Menu(log)
    menue.add_command(label='Protokoll \xf6ffnen',command=oeffnen,underline=0)
    menue.add_command(label='Fenster Leeren',command=leeren,underline=8)
    menue.add_command(label='Alles Kopieren',command=kopieren,underline=6)
    menue.add_command(label='Textdatei Einf\xfcgen',command=einfuegen,underline=10)
    menue.add_command(label='Fenster Speichern',command=speichern,underline=8)
    menue.add_command(label='Hilfe',command=hilfe,underline=0)
    menue.add_command(label='Quit',command=ausis,underline=0)
    log.config(menu=menue)
    schreibe()
    log.mainloop()

class Log: #20
       
    def __init__(self,name='Protokoll - Fenster'): #21
        self.laengequeue=1024 #22
        self.warteschlusz=2 #23
        self.puffer=Queue(self.laengequeue)
        thread.start_new_thread(Protokoll,(self.puffer,name)) #24
    def write(self,s): #25
        self.puffer.put(s)
    def close(self):
        self.puffer.put(None) #26
        while not self.puffer.empty(): #27
            sleep(self.warteschlusz)
        while self.puffer.empty(): #28
            sleep(self.warteschlusz)
Beschreibung und Kommentare:
  • Achtung!

    Wichtige Hinweise!

    Diese "Progrämmchen" stelle ich zur freien Verfügung; Ihr dürft es auch "ausschlachten". Ganz unten im Text findet Ihr einen Link zu einem Beispiel für Windoofs-Nutzer.

    ===========================================================================

    K u r z e B e s c h r e i b u n g
    ----------------------------------
    Dieses "Progrämmchen" sollte wirklich nur zur Anzeige und Bearbeitung von Protokollen genutzt werden. Zum Editieren gibt es wirklich besseres! Außerdem sind bei Veränderungen an DOS/ Windows-Dateien Probleme zu erwarten!

    Das Protokoll kann für spätere Verwendung auch ein wenig bearbeitet und gespeichert werden. Alle Menüs werden sofort ausgeführt (ohne weitere Sicherheitsabfragen)! Weitere Untermenüs gibt es nicht und sind für diesen Zweck auch nicht erforderlich.

    <Protokoll öffnen>
    Ein Protokoll öffnen (beliebige Texdateien ohne Umwandlung) Protokollfenster wird ohne Rückfrage gelöscht!

    <Fenster Leeren>
    Protokollfenster wird ohne Rückfrage gelöscht

    <Alles Kopieren>
    Gesamten Fensterinhalt in die Zwischenablage kopieren

    <Textdatei Einfügen>
    Textdatei wird an der aktuellen Cursorposition eingefügt

    <Fenster Speichern>
    Das gesamte Protokollfenster als Textdatei speichern

    <Hilfe>
    Hier befinden wir uns gerade

    <Quit>
    "Und Tschüß!"


    Benötigte Standard- und Third-Party-Module:
    -----------------------------------------

    import sys
    from Tkinter import *
    import tkFileDialog
    import thread
    from Queue import *


    Benutzung der Routine:
    ---------------------

    # Ein mögliches Beispiel
    # Protokollfenster öffnen
    execfile('protokoll.py')
    sys.stdout=sys.stderr=Log('Mein Protokollfenster')

    Als Parameter kann der Name für das Protokollfenster angegeben werden. Ab diesen Moment werden für die Standardausgaben die write-Methoden der Klasse Log benutzt.

    # Protokollfenster schließen
    print 'Ich habe fertig!'
    sys.stdout.close()

    Sollten, wie oben, beide oder mehr Ausgaben in einer Instanz zusammengefaßt sein, bitte nur einmal die close-Methode aufrufen! Die Standardausgaben werden nicht(!) automatisch wiederhergestellt.

    Zielstellung:
    ------------

    Schaffung einer einfachen(!) Protokoll-Routine, die plattformunabhängig eingesetzt werden kann. Insbesondere sollte diese auch unter (manchmal) schwächelnden Windoofs-Varianten funktionieren! Unter Windows sollte auch der Start des Vater-Prozesses ohne Konsole (als .PYW) und die nachträgliche Betrachtung und Verwendung des Protokolles möglich sein. Zu diesem Zweck ist die Fortsetzung des Vater-Prozesses bis zum Schließen des Protokollfensters durch den Nutzer zu verzögern.

    Lösungsansatz:
    -------------

    Die notwendige Routine für die Benutzung und Bedienung des Protokollfensters wurde als unabhängige Funktion programmiert, um den wiederholten Start und ggf. das Löschen der Routine durch den Vater-Prozess zu ermöglichen.

    Der Zugang zu dieser Routine erfolgt durch eine Klasse (Log) mit den notwendigen Methoden write und close, um
    a) die weitere Verwendung der Anweisung print zu ermöglichen
    b) die Umleitung der Fehlermeldungen des Interpreters sicherzustellen
    c) die close-Methode synchronisiert Vater-Prozess und Protokoll

    Die Methode __init__ der Klasse Log richtet eine Queue ein und startet die Routine als Thread. Diese wird dann über die Queue von der Methode write versorgt.

    Die close-Methode sendet über die Queue den Wert 'None' an die Routine. Daraufhin stellt die Routine die Abfrage der Queue ein und sendet erst nach Schließen des Protokollfensters durch den Nutzer den Wert 'None' an die Queue. Bis zu diesem Ereignis verbleibt die close-Methode in einer Warteschleife.

    Das Protokollfenster verfügt über ein einfaches Menü, wie es für die Betrachtung, Bearbeitung und Speicherung einer Protokolldatei nötig ist. Während der ganzen Lebenszeit des Protokollfensters kann dieses verschoben und in der Größe geändert sowie alle Menüpunkte können (sehr konsequent!) benutzt werden! Wenn der Vater-Prozess das Protokollfenster schließen will, wird dies verhindert, bis der Nutzer das Fenster schließt. Erst anschließend wird der Vater-Prozeß fortgesetzt.


    Kommentare zum Quelltext:
    ------------------------
    (Die Kommentare wurden aus dem Quelltext ausgelagert, um diesen kompakt zu halten!)

    #10 Routine Protokollfenster, Parameter sind die Queue und der Titel.
    #11 Die Standardwerte für die Routine
    ___600=Wartezeit der Funktion 'schreibe' bis die Queue wieder abgefragt wird in Millisekunden.
    ___33=Die Höhe des Fensters in Zeilen
    ___65=Die Breite des Fensters in Zeichen
    #12 Diese Funktion übernimmt von der Queue die Werte
    #13 Wenn die close-Methode das Signal zum Beenden gesendet hat ('None'), wird die Fensteransicht auf den letzten Eintrag eingestellt und die Funktion gleich verlassen. Damit entfällt auch ab diesem Moment die weitere zyklische Abfrage der Queue.
    #14 Der Wert wird an das Textfenster übergeben
    #15 Die Funktion 'schreibe' wird wieder in Wartestellung versetzt
    #16 Datei öffnen und lesen für 'oeffnen' und 'einfuegen'.
    #17 Schließen des Fensters. Der Parameter ist nur erforderlich, falls später jemand Tastatur-Events einfügt.
    #18 Aufgeben des Fensters.
    #19 Wert 'None' an die close-Methode zurückgeben.

    #20 Die Klasse Log.
    #21 __init__-Methode. Parameter ist Titel des Fensters.
    #22 Länge der Queue (sollte nicht zu klein sein!).
    #23 Wartezeit für die close-Methode in Sekunden.
    #24 Protokollfenster wird als Thread gestartet. Übergeben werden Queue und Titel.
    #25 Wert der write-Methode wird an die Queue übergeben
    #26 close-Methode übergibt mit 'None' das Signal an Protokollfenster
    #27 und wartet bis Queue wirklich geleert,
    #28 und nun auf Signal, daß das Protokollfenster geschlossen wurde.

    ===========================================================================

    Arme Windoofs-Nutzer (ich oft auch wegen Laptop) stehen vor dem Problem, ihre Ausgaben von der Python-Shell (IDLE) nicht willkürlich in ein eigenes Fenster umleiten zu können. Hier kann das Protokoll-Programm ein wenig helfen. Zu diesem Zweck wurde es etwas abgespeckt.

    Die import-Anweisungen wurde auf das Nötigste reduziert. Aber trotzdem lasse ich sie als Kommentar drin; für Testzwecke kann es ganz gut sein, diese Anweisung wieder zu aktivieren!

    #11 Das Fenster soll ein wenig den Bauch einziehen
    #12 Die Synchronisation mit dem Vater-Prozess kann entfallen
    # Hauptroutine:
    ___Die Hilfefunktion weg
    ___Die Texte der Kommandos einkürzen wegen #11
    #23 entfällt
    #26 close-Methode entfällt

    In diesem Beispiel wir nur sys.stdout umgeleitet; Fehlermeldungen bleiben im Hauptfenster.

    Auf meinen Desktop habe ich folgende Verknüpfung gelegt.Diese Zeile bitte 1 zu 1 übernehmen und dann anpassen!

    C:\Python24\pythonw.exe "C:\Python24\Lib\idlelib\idle.pyw" -r "E:\projekt2\x.pyw"
    ____^Das Python-Verzeichnis____^Hier auch anpassen________^Mein Verzeichnis

    Nun ein Doppelklick auf das Desktop-Symbol und die IDLE wird mit einem Protokollfenster gestartet. Ganz nebenbei werden natürlich die angegebenen Module geladen.Das Protokollfenster beendet sich mit Restart bzw. Beendigung der IDLE!

    Hier könnt Ihr Euch die Programm 'x.pyw' anschauen und kopieren:

    http://www.boe-se.org/python/pypro/x.pyw
Und zum Schluß noch die "X.PYW":

Code: Alles auswählen

# import os, fileinput, shelve
from string import *
# from time import *
import sys, tkFileDialog, thread, Queue
from Tkinter import *

# Beschreibung und Kommentare siehe Datei "protokoll.txt"

def Protokoll(puffer,t): #10

    warteschreibe=600 #11
    hoch=33
    breit=40
    
    def schreibe(): #12
        while not puffer.empty():
            text.insert(END,puffer.get()) #14
            text.see(END)
        log.tk.createtimerhandler(warteschreibe, schreibe) #15
    def ein(): #16
        datei=tkFileDialog.askopenfile()
        z=None
        if datei: z=datei.read()
        return z
    def oeffnen():
        z=ein()
        if z:
            text.delete(1.0,END)
            text.insert(1.0,z)
    def einfuegen():
        text.insert(INSERT,ein())
    def speichern():
        datei=tkFileDialog.asksaveasfile()
        if datei: datei.write(text.get(1.0,END))
    def leeren():
        text.delete(1.0,END)
    def kopieren():
        text.clipboard_clear()
        text.clipboard_append(text.get("0.0", END))
    def ausis(event=None): #17
        log.destroy(); log.quit() #18
        puffer.put(None) #19

# Hauptroutine    
    log=Tk()
    log.title(t)
    yscroll=Scrollbar(log)
    yscroll.pack(side=RIGHT,fill=Y)
    xscroll=Scrollbar(log,orient=HORIZONTAL)
    xscroll.pack(side=BOTTOM,fill=X)
    text=Text(log,yscrollcommand=yscroll.set,xscrollcommand=xscroll.set,\
              font=('Courier',10),width=breit,height=hoch,padx=5,wrap='none') #
    yscroll.config(command=text.yview)
    xscroll.config(command=text.xview)
    text.pack(side=LEFT, fill=BOTH, expand=1)
    menue=Menu(log)
    menue.add_command(label='Oeffnen',command=oeffnen,underline=0)
    menue.add_command(label='Leeren',command=leeren,underline=0)
    menue.add_command(label='Kopieren',command=kopieren,underline=0)
    menue.add_command(label='Einfuegen',command=einfuegen,underline=0)
    menue.add_command(label='Speichern',command=speichern,underline=0)
    menue.add_command(label='Quit',command=ausis,underline=0)
    log.config(menu=menue)
    schreibe()
    log.mainloop()

class Log: #20
       
    def __init__(self,name='Protokoll - Fenster'): #21
        self.laengequeue=256 #22
        self.puffer=Queue.Queue(self.laengequeue)
        thread.start_new_thread(Protokoll,(self.puffer,name)) #24
        sys.stderr.write('     + + + Fenster: "'+name+'" wurde gestartet! + + +\n')
    def write(self,s): #25
        self.puffer.put(s)

sys.stdout=Log('IDLE - Ausgaben')
Ihr könnt die Texte aber auch weiterhin bei den o.a. Links abholen (bitte nicht "www." vergessen)!


Nachtrag!
Die o.a. Links stimmen nicht mehr (der server wurde aufgegeben). Bitte kopiert die Quelltexte aus dem Text. Danke!

Michael
Antworten