Konsolenausgabe in tkinter ausgeben

Fragen zu Tkinter.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo Sirius3

Wie soll das aussehen?

Code: Alles auswählen

   
    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 not worker.is_active():
            self.enable_close_button()

        self.win.after(50, self.sampler)
Funktioniert nicht.

Grüße Nobuddy
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Nobuddy

Es heisst nicht:

Code: Alles auswählen

if not worker.is_active()
sondern:

Code: Alles auswählen

if not worker.is_alive()
Damit es auch wirklich funktioniert musst du die Skripte anpassen. Mein Versuch ist wie folgt:
Startskript:

Code: Alles auswählen

#!/usr/bin/env python
# coding: UTF-8

import threading
from vendors import MyVendors
from gui_terminal import App

app = App()
worker = threading.Thread(target=MyVendors)
worker.start()
app.worker = worker
app.run()
Terminalskript (gui_terminal.py):

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, worker=None): #, check):
        self.worker = worker
        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.queue_modules = 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()

        self.close_button = tk.Button(self.win, text='Fenster schließen',
            command=self.close)
        self.close_button.pack()
        self.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')

        print('Hello')
               
        self.sampler()
   
    def sampler(self):

        if not self.queue.empty():
            data = self.queue.get()
            self.text.insert('end', data)

        #if not self.queue_modules.empty():
            #data = self.queue_modules.get()
            #if data == 'end':
                #self.enable_close_button()    
        
        if self.worker:
            if not self.worker.is_alive():
                self.enable_close_button()
          
        self.win.after(50, self.sampler)
       
    def run(self):
        #self.sampler()
        self.win.mainloop()
   
    def enable_close_button(self):
        self.close_button['state']="normal"
        
    def close(self):
        """Process before shutdown"""
        self.win.destroy()
Modulskript (vendors.py):

Code: Alles auswählen

#!/usr/bin/env python
# coding: UTF-8

import time

class MyVendors(object):
    
    #queue = None
    
    def __init__(self):
        
        self.vendors = list()
        self.vendors.append("Vendor-1")
        self.vendors.append("Vendor-2")
        self.vendors.append("Vendor-3")     
        
        self.update_vendors()
        
    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()
        
        # Nur für Simulation (Verlängert die Lebensdauer des Threads um 5 Sek.
        time.sleep(5)
            
        #if MyVendors.queue is not None:
            #MyVendors.queue.put('end')
Gruß wuf :wink:
Take it easy Mates!
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo wuf

Danke für Deine Unterstützung, funktioniert jetzt prima! :wink:

Bin beim Googeln zwar auf is_alive() gestoßen, bei der Umsetzung kam ich aber nicht weiter.

Wenn ich jetzt Deinen Code sehe, ist die Vorgehensweise eigentlich logisch ... muß da wohl mehr an meiner Logik arbeiten... :wink:

Grüße und einen schönen Abend
Nobuddy
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Poste hier den Code, mit dem ich arbeite, damit man nicht erst den ganzen Thread bis zum Ergebnis lesen muß. :wink:

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) == True:
            self.win.quit()
Auszug aus meinem Modul, in dem ich terminal.py benutze.

Code: Alles auswählen

import threading

from gui_terminal import App
from update_vendor import suppliers_data

def terminal(modul):
    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

terminal(suppliers_data)
Nochmals Vielen Dank an Alle!

Grüße Nobuddy
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Da sampler noch immer eine potenzielle Race Codition hat:

Code: Alles auswählen

def sampler(self):
    try:
        data = self.queue.get_nowait()
    except queue.Empty:
        pass
    else:
        self.text.insert("end", data)
        self.text.see("end")

    if self.worker:
        ...
Und dann ist mir beim Durchscrollen noch ein unsinniges ``== True`` aufgefallen ;-)
Das Leben ist wie ein Tennisball.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo EyDu

Danke für Deine Unterstützung! :wink:

Dein Code funktioniert bei mir so nicht.
Aber so würde er funktionieren:

Code: Alles auswählen

    def sampler(self):
   
        if not self.queue.empty():
            try:
                data = self.queue.get_nowait()
            except queue.Empty:
                pass
            else:
                self.text.insert("end", data)
                self.text.see("end")
       
        if self.worker:
            if not self.worker.is_alive():
                self.enable_close_button()

        self.win.after(50, self.sampler)
Wäre das so in Ordnung?
'self.queue.task_done()' wird das hier nicht mehr benötigt?

Das 'True' habe ich eliminiert.
Was sich verändert hat, ist daß ich 'destroy()' jetzt statt 'quit() verwenden muß, damit das Fenster sich wieder schließt.
Dabei erhalte ich immer folgende Meldung:
invalid command name "28829240callit"
while executing
"28829240callit"
("after" script)
Was kann man dagegen tun?

Grüße Nobuddy
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Was heißt denn "funktioniert nicht"? Was passiert? Gibt es einen Fehler?

Wie weiter oben schon geschrieben: du darfst kein self.queue.empty()-Aufruf machen und dich dann darauf verlassen, dass das Ergebnis noch beim get(_nowait)-Aufruf gilt.
Das Leben ist wie ein Tennisball.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Habe Dein Codeschnipsel 1:1 übernommen.
Das Terminal öffnet sich zwar, aber es kommt keine Ausgabe.
Fehlermeldung, gibt es keine.
Antworten