I would like to write a program with a Text-based User Interface (TUI) that consists of several forms.
- The first form contains a "list". Each list element represents a button.
- If the respective button is pressed, another form should appear in which one can enter the data for the list entry.
- Then the first form is displayed again (with updated list entries).
Here is my attempt, which uses the library npyscreen but does not return to the first form. The code does also not contain the logic to change the list item.
#! /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()
I found myself using Npyscreen and so I found your question. If you're still working on this application, here is your initial code, but returning to the main form this time:
So what follows is my take to this problem, which can be described as an implementation of a master-detail user interface for the console.
This uses the urwid library, building some custom widgets to achieve the described UI, which has two modes: master view (where the main widget is a pile of records) and the detail view (an overlayed dialog, with the master view behind).
There are many things that can be improved, including making it look prettier. :)
Here is the code:
The
App
class holds the state of the app, keeping track of the main widgets and contains methods that are called upon user actions like hitting the buttons save/cancel.The records are updated inplace, in the method
update_contents
of the SelectableRow widget, which represents a record being displayed in the master list.The
CancelableEdit
widget exists just to be able to react to esc from the dialog window.Feel free to ask any further clarifying question, I tried to use decent names and keep the code more or less readable, but I know that there is also a lot going on here and I'm not sure what needs to be explained in detail.
This was a fun exercise, thanks for giving me the excuse to do it! =)