diff --git a/src/ctx_exit.py b/src/ctx_exit.py new file mode 100644 index 0000000..fd9deb7 --- /dev/null +++ b/src/ctx_exit.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +""" +Author: Giampaolo Rodola' +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() diff --git a/src/dotnet.py b/src/dotnet.py new file mode 100644 index 0000000..8b282ef --- /dev/null +++ b/src/dotnet.py @@ -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=] [--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() diff --git a/src/tktable_calendar.py b/src/tktable_calendar.py new file mode 100644 index 0000000..b3dd13d --- /dev/null +++ b/src/tktable_calendar.py @@ -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('', 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('<>') + + _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('<>', print_date) + cal.mainloop() + +if __name__ == "__main__": + sample() + \ No newline at end of file diff --git a/src/tktable_wrapper.py b/src/tktable_wrapper.py new file mode 100644 index 0000000..e0d228d --- /dev/null +++ b/src/tktable_wrapper.py @@ -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 " + +__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 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() diff --git a/src/winsc.py b/src/winsc.py new file mode 100644 index 0000000..a07570e --- /dev/null +++ b/src/winsc.py @@ -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) \ No newline at end of file diff --git a/src/winservice.py b/src/winservice.py new file mode 100644 index 0000000..1130d05 --- /dev/null +++ b/src/winservice.py @@ -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') \ No newline at end of file