首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何监听urwid中的文件更改?

如何监听urwid中的文件更改?
EN

Stack Overflow用户
提问于 2021-09-18 14:19:38
回答 1查看 39关注 0票数 0

我想远程控制一个python应用程序,它使用urwid作为用户界面。

我的想法是创建一个文件,将它的名称作为命令行参数传递给应用程序,每当我写入文件时,应用程序都会从该文件中读取。

Urwid的事件循环有一个watch_file(fd, callback)方法。这个方法被描述为“当fd有一些数据要读取时调用callback()”。这听起来和我想要的一模一样,但它会导致无限循环。回调被尽可能频繁地执行,尽管文件是空的。即使我删除了文件,仍然会调用回调。

代码语言:javascript
复制
#!/usr/bin/env python3

import urwid
import atexit

def onkeypress(key, size=None):
    if key == 'q':
        raise urwid.ExitMainLoop()
    text.set_text(key)

def onfilechange():
    text.set_text(cmdfile.read())
    # clear file so that I don't read already executed commands again
    # and don't run into an infinite loop - but I am doing that anyway
    with open(cmdfile.name, 'w') as f:
        pass

cmdfile = open('/tmp/cmd', 'rt')
atexit.register(cmdfile.close)

text = urwid.Text("hello world")
filler = urwid.Filler(text)
loop = urwid.MainLoop(filler, unhandled_input=onkeypress)
loop.watch_file(cmdfile, onfilechange)

if __name__ == '__main__':
    loop.run()

(我最初的想法是只打开文件以供读取,而不是始终保持打开状态,但fd必须是文件对象,而不是路径。)

Urwid提供了几个different event loops。默认情况下,使用SelectEventLoop。GLibEventLoop也有同样的行为,它会陷入无限循环。相反,AsyncioEventLoop会抛出一个“不允许操作”异常。TwistedEventLoop和TornadoEventLoop需要额外的软件才能安装。

我曾考虑过使用独立的watchdog库,但从另一个线程访问用户界面似乎需要编写一个新的循环,请参阅this stack overflow question。这个问题的答案是建议轮询,而我更希望避免轮询。

如果urwid专门提供了一种监视文件的方法,我不敢相信它在任何实现中都不起作用。那么我到底做错了什么呢?

如何对python/urwid应用程序中的文件更改做出反应?

编辑:我试过使用named pipes (并删除了清除文件的代码),但从视觉上看,它有相同的行为:应用程序无法启动。但是,听起来有一个很大的不同:它不会进入无限循环,直到我写入文件。在我写入文件之前,回调没有被调用,但是应用程序也没有启动,它什么也不做。在我写入文件后,它的行为就像上面描述的常规文件一样。

EN

回答 1

Stack Overflow用户

发布于 2021-09-26 09:54:00

我找到了以下解决方法:读取另一个线程中的命名管道,保护队列中的每一行,并在UI线程中轮询以查看队列中是否有东西。

使用mkfifo /tmp/mypipe创建命名管道。然后使用echo >>/tmp/mypipe "some text"对其进行写入。

代码语言:javascript
复制
#!/usr/bin/env python3

import os
import threading
import queue

import urwid


class App:

    POLL_TIME_S = .5

    def __init__(self):
        self.text = urwid.Text("hello world")
        self.filler = urwid.Filler(self.text)
        self.loop = urwid.MainLoop(self.filler, unhandled_input=self.onkeypress)


    def watch_pipe(self, path):
        self._cmd_pipe = path
        self.queue = queue.Queue()
        threading.Thread(target=self._read_pipe_thread, args=(path,)).start()
        self.loop.set_alarm_in(0, self._poll_queue)

    def _read_pipe_thread(self, path):
        while self._cmd_pipe:
            with open(path, 'rt') as pipe:
                for ln in pipe:
                    self.queue.put(ln)

        self.queue.put("!! EOF !!")

    def _poll_queue(self, loop, args):
        while not self.queue.empty():
            ln = self.queue.get()
            self.text.set_text(ln)
        self.loop.set_alarm_in(self.POLL_TIME_S, self._poll_queue)

    def close(self):
        path = self._cmd_pipe

        # stop reading
        self._cmd_pipe = None
        with open(path, 'wt') as pipe:
            pipe.write("")

        os.remove(path)


    def run(self):
        self.loop.run()


    def onkeypress(self, key, size=None):
        if key == 'q':
            raise urwid.ExitMainLoop()
        self.text.set_text(key)


if __name__ == '__main__':
    a = App()
    a.watch_pipe('/tmp/mypipe')
    a.run()
    a.close()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69235483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档