如何在 gtk.TextBuffer 中等待用户输入暂停?

发布于 2024-08-19 22:22:28 字数 1191 浏览 2 评论 0原文

我正在尝试在 pygtk 中编写一个简单的基于 gui 的应用程序,它提供基于文本的标记的“实时”预览。然而,标记处理的计算成本可能相当高,而且运行速度很慢,因此在每次击键时更新预览实际上并不可行。相反,我希望仅在用户输入失败时才运行更新。理想情况下,我希望它在序列中最后一次击键后更新指定的间隔。

我研究过使用 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()

I'm trying to write a simple gui-based application in pygtk which provides 'live' previewing of text-based markup. The markup processing, however, can be quite computationally expensive and slow to run, so updating the preview on every keystroke is not really viable. Instead I'd like to have the update run only when user input lapses. Ideally, I'd have it update a specified interval after the last keystroke in a sequence.

I've looked into using threading.Timer, by cancelling and re-starting a timer to invoke the update function each time the "changed" signal is emitted. I've also tried to use gtk.idle_add(), but I can't seem to get it working.

Below is a very simple example - could someone suggest the best strategy to achieve what I'm after, preferably with an example?

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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

迷爱 2024-08-26 22:22:28

所以我想我找到了一个使用 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 完全陌生(以及一般的桌面 dui 编程 - 以防万一您无法分辨;) ) 我想将这个问题保留下来,希望有更多经验的人可以对实现这种效果的最佳方法发表评论。我希望这样可以吗?

so I think I found a solution using glib.timeout_add() instead of 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()

this seems to work as desired, but since I'm completely new to gtk (and desktop dui programming in general - in case you couldn't tell ;) ) I'd like to leave this question open in the hope that someone more experience might comment on the best way to achieve this kind of effect. I hope that's okay?

对岸观火 2024-08-26 22:22:28

所以我没有 pygtk,所以我使用 termios 和一个自制的文本输入循环来模仿你的问题。但是关于计时器的部分也应该在 gtk 中工作。请注意,如果您在多个线程中使用类示例的对象,同时必须锁定 self.txt 和 self.timer,则对 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)

So i did not have pygtk, so i mimiced your problem using termios and a home made loop for the text input. But the part about the timer should work in gtk too. Please note that the access to the self.timer is not thread safe if you use an object of class example within more than one thread at the time you must lock self.txt and 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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文