Python 如何组合多个TUI表单来编写更复杂的应用程序?
我想用一个Text-basedUserIinterface()编写一个程序,它由几种形式组成Python 如何组合多个TUI表单来编写更复杂的应用程序?,python,console-application,urwid,tui,npyscreen,Python,Console Application,Urwid,Tui,Npyscreen,我想用一个Text-basedUserIinterface()编写一个程序,它由几种形式组成 第一个表单包含一个“列表”。每个列表元素代表一个按钮 如果按下相应的按钮,则会出现另一个表单,可以在其中输入列表条目的数据 然后再次显示第一个表单(带有更新的列表条目) 这是我的尝试,它使用了库,但没有返回到第一个表单。代码也不包含更改列表项的逻辑 #/usr/bin/env蟒蛇3 #编码:utf8 导入NPY屏幕 #内容 标题=[“第1列”、“第2列”、“第3列”、“第4列”] 条目=[“a1”
- 第一个表单包含一个“列表”。每个列表元素代表一个按钮李>
- 如果按下相应的按钮,则会出现另一个表单,可以在其中输入列表条目的数据
- 然后再次显示第一个表单(带有更新的列表条目)
#/usr/bin/env蟒蛇3
#编码:utf8
导入NPY屏幕
#内容
标题=[“第1列”、“第2列”、“第3列”、“第4列”]
条目=[“a1”、“a2”、“a3”、“a4”],
[“b1”、“b2”、“b3”、“b4”],
[“c1”、“c2”、“c3”、“c4”],
[“d1”、“d2”、“d3”、“d4”],
[“e1”、“e2”、“e3”、“e4”]]
#返回一个字符串,其中段用空格填充。
def格式_条目(条目):
返回“{:10}{:10}{:10}{:10}{:10}”。格式(条目[0]、条目[1]、条目[2]、条目[3])
第二类表格(npyscreen.Form):
def on_正常(自身):
self.parentApp.switchFormPrevious()
#添加第二个表单的小部件
def创建(自我):
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:”)
类主窗体(npyscreen.Form):
def on_正常(自身):
self.parentApp.switchForm(无)
def更改为秒格式(自):
self.parentApp.change_表单(“第二个”)
#添加主窗体的小部件
def创建(自我):
self.add(npyscreen.FixedText,value=format\u条目(标题),editable=False,name=“标题”)
对于i,枚举中的条目(条目):
self.add(按下时按下npyscreen.ButtonPress\u function=self.changeToSecondForm,name=format\u entry(条目))
类TestTUI(npyscreen.NPSAppManaged):
def onStart(自启动):
self.addForm(“MAIN”,MainForm)
self.addForm(“第二”,第二个表单,name=“编辑行”)
def onCleanExit(自):
npyscreen.通知您等待(“再见!”)
def更改表格(自我、姓名):
self.switchForm(名称)
如果名称=“\uuuuu main\uuuuuuuu”:
tui=TestTUI()
tui.run()
下面是我对这个问题的看法,可以将其描述为控制台的一个实现
这使用了,构建一些来实现所描述的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()
App
类保存应用程序的状态,跟踪主要小部件,并包含在用户操作时调用的方法,如点击保存/取消按钮
记录在SelectableRow小部件的方法update\u contents
中就地更新,该小部件表示主列表中显示的记录
CancelableEdit
小部件的存在只是为了能够从对话框窗口对esc做出反应
请随意提出任何进一步澄清的问题,我试图使用合适的名称,并保持代码或多或少的可读性,但我知道这里也有很多事情,我不确定需要详细解释什么
这是一个有趣的练习,谢谢你给我这样做的借口!=) 我发现自己在使用Npyscreen,所以我找到了你的问题。如果您仍在使用此应用程序,以下是您的初始代码,但这次返回到主窗体:
#! /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:")
def afterEditing(self):
self.parentApp.setNextForm("MAIN")
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()
你好这是一个非常有趣的问题,谢谢你的提问!我知道你已经将它重新标记为npyscreen(我正在跟踪urwid标记),我将尝试在urwid中实现它,因为我通常对它感兴趣,我希望你不会介意。您好,每种方法都非常受欢迎!我还取消了对特定库的限制。感谢您的努力!你的回答对我也很有启发性。