Ich bin dabei, eine GUI-basierte Anwendung mit Python/Tkinter zu erstellen, die auf dem vorhandenen Python-bdb-Modul aufbaut. In dieser Anwendung möchte ich alle stdout/stderr von der Konsole stummschalten und es auf meine GUI umleiten. Um diesen Zweck zu erreichen, habe ich ein spezielles Tkinter.Text-Objekt geschrieben (Code am Ende des Posts).Segmentierungsfehler beim Umleiten von sys.stdout an das Tkinter.Text-Widget
Die Grundidee ist, dass, wenn etwas in sys.stdout geschrieben wird, es als eine Zeile im "Text" mit der Farbe schwarz angezeigt wird. Wenn etwas in sys.stderr geschrieben wird, erscheint es als eine Zeile im "Text" mit der Farbe rot. Sobald etwas geschrieben wird, scrollt der Text immer nach unten, um die letzte Zeile anzuzeigen.
Ich benutze Python 2.6.1 im Moment. Unter Mac OS X 10.5 scheint das großartig zu funktionieren. Ich hatte keine Probleme damit. Auf RedHat Enterprise Linux 5 erhalte ich jedoch ziemlich zuverlässig einen Segmentierungsfehler während der Ausführung eines Skripts. Der Segmentierungsfehler tritt nicht immer am selben Ort auf, aber er tritt ziemlich oft auf. Wenn ich die Zeilen sys.stdout=
und sys.stderr=
aus meinem Code auskommentiere, scheinen die Segmentierungsfehler zu verschwinden.
Ich bin mir sicher, dass es andere Wege gibt, auf die ich wahrscheinlich zurückgreifen muss, aber kann irgendjemand irgendetwas sehen, was ich hier eklatant falsch mache, was diese Segmentierungsfehler verursachen könnte? Es macht mich verrückt. Vielen Dank!
PS - Ich weiß, dass die Umleitung von sys.stderr zur GUI vielleicht keine gute Idee ist, aber ich bekomme immer noch Segmentierungsfehler, selbst wenn ich nur sys.stdout und nicht sys.stderr umleite. Ich erkenne auch, dass ich den Text im Moment unbegrenzt wachsen lasse.
class ConsoleText(tk.Text):
'''A Tkinter Text widget that provides a scrolling display of console
stderr and stdout.'''
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self,text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def write(self,str):
self.text_area.write(str,False)
class StderrRedirector(IORedirector):
'''A class for redirecting stderr to this Text widget.'''
def write(self,str):
self.text_area.write(str,True)
def __init__(self, master=None, cnf={}, **kw):
'''See the __init__ for Tkinter.Text for most of this stuff.'''
tk.Text.__init__(self, master, cnf, **kw)
self.started = False
self.write_lock = threading.Lock()
self.tag_configure('STDOUT',background='white',foreground='black')
self.tag_configure('STDERR',background='white',foreground='red')
self.config(state=tk.DISABLED)
def start(self):
if self.started:
return
self.started = True
self.original_stdout = sys.stdout
self.original_stderr = sys.stderr
stdout_redirector = ConsoleText.StdoutRedirector(self)
stderr_redirector = ConsoleText.StderrRedirector(self)
sys.stdout = stdout_redirector
sys.stderr = stderr_redirector
def stop(self):
if not self.started:
return
self.started = False
sys.stdout = self.original_stdout
sys.stderr = self.original_stderr
def write(self,val,is_stderr=False):
#Fun Fact: The way Tkinter Text objects work is that if they're disabled,
#you can't write into them AT ALL (via the GUI or programatically). Since we want them
#disabled for the user, we have to set them to NORMAL (a.k.a. ENABLED), write to them,
#then set their state back to DISABLED.
self.write_lock.acquire()
self.config(state=tk.NORMAL)
self.insert('end',val,'STDERR' if is_stderr else 'STDOUT')
self.see('end')
self.config(state=tk.DISABLED)
self.write_lock.release()
nur beiseite, ich würde vorschlagen * nicht * automatisch in allen Fällen nach unten scrollen. Wenn ein Benutzer nach oben geblättert hat, um etwas anzusehen, und dann ein neues Element hinzugefügt wird, wird es ein unzufriedener Benutzer, wenn das, was er betrachtet, aus dem Blickfeld verschwindet. Der Algorithmus, den ich benutze, ist, wenn die letzte Zeile vor der Eingabe von neuem Text sichtbar ist, scrolle ich automatisch. Sonst nicht. –
Guter Anruf. Ich bin mir sicher, dass man bald genug in meiner "To Fix" -Liste aufgetaucht wäre. –