如何组合多个 TUI 表单来编写更复杂的应用程序?

2024-04-30

我想写一个程序T基于外部的User I界面 (TUI https://en.wikipedia.org/wiki/Text-based_user_interface)由多种形式组成。

  • 第一种形式包含一个“列表”。每个列表元素代表一个按钮。
  • 如果按下相应的按钮,则会出现另一个表单,可以在其中输入列表条目的数据。
  • 然后再次显示第一个表单(带有更新的列表条目)。

这是我的尝试,它使用了该库npy屏幕 https://npyscreen.readthedocs.io/introduction.html但不会返回到第一种形式。该代码也不包含更改列表项的逻辑。

#! /usr/bin/env python3
# coding:utf8

import npyscreen

# content
headers = ["column 1", "column 2", "column 3", "column 4"]
entries = [["a1", "a2", "a3", "a4"],
           ["b1", "b2", "b3", "b4"],
           ["c1", "c2", "c3", "c4"],
           ["d1", "d2", "d3", "d4"], 
           ["e1", "e2", "e3", "e4"]]


# returns a string in which the segments are padded with spaces.
def format_entry(entry):
    return "{:10} | {:10} | {:10} | {:10}".format(entry[0], entry[1] , entry[2], entry[3])


class SecondForm(npyscreen.Form):
    def on_ok(self):
        self.parentApp.switchFormPrevious()

    # add the widgets of the second form
    def create(self):
        self.col1 = self.add(npyscreen.TitleText, name="column 1:")
        self.col2 = self.add(npyscreen.TitleText, name="column 2:")
        self.col3 = self.add(npyscreen.TitleText, name="column 3:")
        self.col4 = self.add(npyscreen.TitleText, name="column 4:")


class MainForm(npyscreen.Form):    
    def on_ok(self):
        self.parentApp.switchForm(None)

    def changeToSecondForm(self):
        self.parentApp.change_form("SECOND")

    # add the widgets of the main form
    def create(self):
        self.add(npyscreen.FixedText, value=format_entry(headers), editable=False, name="header")

        for i, entry in enumerate(entries):
            self.add(npyscreen.ButtonPress, when_pressed_function=self.changeToSecondForm, name=format_entry(entry))


class TestTUI(npyscreen.NPSAppManaged):
    def onStart(self):
        self.addForm("MAIN", MainForm)
        self.addForm("SECOND", SecondForm, name="Edit row")

    def onCleanExit(self):
        npyscreen.notify_wait("Goodbye!")

    def change_form(self, name):
        self.switchForm(name)


if __name__ == "__main__":
    tui = TestTUI()
    tui.run()

接下来是我对这个问题的看法,它可以被描述为一个实现主从用户界面 https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface对于控制台。

这使用了乌尔维德图书馆 http://urwid.org,建造一些自定义小部件 http://urwid.org/manual/widgets.html#custom-widgets实现所描述的 UI,它有两种模式:主视图(其中主小部件是一堆记录)和详细视图(一个覆盖的对话框,主视图在后面)。

有很多事情可以改进,包括让它看起来更漂亮。 :)

这是代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Sample program demonstrating how to implement widgets for a master-detail UI
for a list of records using the urwid library (http://urwid.org)
"""

from __future__ import print_function, absolute_import, division
from functools import partial
import urwid


PALETTE = [
    ('bold', 'bold', ''),
    ('reveal focus', 'black', 'dark cyan', 'standout'),
]


def show_or_exit(key):
    if key in ('q', 'Q', 'esc'):
        raise urwid.ExitMainLoop()


HEADERS = ["Field 1", "Field 2", "Field 3", "Field 4"]
ENTRIES = [
    ["a1", "a2", "a3", "a4"],
    ["b1", "b2", "b3", "b4"],
    ["c1", "c2", "c3", "c4"],
    ["d1", "d2", "d3", "d4"],
    ["e1", "e2", "e3", "e4"],
    ["e1", "e2", "e3", "e4"],
    ["f1", "f2", "f3", "f4"],
    ["g1", "g2", "g3", "g4"],
    ["h1", "h2", "h3", "h4"],
]


class SelectableRow(urwid.WidgetWrap):
    def __init__(self, contents, on_select=None):
        self.on_select = on_select
        self.contents = contents
        self._columns = urwid.Columns([urwid.Text(c) for c in contents])
        self._focusable_columns = urwid.AttrMap(self._columns, '', 'reveal focus')
        super(SelectableRow, self).__init__(self._focusable_columns)

    def selectable(self):
        return True

    def update_contents(self, contents):
        # update the list record inplace...
        self.contents[:] = contents

        # ... and update the displayed items
        for t, (w, _) in zip(contents, self._columns.contents):
            w.set_text(t)

    def keypress(self, size, key):
        if self.on_select and key in ('enter',):
            self.on_select(self)
        return key

    def __repr__(self):
        return '%s(contents=%r)' % (self.__class__.__name__, self.contents)


class CancelableEdit(urwid.Edit):
    def __init__(self, *args, **kwargs):
        self.on_cancel = kwargs.pop('on_cancel', None)
        super(CancelableEdit, self).__init__(*args, **kwargs)

    def keypress(self, size, key):
        if key == 'esc':
            self.on_cancel(self)
        else:
            return super(CancelableEdit, self).keypress(size, key)


def build_dialog(title, contents, background, on_save=None, on_cancel=None):
    buttons = urwid.Columns([
        urwid.Button('Save', on_press=on_save),
        urwid.Button('Cancel', on_press=on_cancel),
    ])
    pile = urwid.Pile(
        [urwid.Text(title), urwid.Divider('-')]
        + contents
        + [urwid.Divider(' '), buttons]
    )
    return urwid.Overlay(
        urwid.Filler(urwid.LineBox(pile)),
        urwid.Filler(background),
        'center',
        ('relative', 80),
        'middle',
        ('relative', 80),
    )


class App(object):
    def __init__(self, entries):
        self.entries = entries
        self.header = urwid.Text('Welcome to the Master Detail Urwid Sample!')
        self.footer = urwid.Text('Status: ready')

        contents = [
            SelectableRow(row, on_select=self.show_detail_view)
            for row in entries
        ]
        listbox = urwid.ListBox(urwid.SimpleFocusListWalker(contents))

        # TODO: cap to screen size
        size = len(entries)

        self.master_pile = urwid.Pile([
            self.header,
            urwid.Divider(u'─'),
            urwid.BoxAdapter(listbox, size),
            urwid.Divider(u'─'),
            self.footer,
        ])
        self.widget = urwid.Filler(self.master_pile, 'top')
        self.loop = urwid.MainLoop(self.widget, PALETTE, unhandled_input=show_or_exit)

    def show_detail_view(self, row):
        self._edits = [
            CancelableEdit('%s: ' % key, value, on_cancel=self.close_dialog)
            for key, value in zip(HEADERS, row.contents)
        ]
        self.loop.widget = build_dialog(
            title='Editing',
            contents=self._edits,
            background=self.master_pile,
            on_save=partial(self.save_and_close_dialog, row),
            on_cancel=self.close_dialog,
        )
        self.show_status('Detail: %r' % row)

    def save_and_close_dialog(self, row, btn):
        new_content = [e.edit_text for e in self._edits]

        row.update_contents(new_content)

        self.show_status('Updated')
        self.loop.widget = self.widget

    def close_dialog(self, btn):
        self.loop.widget = self.widget

    def show_status(self, mesg):
        self.footer.set_text(str(mesg))

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


if __name__ == '__main__':
    app = App(ENTRIES)
    app.start()

The App类保存应用程序的状态,跟踪主要小部件,并包含根据用户操作(例如点击保存/取消按钮)调用的方法。

记录在方法中就地更新update_contentsSelectableRow 小部件的 ,它表示主列表中显示的记录。

The CancelableEdit widget exists just to be able to react to esc from the dialog window.

请随意提出任何进一步澄清的问题,我尝试使用合适的名称并保持代码或多或少的可读性,但我知道这里还发生了很多事情,我不确定需要详细解释什么。

这是一个有趣的练习,谢谢你给我这个借口! =)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何组合多个 TUI 表单来编写更复杂的应用程序? 的相关文章

  • 如何关闭python服务器

    使用此代码来运行 python 服务器 import os from http server import SimpleHTTPRequestHandler HTTPServer os chdir c users owner desktop
  • 不能在jinja2宏中使用current_user?

    我使用 Flask Login 它提供了current user模板中的对象 我想编写一个宏来显示评论表单或登录链接 具体取决于用户是否登录 如果我直接在模板中使用此代码 它会起作用 if current user is authentic
  • AttributeError:模块“tensorflow.python.framework.ops”没有属性“RegisterShape”

    我正在使用 TensorFlow 2 1 0 dev20191125 不幸的是 我无法编译一个带有错误的简单示例 AttributeError 模块 tensorflow python framework ops 没有 属性 Registe
  • 如何在 python 中使用 libSVM 计算精度、召回率和 F 分数

    我想计算precision recall and f score using libsvm在Python中 但我不知道如何 我已经发现这个网站 http www csie ntu edu tw cjlin libsvmtools eval
  • 使用解析将 ** 运算符更改为幂函数?

    我的要求是将 运算符更改为幂函数 例如 1 Input B 2 Output power B 2 2 B 2 T 2 X Output power B 2 我写了下面的正则表达式来解决这个问题 rx r a zA Z0 9 a zA Z0
  • Python3如何安装.ttf字体文件?

    我想使用 python3 更精确的 Python 3 6 代码在 Windows 10 上安装 ttf 字体文件 我用谷歌搜索 但我发现的唯一的就是这个使用python在windows上安装TTF字体 https stackoverflow
  • SQLAlchemy 默认日期时间

    这是我的声明模型 import datetime from sqlalchemy import Column Integer DateTime from sqlalchemy ext declarative import declarati
  • 使用 Twisted Python 的 UDP 客户端和服务器

    我想创建一个服务器和客户端 使用 Twisted 从网络发送和接收 UDP 数据包 我已经用 Python 中的套接字编写了此代码 但想利用 Twisted 的回调和线程功能 然而 我需要 Twisted 设计方面的帮助 我想接收多种类型的
  • 如何使用lxml和python更新xml文件?

  • Pandas:向量化局部范围操作([i:i+2] 行的最大值和总和)

    我希望在数据帧中的每一行的局部范围内进行计算 同时避免速度缓慢for环形 例如 对于下面数据中的每一行 我想找到未来 3 天内 包括当天 的最高气温以及未来 3 天内的总降雨量 Day Temperature Rain 0 30 4 1 3
  • 计算二维笛卡尔坐标中不规则形状的边界

    我正在寻找一种计算不规则形状边界的解决方案 Lats take a look at Square example 如果我有Minimum x and y and Maximum x and y like MaxX 5 MinX 1 MaxY
  • 如何在 Python 中包含 PHP 脚本?

    我有一个 PHP 脚本 news generator php 当我包含它时 它会抓取一堆新闻项并打印它们 现在 我在我的网站 CGI 中使用 Python 当我使用 PHP 时 我在 新闻 页面上使用了这样的内容 为了简单起见 我删掉了这个
  • python 中的异步编程

    python 中有异步编程的通用概念吗 我可以为一个函数分配一个回调 执行它并立即返回主程序流 无论该函数的执行需要多长时间吗 您所描述的 主程序流程在另一个函数执行时立即恢复 不是通常所说的 异步 又名 事件驱动 编程 而是 多任务 又名
  • 在 Django 中翻译文件时的 Git 命令

    我在 Django 中有一个现有的应用程序 我想在页面上添加翻译 在页面上我有 trans Projects 在 po 文件中我添加了 templates staff site html 200 msgid Projects msgid P
  • 在 Mac (Catalina) 上安装 PyGame 时出错 [重复]

    这个问题在这里已经有答案了 我一直在尝试将 PyGame 安装到 Catalina 上的 Mac 上 但不知道如何安装 我收到的错误消息是 SystemExit error command gcc failed with exit stat
  • 磁盘寻道时间测量方法

    我编写了一个脚本来测量 HDD 上的寻道时间 并且其完成方式的微小变化会导致显着不同的时间 第一个周期在磁盘开头的区域内进行跳转 第二个周期选择磁盘上执行查找的随机区域 相同大小 这种方法显然不同 但我不明白为什么它会改变结果 请注意 对于
  • 为什么 Python exec 中的模块级变量无法访问?

    我正在尝试使用Pythonexec in a project https github com arjungmenon pypage执行嵌入的Python代码 我遇到的问题是在模块级 in an exec声明是难以接近的来自同一模块中定义的
  • “ModuleNotFoundError:我的 Docker 容器中没有名为 的模块”

    我正在尝试在 Docker 容器中运行 python 脚本 但我不知道为什么 python 找不到任何 python 模块 我认为它与 PYTHONPATH 环境变量有关 所以我尝试将其添加到 Dockerfile 中 如下所示 ENV P
  • 从 xgb.train() 获取概率

    我是 Python 和机器学习的新手 我在网上搜索了我的问题 并尝试了人们建议的解决方案 但仍然没有得到它 如果有人能帮助我 我将非常感激 我正在开发我的第一个 XGboost 模型 我已经使用 xgb XGBClassifier 调整了参
  • Scrapy - 持续从数据库中获取要爬取的url

    我想不断地从数据库中获取要爬行的网址 到目前为止 我成功地从基地获取了 url 但我希望我的蜘蛛继续从该基地读取 因为该表将由另一个线程填充 我有一个管道 一旦爬行 工作 就会从表中删除 url 换句话说 我想使用我的数据库作为队列 我尝试

随机推荐