Text entry allows wider strings, and fixed code

This commit is contained in:
John Ladan 2013-10-25 15:33:49 -04:00
parent b33b69e7bc
commit 4b658477ea
3 changed files with 161 additions and 129 deletions

View File

@ -1,7 +1,7 @@
import sys import sys
import curses import curses
import db_layer as db import db_layer as db
from form import bookForm,categoryForm from gui import BookForm,CategoryForm
class browserWindow: class browserWindow:
hl=0 hl=0
@ -259,11 +259,11 @@ class trashBrowser(browserWindow):
def viewSelection(self,book): def viewSelection(self,book):
bookid = book['id'] bookid = book['id']
w=curses.newwin(1,1,20,20) w=curses.newwin(1,1,20,20)
bf = bookForm(w,self.hb,book) bf = BookForm(w,self.hb,book)
self.centreChild(w) self.centreChild(w)
bf.caption='Viewing Book '+str(bookid) bf.caption='Viewing Book '+str(bookid)
bf.blabel='done' bf.blabel='done'
bf.eventLoop() bf.event_loop()
bf.clear() bf.clear()
def restoreSelected(self): def restoreSelected(self):
@ -325,11 +325,11 @@ class bookBrowser(browserWindow):
bookid = book['id'] bookid = book['id']
w=curses.newwin(1,1) w=curses.newwin(1,1)
bf=bookForm(w,self.hb,book) bf=BookForm(w,self.hb,book)
self.centreChild(w) self.centreChild(w)
bf.caption='Update Book '+str(bookid) bf.caption='Update Book '+str(bookid)
bf.blabel='update' bf.blabel='update'
newbook = bf.eventLoop() newbook = bf.event_loop()
if len(newbook)!=0: if len(newbook)!=0:
db.updateBook(newbook,bookid) db.updateBook(newbook,bookid)
bf.clear() bf.clear()
@ -337,11 +337,11 @@ class bookBrowser(browserWindow):
def viewSelection(self,book): def viewSelection(self,book):
bookid = book['id'] bookid = book['id']
w=curses.newwin(1,1,20,20) w=curses.newwin(1,1,20,20)
bf = bookForm(w,self.hb,book) bf = BookForm(w,self.hb,book)
self.centreChild(w) self.centreChild(w)
bf.caption='Viewing Book '+str(bookid) bf.caption='Viewing Book '+str(bookid)
bf.blabel='done' bf.blabel='done'
bf.eventLoop() bf.event_loop()
bf.clear() bf.clear()
def categorizeSelection(self,book): def categorizeSelection(self,book):
@ -407,9 +407,9 @@ class categoryBrowser(browserWindow):
def addCategory(self): def addCategory(self):
w = curses.newwin(1,1,10,10) w = curses.newwin(1,1,10,10)
cf = categoryForm(w,self.hb) cf = CategoryForm(w,self.hb)
self.centreChild(w) self.centreChild(w)
cats = cf.eventLoop() cats = cf.event_loop()
for c in cats: for c in cats:
db.addCategory(c) db.addCategory(c)
cf.clear() cf.clear()
@ -481,9 +481,9 @@ class categorySelector(browserWindow):
def addCategory(self): def addCategory(self):
w = curses.newwin(1,1,10,10) w = curses.newwin(1,1,10,10)
cf = categoryForm(w,self.hb) cf = CategoryForm(w,self.hb)
self.centreChild(w) self.centreChild(w)
cats = cf.eventLoop() cats = cf.event_loop()
for c in cats: for c in cats:
db.addCategory(c) db.addCategory(c)
cf.clear() cf.clear()

262
gui.py
View File

@ -1,90 +1,120 @@
import curses import curses
import sys import sys
class textEntry:
class TextEntry:
"""A part of a window that handles text entry.
Properties:
value holds the string that was entered
Public Methods:
set_geom(row,column,width) Sets the geometry in the window
set_value(string) Set the value and redraw
gain_focus() Gives it focus, moving cursor and changing the drawing
lose_focus() Takes focus, moving cursor to start, changing drawing
handle_input(ch) Pass this the ncurses key, and it manages input
redraw() Redraw the text entry (should never need to do this
"""
# Public
value = "" # Use the set_value function to set, but retrieve with value
# Should be Private
cursor = 0 cursor = 0
start = 0 start = 0
focus = False focus = False
x = 0 x = 0
y = 0 y = 0
width = 10 width = 10
value = ""
def __init__(self, parent_window, value=""): def __init__(self, parent_window, value=""):
self.w = parent_window self.w = parent_window
self.value = value self.value = value
def setGeom(self,y,x,width): def set_geom(self,y,x,width):
self.x = x self.x = x
self.y = y self.y = y
self.width = width self.width = width
def set_value(self,v):
self.value=v
self.cursor=len(v)
self.redraw()
def gain_focus(self):
#sys.stderr.write('I have focus!\n')
self.focus = True
self._mv_cursor(+len(self.value))
self.start = max(0,self.cursor-self.width)
self.redraw()
def lose_focus(self):
self.focus = False
self.cursor = 0
self.start = 0
self.redraw()
def handle_input(self,ch):
if ch==curses.KEY_LEFT:
self._mv_cursor(-1)
elif ch==curses.KEY_HOME:
self._set_cursor(0)
elif ch==curses.KEY_RIGHT:
self._mv_cursor(+1)
elif ch==curses.KEY_END:
self._set_cursor(len(self.value))
elif ch>=32 and ch<=126:
self._insert(curses.keyname(ch).decode('utf-8'))
elif ch==curses.KEY_BACKSPACE:
self._backspace()
elif ch==curses.KEY_DC:
self._delete()
def redraw(self): def redraw(self):
self.w.addnstr(self.y,self.x, self.value[self.start:]+" "*self.width, self.width) self.w.addnstr(self.y,self.x, self.value[self.start:]+" "*self.width, self.width)
if self.focus: if self.focus:
self.w.chgat(self.y, self.x, self.width, curses.A_UNDERLINE) self.w.chgat(self.y, self.x, self.width, curses.A_UNDERLINE)
curses.curs_set(1) curses.curs_set(1)
def gainFocus(self): # Private functions
sys.stderr.write('I have focus!\n') def _mv_cursor(self,delta):
self.focus = True self._set_cursor(self.cursor + delta)
self.mvCursor(+len(self.value))
self.start = max(0,self.cursor-self.width)
self.redraw()
def loseFocus(self): def _set_cursor(self, new_c):
self.focus = False self.cursor = max(0, min(len(self.value), new_c))
self.cursor = 0
self.start = 0
self.redraw()
def mvCursor(self,delta):
n = self.cursor + delta
# make sure new position is legal
n = max(n,0)
n = min(n,len(self.value))
self.cursor = n
self.start = max(0,self.cursor-self.width+1) self.start = max(0,self.cursor-self.width+1)
self.redraw() self.redraw()
# Place the drawn cursor in the correct spot
col = self.x + self.cursor - self.start col = self.x + self.cursor - self.start
self.w.move(self.y,col) self.w.move(self.y,col)
def insert(self,ch): def _insert(self,ch):
c = self.cursor c = self.cursor
self.value = self.value[:c] +ch+ self.value[c:] self.value = self.value[:c] +ch+ self.value[c:]
self.mvCursor(+1) self._mv_cursor(+1)
def backspace(self): def _backspace(self):
if self.cursor>0: if self.cursor>0:
c = self.cursor c = self.cursor
self.value=self.value[:c-1] + self.value[c:] self.value=self.value[:c-1] + self.value[c:]
self.mvCursor(-1) self._mv_cursor(-1)
def delete(self): def _delete(self):
c = self.cursor c = self.cursor
self.value = self.value[:c] + self.value[c+1:] self.value = self.value[:c] + self.value[c+1:]
self.mvCursor(0) self._mv_cursor(0)
def handle_input(self,ch):
if ch==curses.KEY_LEFT:
self.mvCursor(-1)
elif ch==curses.KEY_HOME:
self.mvCursor(-len(self.value))
elif ch==curses.KEY_RIGHT:
self.mvCursor(+1)
elif ch==curses.KEY_END:
self.mvCursor(+len(self.value))
elif ch>=32 and ch<=126:
self.insert(curses.keyname(ch))
elif ch==curses.KEY_BACKSPACE:
self.backspace()
elif ch==curses.KEY_DC:
self.delete()
class formWindow: class FormWindow:
"""General class for a Form Window.
To use, make the window for it, call the constructor, then call event_loop.
"""
# Private variables
mx = my = 0 mx = my = 0
hl = 0 hl = 0
bt = -1 bt = -1
@ -94,27 +124,51 @@ class formWindow:
caption = "Form" caption = "Form"
blabel = "Done" blabel = "Done"
labels = ["label1"] labels = ["label1"]
entries=[]
commands = [('pU', 'top'),('pD', 'bottom'),('Es', 'cancel')] commands = [('pU', 'top'),('pD', 'bottom'),('Es', 'cancel')]
# Public functions
def __init__(self,window,helpbar,book={}):
self.w = window
self.w.resize(len(self.labels)+6,50)
self.hb = helpbar
self._make_entries()
self._update_geometry()
self._set_entries(book)
def clear(self): def clear(self):
self.w.erase() self.w.erase()
self.w.refresh() self.w.refresh()
def __init__(self,window,helpbar,book={}): def event_loop(self):
self.w = window self.w.keypad(1)
self.w.resize(len(self.labels)+6,50) self.refresh()
self.hb = helpbar self.hl=0;
self.makeEntries() self.entries[self.hl].gain_focus()
self.updateGeometry()
self.updateEntries(book)
def makeEntries(self): ch = self.w.getch()
while ch != 27:
#sys.stderr.write(curses.keyname(ch).decode('utf-8'))
self.handle_input(ch)
if ch==10 or ch==curses.KEY_ENTER:
if self.bt==0:
return {}
elif self.bt==1:
return self.return_values()
else:
self._mv_focus(+1)
self.w.refresh()
ch = self.w.getch()
curses.curs_set(0)
return {}
def _make_entries(self):
self.entries = []
for e in range(len(self.labels)): for e in range(len(self.labels)):
self.entries.append(textEntry(self.w)) self.entries.append(TextEntry(self.w))
def updateGeometry(self): def _update_geometry(self):
(self.my, self.mx) = self.w.getmaxyx() (self.my, self.mx) = self.w.getmaxyx()
self.left=0 self.left=0
for l in self.labels: for l in self.labels:
@ -123,21 +177,21 @@ class formWindow:
width = self.mx-self.left-2 width = self.mx-self.left-2
self.top = 2 self.top = 2
for r in range(len(self.entries)): for r in range(len(self.entries)):
self.entries[r].setGeom(r+self.top, self.left, width) self.entries[r].set_geom(r+self.top, self.left, width)
# next, the buttons # next, the buttons
self.brow = self.top+len(self.labels)+1 self.brow = self.top+len(self.labels)+1
self.bcol = [self.mx-len(self.blabel)-14, self.mx-len(self.blabel)-4] self.bcol = [self.mx-len(self.blabel)-14, self.mx-len(self.blabel)-4]
self.bwidth = [8,len(self.blabel)+2] self.bwidth = [8,len(self.blabel)+2]
def updateEntries(self,book): def _set_entries(self,book):
e = 0 e = 0
for l in self.labels: for l in self.labels:
sys.stderr.write('updating label: '+l+'\n') #sys.stderr.write('updating label: '+l+'\n')
if l.lower() in book: if l.lower() in book:
sys.stderr.write(' '+l+' found\n') #sys.stderr.write(' '+l+' found\n')
self.entries[e].value = str(book[l.lower()]) self.entries[e].value = str(book[l.lower()])
else: else:
sys.stderr.write(' '+l+' notfound\n') #sys.stderr.write(' '+l+' notfound\n')
self.entries[e].value = "" self.entries[e].value = ""
e += 1 e += 1
@ -156,89 +210,67 @@ class formWindow:
def refresh(self): def refresh(self):
self.hb.commands = self.commands self.hb.commands = self.commands
self.hb.refresh() self.hb.refresh()
self.updateGeometry() self._update_geometry()
self.redraw() self.redraw()
def highlightButton(self): def _highlight_button(self):
self.w.chgat(self.brow, self.bcol[self.bt], self.bwidth[self.bt], curses.A_REVERSE) self.w.chgat(self.brow, self.bcol[self.bt], self.bwidth[self.bt], curses.A_REVERSE)
curses.curs_set(0) curses.curs_set(0)
def unHighlightButton(self): def _unhighlight_button(self):
self.w.chgat(self.brow,1,self.mx-2,curses.A_NORMAL) self.w.chgat(self.brow,1,self.mx-2,curses.A_NORMAL)
def mvFocus(self,delta): def _mv_focus(self,delta):
if self.bt==-1: if self.bt==-1:
self.entries[self.hl].loseFocus() self.entries[self.hl].lose_focus()
else: else:
self.unHighlightButton() self._unhighlight_button()
new = self.hl+delta new = self.hl+delta
new = max(new,0) new = max(0, min(len(self.labels), new)) # the extra is for the buttons
new = min(new,len(self.labels)) # the extra is for the buttons
self.hl = new self.hl = new
if new == len(self.labels): if new == len(self.labels):
self.bt = 1 self.bt = 1
self.bt = min(self.bt,1) self.bt = min(self.bt,1)
self.highlightButton() self._highlight_button()
else: else:
self.bt=-1 self.bt=-1
self.entries[self.hl].gainFocus() self.entries[self.hl].gain_focus()
def returnValues(self): def _return_values(self):
book = {} book = {}
for k,e in zip(self.labels, self.entries): for k,e in zip(self.labels, self.entries):
if v!="" and k.lower()!="publish date": if v!="" and k.lower()!="publish date":
book[k.lower()]=e.value book[k.lower()]=e.value
return book return book
def eventLoop(self):
self.w.keypad(1)
self.refresh()
self.hl=0;
self.entries[self.hl].gainFocus()
ch = self.w.getch() def handle_input(self,ch):
while ch != 27:
self.handleInput(ch)
if ch==10 or ch==curses.KEY_ENTER:
if self.bt==0:
return {}
elif self.bt==1:
return self.returnValues()
else:
self.mvFocus(+1)
self.w.refresh()
ch = self.w.getch()
curses.curs_set(0)
return {}
def handleInput(self,ch):
if ch==curses.KEY_UP: if ch==curses.KEY_UP:
self.mvFocus(-1) self._mv_focus(-1)
elif ch==curses.KEY_PPAGE: elif ch==curses.KEY_PPAGE:
self.mvFocus(-len(self.labels)) self._mv_focus(-len(self.labels))
elif ch==curses.KEY_DOWN: elif ch==curses.KEY_DOWN:
self.mvFocus(+1) self._mv_focus(+1)
elif ch==curses.KEY_NPAGE: elif ch==curses.KEY_NPAGE:
self.mvFocus(+len(self.labels)) self._mv_focus(+len(self.labels))
elif ch==curses.KEY_LEFT: elif ch==curses.KEY_LEFT:
if self.bt==-1: if self.bt==-1:
self.entries[self.hl].handle_input(ch) self.entries[self.hl].handle_input(ch)
else: else:
self.unHighlightButton() self._unhighlight_button()
self.bt=0 self.bt=0
self.highlightButton() self._highlight_button()
elif ch==curses.KEY_HOME: elif ch==curses.KEY_HOME:
if self.bt==-1: if self.bt==-1:
self.mvCursor(-len(self.entries[self.hl])) self._mv_cursor(-len(self.entries[self.hl]))
elif ch==curses.KEY_RIGHT: elif ch==curses.KEY_RIGHT:
if self.bt==-1: if self.bt==-1:
self.entries[self.hl].handle_input(ch) self.entries[self.hl].handle_input(ch)
else: else:
self.unHighlightButton() self._unhighlight_button()
self.bt=1 self.bt=1
self.highlightButton() self._highlight_button()
else: else:
if self.bt==-1: if self.bt==-1:
self.entries[self.hl].handle_input(ch) self.entries[self.hl].handle_input(ch)
@ -246,7 +278,7 @@ class formWindow:
class bookForm(formWindow): class BookForm(FormWindow):
caption = "Add a Book" caption = "Add a Book"
blabel = "Add" blabel = "Add"
labels = ["ISBN", "LCCN", "Title", "Subtitle", "Authors", "Edition", labels = ["ISBN", "LCCN", "Title", "Subtitle", "Authors", "Edition",
@ -262,31 +294,31 @@ class bookForm(formWindow):
def lookup_lccn(self,lccn): def lookup_lccn(self,lccn):
return {'lccn':lccn} return {'lccn':lccn}
def returnBook(self): def return_book(self):
return self.returnValues() return self.return_values()
def handleInput(self,ch): def handle_input(self,ch):
if ch==10 or ch==curses.KEY_ENTER: if ch==10 or ch==curses.KEY_ENTER:
if self.hl==0: # lookup by isbn if self.hl==0: # lookup by isbn
book = self.lookup_isbn(self.entries[0].value) book = self.lookup_isbn(self.entries[0].value)
if book != {}: if book != {}:
sys.stderr.write('updating entries\n') #sys.stderr.write('updating entries\n')
self.updateEntries(book) self._set_entries(book)
self.refresh() self.refresh()
self.mvFocus(+7) self._mv_focus(+7)
if self.hl==1: # lookup by lccn if self.hl==1: # lookup by lccn
book = self.lookup_lccn(self.entries[1].value) book = self.lookup_lccn(self.entries[1].value)
if book != {}: if book != {}:
self.updateEntries(book) self._set_entries(book)
self.refresh() self.refresh()
self.mvFocus(+6) self._mv_focus(+6)
else: else:
formWindow.handleInput(self,ch) FormWindow.handle_input(self,ch)
class categoryForm(formWindow): class CategoryForm(FormWindow):
caption = "Add a Category" caption = "Add a Category"
blabel = "Add" blabel = "Add"
labels = ["Category"] labels = ["Category"]
def returnValues(self): def return_values(self):
return self.entries return self.entries

View File

@ -3,7 +3,7 @@
import curses import curses
import db_layer as db import db_layer as db
import browser import browser
import form as form import gui as form
import help_bar as helpBar import help_bar as helpBar
import book_data import book_data
@ -82,14 +82,14 @@ def redrawMenu(w,items,highlight):
def addForm(): def addForm():
w=curses.newwin(1,1) w=curses.newwin(1,1)
(my,mx)=stdscr.getmaxyx() (my,mx)=stdscr.getmaxyx()
bf = form.bookForm(w,hb) bf = form.BookForm(w,hb)
(r,c)=w.getmaxyx() (r,c)=w.getmaxyx()
w.mvwin((my-r)//2,(mx-c)//2) w.mvwin((my-r)//2,(mx-c)//2)
bf.lookup_isbn=book_data.openLibrary_isbn bf.lookup_isbn=book_data.openLibrary_isbn
bf.lookup_lccn=book_data.openLibrary_lccn bf.lookup_lccn=book_data.openLibrary_lccn
bf.caption='Add a Book' bf.caption='Add a Book'
bf.blabel = 'Add' bf.blabel = 'Add'
book = bf.eventLoop() book = bf.event_loop()
bf.clear() bf.clear()
if len(book)!=0: if len(book)!=0:
db.addBook(book) db.addBook(book)