stdout ile exec çıktısını nasıl alabilirim?
Aşağıdaki örnekte, stdout ve stderr resmin sağ tarafındaki text widgetine yönlendirilmiştir. İsterseniz kodları bir inceleyin, belki işinize yarar.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
root = tk.Tk()
def widgetleri_olustur():
for i, j in enumerate(("Çalıştır", "Temizle")):
text = tk.Text(master=root)
text.grid(row=0, column=i)
button = tk.Button(master=root, text=j)
button.grid(row=1, column=i)
yield text
yield button
t1, b1, t2, b2 = widgetleri_olustur()
class StdIOYonlendiricisi:
"""
Bu sınıfta sys.stdout'un write, flush
özelliklerine benzer özellikler tanımladık.
Bu özelliklerin yapısı, widgetin
yapısına uygun olacak şekilde düzenlendi.
Böylece standart çıktıları bir widgete yönlendirebiliriz.
"""
def __init__(self, text):
self.text = text
def write(self, string):
self.text.insert("insert", string)
def flush(self):
self.text.update()
sys.stdout = StdIOYonlendiricisi(text=t2)
sys.stderr = StdIOYonlendiricisi(text=t2)
def command1():
exec(t1.get("1.0", "end"))
def command2():
t2.delete("1.0", "end")
b1.configure(command=command1)
b2.configure(command=command2)
root.mainloop()
sys.stdout = class olmak zorundamı?
Başka bir yöntem varsa da bilmiyorum.
write
fonksiyonu olan herhangi bir şey olabilir. flush
da gerekli gibi duruyor ancak emin değilim. @dildeolupbiten sen biliyor musun?
@ismailarilik’in bahsettiği gibi write
fonksiyonu olan bir veri tipi gerekiyor.
Normalde tk.Text()
widgetine yazı yazma fonksiyonu tk.Text().insert("insert", string)
. Dolayısıyla sys.stdout
'u, tk.Text()
widgetine doğrudan yönlendiremeyiz. Yönlendirebilmek için write()
metodu olan bir veri tipine ihtiyaç var. Bu veri tipini bir sınıf ile oluşturabiliriz. Bu sınıfın örnek metotlarının bir tanesi (write()
olan) tk.Text()
widgetinin tk.Text().insert("insert", string)
fonksiyonunu çalıştırır. Özetle bu sınıfın metotlarının isimleri tıpkı sys.stdout
'un metotlarının ismine benzer olursa, sys.stdout
'u sınıfa yönlendirebiliriz.
flush()
‘ı eklemezsek şöyle bir durumla karşılaşabiliriz:
Program çalışırken, sys.stdout.flush()
fonksiyonunu veya print(string, flush=True)
fonksiyonunu kullanırsak bir AttributeError
hatası alırız (AttributeError: ‘StdIOYonlendiricisi’ object has no attribute 'flush’).
Bir de, şu hatırlatmayı da yapmak isterim. Diyelim widgetin içine aşağıdaki kodları yazdık:
import time
count = 1
while count < 10:
print(count)
count += 1
time.sleep(1)
Bu kodların çıktısı, widgetin şu haliyle ancak 10 saniye sonra standart çıktılara yazdırılır ve bu arada program geçici olarak donar.
Bu durumu önlemenin bildiğim kadarıyla üç yolu var:
- Ya yukarıdaki kodları şöyle yazacağız:
import time
count = 1
while count < 10:
print(count, flush=True)
count += 1
time.sleep(1)
- Ya, aşağıdaki gibi olan
command1()
fonksiyonunu;
def command1():
exec(t1.get("1.0", "end"))
Şu şekilde değiştireceğiz:
def command1():
def inner_function():
exec(t1.get("1.0", "end"))
thread = threading.Thread(target=inner_function)
thread.daemon = True
thread.start()
exec()
fonksiyonunu bir iş parçacığının hedefi haline getirirsek, exec()
fonksiyonunun yürüttüğü işlem, Tkinter
uygulamasına engel olmaz.
- Ya da,
StdIOYonlendiricisi()
sınıfınınwrite()
metodunu şu şekilde değiştireceğiz:
def write(self, string):
self.text.insert("insert", string)
self.flush()
Yani her write()
metodu kullanımında aynı zamanda flush()
metodunu da kullanacağız.