Seite 1 von 1
Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 09:35
von Nobuddy
Hallo zusammen
Habe ein Problem und hoffe auf Eure Hilfe.
Ich habe eine Funktion, die Aufgaben über ein Terminal ausgeben lässt.
Code: Alles auswählen
def terminal(modul):
"""Vorbereitung für Terminalausgabe"""
app = App() # redirect stdout auf Terminal-Fenster
worker = threading.Thread(target=modul)
worker.start() # macht die Arbeit in Hintergrund-Thread
app.worker = worker
app.run() # aktiviert das Terminal-Fenster
return
Ich nutze diese Funktion z.B. so:
Code: Alles auswählen
if sub_button_name == 'Backup':
from update_backup import backup_work
print('00')
terminal(backup_work)
print('1')
self.LoadNewLists()
return
Mit z.B. 'terminal(backup_work)' starte ich das Terminal, in dem dann z.B. 'backup_work' abläuft.
Wenn dies dann fertig ist und ich das Terminal schließe, sollte noch die Funktion 'self.LoadNewLists()' abgearbeitet werden.
Zum Testen, habe ich die anschließende Print-Anweisung 'print('1')' platziert, um zu sehen ob der weitere Ablauf funktioniert, was es aber nicht tut. print('1') wird nicht ausgegeben und somit kann auch der weitere Ablauf nicht durchgearbeitet werden.
Wie kann ich dieses Problem lösen?
Grüße Nobuddy
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 10:13
von BlackJack
@Nobuddy: Wenn ich mal raten müsste, dann blockiert `app.run()` solange bis, naja keine Ahnung, das hängt halt davon ab was `app.run()` eigentlich macht.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 10:18
von Nobuddy
Was ich noch vergessen hatte, ist daß nach Beendigung des Terminals, ich folgende Ausgabe erhalte:
invalid command name "643223920callit"
while executing
"643223920callit"
("after" script)
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 10:26
von Nobuddy
Hallo BlackJack
Sorry, hatte zuerst das Falsche gepostet.
Hier ist das Modul gui_terminal.py:
Code: Alles auswählen
#!/usr/bin/env python
# coding: UTF-8
import sys
import tkinter.messagebox
try:
#~~ For Python 2.x
import Queue as qu
import Tkinter as tk
except ImportError:
#~~ For Python 3.x
import queue as qu
import tkinter as tk
class TeeStd(object):
def __init__(self, queue, type):
self.queue = queue
if type == 'stderr':
self.std_obj = sys.stderr
sys.stderr = self
else:
self.std_obj = sys.stdout
sys.stdout = self
def write(self, data):
# Ausgabe auf Tk-Textbox
self.queue.put(data)
APP_WIN_XPOS = 170
APP_WIN_YPOS = 70
class App(object):
def __init__(self, worker=None):
self.win = tk.Tk()
self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
self.win.protocol("WM_DELETE_WINDOW", self.close)
self.worker = worker
self.queue = qu.Queue()
self.queue_modules = qu.Queue()
self.text = tk.Text(self.win, width=120, height=20, highlightthickness=0,
bd=0, bg='white', relief='sunken', padx=5, pady=5)
self.text.pack()
self.close_button = tk.Button(self.win, text='Fenster schließen',
state='disabled', command=self.close)
self.close_button.pack()
self.win.title("My Stdout/Stderr Terminal")
# Umleitung der Standard-Ausgabe auf meine Textbox
TeeStd(self.queue, 'stderr')
TeeStd(self.queue, 'stdout')
self.sampler()
def sampler(self):
if not self.queue.empty():
data = self.queue.get_nowait()
# Please do the job
self.text.insert('end', data)
self.text.see('end')
self.queue.task_done()
if self.worker:
if not self.worker.is_alive():
self.enable_close_button()
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def enable_close_button(self):
self.close_button['state']="normal"
def close(self):
"""Process before shutdown"""
text = 'Möchten Sie das Terminal schließen?'
if tkinter.messagebox.askokcancel('Info', text):
self.win.destroy()
Die Importanweisung lautet:
Welches dann hier aufgerufen wird:
Code: Alles auswählen
def terminal(modul):
"""Vorbereitung für Terminalausgabe"""
app = App() # redirect stdout auf Terminal-Fenster
worker = threading.Thread(target=modul)
worker.start() # macht die Arbeit in Hintergrund-Thread
app.worker = worker
app.run() # aktiviert das Terminal-Fenster
return
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 11:22
von Nobuddy
Ich habe im Modul gui_terminal.py, in der Funktion 'close()' vor 'destroy' dies hinzugefügt:
Seit dem kommt die Fehlermeldung nicht mehr, aber der weitere Programmablauf nach Beendigen des Terminals besteht immer noch.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 12:14
von BlackJack
@Nobuddy: Die GUI blockiert nun mal und Tkinter sollte im Hauptthread laufen. Wenn Du neben dem Worker noch etwas anderes parallel haben möchtest, müsstest Du das in einen eigenen Thread auslagern. Übersichtlicher wird es dadurch allerdings nicht.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 12:26
von Nobuddy
Also das Modul 'gui_terminal.py' in mein Hauptmodul einbauen, ja?
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 12:34
von BlackJack
@Nobuddy: Keine Ahnung was Du damit meinst.
Das `app.run()` nicht blockiert, möchtest Du übrigens auch gar nicht, denn danach würde die Funktion enden und `app` wäre nicht mehr erreichbar, womit das Objekt Freiwild für die Speicherbereinigung sein dürfte.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 14:06
von Nobuddy
Ich habe das Ganze jetzt komplett in das Modul 'gui_terminal.py ausgelagert.
Sieht jetzt so aus:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import sys
import tkinter.messagebox
import threading
import queue as qu
import tkinter as tk
class TeeStd(object):
def __init__(self, queue, type):
self.queue = queue
if type == 'stderr':
self.std_obj = sys.stderr
sys.stderr = self
else:
self.std_obj = sys.stdout
sys.stdout = self
def write(self, data):
# Ausgabe auf Tk-Textbox
self.queue.put(data)
APP_WIN_XPOS = 170
APP_WIN_YPOS = 70
class App(object):
def __init__(self, modul):
self.win = tk.Toplevel()
self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
self.win.protocol("WM_DELETE_WINDOW", self.close)
self.worker = threading.Thread(target=modul)
self.queue = qu.Queue()
self.queue_modules = qu.Queue()
self.text = tk.Text(self.win, width=120, height=20,
highlightthickness=0, bd=0, bg='white', relief='sunken',
padx=5, pady=5)
self.text.pack()
self.close_button = tk.Button(self.win, text='Fenster schließen',
state='disabled', command=self.close)
self.close_button.pack()
self.win.title("My Stdout/Stderr Terminal")
# Umleitung der Standard-Ausgabe auf meine Textbox
TeeStd(self.queue, 'stderr')
TeeStd(self.queue, 'stdout')
self.worker.start()
self.sampler()
def sampler(self):
if not self.queue.empty():
data = self.queue.get_nowait()
# Please do the job
self.text.insert('end', data)
self.text.see('end')
self.queue.task_done()
if self.worker:
if not self.worker.is_alive():
self.enable_close_button()
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def enable_close_button(self):
self.close_button['state']="normal"
def close(self):
"""Process before shutdown"""
text = 'Möchten Sie das Terminal schließen?'
if tkinter.messagebox.askokcancel('Info', text):
self.win.quit()
self.win.destroy()
Der Aufruf:
Vielleicht könntest Du da mal drüber schauen, ob Dir ein Fehler auffällt?
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 15:52
von Nobuddy
Nachdem ich wie oben abgeändert habe, funktioniert es wie bisher.
Was mich irritiert, ist folgendes.
Wenn ich das Modul so aufrufe:
Code: Alles auswählen
if sub_button_name == 'Backup':
from update_backup import backup_work
infoneu()
print('00')
App(backup_work).run()
print('01')
return
Wird 'print('00')' ausgegeben aber 'print('01')' nicht.
Sobald ich das Terminal beende, schließt es auch sofort.
Wenn ich das Modul aber so aufrufe:
Code: Alles auswählen
if sub_button_name == 'Backup':
from update_backup import backup_work
infoneu()
print('00')
App(backup_work).run()
print('01')
self.LoadNewLists()
return
Wird 'print('00')' ausgegeben aber 'print('01')' nicht.
Sobald ich das Terminal beende, dauert es eine ganze Weile, bis es sich schließt.
Hier vermute ich das die danach aufgerufene Funktion 'self.LoadNewLists()' ausgeführt wird.
Wenn das so wäre, warum aber nicht die letzte Print-Anweisung?
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 16:31
von BlackJack
@Nobuddy: Vielleicht wird das ``print`` ja ausgegeben aber Du siehst es nicht weil das Fenster beendet ist, also die GUI-Hauptschleife nicht mehr läuft, aber Du ja die Standardausgabe auf das Fenster gelegt hast.
Der ganze Entwurf sieht komisch aus und das mit der Ausgabeumleitung würde ich als unschönen Hack bezeichnen. Statt die Standardausgabe zu kapern sollte man einfach keine ``print``\s für etwas nehmen was nicht auf der Konsole landen soll. Oder mindestens dafür sorgen, dass die Standardausgaben auch wiederhergestellt werden, wenn die GUI das nicht mehr anzeigen kann.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 17:04
von Nobuddy
Hallo BlackJack
Ich arbeite mit Geany, dort kann ich über das Terminal von Geany den Ablauf überwachen, was gerade beim Testen sehr hilfreich ist.
Daher bin ich davon ausgegangen, daß nach Beendigung von 'App(backup_work).run()', die Print-Anweisung im Geany-Terminal ausgegeben wird.
Wie ich aber jetzt festgestellt habe, wenn auch das mit der Print-Anweisung (Test zur Kontrolle) nicht funktioniert, so werden die danach kommenden Funktionen abgearbeitet und das ist gut so.
Für manche Aufgaben benötige ich einfach eine visuelle Kontrolle über den Ablauf, daher auch die Ausgabe auf dem Terminal.
Der Entwurf von gui_terminal.py lässt sich bestimmt noch schöner und effektiver gestalten.
Wie meinst Du das mit:
BlackJack hat geschrieben:Oder mindestens dafür sorgen, dass die Standardausgaben auch wiederhergestellt werden, wenn die GUI das nicht mehr anzeigen kann.
Kannst Dur mir da den richtigen Schubser in die richtige Richtung geben, evtl. ein Beispiel?
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 17:16
von EyDu
Nobuddy hat geschrieben:Daher bin ich davon ausgegangen, daß nach Beendigung von 'App(backup_work).run()', die Print-Anweisung im Geany-Terminal ausgegeben wird.
Wie kommst du auf die Idee? Wenn du deiner Anwendung nicht sagst, dass wieder auf der Standardausgabe geschrieben werden soll, woher soll sie es dann wissen?
Nobuddy hat geschrieben:Wie meinst Du das mit:
BlackJack hat geschrieben:Oder mindestens dafür sorgen, dass die Standardausgaben auch wiederhergestellt werden, wenn die GUI das nicht mehr anzeigen kann.
Kannst Dur mir da den richtigen Schubser in die richtige Richtung geben, evtl. ein Beispiel?
Dass du einfach die Ausgaben wieder auf die alten Ziele umbiegst.
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 17:20
von Nobuddy
Hallo EyDu
Habe das jetzt verstanden, aber wie mache ich das "auf die alten Ziele umbiegen"?
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 17:41
von EyDu
Du speicherst doch schon die ursprünglichen Werte von stdout und stderr in std_obj, dabei hast du dir doch sicher etwas gedacht

Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 18:04
von Nobuddy
Ja hier:
Code: Alles auswählen
class TeeStd(object):
def __init__(self, queue, type):
self.queue = queue
if type == 'stderr':
self.std_obj = sys.stderr
sys.stderr = self
else:
self.std_obj = sys.stdout
sys.stdout = self
Meinst Du so etwas in der Art:
Code: Alles auswählen
def close(self):
"""Process before shutdown"""
text = 'Möchten Sie das Terminal schließen?'
if tkinter.messagebox.askokcancel('Info', text):
sys.stdout = self.std_obj
self = sys.stdout
sys.stderr = self.std_obj
self = sys.stderr
self.win.quit()
self.win.destroy()
return
Re: Ende von Thread - Folgeablauf
Verfasst: Mittwoch 31. Juli 2013, 18:31
von Nobuddy
Ich bin selbst drauf gekommen.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import sys
import tkinter.messagebox
import threading
import queue as qu
import tkinter as tk
class TeeStd(object):
def __init__(self, queue, type):
self.queue = queue
if type == 'stderr':
self.std_obj = sys.stderr
sys.stderr = self
else:
self.std_obj = sys.stdout
sys.stdout = self
def write(self, data):
# Ausgabe auf Tk-Textbox
self.queue.put(data)
APP_WIN_XPOS = 170
APP_WIN_YPOS = 70
class App(object):
def __init__(self, modul):
self.win = tk.Toplevel()
self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
self.win.protocol("WM_DELETE_WINDOW", self.close)
self.worker = threading.Thread(target=modul)
self.queue = qu.Queue()
self.queue_modules = qu.Queue()
self.text = tk.Text(self.win, width=120, height=20,
highlightthickness=0, bd=0, bg='white', relief='sunken',
padx=5, pady=5)
self.text.pack()
self.close_button = tk.Button(self.win, text='Fenster schließen',
state='disabled', command=self.close)
self.close_button.pack()
self.win.title("My Stdout/Stderr Terminal")
# Sicherung der Standard-Ausgabe
self.save_stdout = sys.stdout
self.save_stderr = sys.stderr
# Umleitung der Standard-Ausgabe auf meine Textbox
TeeStd(self.queue, 'stderr')
TeeStd(self.queue, 'stdout')
self.worker.start()
self.sampler()
def sampler(self):
if not self.queue.empty():
data = self.queue.get_nowait()
# Please do the job
self.text.insert('end', data)
self.text.see('end')
self.queue.task_done()
if self.worker:
if not self.worker.is_alive():
self.enable_close_button()
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def enable_close_button(self):
self.close_button['state']="normal"
def close(self):
"""Process before shutdown"""
# Umleitung auf die Standard-Ausgabe
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
text = 'Möchten Sie das Terminal schließen?'
if tkinter.messagebox.askokcancel('Info', text):
self.win.quit()
self.win.destroy()
return