Seite 1 von 3
Konsolenausgabe in tkinter ausgeben
Verfasst: Freitag 11. Januar 2013, 09:45
von Nobuddy
Hallo zusammen
Ein Programm für die Konsole (Terminal), das ich vor längerem geschrieben habe, möchte ich jetzt gerne in einem Programm das mit tkinter läuft ablaufen lassen.
Dabei möchte ich gerne, das was bisher in der Konsole ausgegeben wurde, in tkinter direkt ausgeben lassen.
Welche Tipps könnt Ihr mir dazu geben?
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Samstag 12. Januar 2013, 13:14
von yipyip
1. Die Frage etwas konkreter stellen?

2. Dass man Logik und Userinterface trennen sollte, ist dir ja bekannt.
Wenn man das richtig organisiert, kann man auch die Ausgabekomponenten austauschen. (Stichwort "Dependency Injection")
3. Man sollte sollte sich fuer ein einfaches, sequentiell ablaufendes Programm natuerlich auch die Frage stellen: Lohnt sich das ueberhaupt oder ist es sinnvoll?
4. Alles weitere sollte man am konkreten Problem festmachen.
yipyip
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 11:55
von Nobuddy
Hallo yipyip
Danke für Deine Meldung, auch wenn mein Post kurz und nicht konkret genug war.
Dein Input, hat mir zu Denken gegeben, so daß ich Abstand von einer Konsolenausgabe in meinem tkinter-Programm genommen habe.
Aktuell ist, wie ich die Printausgabe von einem importierten Modul in tkinter ausgeben lassen kann?
Und das nicht zeit-versetzt, also nicht wenn das Modul komplett durchgelaufen ist erst die Ausgabe.
Gibt es da erste Tipps, oder ist das wieder nicht konkret genug?
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 12:09
von BlackJack
@Nobuddy: ``print`` verwendet `sys.stdout` zur Ausgabe. Das müsstest Du durch ein eigenes Objekt ersetzen was die Daten entgegennimmt statt sie auf der Konsole auszugeben, oder was auch immer die Daten von der Standardausgabe an der anderen Seite in Empfang nimmt. Wenn die Funktionen in dem anderen Modul nicht lange laufen, also die GUI nicht blockieren, kannst Du die Daten dann einfach in ein Text-Widget einfügen. Sonst müssten die Funktionen in einem anderen Thread laufen und Dein Objekt müsste die Daten in eine `Queue.Queue` schreiben von wo sie aus dem GUI-Code mittels der `after()`-Methode regelmässig abgeholt und in das Textfeld übertragen werden.
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 12:22
von Sirius3
@Blackjack: besser hätt ichs auch nicht sagen können

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 15:08
von wuf
Hi Nobuddy
Hier noch ein Link der für dich beim Studium interessant sein könnte:
Umleiten der print Ausgabe
Geschrieben in englisch aber die Python-Schnipsel sind gut lesbar.
Gruss wuf

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 16:31
von Nobuddy
Hallo zusammen
Danke für Eure Tipps, werde mich da mal an die Arbeit machen.
Melde mich wieder!
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Sonntag 13. Januar 2013, 22:13
von wuf
Hi Nobuddy
Hier etwas zum herum experimentieren. Skrip noch ohne Kosmetik:
Code: Alles auswählen
#!/usr/bin/env python
# coding: UTF-8
import sys
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, name, mode):
self.queue = queue
self.file = open(name, mode)
if type == 'stderr':
self.out_obj = sys.stderr
sys.stderr = self
else:
self.out_obj = sys.stdout
sys.stdout = self
def write(self, data):
# Ausgabe in eine Datei
#self.file.write(data)
# Ausgabe auf das Standart-Terminal
#self.out_obj.write(data)
# Ausgabe auf Tk-Textbox
self.queue.put(data)
APP_WIN_XPOS = 50
APP_WIN_YPOS = 50
class App(object):
def __init__(self):
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.queue = qu.Queue()
self.text = tk.Text(self.win, width=80, height=20, highlightthickness=0,
bd=0, bg='white', relief='sunken', padx=5, pady=5)
self.text.pack()
print_button = tk.Button(self.win, text='Print',
command=self.print_trigger)
print_button.pack()
err_button = tk.Button(self.win, text='Error',
command=self.error_trigger)
err_button.pack()
self.sampler()
def print_trigger(self):
print('Good day mate! How are you doing!')
def error_trigger(self):
import NoExistModul
def sampler(self):
if self.queue.qsize():
#~~ In der Queue sind Daten vorhanden!
try:
data = self.queue.get()
self.text.insert('end', data)
self.queue.task_done()
except qu.Empty:
pass
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def close(self):
"""Process before shutdown"""
self.win.destroy()
app = App()
app.win.title("New Terminal")
TeeStd(app.queue, 'stderr', "log.txt", "a")
TeeStd(app.queue, 'stdout', "log.txt", "a")
print ('Hello')
app.run()
Gruss wuf

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Dienstag 15. Januar 2013, 18:24
von Nobuddy
Hallo wuf
Sorry, daß ich mich erst jetzt zurück melde, hatte noch ein paar andere Baustellen zuvor.
Danke für Deinen Code, funktioniert so wie ich es mir vorgestellt habe.
Ein paar Fragen habe ich noch dazu.
Die Funktion 'print_trigger(self)' in der Class 'App', übernimmt den Empfang für die Ausgabe im 'NewTerminal.
Die Funktion 'write(self, data)' in der Class 'TeeStd' ist für die Ausgabe zuständig, der Du noch weitere Optionen eingefügt hast.
Habe ich das so richtig verstanden?
Dein Code funktioniert als Modul, so daß ich die Printausgaben an die Funktion 'print_trigger(self)' in der Class 'App' übergeben muß.
Ist das soweit richtig?
Sollte ich da völlig falsch liegen, würde ich mich über eine kurze Erklärung darüber freuen.
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Dienstag 15. Januar 2013, 21:14
von wuf
Hi Nobuddy
a) Beim aktivieren des Buttons 'Print' wird mittels 'print_trigger(self)' ein Printbefehl ausgelöst. Ist nur für Testzwecke. Das gleiche gilt für den Button 'Error'. Hier wird 'error_trigger(self)' aufgerufen womit ein nicht existierendes Modul importiert wird. Dies löst eine Exception aus. Auch nur für Testzwecke. Das heisst diese Print- und Exception-Ausgaben werden für dessen Anzeige in die Textbox katapultiert.
b) 'write(self, data)' ist für die Ausgabe der Print- und Exception-Daten, die in eine Queue geschickt werden. Die Queue wird dann durch 'def sampler(self)' zyklisch nach Daten abgefragt und an die Textbox weitergeleitet.
c) Für die Ausgabe von Print- bzw. Exceptiondaten musst du 'print_trigger(self)' nicht mehr aufrufen. Diese werden von hier an:
Code: Alles auswählen
TeeStd(app.queue, 'stderr', "log.txt", "a")
TeeStd(app.queue, 'stdout', "log.txt", "a")
automatisch in die Textbox geschrieben.
Gruß wuf

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Mittwoch 16. Januar 2013, 09:33
von Nobuddy
Hallo wuf
Danke für Deine Antwort, da war ich völlig daneben.
Was mir noch nicht klar ist, wie ich von einem Modul die Printausgabe ausgeben lasse?
Beispiel update_vendor.py gibt folgende Printausgabe aus:
Es werden von 12 Lieferanten Produktdaten aktualisiert.
Lieferant 001 27000 Datensätze
Lieferant 002 7000 Datensätze
Lieferant 003 37000 Datensätze
...
..
.
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Mittwoch 16. Januar 2013, 10:21
von wuf
@Nobuddy: Ich kenne das Modul
update_vendor.py nicht, verstehe aber wohl was damit gemeint ist. Hast du mehr Info zu diesem Modul?
Gruß wuf

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Mittwoch 16. Januar 2013, 11:41
von Nobuddy
Hallo wuf
Wie Du schon vermutest, ist 'update_vendor.py' ein eigenes Modul, das Lieferantendaten verarbeitet.
Dazu kommen weitere Module für bestimmte Aufgaben in Einsatz, die in 'update_vendor.py' aufgerufen werden.
Ich poste Dir mal den Code, wobei er für Dich nicht lauffähig sein wird, da Dir die restlichen Daten dazu fehlen.
Code: Alles auswählen
#!/usr/bin/python3
# coding: UTF-8
import os
from datetime import date
now = date.today()
heute = now.strftime('%Y.%m.%d-')
from z_list_definition import items_def
from __modul_bericht__ import info, leerzeile, trennzeile
from __modul_abfrage__ import abfrage
# {}liste.txt
base_daten_path = os.path.join(os.path.dirname(__file__),
'base_daten', '{}liste.txt'.format(heute))
# LISTE-HERSTELLER-EAN.txt
liste_hersteller_ean_path = os.path.join(os.path.dirname(__file__),
'base', 'LISTE_HERSTELLER_EAN.txt')
### Datenverarbeitung von Lieferanten-Quelldatei
info('suppliers_list')
from suppliers_list import suppliers_data_load
result = suppliers_data_load()
### Nachbearbeitung
if result > 0:
# Aktualisieren von Herstellernamen
from producer_update import get_producer
info('Aktualisierung von Herstellernamen - producer_update')
info('Liste liste.txt')
hersteller = items_def('base_daten_path', 'hersteller')
get_producer(base_daten_path, hersteller, 'yes')
#get_producer(quellliste_path, 4, 'yes')
info('Liste LISTE-HERSTELLER-EAN.txt')
leerzeile()
hersteller = items_def('liste_hersteller_ean_path', 'hersteller')
get_producer(liste_hersteller_ean_path, hersteller, 'yes')
# Aktualisieren von Herstellernamen über EAN-Code
# die z.B. 'LIEFERANT' heißen.
# producer_name('NAMEN') entsprechend anpassen
info('Aktualisieren von Herstellernamen durch EAN-Code.')
info('producer_ean')
leerzeile()
from producer_ean import get_producer_ean_name
get_producer_ean_name('LIEFERANT')
# Aktualisieren von Herstellernamen über Herstellerliste
# die z.B. 'LIEFERANT' heißen.
# producer_name('NAMEN') entsprechend anpassen
info('Aktualisieren von Herstellernamen laut Bennenung.')
info('producer_name')
leerzeile()
from producer_name import get_producer_name
get_producer_name('LIEFERANT')
info('End of update_vendor')
Das Modul __modul_bericht__.py habe ich bisher für die Ausgabe und die Archivierung in eine Datei verwendet.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import os
from datetime import date
now = date.today()
heute = now.strftime('%Y.%m.%d-')
import time
uhr = time.strftime('Time %H:%M:%S')
from __modul_write__ import write_tmp, writeappend_tmp
# {}bericht.txt
base_bericht_path = (os.path.join(os.path.dirname(__file__),
'base_daten', '{}bericht.txt'.format(heute)))
def infoneu():
write_tmp(base_bericht_path, uhr)
leerzeile()
def info(string):
print(string)
writeappend_tmp(base_bericht_path, uhr)
leerzeile()
writeappend_tmp(base_bericht_path, string)
def leerzeile():
print('')
writeappend_tmp(base_bericht_path, '')
def trennzeile():
print('')
print(35 * '-')
print('')
leerzeile()
writeappend_tmp(base_bericht_path, 35 * '-')
leerzeile()
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Mittwoch 16. Januar 2013, 17:41
von wuf
Hi Nobuddy
Danke für deine Modul-Schnippsel. Aber wie du richtig vermutet hast werde ich diese nicht verwenden, da ich schlicht zu wenig Zeit habe diese so zurechtzustutzen um für meine folgende Demo einzusetzen. Mein obiges Schnippsel, welches du schon kennst ist als reiner Test-Prototyp zum experimentieren und nicht als fertiges Modul gedacht. Da braucht es natürlich noch ein wenig Arbeitsaufwand. Zum Beispiel die Textbox in einem Toplevelfenster zu platzieren.
Um zu zeigen wie die Umleitung der 'print'-Standard-Ausgabe auf deine Tk-Textbox-Ausgabe funktioniert habe ich ein eigenes kleines Modul zusammengebraut. Sein Dateiname ist
vendors.py und wird wie folgt importiert:
Der Inhalt von
vendors.py ist:
Code: Alles auswählen
#!/usr/bin/env python
# coding: UTF-8
class MyVendors(object):
def __init__(self):
self.vendors = list()
def update_vendors(self, new_vendor=None):
if new_vendor != None:
self.vendors.append('Vendor-{0}: {1}'.format(len(self.vendors)+1,
new_vendor))
for vendor in self.vendors:
print(vendor)
print()
Das Demoskript ist:
Code: Alles auswählen
#!/usr/bin/env python
# coding: UTF-8
import sys
from vendors import MyVendors
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 = 50
APP_WIN_YPOS = 50
class App(object):
def __init__(self):
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.queue = qu.Queue()
self.text = tk.Text(self.win, width=80, height=20, highlightthickness=0,
bd=0, bg='white', relief='sunken', padx=5, pady=5)
self.text.pack()
vendor_button = tk.Button(self.win, text='Vendor hinzufügen',
command=self.add_vendor)
vendor_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.my_vendors = MyVendors()
self.sampler()
def add_vendor(self):
self.my_vendors.update_vendors('Nobuddy')
def sampler(self):
if self.queue.qsize():
# In der Queue sind Daten vorhanden!
try:
data = self.queue.get()
self.text.insert('end', data)
self.queue.task_done()
except qu.Empty:
pass
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def close(self):
"""Process before shutdown"""
self.win.destroy()
App().run()
Gruß wuf

Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Mittwoch 16. Januar 2013, 18:00
von Nobuddy
Hallo wuf
Keine Frage, den Code meiner Module muß ich schon selbst zurechtstutzen, ist eh noch Code vom Anfang meines Schaffens, den ich dringend überarbeiten muß.
Herzlichen Dank für Deine Mühe, werde mir das in Ruhe durchschauen und testen!
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Donnerstag 17. Januar 2013, 15:16
von Nobuddy
Hallo wuf
Dein Demoscript funktioniert prächtig, nicht nur mit Deinem selbst zusammengebrauten Modul, sondern auch mit meinen Modulen!
Ich habe noch 2 kleiner Änderungen vorgenommen.
Ich habe der Class App, def __init__(self, check) einen Parameter spendiert, der den Funktionsaufruf beinhaltet.
So lässt sich dann von einem anderen Modul aus, Dein Demoscript ansprechen.
Den Button verwende ich jetzt zum Schließen des Fensters und benutze dazu die Funktion 'close'.
Die Funktion 'add_vendor' habe ich entfernt.
Den Status des Button habe deaktiviert, soll erst nach Beendigung der übergebenen Funktion, zum Schließen Aktiviert werden, damit kein versehentlicher Abbruch möglich ist.
Allerdings gestaltet sich das Positionieren der Button-Aktivierung
schwierig, habe dafür noch keine Lösung gefunden.
Poste mal Dein Demoscripft mit den besagten Änderungen:
Code: Alles auswählen
#!/usr/bin/env python
# coding: UTF-8
import sys
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 = 50
APP_WIN_YPOS = 50
class App(object):
def __init__(self, check):
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.queue = qu.Queue()
self.text = tk.Text(self.win, width=80, height=20, highlightthickness=0,
bd=0, bg='white', relief='sunken', padx=5, pady=5)
self.text.pack()
close_button = tk.Button(self.win, text='Fenster schließen',
command=self.close)
close_button.pack()
close_button['state']="disabled"
self.win.title("My Stdout/Stderr Terminal")
# Umleitung der Standard-Ausgabe auf meine Textbox
TeeStd(self.queue, 'stderr')
TeeStd(self.queue, 'stdout')
self.check = check()
#if ...:
#close_button['state']="normal"
self.sampler()
def sampler(self):
if self.queue.qsize():
# In der Queue sind Daten vorhanden!
try:
data = self.queue.get()
self.text.insert('end', data)
self.queue.task_done()
except qu.Empty:
pass
self.win.after(50, self.sampler)
def run(self):
self.win.mainloop()
def close(self):
"""Process before shutdown"""
self.win.destroy()
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Donnerstag 17. Januar 2013, 17:40
von EyDu
Code: Alles auswählen
def sampler(self):
if self.queue.qsize():
# In der Queue sind Daten vorhanden!
try:
data = self.queue.get()
self.text.insert('end', data)
self.queue.task_done()
except qu.Empty:
pass
Hier hast du gleich mehrere Fehler eingebaut: Wenn du erst die Größe der Warteschlange abfragst und dann ein get machst, dann hast du dir eine prima Race Condition zusammengebaut. Angenommen du erweiterst dein Programm und an einer anderen Stelle werden noch Elemente aus der Queue abgefragt. Dann kann es passieren, dass die Bedingung im if wahr wird, der andere Thread holt sich das Element aus der Schlange und nun wird das get in sampler aufgerufen. Da get blockierend ist, bleibt deine gedamte GUI stehen. Wie lange hängt natürlich davon ab, wann das get in sampler mal wieder Glück hat ein Element zu erwischen. Wirf einen Blick in die Dokumentation von Queue und überlege dir, wie du es richtig machst.
Weiter kann es bei einem get ohne Parameter niemals vorkommen, dass eine Empty-Exception geworfen wird. Du solltest deine Programme so weit testen, dass auch alle Programmzweige mal abgelaufen werden. In diesem Fall hast du das offenbar nicht getan und setzt auf Hoffen und Beten. Oder ein wenig von beidem...
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Donnerstag 17. Januar 2013, 18:35
von Nobuddy
Hallo EyDu
Ich habe den Code, aus Wufś Demoscript genommen.
Ich bin etwas ratlos, da ich mit der Materie Queue noch nicht vertraut bin.
Ich muß wohl noch einige male die deutschsprachige Beschreibung von Galileo
http://openbook.galileocomputing.de/pyt ... .htm#t2t34 lesen, vielleicht komme ich dann auf die Lösung des Problems, welches Du ansprichst.
Hoffen und Beten, sind nicht immer verkehrt, aber bestimmt nicht hier!
Grüße Nobuddy
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Freitag 18. Januar 2013, 15:56
von Nobuddy
@EyDu, habe zigmale Queue in Galileo durchgelesen, muß aber hier passen. :K
Wie sollte dies nach Deiner Kenntnis aussehen?
Code: Alles auswählen
def sampler(self):
if self.queue.qsize():
# In der Queue sind Daten vorhanden!
try:
data = self.queue.get()
self.text.insert('end', data)
self.queue.task_done()
except qu.Empty:
pass
Re: Konsolenausgabe in tkinter ausgeben
Verfasst: Freitag 18. Januar 2013, 16:40
von yipyip
@Nobuddy
Nicht den Galileo lesen, sondern die Original Doku:
http://docs.python.org/2/library/queue.html
Dort steht sinngemaess: Queue.get() === Queue.get(True) blockt, d.h. an dieser Stelle wartet das Programm solange, bis wieder etwas aus der Queue ausgelesen werden kann. Queue.get(False) blockt nicht, d.h. entweder wird etwas ausgelesen oder falls nicht, eine Empty Exception ausgeloest. In einer Multithreading Umgebung sollte mit
das Problem, das EyDu ansprach, wohl behoben sein. (Wenn ich das richtig verstehe.)
Bei dir kommt aber (erstmal ?) gar kein Multithreading vor, vielleicht kommt man dann auch mit list/deque aus.
yipyip