/offlineimap/head: changeset 301

Nominally-working Blinkenlights interface for Curses!
This commit is contained in:
jgoerzen 2003-01-05 08:51:35 +01:00
parent 380f654df5
commit ba0c95f6bc
5 changed files with 204 additions and 12 deletions

View File

@ -21,10 +21,6 @@ class BlinkenBase:
or another appropriate base class. The Tk interface, for instance, or another appropriate base class. The Tk interface, for instance,
will probably mix it in with VerboseUI.""" will probably mix it in with VerboseUI."""
def isusable(s):
"This class is not usable by itself; please override this."
return 0
def acct(s, accountname): def acct(s, accountname):
s.gettf().setcolor('purple') s.gettf().setcolor('purple')
s.__class__.__bases__[-1].acct(s, accountname) s.__class__.__bases__[-1].acct(s, accountname)

View File

@ -19,6 +19,7 @@
from Blinkenlights import BlinkenBase from Blinkenlights import BlinkenBase
from UIBase import UIBase from UIBase import UIBase
from threading import * from threading import *
from offlineimap import version, threadutil
import curses, curses.panel, curses.textpad, curses.wrapper import curses, curses.panel, curses.textpad, curses.wrapper
@ -30,6 +31,9 @@ class CursesUtil:
self.nextpair = 1 self.nextpair = 1
self.pairlock = Lock() self.pairlock = Lock()
def isactive(self):
return hasattr(self, 'stdscr')
def _getpairindex(self, fg, bg): def _getpairindex(self, fg, bg):
return '%d/%d' % (fg,bg) return '%d/%d' % (fg,bg)
@ -63,15 +67,201 @@ class CursesUtil:
(self.height, self.width) = self.stdscr.getmaxyx() (self.height, self.width) = self.stdscr.getmaxyx()
def stop(self): def stop(self):
if not hasattr(self, 'stdscr'):
return
#self.stdscr.addstr(self.height - 1, 0, "\n",
# self.getpair(curses.COLOR_WHITE,
# curses.COLOR_BLACK))
self.stdscr.refresh()
self.stdscr.keypad(0) self.stdscr.keypad(0)
curses.nocbreak() curses.nocbreak()
curses.echo() curses.echo()
curses.endwin() curses.endwin()
del self.stdscr del self.stdscr
#def resize(self): def resize(self):
self.stop()
self.start()
class CursesThreadFrame:
def __init__(s, master):
"""master should be a CursesUtil object."""
s.c = master
bg = curses.COLOR_BLACK
s.colormap = {'black': s.c.getpair(curses.COLOR_BLACK, bg),
'gray': s.c.getpair(curses.COLOR_WHITE, bg),
'white': curses.A_BOLD | s.c.getpair(curses.COLOR_WHITE, bg),
'blue': s.c.getpair(curses.COLOR_BLUE, bg),
'red': s.c.getpair(curses.COLOR_RED, bg),
'purple': s.c.getpair(curses.COLOR_MAGENTA, bg),
'cyan': s.c.getpair(curses.COLOR_CYAN, bg),
'green': s.c.getpair(curses.COLOR_GREEN, bg),
'orange': s.c.getpair(curses.COLOR_YELLOW, bg),
'yellow': curses.A_BOLD | s.c.getpair(curses.COLOR_YELLOW, bg),
'pink': curses.A_BOLD | s.c.getpair(curses.COLOR_RED, bg)}
s.setcolor('gray')
def setcolor(self, color):
self.color = self.colormap[color]
def getcolor(self):
return self.color
class InputHandler:
def __init__(s, util):
s.c = util
s.bgchar = None
s.inputlock = Lock()
s.lockheld = 0
s.statuslock = Lock()
s.startup = Event()
s.startthread()
def startthread(s):
s.thread = threadutil.ExitNotifyThread(target = s.bgreaderloop,
name = "InputHandler loop")
s.thread.setDaemon(1)
s.thread.start()
def bgreaderloop(s):
while 1:
s.statuslock.acquire()
if s.lockheld or s.bgchar == None:
s.statuslock.release()
s.startup.wait()
else:
s.statuslock.release()
ch = s.c.stdscr.getch()
s.statuslock.acquire()
try:
if s.lockheld or s.bgchar == None:
curses.ungetch(ch)
else:
s.bgchar(ch)
finally:
s.statuslock.release()
def set_bgchar(s, callback):
"""Sets a "background" character handler. If a key is pressed
while not doing anything else, it will be passed to this handler.
callback is a function taking a single arg -- the char pressed.
If callback is None, clears the request."""
s.statuslock.acquire()
oldhandler = s.bgchar
newhandler = callback
s.bgchar = callback
if oldhandler and not newhandler:
pass
if newhandler and not oldhandler:
s.startup.set()
s.statuslock.release()
def input_acquire(s):
"""Call this method when you want exclusive input control.
Make sure to call input_release afterwards!
"""
s.inputlock.acquire()
s.statuslock.acquire()
s.lockheld = 1
s.statuslock.release()
def input_release(s):
"""Call this method when you are done getting input."""
s.statuslock.acquire()
s.lockheld = 0
s.statuslock.release()
s.inputlock.release()
s.startup.set()
class Blinkenlights(BlinkenBase, UIBase):
def init_banner(s):
s.iolock = Lock()
s.c = CursesUtil()
s.accounts = []
s.text = []
s.tf = CursesThreadFrame(s.c)
s.setupwindows()
s.inputhandler = InputHandler(s.c)
s._msg(version.banner)
s._msg(str(dir(s.c.stdscr)))
s.inputhandler.set_bgchar(s.keypress)
def keypress(s, key):
s._msg("Key pressed: " + str(key))
def setupwindows(s):
s.bannerwindow = curses.newwin(1, s.c.width, 0, 0)
s.drawbanner()
s.logheight = s.c.height - 1 - len(s.accounts) * 2
s.logwindow = curses.newwin(s.logheight, s.c.width, 1, 0)
s.logwindow.idlok(1)
s.logwindow.scrollok(1)
s.drawlog()
curses.doupdate()
def drawbanner(s):
s.bannerwindow.bkgd(' ', curses.A_BOLD | \
s.c.getpair(curses.COLOR_WHITE,
curses.COLOR_BLUE))
s.bannerwindow.addstr("%s %s" % (version.productname,
version.versionstr))
s.bannerwindow.addstr(0, s.bannerwindow.getmaxyx()[1] - len(version.copyright) - 1,
version.copyright)
s.bannerwindow.noutrefresh()
def drawlog(s):
s.logwindow.bkgd(' ', s.c.getpair(curses.COLOR_WHITE, curses.COLOR_BLACK))
for line, color in s.text:
s.logwindow.addstr(line + "\n", color)
s.logwindow.noutrefresh()
def gettf(s):
return s.tf
def _msg(s, msg):
if "\n" in msg:
for thisline in msg.split("\n"):
s._msg(thisline)
return
s.iolock.acquire()
try:
if not s.c.isactive():
# For dumping out exceptions and stuff.
print msg
return
color = s.gettf().getcolor()
s.logwindow.addstr(msg + "\n", color)
s.text.append((msg, color))
while len(s.text) > s.logheight:
s.text = s.text[1:]
s.logwindow.refresh()
finally:
s.iolock.release()
def terminate(s, exitstatus = 0):
s.c.stop()
UIBase.terminate(s, exitstatus)
def threadException(s, thread):
s.c.stop()
UIBase.threadException(s, thread)
def mainException(s):
s.c.stop()
UIBase.mainException()
if __name__ == '__main__': if __name__ == '__main__':
x = Blinkenlights(None)
x.init_banner()
import time
time.sleep(10)
x.c.stop()
fgs = {'black': curses.COLOR_BLACK, 'red': curses.COLOR_RED, fgs = {'black': curses.COLOR_BLACK, 'red': curses.COLOR_RED,
'green': curses.COLOR_GREEN, 'yellow': curses.COLOR_YELLOW, 'green': curses.COLOR_GREEN, 'yellow': curses.COLOR_YELLOW,
'blue': curses.COLOR_BLUE, 'magenta': curses.COLOR_MAGENTA, 'blue': curses.COLOR_BLUE, 'magenta': curses.COLOR_MAGENTA,
@ -111,6 +301,3 @@ if __name__ == '__main__':
print x.height print x.height
print x.width print x.width
#class Blinkenlights(BlinkenBase, UIBase):
# def init_banner(s):

View File

@ -17,11 +17,12 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import UIBase, BlinkenBase import UIBase, Blinkenlights
try: try:
import TTY import TTY
except ImportError: except ImportError:
pass pass
try: try:
import Tkinter import Tkinter
except ImportError: except ImportError:
@ -29,6 +30,13 @@ except ImportError:
else: else:
import Tk import Tk
try:
import curses
except ImportError:
pass
else:
import Curses
import Noninteractive import Noninteractive
# Must be last # Must be last

View File

@ -46,5 +46,6 @@ def getUImod(uistr, localeval, namespace):
try: try:
uimod = localeval.eval(uistr, namespace) uimod = localeval.eval(uistr, namespace)
except (AttributeError, NameError), e: except (AttributeError, NameError), e:
#raise
return None return None
return uimod return uimod

View File

@ -1,14 +1,14 @@
productname = 'OfflineIMAP' productname = 'OfflineIMAP'
versionstr = "3.99.5" versionstr = "3.99.5"
revno = long('$Rev: 288 $'[6:-2]) revno = long('$Rev: 301 $'[6:-2])
revstr = "Rev %d" % revno revstr = "Rev %d" % revno
datestr = '$Date: 2002-11-19 16:34:09 -0600 (Tue, 19 Nov 2002) $' datestr = '$Date: 2003-01-04 19:51:35 -0600 (Sat, 04 Jan 2003) $'
versionlist = versionstr.split(".") versionlist = versionstr.split(".")
major = versionlist[0] major = versionlist[0]
minor = versionlist[1] minor = versionlist[1]
patch = versionlist[2] patch = versionlist[2]
copyright = "Copyright (C) 2002 John Goerzen" copyright = "Copyright (C) 2002, 2003 John Goerzen"
author = "John Goerzen" author = "John Goerzen"
author_email = "jgoerzen@complete.org" author_email = "jgoerzen@complete.org"
description = "Disconnected Universal IMAP Mail Synchronization/Reader Support" description = "Disconnected Universal IMAP Mail Synchronization/Reader Support"