Merge with 98928d0cb657697265ae143b2e8cc63d0a7b07f5
--HG-- branch : aspn
This commit is contained in:
103
src/ctx_exit.py
Normal file
103
src/ctx_exit.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Author: Giampaolo Rodola' <g.rodola [AT] gmail [DOT] com>
|
||||
License: MIT
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
import contextlib
|
||||
import signal
|
||||
import sys
|
||||
|
||||
|
||||
_sigterm_handler = lambda signum, frame: None
|
||||
|
||||
@contextlib.contextmanager
|
||||
def handle_exit(callback=None, append=False):
|
||||
"""A context manager which properly handles SIGTERM and
|
||||
SIGINT (KeyboardInterrupt) signals, registering a function
|
||||
which is guaranteed to be called after signals are received.
|
||||
|
||||
Common usage:
|
||||
|
||||
>>> app = App()
|
||||
>>> with handle_exit(app.stop):
|
||||
... app.start()
|
||||
...
|
||||
>>>
|
||||
|
||||
Working (and useless) example:
|
||||
|
||||
>>> import os, signal
|
||||
>>>
|
||||
>>> def start():
|
||||
... print "starting"
|
||||
... os.kill(os.getpid(), signal.SIGTERM)
|
||||
...
|
||||
>>> def stop():
|
||||
... print "exiting"
|
||||
...
|
||||
>>> with handle_exit(stop):
|
||||
... start()
|
||||
...
|
||||
starting
|
||||
exiting
|
||||
>>>
|
||||
|
||||
If append == False raise RuntimeError if there's already a handler
|
||||
registered for SIGTERM, otherwise both new and old handlers are
|
||||
executed in this order.
|
||||
"""
|
||||
old_handler = signal.signal(signal.SIGTERM, _sigterm_handler)
|
||||
if (old_handler != signal.SIG_DFL) and (old_handler != _sigterm_handler):
|
||||
if not append:
|
||||
raise RuntimeError("there is already a handler registered for " \
|
||||
"SIGTERM: %r, use 'append=True' option" % old_handler)
|
||||
else:
|
||||
def handler(signum, frame):
|
||||
try:
|
||||
_sigterm_handler(signum, frame)
|
||||
finally:
|
||||
old_handler(signum, frame)
|
||||
signal.signal(signal.SIGTERM, handler)
|
||||
try:
|
||||
yield
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
pass
|
||||
finally:
|
||||
if callback is not None:
|
||||
callback()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import time
|
||||
|
||||
def start():
|
||||
print "starting"
|
||||
while 1:
|
||||
time.sleep(1)
|
||||
|
||||
def stop():
|
||||
print "exiting"
|
||||
|
||||
with handle_exit(stop):
|
||||
start()
|
||||
|
||||
import atexit
|
||||
import time
|
||||
import os
|
||||
import signal
|
||||
|
||||
@atexit.register
|
||||
def cleanup():
|
||||
# ==== XXX ====
|
||||
# this never gets called
|
||||
print "exiting"
|
||||
|
||||
def main():
|
||||
print "starting"
|
||||
time.sleep(1)
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
193
src/dotnet.py
Normal file
193
src/dotnet.py
Normal file
@@ -0,0 +1,193 @@
|
||||
'''
|
||||
A simple python script to find out the .NET framework versions
|
||||
installed on a local or remote machine. (remote machine does not work yet ;)
|
||||
|
||||
Usage:
|
||||
donet.py [--machine|-m=<computer_name>] [--check|-c=all|1.0|1.1|2.0|3.0|3.5|4]
|
||||
if invoked with a 32 bit python, 32 bit versions of .net framework will be returned;
|
||||
if invoked with a 64 bit python, 64 bit versions of .net framework will be returned.
|
||||
|
||||
Sample Run:
|
||||
C:\IronPythonPlay>'C:\Program Files (x86)\IronPython 2.7.1\ipy64.exe' dotnet.py
|
||||
|
||||
2.0.50727.5420 SP2 - None
|
||||
3.0.30729.5420 SP2 - None
|
||||
3.5.30729.5420 SP1 64bit C:\Windows\Microsoft.NET\Framework64\v3.5\
|
||||
4.0.30319:Client GA 64bit C:\Windows\Microsoft.NET\Framework64\v4.0.30319\
|
||||
4.0.30319:Full GA 64bit c:\Windows\Microsoft.NET\Framework64\v4.0.30319\
|
||||
|
||||
C:\IronPythonPlay>"C:\Program Files (x86)\IronPython 2.7.1\ipy.exe" dotnet.py
|
||||
|
||||
2.0.50727.5420 SP2 - None
|
||||
3.0.30729.5420 SP2 - None
|
||||
3.5.30729.5420 SP1 32bit C:\Windows\Microsoft.NET\Framework\v3.5\
|
||||
4.0.30319:Client GA 32bit C:\Windows\Microsoft.NET\Framework\v4.0.30319\
|
||||
4.0.30319:Full GA 32bit c:\Windows\Microsoft.NET\Framework\v4.0.30319\
|
||||
|
||||
Author: Yong Zhao (zonplm At gmail dot com)
|
||||
Date: 2012-05-22
|
||||
Rev: 0.1
|
||||
|
||||
http://code.activestate.com/recipes/578143-a-small-python-script-to-detect-net-framwork-versi/
|
||||
'''
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from _winreg import *
|
||||
except:
|
||||
print '''Unable to import _winreg module!
|
||||
Please Check your python installation.
|
||||
'''
|
||||
exit(-1)
|
||||
|
||||
DOT_NET_VERSIONS = {
|
||||
'1.0': (r'Software\Microsoft\Active Setup\Installed Components\{78705f0d-e8db-4b2d-8193-982bdda15ecd}',
|
||||
#1.0 Windows XP Media Center 2002/2004/2005 and Tablet PC 2004/2005
|
||||
r'Software\Microsoft\Active Setup\Installed Components\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}'
|
||||
),
|
||||
'1.1': (r'SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322', ),
|
||||
'2.0': (r'Software\Microsoft\NET Framework Setup\NDP\v2.0.50727', ),
|
||||
'3.0': (r'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0',),
|
||||
'3.5': (r'SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5',),
|
||||
'4': (r'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client',
|
||||
r'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full') # v4 has Client or Full profiles
|
||||
# add future .NET framework info below
|
||||
}
|
||||
|
||||
class DotNetVersion(object):
|
||||
def __init__(self, version, sp, is32or64, installpath):
|
||||
self.version = version
|
||||
self.servicepack = sp
|
||||
self.is32or64 = is32or64
|
||||
self.installpath = installpath
|
||||
|
||||
def __repr__(self):
|
||||
return json.dumps( {'dotnetversion': self.version,
|
||||
'servicepack': self.servicepack,
|
||||
'is32or64': self.is32or64,
|
||||
'installpath': self.installpath})
|
||||
|
||||
def __str__(self):
|
||||
sp = 'GA'
|
||||
if self.servicepack:
|
||||
sp = 'SP'+ str(self.servicepack)
|
||||
|
||||
return "{0:18} {1:<4} {2:5} {3}".format(self.version, sp,
|
||||
self.is32or64,self.installpath)
|
||||
|
||||
class DotNetManager(object):
|
||||
def __init__(self, machine=None):
|
||||
try:
|
||||
if machine == None:
|
||||
self.lm_hive = OpenKey(HKEY_LOCAL_MACHINE, '')
|
||||
else:
|
||||
self.lm_hive = ConnectRegistry(machine, HKEY_LOCAL_MACHINE)
|
||||
|
||||
except WindowsError, ex:
|
||||
print ex
|
||||
exit(-2)
|
||||
|
||||
def __del__(self):
|
||||
if self.lm_hive:
|
||||
CloseKey(self.lm_hive)
|
||||
|
||||
def _getdotnetinfo(self, subkeyname):
|
||||
thever = None
|
||||
try:
|
||||
if subkeyname:
|
||||
subkey = OpenKey(self.lm_hive, subkeyname)
|
||||
install, itype = QueryValueEx(subkey, 'Install')
|
||||
version, vtype = QueryValueEx(subkey, 'Version')
|
||||
sp, sptype = QueryValueEx(subkey, 'SP')
|
||||
installPath, iptype = QueryValueEx(subkey, 'InstallPath')
|
||||
is32or64 = '-'
|
||||
if installPath and installPath.find('Framework64') > -1:
|
||||
is32or64 = '64bit'
|
||||
elif installPath and installPath.find('Framework') > -1:
|
||||
is32or64 = '32bit'
|
||||
|
||||
if install:
|
||||
thever = DotNetVersion(version, sp, is32or64, installPath)
|
||||
|
||||
if subkey: CloseKey(subkey)
|
||||
|
||||
except Exception, ex:
|
||||
#print ex
|
||||
pass
|
||||
|
||||
return thever
|
||||
|
||||
def getdotnetversion(self, iver):
|
||||
'''
|
||||
Given a version string such as 3.0, return a list of DotNetVersion object
|
||||
'''
|
||||
thever = None
|
||||
allProfile = []
|
||||
|
||||
for subkeyname in DOT_NET_VERSIONS.get(iver, []):
|
||||
theVer = self._getdotnetinfo(subkeyname)
|
||||
#1.0, return as soon as find a valid installation
|
||||
if iver == "1.0":
|
||||
if theVer:
|
||||
allProfile.append(theVer)
|
||||
break
|
||||
#4, return both Client and Full profiles
|
||||
elif iver == "4":
|
||||
profile = subkeyname.split("\\")[-1]
|
||||
theVer.version += ":"+ profile
|
||||
|
||||
if theVer: allProfile.append(theVer)
|
||||
|
||||
return allProfile
|
||||
#return DotNetVersion('v'+ iver, '0', '32bit', r'C:\dummy\path\v' + iver)
|
||||
|
||||
|
||||
def getalldotnetversions(self):
|
||||
'''
|
||||
Get all .net framework versions installed on the given MACHINE.
|
||||
A list of DotNetVersion objects is returned
|
||||
'''
|
||||
allversions = []
|
||||
for ver in DOT_NET_VERSIONS.keys():
|
||||
allversions.extend(self.getdotnetversion(ver) )
|
||||
|
||||
return allversions
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
import pprint
|
||||
|
||||
parser = argparse.ArgumentParser(description=
|
||||
'''find .NET framework versions installed on MACHINE.
|
||||
for now, the script only works on the local machine.
|
||||
''')
|
||||
parser.add_argument("-m", "--machine")
|
||||
parser.add_argument("-c", "--check", default="all",
|
||||
help=".net versions to check: all|1.0|1.1|2.0|3.0|3.5|4")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
#for now we just ignore remote machine option
|
||||
#pprint.pprint(DOT_NET_VERSIONS)
|
||||
if args.machine:
|
||||
args.machine = r"\\" + args.machine
|
||||
|
||||
if args.machine == None:
|
||||
print os.environ['COMPUTERNAME'], ':'
|
||||
else:
|
||||
print args.machine, ":"
|
||||
|
||||
dotnetmgr = DotNetManager(args.machine)
|
||||
if (args.check == "all"):
|
||||
allvers = dotnetmgr.getalldotnetversions()
|
||||
#pprint.pprint(allvers)
|
||||
else:
|
||||
allvers = dotnetmgr.getdotnetversion(args.check)
|
||||
|
||||
for ver in sorted(allvers, lambda x,y: cmp(x.version, y.version)):
|
||||
print str(ver)
|
||||
|
||||
exit(0)
|
||||
#sys.stdin.readline()
|
||||
210
src/tktable_calendar.py
Normal file
210
src/tktable_calendar.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import platform
|
||||
import calendar
|
||||
from datetime import datetime
|
||||
|
||||
import ttk
|
||||
import tktable
|
||||
|
||||
LINUX = platform.system() == 'Linux'
|
||||
|
||||
def get_calendar(locale, fwday):
|
||||
if locale is None:
|
||||
return calendar.TextCalendar(fwday)
|
||||
else:
|
||||
return calendar.LocaleTextCalendar(fwday, locale)
|
||||
|
||||
|
||||
class ArrowButton(ttk.Button):
|
||||
arrow_layout = lambda self, direc: (
|
||||
[('Button.focus', {'children': [('Button.%sarrow' % direc, None)]})]
|
||||
)
|
||||
|
||||
def __init__(self, master, **kw):
|
||||
direction = kw.pop('direction', 'left')
|
||||
style = ttk.Style(master)
|
||||
|
||||
# XXX urgh
|
||||
if LINUX:
|
||||
style.layout('L.TButton', self.arrow_layout('left'))
|
||||
style.layout('R.TButton', self.arrow_layout('right'))
|
||||
kw['style'] = 'L.TButton' if direction == 'left' else 'R.TButton'
|
||||
else:
|
||||
kw['text'] = u'\u25C0' if direction == 'left' else u'\u25B6'
|
||||
kw['style'] = 'Arrow.TButton'
|
||||
style.configure(kw['style'], width=2, padding=0)
|
||||
# urgh end
|
||||
|
||||
ttk.Button.__init__(self, master, **kw)
|
||||
|
||||
|
||||
class Calendar(ttk.Frame, object):
|
||||
def __init__(self, master=None, **kw):
|
||||
ttk.Frame.__init__(self, master)
|
||||
|
||||
params = {'locale': None, 'titlebg': 'blue', 'titlefg': 'white',
|
||||
'calendarbg': 'white'}
|
||||
params.update(kw)
|
||||
for arg, val in params.iteritems():
|
||||
setattr(self, "_%s" % arg, val)
|
||||
|
||||
date = datetime.now()
|
||||
self._year, self._month = date.year, date.month
|
||||
|
||||
self._setup_style()
|
||||
self._build_topbar()
|
||||
|
||||
# calendar
|
||||
self._cal = get_calendar(self._locale, calendar.SUNDAY)
|
||||
self._tclarray = tktable.ArrayVar(self)
|
||||
cols = self._cal.formatweekheader(3).split()
|
||||
self.table = tktable.Table(self, variable=self._tclarray,
|
||||
highlightthickness=4, highlightcolor=self._calendarbg,
|
||||
highlightbackground=self._calendarbg,
|
||||
cols=len(cols) + 1, rows=7, background=self._calendarbg,
|
||||
titlerows=1, titlecols=1, roworigin=-1, colorigin=-1,
|
||||
bd=0, cursor='arrow', resizeborders='none', colwidth=5,
|
||||
state='disabled', browsecommand=self._set_selection)
|
||||
self.table.pack(side='bottom')
|
||||
self.table.bind('<Map>', self._set_minsize)
|
||||
|
||||
self._setup_table(cols)
|
||||
# update calendar
|
||||
self._yeardates = self._year
|
||||
|
||||
|
||||
def next_month(self):
|
||||
if self._month == 12:
|
||||
self._month = 1
|
||||
self._year += 1
|
||||
self._yeardates = self._year
|
||||
else:
|
||||
self._month += 1
|
||||
self._adjust_calendar(self._month)
|
||||
|
||||
|
||||
def prev_month(self):
|
||||
if self._month == 1:
|
||||
self._month = 12
|
||||
self._year -= 1
|
||||
self._yeardates = self._year
|
||||
else:
|
||||
self._month -= 1
|
||||
self._adjust_calendar(self._month)
|
||||
|
||||
|
||||
def next_year(self):
|
||||
self._year += 1
|
||||
self._yeardates = self._year
|
||||
|
||||
|
||||
def prev_year(self):
|
||||
self._year -= 1
|
||||
self._yeardates = self._year
|
||||
|
||||
|
||||
|
||||
def _setup_style(self):
|
||||
style = ttk.Style(self)
|
||||
if LINUX:
|
||||
style.theme_use('clam')
|
||||
|
||||
def _build_topbar(self):
|
||||
bar = ttk.Frame(self, relief='raised', padding=4)
|
||||
bar.pack(side='top', fill='x')
|
||||
lbtn = ArrowButton(bar, direction='left', command=self.prev_month)
|
||||
rbtn = ArrowButton(bar, direction='right', command=self.next_month)
|
||||
self._monthlbl = ttk.Label(bar, text=calendar.month_name[self._month],
|
||||
width=len(max(calendar.month_name)), anchor='center')
|
||||
lbtn.grid(row=0, column=0, sticky='w')
|
||||
self._monthlbl.grid(row=0, column=1, padx=6)
|
||||
rbtn.grid(row=0, column=2, sticky='w')
|
||||
|
||||
spacer = ttk.Label(bar, text='')
|
||||
spacer.grid(row=0, column=3, sticky='ew')
|
||||
|
||||
lbtn2 = ArrowButton(bar, direction='left', command=self.prev_year)
|
||||
rbtn2 = ArrowButton(bar, direction='right', command=self.next_year)
|
||||
self._yearlbl = ttk.Label(bar, text=self._year)
|
||||
lbtn2.grid(row=0, column=4, sticky='e')
|
||||
self._yearlbl.grid(row=0, column=5, padx=6, sticky='e')
|
||||
rbtn2.grid(row=0, column=6, sticky='e')
|
||||
|
||||
bar.grid_columnconfigure(3, weight=1)
|
||||
|
||||
def _setup_table(self, cols):
|
||||
table = self.table
|
||||
table.tag_configure('title', bg=self._titlebg, fg=self._titlefg)
|
||||
|
||||
array = self._tclarray
|
||||
for indx, col in enumerate(cols):
|
||||
table_indx = '-1,%d' % indx
|
||||
array[table_indx] = col
|
||||
|
||||
def _adjust_calendar(self, month_now):
|
||||
array = self._tclarray
|
||||
table = self.table
|
||||
month_0 = month_now - 1
|
||||
# remove the 'not_this_month' tag from items that were using it and
|
||||
# possibly won't be redisplayed now.
|
||||
table.tag_delete('not_this_month')
|
||||
table.tag_configure('not_this_month', fg='grey70')
|
||||
# XXX clear selection
|
||||
table.selection_clear('all')
|
||||
|
||||
# update values in calendar
|
||||
self._monthlbl['text'] = calendar.month_name[month_now]
|
||||
for week_indx, week in enumerate(self._yeardates[month_0]):
|
||||
array['%d,-1' % week_indx] = week[0].strftime('%U')
|
||||
for day_indx, date in enumerate(week):
|
||||
table_indx = '%d,%d' % (week_indx, day_indx)
|
||||
array[table_indx] = date.day
|
||||
if date.month != month_now:
|
||||
table.tag_cell('not_this_month', table_indx)
|
||||
|
||||
# erase data in rows that weren't overrwritten
|
||||
for row in range(len(self._yeardates[month_0]), 6):
|
||||
for i in range(-1, 7):
|
||||
array.unset('%d,%d' % (row, i))
|
||||
|
||||
def _set_minsize(self, event):
|
||||
self.master.wm_minsize(self.winfo_width(), self.winfo_height())
|
||||
|
||||
def _get_year_dates(self):
|
||||
return self.__year_dates
|
||||
|
||||
def _set_year_dates(self, year):
|
||||
self.__year_dates = [
|
||||
self._cal.monthdatescalendar(year, i)
|
||||
for i in range(calendar.January, calendar.January+12)
|
||||
]
|
||||
self._yearlbl['text'] = year
|
||||
self._adjust_calendar(self._month)
|
||||
|
||||
def _get_selected(self):
|
||||
week, day = self.__selected
|
||||
date = self._yeardates[self._month - 1][week][day]
|
||||
return (date.year, date.month, date.day)
|
||||
|
||||
def _set_selection(self, event):
|
||||
if event.r == -1 or event.c == -1 or not self._tclarray.get(event.C):
|
||||
return
|
||||
|
||||
self.__selected = (event.r, event.c)
|
||||
self.event_generate('<<date-selected>>')
|
||||
|
||||
_yeardates = property(_get_year_dates, _set_year_dates)
|
||||
selected = property(_get_selected, _set_selection)
|
||||
|
||||
|
||||
def sample():
|
||||
def print_date(event):
|
||||
print event.widget.selected
|
||||
|
||||
cal = Calendar(titlebg='#2077ed', titlefg='white')
|
||||
cal.pack()
|
||||
cal.bind('<<date-selected>>', print_date)
|
||||
cal.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
sample()
|
||||
|
||||
683
src/tktable_wrapper.py
Normal file
683
src/tktable_wrapper.py
Normal file
@@ -0,0 +1,683 @@
|
||||
# Copyright (c) 2008, Guilherme Polo
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
This contains a wrapper class for the tktable widget as well a class for using
|
||||
tcl arrays that are, in some instances, required by tktable.
|
||||
"""
|
||||
|
||||
__author__ = "Guilherme Polo <ggpolo@gmail.com>"
|
||||
|
||||
__all__ = ["ArrayVar", "Table"]
|
||||
|
||||
import os
|
||||
import Tkinter
|
||||
|
||||
def _setup_master(master):
|
||||
if master is None:
|
||||
if Tkinter._support_default_root:
|
||||
master = Tkinter._default_root or Tkinter.Tk()
|
||||
else:
|
||||
raise RuntimeError("No master specified and Tkinter is "
|
||||
"configured to not support default master")
|
||||
return master
|
||||
|
||||
class ArrayVar(Tkinter.Variable):
|
||||
"""Class for handling Tcl arrays.
|
||||
|
||||
An array is actually an associative array in Tcl, so this class supports
|
||||
some dict operations.
|
||||
"""
|
||||
|
||||
def __init__(self, master=None, name=None):
|
||||
# Tkinter.Variable.__init__ is not called on purpose! I don't wanna
|
||||
# see an ugly _default value in the pretty array.
|
||||
self._master = _setup_master(master)
|
||||
self._tk = self._master.tk
|
||||
if name:
|
||||
self._name = name
|
||||
else:
|
||||
self._name = 'PY_VAR%s' % id(self)
|
||||
|
||||
def __del__(self):
|
||||
if bool(self._tk.call('info', 'exists', self._name)):
|
||||
self._tk.globalunsetvar(self._name)
|
||||
|
||||
def __len__(self):
|
||||
return int(self._tk.call('array', 'size', str(self)))
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.set(**{str(key): value})
|
||||
|
||||
def names(self):
|
||||
return self._tk.call('array', 'names', self._name)
|
||||
|
||||
def get(self, key=None):
|
||||
if key is None:
|
||||
flatten_pairs = self._tk.call('array', 'get', str(self))
|
||||
return dict(zip(flatten_pairs[::2], flatten_pairs[1::2]))
|
||||
|
||||
return self._tk.globalgetvar(str(self), str(key))
|
||||
|
||||
def set(self, **kw):
|
||||
self._tk.call('array', 'set', str(self), Tkinter._flatten(kw.items()))
|
||||
|
||||
def unset(self, pattern=None):
|
||||
"""Unsets all of the elements in the array. If pattern is given, only
|
||||
the elements that match pattern are unset. """
|
||||
self._tk.call('array', 'unset', str(self), pattern)
|
||||
|
||||
|
||||
_TKTABLE_LOADED = False
|
||||
|
||||
class Table(Tkinter.Widget):
|
||||
"""Create and manipulate tables."""
|
||||
|
||||
_switches = ('holddimensions', 'holdselection', 'holdtags', 'holdwindows',
|
||||
'keeptitles', '-')
|
||||
_tabsubst_format = ('%c', '%C', '%i', '%r', '%s', '%S', '%W')
|
||||
_tabsubst_commands = ('browsecommand', 'browsecmd', 'command',
|
||||
'selectioncommand', 'selcmd',
|
||||
'validatecommand', 'valcmd')
|
||||
|
||||
def __init__(self, master=None, **kw):
|
||||
master = _setup_master(master)
|
||||
global _TKTABLE_LOADED
|
||||
if not _TKTABLE_LOADED:
|
||||
tktable_lib = os.environ.get('TKTABLE_LIBRARY')
|
||||
if tktable_lib:
|
||||
master.tk.eval('global auto_path; '
|
||||
'lappend auto_path {%s}' % tktable_lib)
|
||||
master.tk.call('package', 'require', 'Tktable')
|
||||
_TKTABLE_LOADED = True
|
||||
|
||||
Tkinter.Widget.__init__(self, master, 'table', kw)
|
||||
|
||||
|
||||
def _options(self, cnf, kw=None):
|
||||
if kw:
|
||||
cnf = Tkinter._cnfmerge((cnf, kw))
|
||||
else:
|
||||
cnf = Tkinter._cnfmerge(cnf)
|
||||
|
||||
res = ()
|
||||
for k, v in cnf.iteritems():
|
||||
if callable(v):
|
||||
if k in self._tabsubst_commands:
|
||||
v = "%s %s" % (self._register(v, self._tabsubst),
|
||||
' '.join(self._tabsubst_format))
|
||||
else:
|
||||
v = self._register(v)
|
||||
res += ('-%s' % k, v)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def _tabsubst(self, *args):
|
||||
if len(args) != len(self._tabsubst_format):
|
||||
return args
|
||||
|
||||
tk = self.tk
|
||||
c, C, i, r, s, S, W = args
|
||||
e = Tkinter.Event()
|
||||
|
||||
e.widget = self
|
||||
e.c = tk.getint(c)
|
||||
e.i = tk.getint(i)
|
||||
e.r = tk.getint(r)
|
||||
e.C = "%d,%d" % (e.r, e.c)
|
||||
e.s = s
|
||||
e.S = S
|
||||
try:
|
||||
e.W = self._nametowidget(W)
|
||||
except KeyError:
|
||||
e.W = None
|
||||
|
||||
return (e,)
|
||||
|
||||
|
||||
def _handle_switches(self, args):
|
||||
args = args or ()
|
||||
return tuple(('-%s' % x) for x in args if x in self._switches)
|
||||
|
||||
|
||||
def activate(self, index):
|
||||
"""Set the active cell to the one indicated by index."""
|
||||
self.tk.call(self._w, 'activate', index)
|
||||
|
||||
|
||||
def bbox(self, first, last=None):
|
||||
"""Return the bounding box for the specified cell (range) as a
|
||||
4-tuple of x, y, width and height in pixels. It clips the box to
|
||||
the visible portion, if any, otherwise an empty tuple is returned."""
|
||||
return self._getints(self.tk.call(self._w, 'bbox', first, last)) or ()
|
||||
|
||||
|
||||
def clear(self, option, first=None, last=None):
|
||||
"""This is a convenience routine to clear certain state information
|
||||
managed by the table. first and last represent valid table indices.
|
||||
If neither are specified, then the command operates on the whole
|
||||
table."""
|
||||
self.tk.call(self._w, 'clear', option, first, last)
|
||||
|
||||
|
||||
def clear_cache(self, first=None, last=None):
|
||||
"""Clear the specified section of the cache, if the table has been
|
||||
keeping one."""
|
||||
self.clear('cache', first, last)
|
||||
|
||||
|
||||
def clear_sizes(self, first=None, last=None):
|
||||
"""Clear the specified row and column areas of specific height/width
|
||||
dimensions. When just one index is specified, for example 2,0, that
|
||||
is interpreted as row 2 and column 0."""
|
||||
self.clear('sizes', first, last)
|
||||
|
||||
|
||||
def clear_tags(self, first=None, last=None):
|
||||
"""Clear the specified area of tags (all row, column and cell tags)."""
|
||||
self.clear('tags', first, last)
|
||||
|
||||
|
||||
def clear_all(self, first=None, last=None):
|
||||
"""Perform all of the above clear functions on the specified area."""
|
||||
self.clear('all', first, last)
|
||||
|
||||
|
||||
def curselection(self, value=None):
|
||||
"""With no arguments, it returns the sorted indices of the currently
|
||||
selected cells. Otherwise it sets all the selected cells to the given
|
||||
value if there is an associated ArrayVar and the state is not
|
||||
disabled."""
|
||||
result = self.tk.call(self._w, 'curselection', value)
|
||||
if value is None:
|
||||
return result
|
||||
|
||||
|
||||
def curvalue(self, value=None):
|
||||
"""If no value is given, the value of the cell being edited (indexed
|
||||
by active) is returned, else it is set to the given value. """
|
||||
return self.tk.call(self._w, 'curvalue', value)
|
||||
|
||||
|
||||
def delete_active(self, index1, index2=None):
|
||||
"""Deletes text from the active cell. If only one index is given,
|
||||
it deletes the character after that index, otherwise it deletes from
|
||||
the first index to the second. index can be a number, insert or end."""
|
||||
self.tk.call(self._w, 'delete', 'active', index1, index2)
|
||||
|
||||
|
||||
def delete_cols(self, index, count=None, switches=None):
|
||||
args = self._handle_switches(switches) + (index, count)
|
||||
self.tk.call(self._w, 'delete', 'cols', *args)
|
||||
|
||||
|
||||
def delete_rows(self, index, count=None, switches=None):
|
||||
args = self._handle_switches(switches) + (index, count)
|
||||
self.tk.call(self._w, 'delete', 'rows', *args)
|
||||
|
||||
|
||||
def get(self, first, last=None):
|
||||
"""Returns the value of the cells specified by the table indices
|
||||
first and (optionally) last."""
|
||||
return self.tk.call(self._w, 'get', first, last)
|
||||
|
||||
|
||||
def height(self, row=None, **kwargs):
|
||||
"""If row and kwargs are not given, a list describing all rows for
|
||||
which a width has been set is returned.
|
||||
If row is given, the height of that row is returnd.
|
||||
If kwargs is given, then it sets the key/value pairs, where key is a
|
||||
row and value represents the height for the row."""
|
||||
if row is None and not kwargs:
|
||||
pairs = self.tk.splitlist(self.tk.call(self._w, 'height'))
|
||||
return dict(pair.split() for pair in pairs)
|
||||
elif row:
|
||||
return int(self.tk.call(self._w, 'height', str(row)))
|
||||
|
||||
args = Tkinter._flatten(kwargs.items())
|
||||
self.tk.call(self._w, 'height', *args)
|
||||
|
||||
|
||||
def hidden(self, *args):
|
||||
"""When called without args, it returns all the hidden cells (those
|
||||
cells covered by a spanning cell). If one index is specified, it
|
||||
returns the spanning cell covering that index, if any. If multiple
|
||||
indices are specified, it returns 1 if all indices are hidden cells,
|
||||
0 otherwise."""
|
||||
return self.tk.call(self._w, 'hidden', *args)
|
||||
|
||||
|
||||
def icursor(self, arg=None):
|
||||
"""If arg is not specified, return the location of the insertion
|
||||
cursor in the active cell. Otherwise, set the cursor to that point in
|
||||
the string.
|
||||
|
||||
0 is before the first character, you can also use insert or end for
|
||||
the current insertion point or the end of the text. If there is no
|
||||
active cell, or the cell or table is disabled, this will return -1."""
|
||||
return self.tk.call(self._w, 'icursor', arg)
|
||||
|
||||
|
||||
def index(self, index, rc=None):
|
||||
"""Return the integer cell coordinate that corresponds to index in the
|
||||
form row, col. If rc is specified, it must be either 'row' or 'col' so
|
||||
only the row or column index is returned."""
|
||||
res = self.tk.call(self._w, 'index', index, rc)
|
||||
if rc is None:
|
||||
return res
|
||||
else:
|
||||
return int(res)
|
||||
|
||||
|
||||
def insert_active(self, index, value):
|
||||
"""The value is a text string which is inserted at the index postion
|
||||
of the active cell. The cursor is then positioned after the new text.
|
||||
index can be a number, insert or end. """
|
||||
self.tk.call(self._w, 'insert', 'active', index, value)
|
||||
|
||||
|
||||
def insert_cols(self, index, count=None, switches=None):
|
||||
args = self._handle_switches(switches) + (index, count)
|
||||
self.tk.call(self._w, 'insert', 'cols', *args)
|
||||
|
||||
|
||||
def insert_rows(self, index, count=None, switches=None):
|
||||
args = self._handle_switches(switches) + (index, count)
|
||||
self.tk.call(self._w, 'insert', 'rows', *args)
|
||||
|
||||
|
||||
#def postscript(self, **kwargs):
|
||||
# """Skip this command if you are under Windows.
|
||||
#
|
||||
# Accepted options:
|
||||
# colormap, colormode, file, channel, first, fontmap, height,
|
||||
# last, pageanchor, pageheight, pagewidth, pagex, pagey, rotate,
|
||||
# width, x, y
|
||||
# """
|
||||
# args = ()
|
||||
# for key, val in kwargs.iteritems():
|
||||
# args += ('-%s' % key, val)
|
||||
#
|
||||
# return self.tk.call(self._w, 'postscript', *args)
|
||||
|
||||
|
||||
def reread(self):
|
||||
"""Rereads the old contents of the cell back into the editing buffer.
|
||||
Useful for a key binding when <Escape> is pressed to abort the edit
|
||||
(a default binding)."""
|
||||
self.tk.call(self._w, 'reread')
|
||||
|
||||
|
||||
def scan_mark(self, x, y):
|
||||
self.tk.call(self._w, 'scan', 'mark', x, y)
|
||||
|
||||
|
||||
def scan_dragto(self, x, y):
|
||||
self.tk.call(self._w, 'scan', 'dragto', x, y)
|
||||
|
||||
|
||||
def see(self, index):
|
||||
self.tk.call(self._w, 'see', index)
|
||||
|
||||
|
||||
def selection_anchor(self, index):
|
||||
self.tk.call(self._w, 'selection', 'anchor', index)
|
||||
|
||||
|
||||
def selection_clear(self, first, last=None):
|
||||
self.tk.call(self._w, 'selection', 'clear', first, last)
|
||||
|
||||
|
||||
def selection_includes(self, index):
|
||||
return self.getboolean(self.tk.call(self._w, 'selection', 'includes',
|
||||
index))
|
||||
|
||||
|
||||
def selection_set(self, first, last=None):
|
||||
self.tk.call(self._w, 'selection', 'set', first, last)
|
||||
|
||||
|
||||
def set(self, rc=None, index=None, *args, **kwargs):
|
||||
"""If rc is specified (either 'row' or 'col') then it is assumes that
|
||||
args (if given) represents values which will be set into the
|
||||
subsequent columns (if row is specified) or rows (for col).
|
||||
If index is not None and args is not given, then it will return the
|
||||
value(s) for the cell(s) specified.
|
||||
|
||||
If kwargs is given, assumes that each key in kwargs is a index in this
|
||||
table and sets the specified index to the associated value. Table
|
||||
validation will not be triggered via this method.
|
||||
|
||||
Note that the table must have an associated array (defined through the
|
||||
variable option) in order to this work."""
|
||||
if not args and index is not None:
|
||||
if rc:
|
||||
args = (rc, index)
|
||||
else:
|
||||
args = (index, )
|
||||
return self.tk.call(self._w, 'set', *args)
|
||||
|
||||
if rc is None:
|
||||
args = Tkinter._flatten(kwargs.items())
|
||||
self.tk.call(self._w, 'set', *args)
|
||||
else:
|
||||
self.tk.call(self._w, 'set', rc, index, args)
|
||||
|
||||
|
||||
def spans(self, index=None, **kwargs):
|
||||
"""Manipulate row/col spans.
|
||||
|
||||
When called with no arguments, all known spans are returned as a dict.
|
||||
When called with only the index, the span for that index only is
|
||||
returned, if any. Otherwise kwargs is assumed to contain keys/values
|
||||
pairs used to set spans. A span starts at the row,col defined by a key
|
||||
and continues for the specified number of rows,cols specified by
|
||||
its value. A span of 0,0 unsets any span on that cell."""
|
||||
if kwargs:
|
||||
args = Tkinter._flatten(kwargs.items())
|
||||
self.tk.call(self._w, 'spans', *args)
|
||||
else:
|
||||
return self.tk.call(self._w, 'spans', index)
|
||||
|
||||
|
||||
def tag_cell(self, tagname, *args):
|
||||
return self.tk.call(self._w, 'tag', 'cell', tagname, *args)
|
||||
|
||||
|
||||
def tag_cget(self, tagname, option):
|
||||
return self.tk.call(self._w, 'tag', 'cget', tagname, '-%s' % option)
|
||||
|
||||
|
||||
def tag_col(self, tagname, *args):
|
||||
return self.tk.call(self._w, 'tag', 'col', tagname, *args)
|
||||
|
||||
|
||||
def tag_configure(self, tagname, option=None, **kwargs):
|
||||
"""Query or modify options associated with the tag given by tagname.
|
||||
|
||||
If no option is specified, a dict describing all of the available
|
||||
options for tagname is returned. If option is specified, then the
|
||||
command returns a list describing the one named option. Lastly, if
|
||||
kwargs is given then it corresponds to option-value pairs that should
|
||||
be modified."""
|
||||
if option is None and not kwargs:
|
||||
split1 = self.tk.splitlist(
|
||||
self.tk.call(self._w, 'tag', 'configure', tagname))
|
||||
|
||||
result = {}
|
||||
for item in split1:
|
||||
res = self.tk.splitlist(item)
|
||||
result[res[0]] = res[1:]
|
||||
|
||||
return result
|
||||
|
||||
elif option:
|
||||
return self.tk.call(self._w, 'tag', 'configure', tagname,
|
||||
'-%s' % option)
|
||||
|
||||
else:
|
||||
args = ()
|
||||
for key, val in kwargs.iteritems():
|
||||
args += ('-%s' % key, val)
|
||||
|
||||
self.tk.call(self._w, 'tag', 'configure', tagname, *args)
|
||||
|
||||
|
||||
def tag_delete(self, tagname):
|
||||
self.tk.call(self._w, 'tag', 'delete', tagname)
|
||||
|
||||
|
||||
def tag_exists(self, tagname):
|
||||
return self.getboolean(self.tk.call(self._w, 'tag', 'exists', tagname))
|
||||
|
||||
|
||||
def tag_includes(self, tagname, index):
|
||||
return self.getboolean(self.tk.call(self._w, 'tag', 'includes',
|
||||
tagname, index))
|
||||
|
||||
|
||||
def tag_lower(self, tagname, belowthis=None):
|
||||
self.tk.call(self._w, 'tag', 'lower', belowthis)
|
||||
|
||||
|
||||
def tag_names(self, pattern=None):
|
||||
return self.tk.call(self._w, 'tag', 'names', pattern)
|
||||
|
||||
|
||||
def tag_raise(self, tagname, abovethis=None):
|
||||
self.tk.call(self._w, 'tag', 'raise', tagname, abovethis)
|
||||
|
||||
|
||||
def tag_row(self, tagname, *args):
|
||||
return self.tk.call(self._w, 'tag', 'row', tagname, *args)
|
||||
|
||||
|
||||
def validate(self, index):
|
||||
"""Explicitly validates the specified index based on the current
|
||||
callback set for the validatecommand option. Return 0 or 1 based on
|
||||
whether the cell was validated."""
|
||||
return self.tk.call(self._w, 'validate', index)
|
||||
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Return tktable's package version."""
|
||||
return self.tk.call(self._w, 'version')
|
||||
|
||||
|
||||
def width(self, column=None, **kwargs):
|
||||
"""If column and kwargs are not given, a dict describing all columns
|
||||
for which a width has been set is returned.
|
||||
If column is given, the width of that column is returnd.
|
||||
If kwargs is given, then it sets the key/value pairs, where key is a
|
||||
column and value represents the width for the column."""
|
||||
if column is None and not kwargs:
|
||||
pairs = self.tk.splitlist(self.tk.call(self._w, 'width'))
|
||||
return dict(pair.split() for pair in pairs)
|
||||
elif column is not None:
|
||||
return int(self.tk.call(self._w, 'width', str(column)))
|
||||
|
||||
args = Tkinter._flatten(kwargs.items())
|
||||
self.tk.call(self._w, 'width', *args)
|
||||
|
||||
|
||||
def window_cget(self, index, option):
|
||||
return self.tk.call(self._w, 'window', 'cget', index, option)
|
||||
|
||||
|
||||
def window_configure(self, index, option=None, **kwargs):
|
||||
"""Query or modify options associated with the embedded window given
|
||||
by index. This should also be used to add a new embedded window into
|
||||
the table.
|
||||
|
||||
If no option is specified, a dict describing all of the available
|
||||
options for index is returned. If option is specified, then the
|
||||
command returns a list describing the one named option. Lastly, if
|
||||
kwargs is given then it corresponds to option-value pairs that should
|
||||
be modified."""
|
||||
if option is None and not kwargs:
|
||||
return self.tk.call(self._w, 'window', 'configure', index)
|
||||
elif option:
|
||||
return self.tk.call(self._w, 'window', 'configure', index,
|
||||
'-%s' % option)
|
||||
else:
|
||||
args = ()
|
||||
for key, val in kwargs.iteritems():
|
||||
args += ('-%s' % key, val)
|
||||
|
||||
self.tk.call(self._w, 'window', 'configure', index, *args)
|
||||
|
||||
|
||||
def window_delete(self, *indexes):
|
||||
self.tk.call(self._w, 'window', 'delete', *indexes)
|
||||
|
||||
|
||||
def window_move(self, index_from, index_to):
|
||||
self.tk.call(self._w, 'window', 'move', index_from, index_to)
|
||||
|
||||
|
||||
def window_names(self, pattern=None):
|
||||
return self.tk.call(self._w, 'window', 'names', pattern)
|
||||
|
||||
|
||||
def xview(self, index=None):
|
||||
"""If index is not given a tuple containing two fractions is returned,
|
||||
each fraction is between 0 and 1. Together they describe the
|
||||
horizontal span that is visible in the window.
|
||||
|
||||
If index is given the view in the window is adjusted so that the
|
||||
column given by index is displayed at the left edge of the window."""
|
||||
res = self.tk.call(self._w, 'xview', index)
|
||||
if index is None:
|
||||
return self._getdoubles(res)
|
||||
|
||||
|
||||
def xview_moveto(self, fraction):
|
||||
"""Adjusts the view in the window so that fraction of the total width
|
||||
of the table text is off-screen to the left. The fraction parameter
|
||||
must be a fraction between 0 and 1."""
|
||||
self.tk.call(self._w, 'xview', 'moveto', fraction)
|
||||
|
||||
|
||||
def xview_scroll(self, *L):
|
||||
#change by frank gao for attach scrollbar 11/11/2010
|
||||
"""Shift the view in the window left or right according to number and
|
||||
what. The 'number' parameter must be an integer. The 'what' parameter
|
||||
must be either units or pages or an abbreviation of one of these.
|
||||
|
||||
If 'what' is units, the view adjusts left or right by number cells on
|
||||
the display; if it is pages then the view adjusts by number screenfuls.
|
||||
If 'number' is negative then cells farther to the left become visible;
|
||||
if it is positive then cells farther to the right become visible. """
|
||||
#self.tk.call(self._w, 'xview', 'scroll', number, what)
|
||||
if op=='scroll':
|
||||
units=L[2]
|
||||
self.tk.call(self._w, 'xview', 'scroll',howMany,units)
|
||||
elif op=='moveto':
|
||||
self.tk.call(self._w, 'xview', 'moveto',howMany)
|
||||
|
||||
|
||||
def yview(self, index=None):
|
||||
"""If index is not given a tuple containing two fractions is returned,
|
||||
each fraction is between 0 and 1. The first element gives the position
|
||||
of the table element at the top of the window, relative to the table
|
||||
as a whole. The second element gives the position of the table element
|
||||
just after the last one in the window, relative to the table as a
|
||||
whole.
|
||||
|
||||
If index is given the view in the window is adjusted so that the
|
||||
row given by index is displayed at the top of the window."""
|
||||
res = self.tk.call(self._w, 'yview', index)
|
||||
if index is None:
|
||||
return self._getdoubles(res)
|
||||
|
||||
|
||||
def yview_moveto(self, fraction):
|
||||
"""Adjusts the view in the window so that the element given by
|
||||
fraction appears at the top of the window. The fraction parameter
|
||||
must be a fraction between 0 and 1."""
|
||||
self.tk.call(self._w, 'yview', 'moveto', fraction)
|
||||
|
||||
|
||||
def yview_scroll(self, *L):
|
||||
#change by frank gao for attach scrollbar 11/11/2010
|
||||
"""Adjust the view in the window up or down according to number and
|
||||
what. The 'number' parameter must be an integer. The 'what' parameter
|
||||
must be either units or pages or an abbreviation of one of these.
|
||||
|
||||
If 'what' is units, the view adjusts up or down by number cells; if it
|
||||
is pages then the view adjusts by number screenfuls.
|
||||
If 'number' is negative then earlier elements become visible; if it
|
||||
is positive then later elements become visible. """
|
||||
#self.tk.call(self._w, 'yview', 'scroll', number, what)
|
||||
op,howMany=L[0],L[1]
|
||||
if op=='scroll':
|
||||
units=L[2]
|
||||
self.tk.call(self._w, 'yview', 'scroll',howMany,units)
|
||||
elif op=='moveto':
|
||||
self.tk.call(self._w, 'yview', 'moveto',howMany)
|
||||
|
||||
|
||||
# Sample test taken from tktable cvs, original tktable python wrapper
|
||||
def sample_test():
|
||||
from Tkinter import Tk, Label, Button
|
||||
|
||||
def test_cmd(event):
|
||||
if event.i == 0:
|
||||
return '%i, %i' % (event.r, event.c)
|
||||
else:
|
||||
return 'set'
|
||||
|
||||
def browsecmd(event):
|
||||
print "event:", event.__dict__
|
||||
print "curselection:", test.curselection()
|
||||
print "active cell index:", test.index('active')
|
||||
print "active:", test.index('active', 'row')
|
||||
print "anchor:", test.index('anchor', 'row')
|
||||
|
||||
root = Tk()
|
||||
|
||||
var = ArrayVar(root)
|
||||
for y in range(-1, 4):
|
||||
for x in range(-1, 5):
|
||||
index = "%i,%i" % (y, x)
|
||||
var[index] = index
|
||||
|
||||
label = Label(root, text="Proof-of-existence test for Tktable")
|
||||
label.pack(side = 'top', fill = 'x')
|
||||
|
||||
quit = Button(root, text="QUIT", command=root.destroy)
|
||||
quit.pack(side = 'bottom', fill = 'x')
|
||||
|
||||
test = Table(root,
|
||||
rows=10,
|
||||
cols=5,
|
||||
state='disabled',
|
||||
width=6,
|
||||
height=6,
|
||||
titlerows=1,
|
||||
titlecols=1,
|
||||
roworigin=-1,
|
||||
colorigin=-1,
|
||||
selectmode='browse',
|
||||
selecttype='row',
|
||||
rowstretch='unset',
|
||||
colstretch='last',
|
||||
browsecmd=browsecmd,
|
||||
flashmode='on',
|
||||
variable=var,
|
||||
usecommand=0,
|
||||
command=test_cmd)
|
||||
test.pack(expand=1, fill='both')
|
||||
test.tag_configure('sel', background = 'yellow')
|
||||
test.tag_configure('active', background = 'blue')
|
||||
test.tag_configure('title', anchor='w', bg='red', relief='sunken')
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
sample_test()
|
||||
25
src/winsc.py
Normal file
25
src/winsc.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# http://code.activestate.com/recipes/59872-manipulating-windows-services/
|
||||
|
||||
import win32serviceutil
|
||||
|
||||
def service_info(action, service):
|
||||
if action == 'stop':
|
||||
win32serviceutil.StopService(service)
|
||||
print '%s stopped successfully' % service
|
||||
elif action == 'start':
|
||||
win32serviceutil.StartService(service)
|
||||
print '%s started successfully' % service
|
||||
elif action == 'restart':
|
||||
win32serviceutil.RestartService(service)
|
||||
print '%s restarted successfully' % service
|
||||
elif action == 'status':
|
||||
if win32serviceutil.QueryServiceStatus(service)[1] == 4:
|
||||
print "%s is running normally" % service
|
||||
else:
|
||||
print "%s is *not* running" % service
|
||||
|
||||
if __name__ == '__main__':
|
||||
machine = 'cr582427-a'
|
||||
service = 'Zope23'
|
||||
action = 'start'
|
||||
service_info(action, service)
|
||||
106
src/winservice.py
Normal file
106
src/winservice.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# winservice.py
|
||||
# Recipe 551780 revision 3
|
||||
|
||||
from os.path import splitext, abspath
|
||||
from sys import modules
|
||||
|
||||
import win32serviceutil
|
||||
import win32service
|
||||
import win32event
|
||||
import win32api
|
||||
|
||||
class Service(win32serviceutil.ServiceFramework):
|
||||
_svc_name_ = '_unNamed'
|
||||
_svc_display_name_ = '_Service Template'
|
||||
def __init__(self, *args):
|
||||
win32serviceutil.ServiceFramework.__init__(self, *args)
|
||||
self.log('init')
|
||||
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
|
||||
def log(self, msg):
|
||||
import servicemanager
|
||||
servicemanager.LogInfoMsg(str(msg))
|
||||
def sleep(self, sec):
|
||||
win32api.Sleep(sec*1000, True)
|
||||
def SvcDoRun(self):
|
||||
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
|
||||
try:
|
||||
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
|
||||
self.log('start')
|
||||
self.start()
|
||||
self.log('wait')
|
||||
win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
|
||||
self.log('done')
|
||||
except Exception, x:
|
||||
self.log('Exception : %s' % x)
|
||||
self.SvcStop()
|
||||
def SvcStop(self):
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||
self.log('stopping')
|
||||
self.stop()
|
||||
self.log('stopped')
|
||||
win32event.SetEvent(self.stop_event)
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
|
||||
# to be overridden
|
||||
def start(self): pass
|
||||
# to be overridden
|
||||
def stop(self): pass
|
||||
|
||||
def instart(cls, name, display_name=None, stay_alive=True):
|
||||
''' Install and Start (auto) a Service
|
||||
|
||||
cls : the class (derived from Service) that implement the Service
|
||||
name : Service name
|
||||
display_name : the name displayed in the service manager
|
||||
stay_alive : Service will stop on logout if False
|
||||
'''
|
||||
cls._svc_name_ = name
|
||||
cls._svc_display_name_ = display_name or name
|
||||
try:
|
||||
module_path=modules[cls.__module__].__file__
|
||||
except AttributeError:
|
||||
# maybe py2exe went by
|
||||
from sys import executable
|
||||
module_path=executable
|
||||
module_file = splitext(abspath(module_path))[0]
|
||||
cls._svc_reg_class_ = '%s.%s' % (module_file, cls.__name__)
|
||||
if stay_alive: win32api.SetConsoleCtrlHandler(lambda x: True, True)
|
||||
try:
|
||||
win32serviceutil.InstallService(
|
||||
cls._svc_reg_class_,
|
||||
cls._svc_name_,
|
||||
cls._svc_display_name_,
|
||||
startType = win32service.SERVICE_AUTO_START
|
||||
)
|
||||
print 'Install ok'
|
||||
win32serviceutil.StartService(
|
||||
cls._svc_name_
|
||||
)
|
||||
print 'Start ok'
|
||||
except Exception, x:
|
||||
print str(x)
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
##### TEST MODULE
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
# winservice_test.py
|
||||
|
||||
from winservice import Service, instart
|
||||
|
||||
class Test(Service):
|
||||
def start(self):
|
||||
self.runflag=True
|
||||
while self.runflag:
|
||||
self.sleep(10)
|
||||
self.log("I'm alive ...")
|
||||
def stop(self):
|
||||
self.runflag=False
|
||||
self.log("I'm done")
|
||||
|
||||
instart(Test, 'aTest', 'Python Service Test')
|
||||
Reference in New Issue
Block a user