/offlineimap/head: changeset 301
Nominally-working Blinkenlights interface for Curses!
This commit is contained in:
parent
380f654df5
commit
ba0c95f6bc
@ -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)
|
||||||
|
@ -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):
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user