[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r7544 - trunk/gnue-forms/src/uidrivers/curses
From: |
johannes |
Subject: |
[gnue] r7544 - trunk/gnue-forms/src/uidrivers/curses |
Date: |
Wed, 18 May 2005 05:13:28 -0500 (CDT) |
Author: johannes
Date: 2005-05-18 05:13:26 -0500 (Wed, 18 May 2005)
New Revision: 7544
Modified:
trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py
trunk/gnue-forms/src/uidrivers/curses/dialogs.py
Log:
Added versatile input dialog, updated login handler
Modified: trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py 2005-05-17
15:31:08 UTC (rev 7543)
+++ trunk/gnue-forms/src/uidrivers/curses/UILoginHandler.py 2005-05-18
10:13:26 UTC (rev 7544)
@@ -24,6 +24,7 @@
import curses
from gnue.common.datasources import GLoginHandler
+from gnue.forms.uidrivers.curses import dialogs
# =============================================================================
# This class implements a login handler for curses
@@ -35,10 +36,11 @@
# Get input for all fields listed in loginData
# ---------------------------------------------------------------------------
- def getLogin (self, loginData, error = None):
+ def _askLogin (self, title, fields):
- (connection, description, fields) = loginData
+ dlg = dialogs.InputDialog (title, fields, self.uiDriver.attr)
- text = u_('Login required for "%s"') % (description or connection)
+ dlg.run ()
+ return dlg.inputData
- return self.uiDriver.askLogin (text, fields, error)
+
Modified: trunk/gnue-forms/src/uidrivers/curses/dialogs.py
===================================================================
--- trunk/gnue-forms/src/uidrivers/curses/dialogs.py 2005-05-17 15:31:08 UTC
(rev 7543)
+++ trunk/gnue-forms/src/uidrivers/curses/dialogs.py 2005-05-18 10:13:26 UTC
(rev 7544)
@@ -22,7 +22,11 @@
# $Id$
import curses
+import curses.ascii
+import sys
+from gnue.common.apps import i18n
+
# =============================================================================
# Dialog box for selection an option from a given options dictionary
# =============================================================================
@@ -34,7 +38,7 @@
# ---------------------------------------------------------------------------
def __init__ (self, title, options, attrs, quit = None, left = 0, top = 0,
- right = 80, bottom = 24):
+ right = 80, bottom = 24, returnKeys = False):
curses.noecho ()
curses.cbreak ()
@@ -44,10 +48,10 @@
except:
self.__oldcursor = 1
- self.__data = ["%s" % item for item in options.values ()]
+ self.__data = [("%s" % val, key) for (key, val) in options.items ()]
self.__data.sort ()
- maxString = max ([len (item) for item in self.__data])
+ maxString = max ([len (item) for (item, key) in self.__data])
width = min (right - left, max ((maxString + 4), len (title) + 4))
height = min (bottom - top, len (options) + 4)
@@ -75,6 +79,7 @@
self.__index = 0
self.__pIndex = 1
self.__page = height - 4
+ self.__returnKeys = returnKeys
self.__refresh ()
@@ -106,7 +111,7 @@
self.__move (+1)
elif key == 10:
- return self.__data [self.__index]
+ return self.__data [self.__index][self.__returnKeys]
finally:
try:
@@ -148,7 +153,7 @@
def __refresh (self):
lines = self.__data [self.__offset:self.__offset + self.__page]
- for (line, value) in enumerate (lines):
+ for (line, (value, key)) in enumerate (lines):
text = " %s " % value.ljust (self.__dwidth) [:self.__dwidth]
if line == self.__pIndex - 1:
attr = self.__reverse
@@ -160,33 +165,342 @@
self.__window.move (3 + self.__pIndex - 1, 1)
+
# =============================================================================
+# Class implementing a versatile input dialog
+# =============================================================================
+
+class InputDialog:
+
+ # ---------------------------------------------------------------------------
+ # Create a new input dialog
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, title, fields, attrs, cancel = True, left = 0, top = 0,
+ right = 80, bottom = 24):
+
+ curses.noecho ()
+ curses.cbreak ()
+
+ self.attrs = attrs
+ self.fields = fields
+ self.__buildWindow (title, left, top, right, bottom)
+
+ self.inputData = {}
+
+ # A dictionary with the fieldname as key and a triple (master, allowed,
+ # current) as data items
+ self.lookups = {}
+
+ y = 2
+ self.__entries = []
+
+ for (label, name, ftype, default, master, elements) in fields:
+ if ftype == 'image':
+ continue
+
+ y += 1
+
+ if ftype in ['label', 'warning']:
+ attr = [attrs ['background'], attrs ['warnmsg']][ftype == 'warning']
+ cw = self.__width - 2
+ lh = label.count ('\n') + 1
+
+ for item in label.splitlines ():
+ self.__window.addnstr (y, 1, o(item.center (cw)), cw, attr)
+ y += 1
+
+ if lh > 1: y -= 1
+
+ elif ftype in ['string', 'password', 'dropdown']:
+ self.__window.addnstr (y, 2, o(label), self.__leftcol)
+
+ win = curses.newwin (1, self.__rightcol,
+ self.__wtop + y, self.__wleft + self.__leftcol + 2)
+ self.__entries.append ((win, name, default, ftype == 'password'))
+
+ if ftype == 'dropdown':
+ values = elements [0][1]
+ self.lookups [name] = (master, values,
+ self.__getCurrent (master, values))
+ dispValue = self.lookups [name][2].get (default, '')
+ else:
+ dispValue = default
+
+ self.__display (win, dispValue, ftype == 'password')
+
+
+
+ # ---------------------------------------------------------------------------
+ # Run the dialog
+ # ---------------------------------------------------------------------------
+
+ def run (self):
+
+ self.__window.refresh ()
+
+ index = 0
+ while index < len (self.__entries):
+ (win, name, default, hidden) = self.__entries [index]
+ cvalue = self.inputData.get (name, default)
+ (value, code) = self.__accept (win, cvalue, name, hidden)
+ if value is None:
+ if name in self.inputData:
+ del self.inputData [name]
+ else:
+ self.inputData [name] = value
+
+ if code == 27:
+ self.inputData = None
+ break
+
+ elif code == curses.KEY_UP:
+ index = max (0, index - 1)
+
+ else:
+ index += 1
+
+
+ # ---------------------------------------------------------------------------
+ # Calculate and build the window
+ # ---------------------------------------------------------------------------
+
+ def __buildWindow (self, title, left, top, right, bottom):
+
+ leftcol = 0
+ rightcol = 0
+ width = 0
+ height = 5 # box + title + spacer + bottom
+
+ for (label, name, ftype, default, master, elements) in self.fields:
+ if ftype in ['label', 'warning']:
+ arr = ([width] + [len (part) + 2 for part in label.splitlines ()])
+ width = max ([width] + [len (part) + 2 for part in label.splitlines
()])
+ height += label.count ('\n') + 1
+
+ elif ftype in ['string', 'password']:
+ leftcol = max (leftcol, len (label) + 1)
+ rightcol = max (rightcol, 20)
+ height += 1
+
+ elif ftype == 'dropdown':
+ leftcol = max (leftcol, len (label) + 1)
+ height += 1
+
+ # We introspect only the first control since we do not provide them all
+ # for input
+ (label, allowed) = elements [0]
+ widest = 0
+ if master is None:
+ data = [allowed]
+ else:
+ data = allowed.values ()
+
+ for vdict in data:
+ widest = max ([widest] + [len (i) for i in vdict.values ()])
+
+ rightcol = max (rightcol, widest)
+
+
+ self.__width = min (max (leftcol + rightcol + 4, width), right - left)
+ self.__height = min (height, bottom - top)
+ self.__wtop = (bottom - top - self.__height) / 2
+ self.__wleft = (right - left - self.__width) / 2
+
+ self.__window = curses.newwin (self.__height, self.__width,
+ self.__wtop, self.__wleft)
+ self.__window.bkgd (' ', self.attrs ['background'])
+ self.__window.box ()
+ self.__window.keypad (1)
+
+ self.__window.addstr (1, 1, title.center (self.__width - 2))
+
+ self.__leftcol = leftcol
+ self.__rightcol = rightcol
+
+
+
+ # ---------------------------------------------------------------------------
+ # Get input for a given field
+ # ---------------------------------------------------------------------------
+
+ def __accept (self, win, default, name, hidden = False):
+
+ if name in self.lookups:
+ (master, allowed, current) = self.lookups [name]
+ current = self.__getCurrent (master, allowed)
+ self.lookups [name] = (master, allowed, current)
+
+ reverse = {}
+ for (k, v) in current.items ():
+ reverse [v] = k
+
+ value = current.get (default, '')
+ else:
+ value = default or ''
+ current = None
+ revese = None
+
+ win.keypad (1)
+
+ while True:
+ self.__display (win, value, hidden, True)
+
+ code = self.__window.getch ()
+
+ if code in [9, 10, 27, curses.KEY_DOWN, curses.KEY_UP]:
+ if code == 27:
+ rvalue = None
+
+ elif code == 9:
+ code = curses.KEY_DOWN
+
+ if code != 27 and name in self.lookups:
+ if reverse and not value in reverse:
+ curses.beep ()
+ continue
+ else:
+ rvalue = reverse.get (value)
+ else:
+ rvalue = value
+
+ break
+
+ elif code == curses.KEY_BACKSPACE:
+ value = value [:-1]
+
+ elif current and code == 23:
+ op = OptionsDialog ('Select Option', current, self.attrs, 23)
+ v = op.run ()
+ self.__window.redrawwin ()
+ curses.doupdate ()
+
+ if v is not None:
+ value = v
+ curses.ungetch (10)
+
+ elif curses.ascii.isprint (code):
+ value += chr (code)
+
+ self.__display (win, value, hidden, False)
+
+ return (rvalue, code)
+
+
+ # ---------------------------------------------------------------------------
+ # Display the visible portion of a given value within a window
+ # ---------------------------------------------------------------------------
+
+ def __display (self, win, value, hidden = False, edit = False):
+
+ data = value or ''
+ attr = self.attrs [edit and 'focusentry' or 'entry']
+ win.bkgd (' ', attr)
+
+ length = win.getmaxyx () [1] - 1
+ (top, left) = win.getbegyx ()
+ (mtop, mleft) = self.__window.getbegyx ()
+ top -= mtop
+ left -= mleft
+
+ dvalue = [data, '*' * len (data)] [hidden]
+ offset = max (0, len (dvalue) - length)
+ pos = min (len (dvalue) - offset, length)
+
+ out = dvalue [offset:offset + length].ljust (length + 1)
+ self.__window.addnstr (top, left, out, length + 1, attr)
+
+ self.__window.move (top, (pos + left))
+
+
+ # ---------------------------------------------------------------------------
+ # Get the current value dictionary for a given dropdown
+ # ---------------------------------------------------------------------------
+
+ def __getCurrent (self, master, values):
+
+ if master is None:
+ return values
+ else:
+ return values.get (self.inputData.get (master), {})
+
+
+# =============================================================================
# Module self test
# =============================================================================
if __name__ == '__main__':
curses.initscr ()
+ curses.cbreak ()
curses.raw ()
curses.start_color ()
curses.init_pair (3, curses.COLOR_BLACK, curses.COLOR_CYAN)
curses.init_pair (4, curses.COLOR_BLUE, curses.COLOR_WHITE)
curses.init_pair (5, curses.COLOR_WHITE, curses.COLOR_RED)
+ curses.init_pair (6, curses.COLOR_BLACK, curses.COLOR_CYAN)
+ curses.init_pair (9, curses.COLOR_WHITE, curses.COLOR_RED)
attrs = {'background': curses.color_pair (4),
'focusentry': curses.color_pair (5),
- 'window 1': curses.color_pair (3)}
+ 'entry' : curses.color_pair (6),
+ 'window 1' : curses.color_pair (3),
+ 'warnmsg' : curses.color_pair (9) + curses.A_BOLD}
opts = {'foo': 'Foobar', 'baz': 'Barbaz and the Gang!',
2: 'something', 3: 'quite', 4: 'interesting stuff', 5: 'hey ho',
7: 'number 7'}
opts = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8, 9:9, 10:10, 11:11, 12:12,
- 13:13, 14:14, 15:15, 16:16, 17:17, 18:18, 19:19, 20:20}
- d = OptionsDialog ('Foos', opts, attrs, quit = 23, top = 2, bottom = 22)
- r = d.run ()
+ 13:13, 14:14, "k15":15, 16:16, 17:17, 18:18, 19:19, 20:20}
+ #d = OptionsDialog ('Foos', opts, attrs, quit = 23, top = 2, bottom = 22,
+ #returnKeys = True)
+ #r = d.run ()
+ #curses.endwin ()
+
+
+ #print "RESULT:", r
+
+ #sys.exit ()
+
+ # ---------------------------------------------------------------------------
+
+ cname = {'c1': 'demoa', 'c2': 'demob'}
+ ckey = {'c1': 'ck-A', 'c2': 'ck-B'}
+
+ wija = {'c1': {'04': '2004', '05': '2005'},
+ 'c2': {'24': '2024', '25': '2025', '26': '2026'}}
+
+ codes = {'24': {'241': 'c-24-1', '242': 'c-24-2'},
+ '25': {'251': 'c-25-1'}}
+
+ descr = 'Hier ist ein langer Text\nMit einem Zeilenumbruch'
+ fields = [('Foo!', '/home/johannes/gnue/share/gnue/images/gnue.png', 'image',
+ None, None, []),
+ ('Username', '_username', 'string', 'frodo', None, \
+ [('Name of the user', None)]),
+ ('', None, 'label', None, None, []),
+ ('Password', '_password', 'password', 'foo', None, [('yeah',1)]),
+ ('Foobar', '_foobar', 'dropdown', 'frob', None, \
+ [('single', {'trash': 'Da Trash', 'frob': 'Frob'})]),
+ ('Multi', '_multi', 'dropdown', '100', None, \
+ [('name', {'50': 'A 50', '100': 'B 100', '9': 'C 9'}),
+ ('sepp', {'50': 'se 50', '100': 'se 100', '9': 'se 9'})]),
+ (descr, '_depp', 'label', 'furz', None, []),
+ ('Das ist jetzt ein Fehler', None, 'warning', None, None, []),
+ ('Firma', 'company', 'dropdown', 'c1', None,
+ [('Name', cname), ('Code', ckey)]),
+ ('Wirtschaftsjahr', 'wija', 'dropdown', '05', 'company',
+ [('Jahr', wija)]),
+ ('Codes', 'codes', 'dropdown', None, 'wija',
+ [('Code', codes)]),
+ (u"Dre\xf6ksau 'bl\xf6sepp'", None, 'warning', None, None, [])]
+
+ dlg = InputDialog ('foobar', fields, attrs)
+ dlg.run ()
+
curses.endwin ()
- print "RESULT:", r
+ print "RES:", dlg.inputData
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r7544 - trunk/gnue-forms/src/uidrivers/curses,
johannes <=