如何在gtk.TextBuffer中等待用户输入暂停?
我正在尝试用pygtk写一个简单的图形界面应用程序,目的是提供文本标记的“实时”预览。不过,处理这些标记的过程可能会非常耗费计算资源,而且运行速度也比较慢,所以每次按键都更新预览并不太可行。相反,我希望只有在用户输入停止后才进行更新。理想情况下,我希望在最后一次按键后,过一段指定的时间再进行更新。
我考虑过使用threading.Timer
,通过取消并重新启动一个定时器,在每次发出“变化”信号时调用更新函数。我也尝试过使用gtk.idle_add()
,但似乎没有成功。
下面是一个非常简单的例子——有没有人能建议我实现这个目标的最佳策略,最好能附带一个示例?
import gtk
class example:
def __init__(self):
window = gtk.Window()
window.set_title("example")
window.resize(600,400)
box = gtk.HBox(homogeneous = True, spacing = 2)
buf = gtk.TextBuffer()
buf.connect("changed", self.buf_on_change)
textInput = gtk.TextView(buf)
box.add(textInput)
self.lbl = gtk.Label()
box.add(self.lbl)
window.add(box)
window.connect("destroy", gtk.main_quit)
window.show_all()
def buf_on_change(self, buf):
txt = buf.get_text(*buf.get_bounds())
output = self.renderText(txt)
self.lbl.set_text(output)
def renderText(self, txt):
# perform computation-intensive text-manipulation here
output = txt
return output
if __name__ == '__main__':
example()
gtk.main()
2 个回答
0
因为我没有安装pygtk,所以我用termios和自己写的循环来模拟你的问题,处理文本输入。不过关于定时器的部分在gtk中也应该能正常工作。请注意,如果你在多个线程中同时使用这个类的对象,访问self.timer这个部分就不是线程安全的,这时候你必须锁住self.txt和self.timer。
import termios, fcntl, sys, os, time, threading
fd = sys.stdin.fileno()
#just setting up the terminal input
oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
class example:
def __init__(self, timeout):
self.timeout = timeout
self.timer = threading.Timer(timeout, self.render_text)
self.txt = ''
self.timer.start() #at the end of __init__
def buf_on_change(self, buf):
print "Got buffer:", buf, time.ctime()
self.txt = self.txt + buf
self.timer.cancel() #won't do no harm if timer already elapsed
self.timer = threading.Timer(self.timeout, self.render_text)
self.timer.start()
def render_text(self):
print "starting intensive computation"
print "rendering", self.txt
time.sleep(3)
print "stopping intensive computation"
# just my poor mans event loop for the text input
obj = example(3)
try:
while 1:
time.sleep(0.001) #just to keep processor cool
try:
buf = sys.stdin.read(5)
obj.buf_on_change(buf)
except IOError: pass
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
1
我想我找到了一种解决办法,使用 glib.timeout_add()
来代替 threading.Timer
:
import gtk, glib
class example:
def __init__(self):
window = gtk.Window()
window.set_title("example")
window.resize(600,400)
box = gtk.HBox(homogeneous = True, spacing = 2)
self.buf = gtk.TextBuffer()
self.buf.connect("changed", self.buf_on_change)
textInput = gtk.TextView(self.buf)
box.add(textInput)
self.lbl = gtk.Label()
box.add(self.lbl)
window.add(box)
window.connect("destroy", gtk.main_quit)
window.show_all()
self.timer = glib.timeout_add(1000, self.renderText)
def buf_on_change(self, buf):
glib.source_remove(self.timer)
self.timer = glib.timeout_add(1000, self.renderText)
def renderText(self):
txt = self.buf.get_text(*self.buf.get_bounds())
# perform computation-intensive text-manipulation here
self.lbl.set_text(txt)
return False
if __name__ == '__main__':
example()
gtk.main()
这个方法看起来能按我想要的那样工作,但因为我对gtk(还有桌面应用程序编程)完全是新手——如果你没看出来的话;)我想把这个问题留着,希望有经验的人能给我一些建议,告诉我实现这种效果的最佳方法。我希望这样没问题吧?