]> git.ipfire.org Git - people/ms/bricklayer.git/blob - src/python/tui/__init__.py
ce241f8c934ada182f7d65d584ad54f1e36ab617
[people/ms/bricklayer.git] / src / python / tui / __init__.py
1 ###############################################################################
2 # #
3 # Bricklayer - An Installer for IPFire #
4 # Copyright (C) 2021 IPFire Development Team #
5 # #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 ###############################################################################
20
21 import logging
22 import snack
23
24 from ..i18n import _
25
26 # Setup logging
27 log = logging.getLogger("bricklayer.tui")
28
29 class Tui(object):
30 def __init__(self, bricklayer):
31 self.bricklayer = bricklayer
32
33 # Placeholder for screen
34 self.screen = None
35
36 # Make this class usable as context
37
38 def __enter__(self):
39 log.debug("Entering TUI context")
40
41 # Setup the screen
42 self._setup_screen()
43
44 return self
45
46 def __exit__(self, type, value, traceback):
47 log.debug("Leaving TUI context")
48
49 # Wipe the screen
50 self._finish_screen()
51
52 def refresh(self):
53 """
54 Refreshes what is written on the screen
55 """
56 if self.screen:
57 self.screen.refresh()
58
59 def _setup_screen(self):
60 """
61 Sets up the screen
62 """
63 self.screen = snack.SnackScreen()
64
65 # Setup helpline
66 self.push_helpline(
67 _("<Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen")
68 )
69
70 # Refresh the screen
71 self.refresh()
72
73 def _finish_screen(self):
74 """
75 Cleanup screen
76 """
77 if self.screen:
78 self.screen.finish()
79 self.screen = None
80
81 def push_helpline(self, helpline):
82 """
83 Sets the helpline, but centers it first
84 """
85 if not self.screen:
86 raise RuntimeError()
87
88 # Center the string
89 if self.screen.width:
90 helpline = helpline.center(self.screen.width)
91
92 self.screen.pushHelpLine(helpline)
93
94 def message(self, title, text, buttons=None, help=None, width=40):
95 """
96 Shows a message to the user
97 """
98 assert self.screen
99
100 # Set default buttons
101 if buttons is None:
102 buttons = (_("OK"), _("Cancel"))
103
104 return snack.ButtonChoiceWindow(self.screen, title=title, text=text,
105 buttons=buttons, width=width, help=help)
106
107 def error(self, title, text, buttons=None, width=40):
108 if not buttons:
109 buttons = [_("Abort Installation")]
110
111 return self.message(title, text, buttons=buttons, width=width)
112
113 def progress(self, *args, **kwargs):
114 return ProgressWindow(self, *args, **kwargs)
115
116 def select(self, title, text, items, buttons=None, default=None, help=None):
117 # Translate default
118 if default:
119 default = items.get(default, None)
120
121 # Convert items into a list which is sorted by its values
122 items = sorted(items.items(), key=lambda item: item[1])
123
124 # Set some default buttons
125 if buttons is None:
126 buttons = (_("Select"), _("Cancel"))
127
128 # Show the window
129 button, item = snack.ListboxChoiceWindow(self.screen, title, text,
130 [value for key, value in items], buttons=buttons, default=default, help=help)
131
132 # Find the selected item
133 key, value = items[item]
134
135 # Return the key
136 return key
137
138 def multi_select(self, title, text, items, selection=[], buttons=None,
139 width=40, height=None, help=None):
140 assert self.screen
141
142 if height is None:
143 height = len(items)
144
145 # Set some default buttons
146 if buttons is None:
147 buttons = (_("Select"), _("Cancel"))
148
149 button_bar = snack.ButtonBar(self.screen, buttons)
150 text_box = snack.TextboxReflowed(width, text)
151
152 # Create checkboxes
153 checkboxes = snack.CheckboxTree(height, scroll=len(items) > height)
154 for key in items:
155 checkboxes.append(items[key], key, key in selection)
156
157 # Create grid
158 grid = snack.GridFormHelp(self.screen, title, help, 1, 3)
159 grid.add(text_box, 0, 0)
160 grid.add(checkboxes, 0, 1, padding=(0, 1, 0, 1))
161 grid.add(button_bar, 0, 2, growx=True)
162
163 # Run the window
164 rc = grid.runOnce()
165
166 # Return the selection
167 return checkboxes.getSelection()
168
169
170 class ProgressWindow(object):
171 def __init__(self, tui, title, text, max_value=1, width=60, help=None):
172 self.tui = tui
173
174 # Compose the window
175 textbox = snack.TextboxReflowed(width, text)
176
177 # Add the progressbar
178 scale = snack.Scale(width, total=max_value)
179 self.update_callback = scale.set
180
181 # Create the grid
182 self.grid = snack.GridFormHelp(tui.screen, title, help, 1, 3)
183 self.grid.add(textbox, 0, 0, padding=(0, 0, 0, 1))
184 self.grid.add(scale, 0, 1, growx=1)
185
186 def __enter__(self):
187 # Render the window
188 self.grid.draw()
189 self.tui.refresh()
190
191 def __exit__(self, type, value, traceback):
192 pass
193
194 def update(self, value):
195 """
196 Updates the progressbar value
197 """
198 self.update_callback(value)
199 self.tui.refresh()