PLUTO
menu.py
Go to the documentation of this file.
1 ################################################################
2 #
3 # menu.py provides terminal control capabilities to browse
4 # through a user-defined menu.
5 # It employs the Python curses library.
6 # An alternative (text-based) menu is also possible if the
7 # --no-curses is given in the command line options.
8 #
9 # Last modified:
10 #
11 # Tuesday June 22, 2010 by A. Mignone (mignone@ph.unito.it)
12 #
13 ################################################################
14 
15 import os, sys, traceback, time, string
16 have_curses = 1
17 for x in sys.argv: # avoid curses library with the --no-curses option.
18  if (x == "--no-curses"):
19  have_curses = 0
20 
21 if (have_curses == 1):
22  import curses, curses.textpad
23 
24 #####################################################
25 #
26 # The class gb contains global variables and
27 # pointers to ease up accessibility in coding the
28 # functions of this module
29 #
30 #####################################################
31 class gb:
32  scrn = None # a pointer to a window object
33  rbeg = 6 # starting row
34  cbeg = 1 # starting column
35  row = 0 # the current row
36  ncol = 1 # number of columns in the menu
37  csep = 30 # separation between multi-columns
38  title = 'No Title'
39  subtitle = ''
40  init = 0 # a value of 0 means that curses functionality has not
41  # been activated (i.e. curses.initscr() has never been called)
42 
43 
44 #####################################################
45 #
46 # Sets the title (but does not print it)
47 # that will be used by ShowMenu
48 #
49 #####################################################
50 def SetTitle (title, subtitle = ''):
51 
52  gb.title = title
53  gb.subtitle = subtitle
54 
55 #####################################################
56 #
57 # Print a message on the screen
58 #
59 #####################################################
60 def Print (message, sleep=0.7,row=1):
61 
62  for x in sys.argv: # avoid curses library with the --no-curses option.
63  if (x == "--no-curses" or gb.init == 0):
64  Print_no_curses(message,sleep,row)
65  return
66 
67  if (gb.scrn == None): return # need this when using test_pluto.py script
68  if (row == 1): gb.scrn.erase()
69  gb.scrn.addstr(row,1,message, curses.A_BOLD)
70  gb.scrn.refresh()
71  time.sleep(sleep)
72 
73 #####################################################
74 #
75 # Prompt a message, wait for any key to be pressed
76 #
77 #####################################################
78 def Prompt (message):
79 
80  for x in sys.argv: # avoid curses library with the --no-curses option.
81  if (x == "--no-curses"):
82  Prompt_no_curses(message)
83  return
84 
85  gb.scrn.erase()
86  gb.scrn.addstr(1,1,message, curses.A_BOLD)
87  gb.scrn.refresh()
88  c = gb.scrn.getch()
89 
90 #####################################################
91 #
92 # Show menu using entries (1st column) and
93 # default (2nd column).
94 # row is an optional argument giving the line
95 # to be highlighted (default is first line)
96 #
97 #####################################################
98 def ShowMenu(entries, default, row=0):
99  # display title
100 
101  gb.scrn.clear()
102  gb.scrn.addstr(0,0,">> "+gb.title+" <<", curses.A_BOLD)
103  gb.rbeg = 3
104  if (len(gb.subtitle) > 1):
105  gb.scrn.addstr(2,0, gb.subtitle, curses.A_UNDERLINE)
106  gb.rbeg = 6
107 
108  lastrow = gb.rbeg
109  for ln in entries[0:len(entries)]:
110  indx = entries.index(ln)
111  gb.scrn.addstr(lastrow, gb.cbeg, ln)
112  if (gb.ncol == 2):
113  gb.scrn.addstr(lastrow, gb.cbeg + gb.csep, default[indx])
114  lastrow += 1
115 
116  if (row == 0 or gb.row < gb.rbeg or gb.row > lastrow):
117  gb.row = gb.rbeg # initial position
118 
119  n = gb.row - gb.rbeg
120  gb.scrn.addstr(gb.row, gb.cbeg , entries[n], curses.A_REVERSE)
121  if (gb.ncol == 2): gb.scrn.addstr(gb.row, gb.cbeg+gb.csep, default[n], curses.A_UNDERLINE)
122  gb.scrn.refresh()
123 
124 
125 #####################################################
126 #
127 # Allow the cursor to move up and down in the list
128 #
129 #####################################################
130 def UpDown(entries, default, inc):
131  tmp = gb.row + inc
132 
133  # ignore attempts to go off the edge of menu
134 
135  if tmp >= gb.rbeg and tmp < (gb.rbeg + len(entries)):
136  # unhighlight the current line by rewriting it in default attributes
137  gb.scrn.addstr(gb.row, gb.cbeg , entries[gb.row-gb.rbeg])
138  if (gb.ncol == 2): gb.scrn.addstr(gb.row, gb.cbeg + gb.csep, default[gb.row-gb.rbeg])
139  # highlight the previous/next line
140  gb.row = tmp
141  c1 = entries[gb.row-gb.rbeg]
142  if (gb.ncol == 2): c2 = default[gb.row-gb.rbeg]
143 
144  gb.scrn.addstr(gb.row, gb.cbeg , c1, curses.A_REVERSE)
145  if (gb.ncol == 2): gb.scrn.addstr(gb.row, gb.cbeg + gb.csep, c2, curses.A_UNDERLINE)
146  gb.scrn.refresh()
147 
148 
149 #####################################################
150 #
151 # Allow left/right keys to switch options in the
152 # second column and change default values
153 #
154 #####################################################
155 def LeftRight(entries, default, options, inc):
156 
157  i = gb.row - gb.rbeg
158  idef = options[i].index(default[i])
159  nopt = len(options[i])
160  if (inc > 0): idef = idef + 1
161  if (inc < 0): idef = idef - 1
162  if (idef < 0): idef = nopt-1
163  if (idef == nopt): idef = 0
164 
165  default[i] = options[i][idef]
166  gb.scrn.addstr(gb.row, gb.cbeg , entries[i], curses.A_REVERSE)
167  gb.scrn.addstr(gb.row, gb.cbeg + gb.csep, default[i], curses.A_UNDERLINE)
168  gb.scrn.clrtoeol()
169  gb.scrn.refresh()
170 
171 #####################################################
172 #
173 # Browse a menu with entries (1st column) and
174 # default (2nd column, optional)
175 # Note: with Python > 2.5 we had some troubles
176 # initializing curses more than once.
177 # For this reason we prefer to initialize
178 # curses only at the beginning.
179 #
180 #####################################################
181 def Browse(entries, default=[], options=[]):
182 
183  gb.ncol = 1
184  if (len(default) > 0): gb.ncol = 2
185 
186  for x in sys.argv: # avoid curses library with the --no-curses option.
187  if (x == "--no-curses"):
188  return Browse_no_curses(entries, default, options)
189 
190 #
191 # window setup will be done just once.
192 #
193  if (gb.init == 0):
194  gb.scrn = curses.initscr()
195  curses.noecho()
196  curses.cbreak()
197  gb.scrn.keypad(1)
198  gb.init = 1
199  ShowMenu(entries, default)
200  while True:
201  # get user command
202  c = gb.scrn.getch()
203  try: cc = chr(c)
204  except: cc = 0
205 
206  if (c == 10):
207 # RestoreScreen()
208  return entries[gb.row-gb.rbeg]
209  elif (cc == 'q'):
210  RestoreScreen()
211 # curses.reset_shell_mode()
212  sys.exit()
213  elif (cc == 'u' or c == curses.KEY_UP): UpDown(entries, default, -1)
214  elif (cc == 'd' or c == curses.KEY_DOWN): UpDown(entries, default, 1)
215  elif (gb.ncol > 1):
216  if (cc == 'r' or c == curses.KEY_RIGHT):
217  LeftRight(entries, default, options, 1)
218  elif (cc == 'l' or c == curses.KEY_LEFT):
219  LeftRight(entries, default, options, -1)
220 
221 #####################################################
222 #
223 # Similar to Browse, but allow the user to directly
224 # input the default values by a reading a string
225 #
226 #####################################################
227 def Insert(entries, default):
228 
229  gb.ncol = 2
230 
231  for x in sys.argv: # avoid curses library with the --no-curses option.
232  if (x == "--no-curses"):
233  return Insert_no_curses(entries, default)
234 
235  #
236  # window setup will be done just once.
237  #
238 
239  if (gb.init == 0):
240  gb.scrn = curses.initscr()
241  curses.noecho()
242  curses.cbreak()
243  gb.scrn.keypad(1)
244  gb.init = 1
245 
246 # entries = []
247 # for n in range(num): entries.append(repr(n))
248 
249 # RestoreScreen()
250 # print default
251 # sys.exit()
252 
253  ShowMenu(entries, default)
254  while True:
255  c = gb.scrn.getch() # get user command
256  try: cc = chr(c)
257  except: cc = 0
258 
259  if (c == 10):
260  return
261  elif (cc == 'q'):
262  RestoreScreen()
263  sys.exit()
264  elif (cc == 'u' or c == curses.KEY_UP): UpDown(entries, default, -1)
265  elif (cc == 'd' or c == curses.KEY_DOWN): UpDown(entries, default, 1)
266  elif (cc == 'r' or c == curses.KEY_RIGHT):
267  curses.echo()
268  gb.scrn.addstr(gb.row,gb.cbeg+gb.csep,' ')
269  gb.scrn.addstr(gb.row,gb.cbeg+gb.csep,'NAME or VALUE > ',curses.A_UNDERLINE)
270  new_name = gb.scrn.getstr()
271  i = gb.row-gb.rbeg
272  default.pop(i)
273  default.insert(i,new_name)
274  curses.noecho()
275  gb.scrn.clrtoeol()
276  ShowMenu(entries,default, gb.row)
277 
278 
279 #####################################################
280 #
281 # Restore screen back to shell functionality.
282 # Note that RestoreScreen should be followed by
283 # sys.exit() in order to avoid troubleshooting
284 # observed with Python > 2.5
285 #
286 #####################################################
288 
289  for x in sys.argv: # avoid curses library with the --no-curses option.
290  if (x == "--no-curses"):
291  return
292 
293  curses.reset_shell_mode()
294  curses.nocbreak()
295  gb.scrn.keypad(0)
296  curses.echo()
297  curses.endwin()
298 
299 if __name__ == '__browse__':
300  try:
301  browse()
302  except:
303  RestoreScreen()
304  # print error message re exception
305  traceback.print_exc()
306 
307 #####################################################
308 #
309 # Return 1 if curses have been activated
310 #
311 #####################################################
313 
314  return gb.init
315 
316 #####################################################
317 #
318 # The next set of functions replicate the previous
319 # ones without using curses library.
320 # They are intended to provide a simpler way select
321 # options through a terminal-based replacement.
322 #
323 #####################################################
324 def Print_no_curses(message, sleep, row):
325 
326  global xglb
327 # if (row == 1): os.system("clear")
328  print message
329  time.sleep(sleep)
330 
331 ######################################################
332 def Prompt_no_curses (message):
333 #
334 #
335 ######################################################
336 
337  os.system("clear")
338  print message
339  q = raw_input()
340 
341 ######################################################
342 def Browse_no_curses(entries, default, options):
343 #
344 #
345 ######################################################
346 
347  q = "c"
348  while (q != ''):
349  os.system("clear")
350  print ">> ",gb.title+"\n"
351  for x in entries:
352  i = entries.index(x)
353  if (len(default) > 0):
354  print str(i).rjust(2),') ',x.ljust(28), default[i]
355  else:
356  print str(i).rjust(2),') ',x.ljust(28)
357 
358  print " "
359  q = raw_input(">> choice ? ")
360  if (q == ''):
361  print "Enter"
362  else:
363  try:
364  q = int(q)
365  if (len(default) == 0): return entries[q]
366  except:
367  continue
368 
369  opt_list = ''
370  for x in options[q]:
371  i = options[q].index(x)
372  opt_list += repr(i)+") "+x+" "
373 
374  print "\n"+entries[q]+": ",opt_list
375  c = raw_input(">> choice ["+default[q]+"] ? ")
376  try:
377  c = int(c)
378  except:
379  continue
380 
381  default[q] = options[q][c]
382 
383  return
384 ######################################################
385 def Insert_no_curses(entries, names):
386 #
387 #
388 ######################################################
389 
390  q = "c"
391  while (q != ''):
392  os.system("clear")
393  print ">> ",gb.title+"\n"
394  for x in entries:
395  i = entries.index(x)
396  print str(i).rjust(2),') ',names[i].ljust(28)
397 
398  print " "
399  q = raw_input(">> choice ? ")
400  if (q == ''):
401  print "Enter"
402  else:
403  try:
404  q = int(q)
405  except:
406  continue
407 
408  newname = raw_input(">> new name ? ")
409  names[q] = newname
410 
411  return
def RestoreScreen()
Restore screen back to shell functionality.
Definition: menu.py:287
def ShowMenu
Show menu using entries (1st column) and default (2nd column).
Definition: menu.py:98
def Prompt_no_curses(message)
Definition: menu.py:332
def Browse_no_curses(entries, default, options)
Definition: menu.py:342
def Insert_no_curses(entries, names)
Definition: menu.py:385
def CursesIsActive()
Return 1 if curses have been activated.
Definition: menu.py:312
def Print_no_curses(message, sleep, row)
The next set of functions replicate the previous ones without using curses library.
Definition: menu.py:324
def LeftRight(entries, default, options, inc)
Allow left/right keys to switch options in the second column and change default values.
Definition: menu.py:155
def Print
Print a message on the screen.
Definition: menu.py:60
The class gb contains global variables and pointers to ease up accessibility in coding the functions ...
Definition: menu.py:31
def SetTitle
Definition: menu.py:50
def UpDown(entries, default, inc)
Allow the cursor to move up and down in the list.
Definition: menu.py:130
def Browse
Browse a menu with entries (1st column) and default (2nd column, optional) Note: with Python > 2...
Definition: menu.py:181
def Prompt(message)
Prompt a message, wait for any key to be pressed.
Definition: menu.py:78
def Insert(entries, default)
Similar to Browse, but allow the user to directly input the default values by a reading a string...
Definition: menu.py:227