如何指示 urwid 列表框的项目数多于当前显示的项目数?

2024-05-23

有没有办法向用户显示 urwid 列表框在显示部分上方/下方有其他项目?

我正在考虑类似滚动条的东西,它可以显示条目的数量。

或者列表框顶部/底部的单独栏。

如果这个行为无法实现,有哪些方法可以实现这个通知?

在我的研究过程中,我发现这个问题 https://stackoverflow.com/questions/48737120/how-do-i-determine-the-number-of-visible-items-in-a-listbox-with-urwid,最终试图实现相同的目标。 给出的答案似乎是检查所有元素是否可见。不幸的是,如果由于终端大小未调整而随时隐藏某些元素,则此功能将失去其功能。


我想我已经找到了第二个可视化概念的实现(列表框顶部和底部的栏)。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import urwid

ENTRIES = [letter for letter in "abcdefghijklmnopqrstuvwxyz"]

PALETTE = [
    ("notifier_active",   "dark cyan",  "light gray"),
    ("notifier_inactive", "black", "dark gray"),
    ("reveal_focus",      "black",      "dark cyan", "standout")
]


class MyListBox(urwid.ListBox):
    def __init__(self, body, on_focus_change=None):
        super().__init__(body)

        self.on_focus_change = on_focus_change

    # Overriden
    def change_focus(self, size, position, offset_inset=0, coming_from=None, cursor_coords=None, snap_rows=None):
        super().change_focus(size,
                             position,
                             offset_inset,
                             coming_from,
                             cursor_coords,
                             snap_rows)

        # Implement a hook to be able to deposit additional logic
        if self.on_focus_change != None:
            self.on_focus_change(size,
                                 position,
                                 offset_inset,
                                 coming_from,
                                 cursor_coords,
                                 snap_rows)


class App(object):
    def __init__(self, entries):
        # Get terminal dimensions
        terminal_cols, terminal_rows = urwid.raw_display.Screen().get_cols_rows()
        list_rows = (terminal_rows - 2) if (terminal_rows > 7) else 5       
        # (available_rows - notifier_rows) OR my preferred minimum size

        # At the beginning, "top" is always visible
        self.notifier_top = urwid.AttrMap(urwid.Text('^', align="center"),
                                          "notifier_inactive")

        # Determine presentation depending on size and number of elements
        self.notifier_bottom = urwid.AttrMap(urwid.Text('v', align="center"),
                                             "notifier_inactive" if (len(entries) <= list_rows) else "notifier_active")

        contents = [urwid.AttrMap(urwid.Button(entry), "", "reveal_focus")
                    for entry in entries]

        self.listbox = MyListBox(urwid.SimpleFocusListWalker(contents),
                                 self.update_notifiers)                   # Pass the hook

        master_pile = urwid.Pile([
            self.notifier_top,
            urwid.BoxAdapter(self.listbox, list_rows),
            self.notifier_bottom,
        ])

        widget = urwid.Filler(master_pile,
                              'top')

        self.loop = urwid.MainLoop(widget,
                                   PALETTE,
                                   unhandled_input=self.handle_input)

    # Implementation for hook
    def update_notifiers(self, size, position, offset_inset, coming_from, cursor_coords, snap_rows):
        # which ends are visible? returns "top", "bottom", both or neither.
        result = self.listbox.ends_visible(size)

        if ("top" in result) and ("bottom" in result):
            self.notifier_top.set_attr_map({None:"notifier_inactive"})
            self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
        elif "top" in result:
            self.notifier_top.set_attr_map({None:"notifier_inactive"})
            self.notifier_bottom.set_attr_map({None:"notifier_active"})
        elif "bottom" in result:
            self.notifier_top.set_attr_map({None:"notifier_active"})
            self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
        else:
            self.notifier_top.set_attr_map({None:"notifier_active"})
            self.notifier_bottom.set_attr_map({None:"notifier_active"})

    def handle_input(self, key):
        if key in ('q', 'Q', 'esc'):
            self.exit()

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

    def exit(self):
        raise urwid.ExitMainLoop()


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

基本上,我创建了一个子类urwid.Listbox并覆盖它的change_focus()添加钩子的方法。显然,当焦点发生变化时,该方法会在内部被调用。

实际逻辑使用的结果ends_visible()方法,它返回列表框当前可见的末端(顶部、底部、两者或都不返回)。根据这一点,我修改了两个周围的呈现方式urwid.Text元素。

该代码生成以下 TUI:


我还编写了代码的变体,它基于原始规范:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import urwid

HEADERS = ["column 1",
           "column 2",
           "column 3",
           "column 4"]

ENTRIES = [["{}1".format(letter),
            "{}2".format(letter),
            "{}3".format(letter),
            "{}4".format(letter)] for letter in "abcdefghijklmnopqrstuvwxyz"]

PALETTE = [
    ("column_headers", "white, bold", ""),
    ("notifier_active",   "dark cyan",  "light gray"),
    ("notifier_inactive", "black", "dark gray"),
    ("reveal_focus",      "black",      "dark cyan", "standout")
]


class SelectableRow(urwid.WidgetWrap):
    def __init__(self, contents, on_select=None):
        self.contents = contents
        self.on_select = on_select

        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 MyListBox(urwid.ListBox):
    def __init__(self, body, on_focus_change=None):
        super().__init__(body)

        self.on_focus_change = on_focus_change

    # Overriden
    def change_focus(self, size, position, offset_inset=0, coming_from=None, cursor_coords=None, snap_rows=None):
        super().change_focus(size,
                             position,
                             offset_inset,
                             coming_from,
                             cursor_coords,
                             snap_rows)

        # Implement a hook to be able to deposit additional logic
        if self.on_focus_change != None:
            self.on_focus_change(size,
                                 position,
                                 offset_inset,
                                 coming_from,
                                 cursor_coords,
                                 snap_rows)


class App(object):
    def __init__(self, entries):
        # Get terminal dimensions
        terminal_cols, terminal_rows = urwid.raw_display.Screen().get_cols_rows()
        list_rows = (terminal_rows - 6) if (terminal_rows > 11) else 5       
        # (available_rows - divider_rows - column_headers_row - notifier_rows) OR my preferred minimum size

        column_headers = urwid.AttrMap(urwid.Columns([urwid.Text(c) for c in HEADERS]),
                                       "column_headers")

        # At the beginning, "top" is always visible
        self.notifier_top = urwid.AttrMap(urwid.Text('^', align="center"),
                                          "notifier_inactive")

        # Determine presentation depending on size and number of elements
        self.notifier_bottom = urwid.AttrMap(urwid.Text('v', align="center"),
                                             "notifier_inactive" if (len(entries) <= list_rows) else "notifier_active")

        contents = [SelectableRow(entry) for entry in entries]

        self.listbox = MyListBox(urwid.SimpleFocusListWalker(contents),
                                 self.update_notifiers)                    # Pass the hook

        master_pile = urwid.Pile([
            urwid.Divider(u'─'),
            column_headers,
            urwid.Divider(u'─'),
            self.notifier_top,
            urwid.BoxAdapter(self.listbox, list_rows),
            self.notifier_bottom,
            urwid.Divider(u'─'),
        ])

        widget = urwid.Filler(master_pile,
                              'top')

        self.loop = urwid.MainLoop(widget,
                                   PALETTE,
                                   unhandled_input=self.handle_input)

    # Implementation for hook
    def update_notifiers(self, size, position, offset_inset, coming_from, cursor_coords, snap_rows):
        # which ends are visible? returns "top", "bottom", both or neither.
        result = self.listbox.ends_visible(size)

        if ("top" in result) and ("bottom" in result):
            self.notifier_top.set_attr_map({None:"notifier_inactive"})
            self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
        elif "top" in result:
            self.notifier_top.set_attr_map({None:"notifier_inactive"})
            self.notifier_bottom.set_attr_map({None:"notifier_active"})
        elif "bottom" in result:
            self.notifier_top.set_attr_map({None:"notifier_active"})
            self.notifier_bottom.set_attr_map({None:"notifier_inactive"})
        else:
            self.notifier_top.set_attr_map({None:"notifier_active"})
            self.notifier_bottom.set_attr_map({None:"notifier_active"})

    def handle_input(self, key):
        if key in ('q', 'Q', 'esc'):
            self.exit()

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

    def exit(self):
        raise urwid.ExitMainLoop()


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

唯一真正的区别是,我使用的实例SelectableRow代替urwid.Button. (SelectableRow被取自用户 elias 的回答 https://stackoverflow.com/questions/52106244/how-do-you-combine-multiple-tui-forms-to-write-more-complex-applications#answer-52174629.)

这是相应的 TUI:

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

如何指示 urwid 列表框的项目数多于当前显示的项目数? 的相关文章

  • Scrapy 在抓取一长串 url 时陷入困境

    我正在抓取一个大的 url 列表 1000 左右 并且在设定的时间后 爬虫程序会以 0 页 分钟的速度爬行 爬行时问题总是出现在同一个位置 url 列表是从 MySQL 数据库检索的 我对 python 和 scrapy 相当陌生 所以我不
  • 不能在jinja2宏中使用current_user?

    我使用 Flask Login 它提供了current user模板中的对象 我想编写一个宏来显示评论表单或登录链接 具体取决于用户是否登录 如果我直接在模板中使用此代码 它会起作用 if current user is authentic
  • Python - 定义常量列表或字典的最佳/最简洁的方法

    第一次使用堆栈溢出 我很高兴来到这里 简介 我最近开始了 Python 编程世界的神奇冒险 我喜欢它 现在 在我从 C 语言的尴尬过渡中 一切都进展顺利 但我在创建与标头文件 h 同义的内容时遇到了麻烦 问题 我有中等大小的字典和列表 大约
  • 我无法使用 Python 和 Facebook Marketing API 获取所有 Facebook 营销活动的统计信息

    我正在尝试检索以下指标 date campaign name impressions clicks spend 在我的 Facebook 帐户中的所有活动中 但显然我编写的脚本仅返回某些活动的统计数据 而不是全部 它仅返回大多数营销活动的营
  • PyQt4 信号和槽

    我正在使用 PyQt4 编写我的第一个 Python 应用程序 我有一个 MainWindow 和一个 Dialog 类 它是 MainWindow 类的一部分 self loginDialog LoginDialog 我使用插槽和信号 这
  • 插入多行并返回主键时 Sqlalchemy 的奇怪行为

    插入多行并返回主键时 我注意到一些奇怪的事情 如果我在 isert 查询中添加使用参数值 我会得到预期的行为 但是当将值传递给游标时 不会返回任何内容 这可能是一个错误还是我误解了什么 我的sqlachemy版本是0 9 4 下面如何重现错
  • python 类的属性不在 __init__ 中

    我想知道为什么下面的代码有效 usr bin env python3 import sys class Car def init self pass if name main c Car c speed 3 c time 5 print c
  • 计算 for 循环期间的运行总计 - Python

    编辑 下面是我根据收到的反馈 答案编写的工作代码 这个问题源于我之前使用 MIT 的开放课件学习 Python CS 时提出的问题 在这里查看我之前的问题 https stackoverflow com questions 4990159
  • 预处理 csv 文件以与 tflearn 一起使用

    我的问题是关于在将 csv 文件输入神经网络之前对其进行预处理 我想使用 python 3 中的 tflearn 为著名的 iris 数据集构建一个深度神经网络 数据集 http archive ics uci edu ml machine
  • SQLAlchemy 默认日期时间

    这是我的声明模型 import datetime from sqlalchemy import Column Integer DateTime from sqlalchemy ext declarative import declarati
  • 在 PyCharm 中运行命令行命令

    你好 我正在使用Python 但之前从未真正使用过它 我收到一些命令 需要在终端中运行 基本上 python Test py GET feeds 我正在使用 PyCharm 我想知道是否有办法从该 IDE 中运行这些相同的命令 按 Alt
  • Python:动态向对象添加字段

    我想知道是否可以动态向对象添加字段 例如 我希望能够添加如下内容 user object user first name John user last name Smith 当我在 Python 命令行解释器中执行该命令时 我得到 Attr
  • 使用 python 写入 aws lambda 中的 /tmp 目录

    Goal 我正在尝试将 zip 文件写入 python aws lambda 中的 tmp 文件夹 因此我可以在压缩之前提取操作 并将其放入 s3 存储桶中 Problem 操作系统 Errno30 只读文件系统 这段代码在我的计算机上进行
  • 将多个 isinstance 检查转换为结构模式匹配

    我想转换此现有代码以使用模式匹配 if isinstance x int pass elif isinstance x str x int x elif isinstance x float Decimal x round x else r
  • Mac 上的 Errno 13 权限被拒绝

    我只是测试如何从一个 py 文件调用外部 py 文件 我有 2 个 py 文件 都在同一目录中 这是主要代码 runext py 假设调用 ext py import subprocess subprocess call Users tra
  • 使用 Matplotlib、PyQt 和 Threading 进行实时绘图导致 python 崩溃

    我一直在努力研究我的 Python 应用程序 但找不到任何答案 我有 PyQT GUI 应用程序 它使用 Matplotlib 小部件 GUI 启动一个新线程来处理 mpl 小部件的绘图 恐怕我现在通过从另一个线程访问 matplotlib
  • python 中的异步编程

    python 中有异步编程的通用概念吗 我可以为一个函数分配一个回调 执行它并立即返回主程序流 无论该函数的执行需要多长时间吗 您所描述的 主程序流程在另一个函数执行时立即恢复 不是通常所说的 异步 又名 事件驱动 编程 而是 多任务 又名
  • Python:如何使用生成器来避免 sql 内存问题

    我有以下方法来访问 mysql 数据库 并且查询在服务器中执行 我无权更改有关增加内存的任何内容 我对生成器很陌生 并开始阅读更多有关它的内容 并认为我可以将其转换为使用生成器 def getUNames self globalUserQu
  • Python写入dbf数据时出错

    我得到这个错误 DbfError unable to modify fields individually except in with or Process 如何修复它 这是我的code with dbf Table aa dbf as
  • 无法在 Python 2.4 中解码 unicode 字符串

    这是Python 2 4 中的 这是我的情况 我从数据库中提取一个字符串 它包含一个变音的 o xf6 此时 如果我运行 type value 它会返回 str 然后我尝试运行 decode utf 8 但收到错误 utf8 编解码器无法解

随机推荐

  • 如何从 Boost.PropertyTree 复制子树

    我有一些boost property tree ptree 我需要树来删除一些具有特定标签名称的元素 例如 xml 表示源ptree如下
  • 从请求url获取hash参数

    我有这样的网址 http www coolsite com daily plan id 1 http www coolsite com daily plan id 1解析该字符串并读取哈希值 id 之后的值 的最简单方法是什么 谢谢 在客户
  • 如何使用foldr为列表创建显示实例?

    我想为我的数据类型 我的列表 编写自己的显示实例 到目前为止 我的方法是有效的 但我总是在末尾有一个逗号 我已经尝试用最后一个元素启动折叠并将其从列表中删除 但它很麻烦而且不起作用 有没有更简单的方法来获得正确的解决方案 实际 1 2 3
  • F# 和 MEF:导出函数

    因此 我试图在 F 控制台应用程序中运行这个简单的测试 open System Reflection open System ComponentModel Composition open System ComponentModel Com
  • 未捕获的类型错误:对象 # 在 Chrome 中没有“查找”方法

    可能与 未捕获的类型错误 对象 没有方法 查找 https stackoverflow com q 11134646 561731 这是我的问题的讨论的聊天记录 https chat stackoverflow com rooms 17 c
  • Safari 扩展应用程序未显示在 Safari 首选项“扩展”选项卡中

    我已遵循以下提到的所有说明创建您的第一个 Safari 扩展应用程序 https developer apple com library content documentation NetworkingInternetWeb Concept
  • MySQL 连接器 C++ 64 位在 Visual Studio 2012 中从源代码构建

    我正在尝试建立mySQL 连接器 C 从源头在视觉工作室2012为了64 bit建筑学 我知道这取决于一些boost头文件和C 连接器 跑步CMake生成一个项目文件 但该项目文件无法编译 因为有一大堆非常令人困惑的错误 这些错误可能与包含
  • 如何将项目插入到特定索引处的空数组中?

    我想将一个项目插入到空数组的指定索引中 我看到有 Array prototype splice 方法 但是 如果我在空数组上使用 splice 它只会添加项目来结束数组 如下所示 var a a splice 3 0 item 3 cons
  • 从使用heroku发送的邮件中删除“via sendgrid.me”

    我正在使用免费的 sendgrid 计划从 Heroku 上托管的 Rails 应用程序发送电子邮件 我使用以下组合进行设置这些说明 http devcenter heroku com articles sendgrid and 本教程 h
  • 如何使用 Javascript OAuth 库不暴露您的密钥?

    看着Twitter OAuth 库 https dev twitter com docs twitter libraries 我看到了这个注释 将 JavaScript 与 OAuth 结合使用时要小心 不要暴露你的钥匙 然后 看着jsOA
  • Postgres 按查询分组

    我正在尝试在 postgres 的查询中使用 group by 我无法让它按照我想要的方式工作 以便根据需要对结果进行分组 这是另一个堆栈问题的扩展我刚刚回答过的递归查询 https stackoverflow com questions
  • SQLAPI++ 的免费替代品? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何免费 也许是开源 的替代品SQLAPI http www sqlapi com 这个库看起来
  • dyld:无法加载插入的库,但可以在模拟器和另一部 iPhone 上使用

    所以我在过去的几个小时里在我的应用程序上编码 在某个时候我决定在我的 iPhone 8 13 3 1 上启动我的应用程序而不是使用模拟器 13 3 它立即崩溃并出现以下错误 dyld warning could not load inser
  • 如何将样式应用于我拥有的所有 TextView? [复制]

    这个问题在这里已经有答案了 可能的重复 设计所有 TextView 或自定义视图 的样式 而不向每个 TextView 添加样式属性 https stackoverflow com questions 6801890 styling all
  • 当目标小于 Android O 时,如何在 Android O 上创建快捷方式?

    背景 Android O 对快捷方式的工作方式进行了各种更改 https developer android com preview behavior changes html as https developer android com
  • 了解日期并使用 R 中的 ggplot2 绘制直方图

    主要问题 当尝试使用 ggplot2 制作直方图时 我无法理解为什么日期 标签和中断的处理无法像我在 R 中预期的那样工作 我在找 我的约会频率的直方图 刻度线位于匹配条下方的中心 日期标签在 Y b format 适当的限制 最小化网格空
  • ASP.NET Core 中间件与过滤器

    在阅读了 ASP NET Core 中间件之后 我对何时应该使用过滤器以及何时应该使用中间件感到困惑 因为它们似乎实现了相同的目标 什么时候应该使用中间件而不是过滤器 9频道有一个关于此的视频 ASP NET 怪物 91 中间件与过滤器 h
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 我可以限制分布式应用程序发出的请求吗?

    我的应用程序发出 Web 服务请求 提供商处理的请求有最大速率 因此我需要限制它们 当应用程序在单个服务器上运行时 我曾经在应用程序级别执行此操作 一个对象跟踪到目前为止已发出的请求数量 并在当前请求超出允许的最大负载时等待 现在 我们正在
  • 如何指示 urwid 列表框的项目数多于当前显示的项目数?

    有没有办法向用户显示 urwid 列表框在显示部分上方 下方有其他项目 我正在考虑类似滚动条的东西 它可以显示条目的数量 或者列表框顶部 底部的单独栏 如果这个行为无法实现 有哪些方法可以实现这个通知 在我的研究过程中 我发现这个问题 ht