From 45dafba38a7e11ccdc15f85926518cb091fbca13 Mon Sep 17 00:00:00 2001 From: John Ladan Date: Wed, 23 Oct 2013 12:17:56 -0400 Subject: [PATCH] Cleaned files, started to follow naming convention --- TODO | 23 +- bookData.py => book_data.py | 0 browser.py | 2 +- dbLayer.py => db_layer.py | 0 gui.py | 292 +++++++++++++++++++ helpBar.py => help_bar.py | 0 librarian.py | 6 +- cursestest.py => ncurses_tools/cursestest.py | 0 echokey.py => ncurses_tools/echokey.py | 0 9 files changed, 316 insertions(+), 7 deletions(-) rename bookData.py => book_data.py (100%) rename dbLayer.py => db_layer.py (100%) create mode 100644 gui.py rename helpBar.py => help_bar.py (100%) rename cursestest.py => ncurses_tools/cursestest.py (100%) rename echokey.py => ncurses_tools/echokey.py (100%) diff --git a/TODO b/TODO index befe8be..e3dcf13 100644 --- a/TODO +++ b/TODO @@ -2,15 +2,32 @@ _List of Desired Features_ Categories work based on selection, not just highlight - i.e. assign categories to multiple books at once -Support UTF-8 for everything + - this may involve extra logic if books don't have the same categories beforehand Search ignores Case Regex Search Choose shown columns in browser Support for multiple copies Text entry supports longer string + - implementation started in gui.py +Home and End navigate to top and bottom of catalogue respectively. + + +_Code Quality Improvements_ +Document all functions +Conform to python naming conventions and code style +Make db_layer use a helper function to handle most of the database queries -_Implemented Features_ -Sort by column in browser _Bugs_ Error when entering bad ISBN e.g. 02010798X (instead of 020107981X) + - seems to be an intermittant error, likely related to polling openLibrary +When the top element shown is highlighted in the browser window, and pgUp is pressed (causing a scrollup), the bottom border of the window turns into 'qqqqqqqq'. + - does not happen when up_arrow is pressed. + + + + +_Implemented Features_ +Sort by column in browser +Support UTF-8 for everything + diff --git a/bookData.py b/book_data.py similarity index 100% rename from bookData.py rename to book_data.py diff --git a/browser.py b/browser.py index 0bef6f3..d93fc2c 100644 --- a/browser.py +++ b/browser.py @@ -1,6 +1,6 @@ import sys import curses -import dbLayer as db +import db_layer as db from form import bookForm,categoryForm class browserWindow: diff --git a/dbLayer.py b/db_layer.py similarity index 100% rename from dbLayer.py rename to db_layer.py diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..0e35764 --- /dev/null +++ b/gui.py @@ -0,0 +1,292 @@ +import curses +import sys + +class textEntry: + cursor = 0 + start = 0 + focus = False + x = 0 + y = 0 + width = 10 + value = "" + + def __init__(self, parent_window, value=""): + self.w = parent_window + self.value = value + + def setGeom(self,y,x,width): + self.x = x + self.y = y + self.width = width + + def redraw(self): + self.w.addnstr(self.y,self.x, self.value[self.start:]+" "*self.width, self.width) + if self.focus: + self.w.chgat(self.y, self.x, self.width, curses.A_UNDERLINE) + curses.curs_set(1) + + def gainFocus(self): + sys.stderr.write('I have focus!\n') + self.focus = True + self.mvCursor(+len(self.value)) + self.start = max(0,self.cursor-self.width) + self.redraw() + + def loseFocus(self): + self.focus = False + 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.redraw() + col = self.x + self.cursor - self.start + self.w.move(self.y,col) + + def insert(self,ch): + c = self.cursor + self.value = self.value[:c] +ch+ self.value[c:] + self.mvCursor(+1) + + def backspace(self): + if self.cursor>0: + c = self.cursor + self.value=self.value[:c-1] + self.value[c:] + self.mvCursor(-1) + + def delete(self): + c = self.cursor + self.value = self.value[:c] + self.value[c+1:] + self.mvCursor(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: + mx = my = 0 + hl = 0 + bt = -1 + left = 0 + top = 2 + row = 2 + caption = "Form" + blabel = "Done" + labels = ["label1"] + entries=[] + + commands = [('pU', 'top'),('pD', 'bottom'),('Es', 'cancel')] + + def clear(self): + self.w.erase() + self.w.refresh() + + def __init__(self,window,helpbar,book={}): + self.w = window + self.w.resize(len(self.labels)+6,50) + self.hb = helpbar + self.makeEntries() + self.updateGeometry() + self.updateEntries(book) + + def makeEntries(self): + for e in range(len(self.labels)): + self.entries.append(textEntry(self.w)) + + def updateGeometry(self): + (self.my, self.mx) = self.w.getmaxyx() + self.left=0 + for l in self.labels: + self.left = max(len(l),self.left) + self.left += 4 + width = self.mx-self.left-2 + self.top = 2 + for r in range(len(self.entries)): + self.entries[r].setGeom(r+self.top, self.left, width) + # next, the buttons + self.brow = self.top+len(self.labels)+1 + self.bcol = [self.mx-len(self.blabel)-14, self.mx-len(self.blabel)-4] + self.bwidth = [8,len(self.blabel)+2] + + def updateEntries(self,book): + e = 0 + for l in self.labels: + sys.stderr.write('updating label: '+l+'\n') + if l.lower() in book: + sys.stderr.write(' '+l+' found\n') + self.entries[e].value = str(book[l.lower()]) + else: + sys.stderr.write(' '+l+' notfound\n') + self.entries[e].value = "" + e += 1 + + def redraw(self): + self.w.box() + self.w.addstr(0,(self.mx-len(self.caption))//2,self.caption) + r=0 + for l in self.labels: + c = self.left-len(l)-2 + self.w.addstr(r+self.top,c,l+":") + self.entries[r].redraw() + r+=1 + self.w.addstr(self.brow,self.bcol[0], " <"+self.blabel+">") + self.w.refresh() + + def refresh(self): + self.hb.commands = self.commands + self.hb.refresh() + self.updateGeometry() + self.redraw() + + def highlightButton(self): + self.w.chgat(self.brow, self.bcol[self.bt], self.bwidth[self.bt], curses.A_REVERSE) + curses.curs_set(0) + + def unHighlightButton(self): + self.w.chgat(self.brow,1,self.mx-2,curses.A_NORMAL) + + def mvFocus(self,delta): + if self.bt==-1: + self.entries[self.hl].loseFocus() + else: + self.unHighlightButton() + new = self.hl+delta + new = max(new,0) + new = min(new,len(self.labels)) # the extra is for the buttons + self.hl = new + if new == len(self.labels): + self.bt = 1 + self.bt = min(self.bt,1) + self.highlightButton() + else: + self.bt=-1 + self.entries[self.hl].gainFocus() + + + def returnValues(self): + book = {} + for k,e in zip(self.labels, self.entries): + if v!="" and k.lower()!="publish date": + book[k.lower()]=e.value + return book + + def eventLoop(self): + self.w.keypad(1) + self.refresh() + self.hl=0; + self.entries[self.hl].gainFocus() + + ch = self.w.getch() + 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: + self.mvFocus(-1) + elif ch==curses.KEY_PPAGE: + self.mvFocus(-len(self.labels)) + elif ch==curses.KEY_DOWN: + self.mvFocus(+1) + elif ch==curses.KEY_NPAGE: + self.mvFocus(+len(self.labels)) + elif ch==curses.KEY_LEFT: + if self.bt==-1: + self.entries[self.hl].handle_input(ch) + else: + self.unHighlightButton() + self.bt=0 + self.highlightButton() + elif ch==curses.KEY_HOME: + if self.bt==-1: + self.mvCursor(-len(self.entries[self.hl])) + elif ch==curses.KEY_RIGHT: + if self.bt==-1: + self.entries[self.hl].handle_input(ch) + else: + self.unHighlightButton() + self.bt=1 + self.highlightButton() + else: + if self.bt==-1: + self.entries[self.hl].handle_input(ch) + + + + +class bookForm(formWindow): + caption = "Add a Book" + blabel = "Add" + labels = ["ISBN", "LCCN", "Title", "Subtitle", "Authors", "Edition", + "Publisher", "Publish Date", "Publish Year", "Publish Month", "Publish location", + "Pages", "Pagination", "Weight"] + + + # redefineable functions lookup is called when 'enter' is pressed on ISBN + # and returns the looked-up book. Default returns nothing + def lookup_isbn(self,isbn): + return {'isbn':isbn} + + def lookup_lccn(self,lccn): + return {'lccn':lccn} + + def returnBook(self): + return self.returnValues() + + def handleInput(self,ch): + if ch==10 or ch==curses.KEY_ENTER: + if self.hl==0: # lookup by isbn + book = self.lookup_isbn(self.entries[0].value) + if book != {}: + sys.stderr.write('updating entries\n') + self.updateEntries(book) + self.refresh() + self.mvFocus(+7) + if self.hl==1: # lookup by lccn + book = self.lookup_lccn(self.entries[1].value) + if book != {}: + self.updateEntries(book) + self.refresh() + self.mvFocus(+6) + else: + formWindow.handleInput(self,ch) + +class categoryForm(formWindow): + caption = "Add a Category" + blabel = "Add" + labels = ["Category"] + + def returnValues(self): + return self.entries diff --git a/helpBar.py b/help_bar.py similarity index 100% rename from helpBar.py rename to help_bar.py diff --git a/librarian.py b/librarian.py index 6be597b..387b692 100755 --- a/librarian.py +++ b/librarian.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 import curses -import dbLayer as db +import db_layer as db import browser import form as form -import helpBar +import help_bar as helpBar -import bookData +import book_data stdscr=0 diff --git a/cursestest.py b/ncurses_tools/cursestest.py similarity index 100% rename from cursestest.py rename to ncurses_tools/cursestest.py diff --git a/echokey.py b/ncurses_tools/echokey.py similarity index 100% rename from echokey.py rename to ncurses_tools/echokey.py