Remove the Tk interfaces
These were a constant source of trouble. Tkinter likely has multiple memory leaks that OfflineIMAP was tickling. I never used these, so poof, goodbye.
This commit is contained in:
parent
35e7250187
commit
1e90e0fd78
@ -51,8 +51,6 @@ maxsyncaccounts = 1
|
|||||||
# fails, the second, and so forth.
|
# fails, the second, and so forth.
|
||||||
#
|
#
|
||||||
# The pre-defined options are:
|
# The pre-defined options are:
|
||||||
# Tk.Blinkenlights -- A graphical interface, shows LEDs and a single log
|
|
||||||
# Tk.VerboseUI -- A graphical interface, shows logs per thread
|
|
||||||
# Curses.Blinkenlights -- A text-based (terminal) interface similar to
|
# Curses.Blinkenlights -- A text-based (terminal) interface similar to
|
||||||
# Tk.Blinkenlights
|
# Tk.Blinkenlights
|
||||||
# TTY.TTYUI -- a text-based (terminal) interface
|
# TTY.TTYUI -- a text-based (terminal) interface
|
||||||
@ -62,7 +60,7 @@ maxsyncaccounts = 1
|
|||||||
#
|
#
|
||||||
# You can override this with a command-line option -u.
|
# You can override this with a command-line option -u.
|
||||||
|
|
||||||
ui = Tk.Blinkenlights, Tk.VerboseUI, Curses.Blinkenlights, TTY.TTYUI,
|
ui = Curses.Blinkenlights, TTY.TTYUI,
|
||||||
Noninteractive.Basic, Noninteractive.Quiet
|
Noninteractive.Basic, Noninteractive.Quiet
|
||||||
|
|
||||||
# If you try to synchronize messages to a read-only folder,
|
# If you try to synchronize messages to a read-only folder,
|
||||||
@ -116,30 +114,6 @@ footer = "\n"
|
|||||||
# Note that this filter can be used only to further restrict mbnames
|
# Note that this filter can be used only to further restrict mbnames
|
||||||
# to a subset of folders that pass the account's folderfilter.
|
# to a subset of folders that pass the account's folderfilter.
|
||||||
|
|
||||||
##################################################
|
|
||||||
# Blinkenlights configuration
|
|
||||||
##################################################
|
|
||||||
|
|
||||||
[ui.Tk.Blinkenlights]
|
|
||||||
|
|
||||||
# Specifies the default number of lines in the log.
|
|
||||||
|
|
||||||
loglines = 5
|
|
||||||
|
|
||||||
# Specifies how many lines are in the scrollback log buffer.
|
|
||||||
|
|
||||||
bufferlines = 500
|
|
||||||
|
|
||||||
# If true, says that the log should be enabled by default.
|
|
||||||
# Otherwise, you have to click "Show Log" to enable the log.
|
|
||||||
|
|
||||||
showlog = false
|
|
||||||
|
|
||||||
# Sets the font information.
|
|
||||||
|
|
||||||
fontfamily = Helvetica
|
|
||||||
fontsize = 8
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
# Accounts
|
# Accounts
|
||||||
##################################################
|
##################################################
|
||||||
|
@ -214,8 +214,7 @@ remoteuser = jgoerzen
|
|||||||
taken care of for you. If you do not have Python already, check with
|
taken care of for you. If you do not have Python already, check with
|
||||||
your system administrator or operating system vendor; or, download it from
|
your system administrator or operating system vendor; or, download it from
|
||||||
<ulink url="http://www.python.org/">the Python website</ulink>.
|
<ulink url="http://www.python.org/">the Python website</ulink>.
|
||||||
If you intend to use the Tk interface, you must have Tkinter
|
If you intend to use the SSL interface, your
|
||||||
(python-tk) installed. If you intend to use the SSL interface, your
|
|
||||||
Python must have been built with SSL support.
|
Python must have been built with SSL support.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
@ -412,27 +411,22 @@ cd offlineimap-x.y.z</ProgramListing>
|
|||||||
option can override the configuration file setting. The available
|
option can override the configuration file setting. The available
|
||||||
values for the configuration file or command-line are described
|
values for the configuration file or command-line are described
|
||||||
in this section.</para>
|
in this section.</para>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Tk.Blinkenlights</title>
|
<title>Curses.Blinkenlights</title>
|
||||||
<para>Tk.Blinkenlights is an interface designed to be sleek, fun to watch, and
|
<para>
|
||||||
|
Curses.Blinkenlights is an interface designed to be sleek, fun to watch, and
|
||||||
informative of the overall picture of what &OfflineIMAP;
|
informative of the overall picture of what &OfflineIMAP;
|
||||||
is doing. I consider it to be the best general-purpose interface in
|
is doing. I consider it to be the best general-purpose interface in
|
||||||
&OfflineIMAP;.
|
&OfflineIMAP;.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Tk.Blinkenlights contains, by default, a small window with a row of
|
Curses.Blinkenlights contains a row of
|
||||||
LEDs, a small log, and a row of command buttons.
|
"LEDs" with command buttons and a log.
|
||||||
The total size of the window is
|
The log shows more
|
||||||
very small, so it uses little desktop space, yet it is quite
|
|
||||||
functional. The optional, toggleable, log shows more
|
|
||||||
detail about what is happening and is color-coded to match the color
|
detail about what is happening and is color-coded to match the color
|
||||||
of the lights.
|
of the lights.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
|
||||||
Tk.Blinkenlights is the only user interface that has configurable
|
|
||||||
parameters; see the example <filename>offlineimap.conf</filename>
|
|
||||||
for more details.
|
|
||||||
</para>
|
|
||||||
<para>
|
<para>
|
||||||
Each light in the Blinkenlights interface represents a thread
|
Each light in the Blinkenlights interface represents a thread
|
||||||
of execution -- that is, a particular task that &OfflineIMAP;
|
of execution -- that is, a particular task that &OfflineIMAP;
|
||||||
@ -538,32 +532,6 @@ cd offlineimap-x.y.z</ProgramListing>
|
|||||||
</blockquote>
|
</blockquote>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
|
||||||
<title>Curses.Blinkenlights</title>
|
|
||||||
<para>
|
|
||||||
Curses.Blinkenlights is an interface very similar to Tk.Blinkenlights,
|
|
||||||
but is designed to be run in a console window (an xterm, Linux virtual
|
|
||||||
terminal, etc.) Since it doesn't have access to graphics, it isn't
|
|
||||||
quite as pretty, but it still gets the job done.
|
|
||||||
</para>
|
|
||||||
<para>Please see the Tk.Blinkenlights section above for more
|
|
||||||
information about the colors used in this interface.
|
|
||||||
</para>
|
|
||||||
</refsect2>
|
|
||||||
|
|
||||||
<refsect2>
|
|
||||||
<title>Tk.VerboseUI</title>
|
|
||||||
<para>
|
|
||||||
Tk.VerboseUI (formerly known as Tk.TkUI) is a graphical interface
|
|
||||||
that presents a variable-sized window. In the window, each
|
|
||||||
currently-executing thread has a section where its name and current
|
|
||||||
status are displayed. This interface is best suited to people running
|
|
||||||
on slower connections, as you get a lot of detail, but for fast
|
|
||||||
connections, the detail may go by too quickly to be useful. People
|
|
||||||
with fast connections may wish to use Tk.Blinkenlights instead.
|
|
||||||
</para>
|
|
||||||
</refsect2>
|
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>TTY.TTYUI</title>
|
<title>TTY.TTYUI</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -1,543 +0,0 @@
|
|||||||
# Tk UI
|
|
||||||
# Copyright (C) 2002, 2003 John Goerzen
|
|
||||||
# <jgoerzen@complete.org>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
from __future__ import nested_scopes
|
|
||||||
|
|
||||||
from Tkinter import *
|
|
||||||
import tkFont
|
|
||||||
from threading import *
|
|
||||||
import thread, traceback, time, threading
|
|
||||||
from StringIO import StringIO
|
|
||||||
from ScrolledText import ScrolledText
|
|
||||||
from offlineimap import threadutil, version
|
|
||||||
from Queue import Queue
|
|
||||||
from UIBase import UIBase
|
|
||||||
from offlineimap.ui.Blinkenlights import BlinkenBase
|
|
||||||
|
|
||||||
usabletest = None
|
|
||||||
|
|
||||||
class PasswordDialog:
|
|
||||||
def __init__(self, accountname, config, master=None, errmsg = None):
|
|
||||||
self.top = Toplevel(master)
|
|
||||||
self.top.title(version.productname + " Password Entry")
|
|
||||||
text = ''
|
|
||||||
if errmsg:
|
|
||||||
text = '%s: %s\n' % (accountname, errmsg)
|
|
||||||
text += "%s: Enter password: " % accountname
|
|
||||||
self.label = Label(self.top, text = text)
|
|
||||||
self.label.pack()
|
|
||||||
|
|
||||||
self.entry = Entry(self.top, show='*')
|
|
||||||
self.entry.bind("<Return>", self.ok)
|
|
||||||
self.entry.pack()
|
|
||||||
self.entry.focus_force()
|
|
||||||
|
|
||||||
self.button = Button(self.top, text = "OK", command=self.ok)
|
|
||||||
self.button.pack()
|
|
||||||
|
|
||||||
self.entry.focus_force()
|
|
||||||
self.top.wait_window(self.label)
|
|
||||||
|
|
||||||
def ok(self, args = None):
|
|
||||||
self.password = self.entry.get()
|
|
||||||
self.top.destroy()
|
|
||||||
|
|
||||||
def getpassword(self):
|
|
||||||
return self.password
|
|
||||||
|
|
||||||
class TextOKDialog:
|
|
||||||
def __init__(self, title, message, blocking = 1, master = None):
|
|
||||||
if not master:
|
|
||||||
self.top = Tk()
|
|
||||||
else:
|
|
||||||
self.top = Toplevel(master)
|
|
||||||
self.top.title(title)
|
|
||||||
self.text = ScrolledText(self.top, font = "Courier 10")
|
|
||||||
self.text.pack()
|
|
||||||
self.text.insert(END, message)
|
|
||||||
self.text['state'] = DISABLED
|
|
||||||
self.button = Button(self.top, text = "OK", command=self.ok)
|
|
||||||
self.button.pack()
|
|
||||||
|
|
||||||
if blocking:
|
|
||||||
self.top.wait_window(self.button)
|
|
||||||
|
|
||||||
def ok(self):
|
|
||||||
self.top.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ThreadFrame(Frame):
|
|
||||||
def __init__(self, master=None):
|
|
||||||
self.threadextraframe = None
|
|
||||||
self.thread = currentThread()
|
|
||||||
self.threadid = thread.get_ident()
|
|
||||||
Frame.__init__(self, master, relief = RIDGE, borderwidth = 2)
|
|
||||||
self.pack(fill = 'x')
|
|
||||||
self.threadlabel = Label(self, foreground = '#FF0000',
|
|
||||||
text ="Thread %d (%s)" % (self.threadid,
|
|
||||||
self.thread.getName()))
|
|
||||||
self.threadlabel.pack()
|
|
||||||
self.setthread(currentThread())
|
|
||||||
|
|
||||||
self.account = "Unknown"
|
|
||||||
self.mailbox = "Unknown"
|
|
||||||
self.loclabel = Label(self,
|
|
||||||
text = "Account/mailbox information unknown")
|
|
||||||
#self.loclabel.pack()
|
|
||||||
|
|
||||||
self.updateloclabel()
|
|
||||||
|
|
||||||
self.message = Label(self, text="Messages will appear here.\n",
|
|
||||||
foreground = '#0000FF')
|
|
||||||
self.message.pack(fill = 'x')
|
|
||||||
|
|
||||||
def setthread(self, newthread):
|
|
||||||
if newthread:
|
|
||||||
self.threadlabel['text'] = newthread.getName()
|
|
||||||
else:
|
|
||||||
self.threadlabel['text'] = "No thread"
|
|
||||||
self.destroythreadextraframe()
|
|
||||||
|
|
||||||
def destroythreadextraframe(self):
|
|
||||||
if self.threadextraframe:
|
|
||||||
self.threadextraframe.destroy()
|
|
||||||
self.threadextraframe = None
|
|
||||||
|
|
||||||
def getthreadextraframe(self):
|
|
||||||
if self.threadextraframe:
|
|
||||||
return self.threadextraframe
|
|
||||||
self.threadextraframe = Frame(self)
|
|
||||||
self.threadextraframe.pack(fill = 'x')
|
|
||||||
return self.threadextraframe
|
|
||||||
|
|
||||||
def setaccount(self, account):
|
|
||||||
self.account = account
|
|
||||||
self.mailbox = "Unknown"
|
|
||||||
self.updateloclabel()
|
|
||||||
|
|
||||||
def setmailbox(self, mailbox):
|
|
||||||
self.mailbox = mailbox
|
|
||||||
self.updateloclabel()
|
|
||||||
|
|
||||||
def updateloclabel(self):
|
|
||||||
self.loclabel['text'] = "Processing %s: %s" % (self.account,
|
|
||||||
self.mailbox)
|
|
||||||
|
|
||||||
def appendmessage(self, newtext):
|
|
||||||
self.message['text'] += "\n" + newtext
|
|
||||||
|
|
||||||
def setmessage(self, newtext):
|
|
||||||
self.message['text'] = newtext
|
|
||||||
|
|
||||||
|
|
||||||
class VerboseUI(UIBase):
|
|
||||||
def isusable(s):
|
|
||||||
global usabletest
|
|
||||||
if usabletest != None:
|
|
||||||
return usabletest
|
|
||||||
|
|
||||||
try:
|
|
||||||
Tk().destroy()
|
|
||||||
usabletest = 1
|
|
||||||
except TclError:
|
|
||||||
usabletest = 0
|
|
||||||
return usabletest
|
|
||||||
|
|
||||||
def _createTopWindow(self, doidlevac = 1):
|
|
||||||
self.notdeleted = 1
|
|
||||||
self.created = threading.Event()
|
|
||||||
|
|
||||||
self.af = {}
|
|
||||||
self.aflock = Lock()
|
|
||||||
|
|
||||||
t = threadutil.ExitNotifyThread(target = self._runmainloop,
|
|
||||||
name = "Tk Mainloop")
|
|
||||||
t.setDaemon(1)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
self.created.wait()
|
|
||||||
del self.created
|
|
||||||
|
|
||||||
if doidlevac:
|
|
||||||
t = threadutil.ExitNotifyThread(target = self.idlevacuum,
|
|
||||||
name = "Tk idle vacuum")
|
|
||||||
t.setDaemon(1)
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
def _runmainloop(s):
|
|
||||||
s.top = Tk()
|
|
||||||
s.top.title(version.productname + " " + version.versionstr)
|
|
||||||
s.top.after_idle(s.created.set)
|
|
||||||
s.top.mainloop()
|
|
||||||
s.notdeleted = 0
|
|
||||||
|
|
||||||
def getaccountframe(s):
|
|
||||||
accountname = s.getthreadaccount()
|
|
||||||
s.aflock.acquire()
|
|
||||||
try:
|
|
||||||
if accountname in s.af:
|
|
||||||
return s.af[accountname]
|
|
||||||
|
|
||||||
s.af[accountname] = LEDAccountFrame(s.top, accountname,
|
|
||||||
s.fontfamily, s.fontsize)
|
|
||||||
finally:
|
|
||||||
s.aflock.release()
|
|
||||||
return s.af[accountname]
|
|
||||||
|
|
||||||
def getpass(s, accountname, config, errmsg = None):
|
|
||||||
pd = PasswordDialog(accountname, config, errmsg = errmsg)
|
|
||||||
return pd.getpassword()
|
|
||||||
|
|
||||||
def gettf(s, newtype=ThreadFrame, master = None):
|
|
||||||
if master == None:
|
|
||||||
master = s.top
|
|
||||||
threadid = thread.get_ident()
|
|
||||||
s.tflock.acquire()
|
|
||||||
try:
|
|
||||||
if threadid in s.threadframes:
|
|
||||||
return s.threadframes[threadid]
|
|
||||||
if len(s.availablethreadframes):
|
|
||||||
tf = s.availablethreadframes.pop(0)
|
|
||||||
tf.setthread(currentThread())
|
|
||||||
else:
|
|
||||||
tf = newtype(master)
|
|
||||||
s.threadframes[threadid] = tf
|
|
||||||
return tf
|
|
||||||
finally:
|
|
||||||
s.tflock.release()
|
|
||||||
|
|
||||||
def _display(s, msg):
|
|
||||||
s.gettf().setmessage(msg)
|
|
||||||
|
|
||||||
def threadExited(s, thread):
|
|
||||||
threadid = thread.threadid
|
|
||||||
s.tflock.acquire()
|
|
||||||
if threadid in s.threadframes:
|
|
||||||
tf = s.threadframes[threadid]
|
|
||||||
tf.setthread(None)
|
|
||||||
tf.setaccount("Unknown")
|
|
||||||
tf.setmessage("Idle")
|
|
||||||
s.availablethreadframes.append(tf)
|
|
||||||
del s.threadframes[threadid]
|
|
||||||
s.tflock.release()
|
|
||||||
UIBase.threadExited(s, thread)
|
|
||||||
|
|
||||||
def idlevacuum(s):
|
|
||||||
while s.notdeleted:
|
|
||||||
time.sleep(10)
|
|
||||||
s.tflock.acquire()
|
|
||||||
while len(s.availablethreadframes):
|
|
||||||
tf = s.availablethreadframes.pop()
|
|
||||||
tf.destroy()
|
|
||||||
s.tflock.release()
|
|
||||||
|
|
||||||
def terminate(s, exitstatus = 0, errortitle = None, errormsg = None):
|
|
||||||
if errormsg <> None:
|
|
||||||
if errortitle == None:
|
|
||||||
errortitle = "Error"
|
|
||||||
TextOKDialog(errortitle, errormsg)
|
|
||||||
UIBase.terminate(s, exitstatus = exitstatus, errortitle = None, errormsg = None)
|
|
||||||
|
|
||||||
def threadException(s, thread):
|
|
||||||
exceptionstr = s.getThreadExceptionString(thread)
|
|
||||||
print exceptionstr
|
|
||||||
|
|
||||||
s.top.destroy()
|
|
||||||
s.top = None
|
|
||||||
TextOKDialog("Thread Exception", exceptionstr)
|
|
||||||
s.delThreadDebugLog(thread)
|
|
||||||
s.terminate(100)
|
|
||||||
|
|
||||||
def mainException(s):
|
|
||||||
exceptionstr = s.getMainExceptionString()
|
|
||||||
print exceptionstr
|
|
||||||
|
|
||||||
s.top.destroy()
|
|
||||||
s.top = None
|
|
||||||
TextOKDialog("Main Program Exception", exceptionstr)
|
|
||||||
|
|
||||||
def warn(s, msg, minor = 0):
|
|
||||||
if minor:
|
|
||||||
# Just let the default handler catch it
|
|
||||||
UIBase.warn(s, msg, minor)
|
|
||||||
else:
|
|
||||||
TextOKDialog("OfflineIMAP Warning", msg)
|
|
||||||
|
|
||||||
def showlicense(s):
|
|
||||||
TextOKDialog(version.productname + " License",
|
|
||||||
version.bigcopyright + "\n" +
|
|
||||||
version.homepage + "\n\n" + version.license,
|
|
||||||
blocking = 0, master = s.top)
|
|
||||||
|
|
||||||
|
|
||||||
def init_banner(s):
|
|
||||||
s.threadframes = {}
|
|
||||||
s.availablethreadframes = []
|
|
||||||
s.tflock = Lock()
|
|
||||||
s._createTopWindow()
|
|
||||||
s._msg(version.productname + " " + version.versionstr + ", " +\
|
|
||||||
version.copyright)
|
|
||||||
tf = s.gettf().getthreadextraframe()
|
|
||||||
|
|
||||||
b = Button(tf, text = "About", command = s.showlicense)
|
|
||||||
b.pack(side = LEFT)
|
|
||||||
|
|
||||||
b = Button(tf, text = "Exit", command = s.terminate)
|
|
||||||
b.pack(side = RIGHT)
|
|
||||||
s.sleeping_abort = {}
|
|
||||||
|
|
||||||
def deletingmessages(s, uidlist, destlist):
|
|
||||||
ds = s.folderlist(destlist)
|
|
||||||
s._msg("Deleting %d messages in %s" % (len(uidlist), ds))
|
|
||||||
|
|
||||||
def _sleep_cancel(s, args = None):
|
|
||||||
s.sleeping_abort[thread.get_ident()] = 1
|
|
||||||
|
|
||||||
def sleep(s, sleepsecs):
|
|
||||||
threadid = thread.get_ident()
|
|
||||||
s.sleeping_abort[threadid] = 0
|
|
||||||
tf = s.gettf().getthreadextraframe()
|
|
||||||
|
|
||||||
def sleep_cancel():
|
|
||||||
s.sleeping_abort[threadid] = 1
|
|
||||||
|
|
||||||
sleepbut = Button(tf, text = 'Sync immediately',
|
|
||||||
command = sleep_cancel)
|
|
||||||
sleepbut.pack()
|
|
||||||
UIBase.sleep(s, sleepsecs)
|
|
||||||
|
|
||||||
def sleeping(s, sleepsecs, remainingsecs):
|
|
||||||
retval = s.sleeping_abort[thread.get_ident()]
|
|
||||||
if remainingsecs:
|
|
||||||
s._msg("Next sync in %d:%02d" % (remainingsecs / 60,
|
|
||||||
remainingsecs % 60))
|
|
||||||
else:
|
|
||||||
s._msg("Wait done; synchronizing now.")
|
|
||||||
s.gettf().destroythreadextraframe()
|
|
||||||
del s.sleeping_abort[thread.get_ident()]
|
|
||||||
time.sleep(sleepsecs)
|
|
||||||
return retval
|
|
||||||
|
|
||||||
TkUI = VerboseUI
|
|
||||||
|
|
||||||
################################################## Blinkenlights
|
|
||||||
|
|
||||||
class LEDAccountFrame:
|
|
||||||
def __init__(self, top, accountname, fontfamily, fontsize):
|
|
||||||
self.top = top
|
|
||||||
self.accountname = accountname
|
|
||||||
self.fontfamily = fontfamily
|
|
||||||
self.fontsize = fontsize
|
|
||||||
self.frame = Frame(self.top, background = 'black')
|
|
||||||
self.frame.pack(side = BOTTOM, expand = 1, fill = X)
|
|
||||||
self._createcanvas(self.frame)
|
|
||||||
|
|
||||||
self.label = Label(self.frame, text = accountname,
|
|
||||||
background = "black", foreground = "blue",
|
|
||||||
font = (self.fontfamily, self.fontsize))
|
|
||||||
self.label.grid(sticky = E, row = 0, column = 1)
|
|
||||||
|
|
||||||
def getnewthreadframe(s):
|
|
||||||
return LEDThreadFrame(s.canvas)
|
|
||||||
|
|
||||||
def _createcanvas(self, parent):
|
|
||||||
c = LEDFrame(parent)
|
|
||||||
self.canvas = c
|
|
||||||
c.grid(sticky = E, row = 0, column = 0)
|
|
||||||
parent.grid_columnconfigure(1, weight = 1)
|
|
||||||
#c.pack(side = LEFT, expand = 0, fill = X)
|
|
||||||
|
|
||||||
def startsleep(s, sleepsecs):
|
|
||||||
s.sleeping_abort = 0
|
|
||||||
s.button = Button(s.frame, text = "Sync now", command = s.syncnow,
|
|
||||||
background = "black", activebackground = "black",
|
|
||||||
activeforeground = "white",
|
|
||||||
foreground = "blue", highlightthickness = 0,
|
|
||||||
padx = 0, pady = 0,
|
|
||||||
font = (s.fontfamily, s.fontsize), borderwidth = 0,
|
|
||||||
relief = 'solid')
|
|
||||||
s.button.grid(sticky = E, row = 0, column = 2)
|
|
||||||
|
|
||||||
def syncnow(s):
|
|
||||||
s.sleeping_abort = 1
|
|
||||||
|
|
||||||
def sleeping(s, sleepsecs, remainingsecs):
|
|
||||||
if remainingsecs:
|
|
||||||
s.button.config(text = 'Sync now (%d:%02d remain)' % \
|
|
||||||
(remainingsecs / 60, remainingsecs % 60))
|
|
||||||
time.sleep(sleepsecs)
|
|
||||||
else:
|
|
||||||
s.button.destroy()
|
|
||||||
del s.button
|
|
||||||
return s.sleeping_abort
|
|
||||||
|
|
||||||
class LEDFrame(Frame):
|
|
||||||
"""This holds the different lights."""
|
|
||||||
def getnewobj(self):
|
|
||||||
retval = Canvas(self, background = 'black', height = 20, bd = 0,
|
|
||||||
highlightthickness = 0, width = 10)
|
|
||||||
retval.pack(side = LEFT, padx = 0, pady = 0, ipadx = 0, ipady = 0)
|
|
||||||
return retval
|
|
||||||
|
|
||||||
class LEDThreadFrame:
|
|
||||||
"""There is one of these for each little light."""
|
|
||||||
def __init__(self, master):
|
|
||||||
self.canvas = master.getnewobj()
|
|
||||||
self.color = ''
|
|
||||||
self.ovalid = self.canvas.create_oval(4, 4, 9,
|
|
||||||
9, fill = 'gray',
|
|
||||||
outline = '#303030')
|
|
||||||
|
|
||||||
def setcolor(self, newcolor):
|
|
||||||
if newcolor != self.color:
|
|
||||||
self.canvas.itemconfigure(self.ovalid, fill = newcolor)
|
|
||||||
self.color = newcolor
|
|
||||||
|
|
||||||
def getcolor(self):
|
|
||||||
return self.color
|
|
||||||
|
|
||||||
def setthread(self, newthread):
|
|
||||||
if newthread:
|
|
||||||
self.setcolor('gray')
|
|
||||||
else:
|
|
||||||
self.setcolor('black')
|
|
||||||
|
|
||||||
|
|
||||||
class Blinkenlights(BlinkenBase, VerboseUI):
|
|
||||||
def __init__(s, config, verbose = 0):
|
|
||||||
VerboseUI.__init__(s, config, verbose)
|
|
||||||
s.fontfamily = 'Helvetica'
|
|
||||||
s.fontsize = 8
|
|
||||||
if config.has_option('ui.Tk.Blinkenlights', 'fontfamily'):
|
|
||||||
s.fontfamily = config.get('ui.Tk.Blinkenlights', 'fontfamily')
|
|
||||||
if config.has_option('ui.Tk.Blinkenlights', 'fontsize'):
|
|
||||||
s.fontsize = config.getint('ui.Tk.Blinkenlights', 'fontsize')
|
|
||||||
|
|
||||||
def isusable(s):
|
|
||||||
return VerboseUI.isusable(s)
|
|
||||||
|
|
||||||
def _createTopWindow(self):
|
|
||||||
VerboseUI._createTopWindow(self, 0)
|
|
||||||
#self.top.resizable(width = 0, height = 0)
|
|
||||||
self.top.configure(background = 'black', bd = 0)
|
|
||||||
|
|
||||||
widthmetric = tkFont.Font(family = self.fontfamily, size = self.fontsize).measure("0")
|
|
||||||
self.loglines = self.config.getdefaultint("ui.Tk.Blinkenlights",
|
|
||||||
"loglines", 5)
|
|
||||||
self.bufferlines = self.config.getdefaultint("ui.Tk.Blinkenlights",
|
|
||||||
"bufferlines", 500)
|
|
||||||
self.text = ScrolledText(self.top, bg = 'black', #scrollbar = 'y',
|
|
||||||
font = (self.fontfamily, self.fontsize),
|
|
||||||
bd = 0, highlightthickness = 0, setgrid = 0,
|
|
||||||
state = DISABLED, height = self.loglines,
|
|
||||||
wrap = NONE, width = 60)
|
|
||||||
self.text.vbar.configure(background = '#000050',
|
|
||||||
activebackground = 'blue',
|
|
||||||
highlightbackground = 'black',
|
|
||||||
troughcolor = "black", bd = 0,
|
|
||||||
elementborderwidth = 2)
|
|
||||||
|
|
||||||
self.textenabled = 0
|
|
||||||
self.tags = []
|
|
||||||
self.textlock = Lock()
|
|
||||||
|
|
||||||
def init_banner(s):
|
|
||||||
BlinkenBase.init_banner(s)
|
|
||||||
s._createTopWindow()
|
|
||||||
menubar = Menu(s.top, activebackground = "black",
|
|
||||||
activeforeground = "white",
|
|
||||||
activeborderwidth = 0,
|
|
||||||
background = "black", foreground = "blue",
|
|
||||||
font = (s.fontfamily, s.fontsize), bd = 0)
|
|
||||||
menubar.add_command(label = "About", command = s.showlicense)
|
|
||||||
menubar.add_command(label = "Show Log", command = s._togglelog)
|
|
||||||
menubar.add_command(label = "Exit", command = s.terminate)
|
|
||||||
s.top.config(menu = menubar)
|
|
||||||
s.menubar = menubar
|
|
||||||
s.text.see(END)
|
|
||||||
if s.config.getdefaultboolean("ui.Tk.Blinkenlights", "showlog", 1):
|
|
||||||
s._togglelog()
|
|
||||||
s.gettf().setcolor('red')
|
|
||||||
s.top.resizable(width = 0, height = 0)
|
|
||||||
s._msg(version.banner)
|
|
||||||
|
|
||||||
def _togglelog(s):
|
|
||||||
if s.textenabled:
|
|
||||||
s.oldtextheight = s.text.winfo_height()
|
|
||||||
s.text.pack_forget()
|
|
||||||
s.textenabled = 0
|
|
||||||
s.menubar.entryconfig('Hide Log', label = 'Show Log')
|
|
||||||
s.top.update()
|
|
||||||
s.top.geometry("")
|
|
||||||
s.top.update()
|
|
||||||
s.top.resizable(width = 0, height = 0)
|
|
||||||
s.top.update()
|
|
||||||
|
|
||||||
else:
|
|
||||||
s.text.pack(side = TOP, expand = 1, fill = BOTH)
|
|
||||||
s.textenabled = 1
|
|
||||||
s.top.update()
|
|
||||||
s.top.geometry("")
|
|
||||||
s.menubar.entryconfig('Show Log', label = 'Hide Log')
|
|
||||||
s._rescroll()
|
|
||||||
s.top.resizable(width = 1, height = 1)
|
|
||||||
|
|
||||||
def sleep(s, sleepsecs):
|
|
||||||
s.gettf().setcolor('red')
|
|
||||||
s._msg("Next sync in %d:%02d" % (sleepsecs / 60, sleepsecs % 60))
|
|
||||||
BlinkenBase.sleep(s, sleepsecs)
|
|
||||||
|
|
||||||
def sleeping(s, sleepsecs, remainingsecs):
|
|
||||||
return BlinkenBase.sleeping(s, sleepsecs, remainingsecs)
|
|
||||||
|
|
||||||
def _rescroll(s):
|
|
||||||
s.text.see(END)
|
|
||||||
lo, hi = s.text.vbar.get()
|
|
||||||
s.text.vbar.set(1.0 - (hi - lo), 1.0)
|
|
||||||
|
|
||||||
def _display(s, msg):
|
|
||||||
if "\n" in msg:
|
|
||||||
for thisline in msg.split("\n"):
|
|
||||||
s._msg(thisline)
|
|
||||||
return
|
|
||||||
#VerboseUI._msg(s, msg)
|
|
||||||
color = s.gettf().getcolor()
|
|
||||||
rescroll = 1
|
|
||||||
s.textlock.acquire()
|
|
||||||
try:
|
|
||||||
if s.text.vbar.get()[1] != 1.0:
|
|
||||||
rescroll = 0
|
|
||||||
s.text.config(state = NORMAL)
|
|
||||||
if not color in s.tags:
|
|
||||||
s.text.tag_config(color, foreground = color)
|
|
||||||
s.tags.append(color)
|
|
||||||
s.text.insert(END, "\n" + msg, color)
|
|
||||||
|
|
||||||
# Trim down. Not quite sure why I have to say 7 instead of 5,
|
|
||||||
# but so it is.
|
|
||||||
while float(s.text.index(END)) > s.bufferlines + 2.0:
|
|
||||||
s.text.delete(1.0, 2.0)
|
|
||||||
|
|
||||||
if rescroll:
|
|
||||||
s._rescroll()
|
|
||||||
finally:
|
|
||||||
s.text.config(state = DISABLED)
|
|
||||||
s.textlock.release()
|
|
||||||
|
|
||||||
|
|
@ -23,13 +23,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
|
||||||
import Tkinter
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
import Tk
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import curses
|
import curses
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -19,8 +19,7 @@
|
|||||||
import offlineimap.ui
|
import offlineimap.ui
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
DEFAULT_UI_LIST = ('Tk.Blinkenlights', 'Tk.VerboseUI',
|
DEFAULT_UI_LIST = ('Curses.Blinkenlights', 'TTY.TTYUI',
|
||||||
'Curses.Blinkenlights', 'TTY.TTYUI',
|
|
||||||
'Noninteractive.Basic', 'Noninteractive.Quiet')
|
'Noninteractive.Basic', 'Noninteractive.Quiet')
|
||||||
|
|
||||||
def findUI(config, chosenUI=None):
|
def findUI(config, chosenUI=None):
|
||||||
|
Loading…
Reference in New Issue
Block a user