diff options
-rw-r--r-- | Lib/lib-stdwin/DirList.py | 63 | ||||
-rw-r--r-- | Lib/lib-stdwin/FormSplit.py | 56 | ||||
-rw-r--r-- | Lib/lib-stdwin/TextEdit.py | 58 | ||||
-rw-r--r-- | Lib/lib-stdwin/WindowSched.py | 57 | ||||
-rwxr-xr-x | Lib/persist.py | 297 | ||||
-rw-r--r-- | Lib/sched.py | 96 | ||||
-rwxr-xr-x | Lib/stdwin/DirList.py | 63 | ||||
-rwxr-xr-x | Lib/stdwin/FormSplit.py | 56 | ||||
-rwxr-xr-x | Lib/stdwin/TextEdit.py | 58 | ||||
-rwxr-xr-x | Lib/stdwin/WindowSched.py | 57 |
10 files changed, 861 insertions, 0 deletions
diff --git a/Lib/lib-stdwin/DirList.py b/Lib/lib-stdwin/DirList.py new file mode 100644 index 0000000..fb0ae99 --- /dev/null +++ b/Lib/lib-stdwin/DirList.py @@ -0,0 +1,63 @@ +# DirList -- Directory Listing widget + +try: + import posix, path + os = posix +except NameError: + import mac, macpath + os = mac + path = macpath + +import stdwin, rect +from stdwinevents import * +from Buttons import PushButton +from WindowParent import WindowParent +from HVSplit import HSplit, VSplit + +class DirList() = VSplit(): + # + def create(self, (parent, dirname)): + self = VSplit.create(self, parent) + names = os.listdir(dirname) + for name in names: + if path.isdir(path.cat(dirname, name)): + fullname = path.cat(dirname, name) + btn = SubdirButton().definetext(self, fullname) + elif name[-3:] = '.py': + btn = ModuleButton().definetext(self, name) + else: + btn = FileButton().definetext(self, name) + return self + # + +class DirListWindow() = WindowParent(): + # + def create(self, dirname): + self = WindowParent.create(self, (dirname, (0, 0))) + child = DirList().create(self, dirname) + self.realize() + return self + # + +class SubdirButton() = PushButton(): + # + def drawpict(self, d): + PushButton.drawpict(self, d) + d.box(rect.inset(self.bounds, (3, 1))) + # + def up_trigger(self): + window = DirListWindow().create(self.text) + # + +class FileButton() = PushButton(): + # + def up_trigger(self): + stdwin.fleep() + # + +class ModuleButton() = FileButton(): + # + def drawpict(self, d): + PushButton.drawpict(self, d) + d.box(rect.inset(self.bounds, (1, 3))) + # diff --git a/Lib/lib-stdwin/FormSplit.py b/Lib/lib-stdwin/FormSplit.py new file mode 100644 index 0000000..4f0bd01 --- /dev/null +++ b/Lib/lib-stdwin/FormSplit.py @@ -0,0 +1,56 @@ +# A FormSplit lets you place its children exactly where you want them +# (including silly places!). +# It does no explicit geometry management except moving its children +# when it is moved. +# The interface to place children is as follows. +# Before you add a child, you may specify its (left, top) position +# relative to the FormSplit. If you don't specify a position for +# a child, it goes right below the previous child; the first child +# goes to (0, 0) by default. +# NB: This places data attributes named form_* on its children. +# XXX Yes, I know, there should be options to do all sorts of relative +# placement, but for now this will do. + +from Split import Split + +class FormSplit() = Split(): + # + def create(self, parent): + self.next_left = self.next_top = 0 + self.last_child = None + return Split.create(self, parent) + # + def minsize(self, m): + max_width, max_height = 0, 0 + for c in self.children: + c.form_width, c.form_height = c.minsize(m) + max_width = max(max_width, c.form_width + c.form_left) + max_height = max(max_height, c.form_height + c.form_top) + return max_width, max_height + # + def getbounds(self): + return self.bounds + # + def setbounds(self, bounds): + self.bounds = bounds + fleft, ftop = bounds[0] + for c in self.children: + left, top = c.form_left + fleft, c.form_top + ftop + right, bottom = left + c.form_width, top + c.form_height + c.setbounds((left, top), (right, bottom)) + # + def placenext(self, (left, top)): + self.next_left = left + self.next_top = top + self.last_child = None + # + def addchild(self, child): + if self.last_child: + width, height = \ + self.last_child.minsize(self.beginmeasuring()) + self.next_top = self.next_top + height + child.form_left = self.next_left + child.form_top = self.next_top + Split.addchild(self, child) + self.last_child = child + # diff --git a/Lib/lib-stdwin/TextEdit.py b/Lib/lib-stdwin/TextEdit.py new file mode 100644 index 0000000..8d12465 --- /dev/null +++ b/Lib/lib-stdwin/TextEdit.py @@ -0,0 +1,58 @@ +# Text editing widget + +from stdwinevents import * + +class TextEdit(): + # + def create(self, (parent, (cols, rows))): + parent.addchild(self) + self.parent = parent + self.cols = cols + self.rows = rows + self.text = '' + # Creation of the editor is done in realize() + self.editor = 0 + return self + # + # Downcalls from parent to child + # + def destroy(self): + del self.parent + del self.editor + del self.window + # + def minsize(self, m): + return self.cols*m.textwidth('n'), self.rows*m.lineheight() + def setbounds(self, bounds): + self.bounds = bounds + if self.editor: + self.editor.move(bounds) + def getbounds(self, bounds): + if self.editor: + return self.editor.getrect() + else: + return self.bounds + def realize(self): + self.window = self.parent.getwindow() + self.editor = self.window.textcreate(self.bounds) + self.editor.replace(self.text) + self.parent.need_mouse(self) + self.parent.need_keybd(self) + self.parent.need_altdraw(self) + def draw(self, (d, area)): + pass + def altdraw(self, area): + self.editor.draw(area) + # + # Event downcalls + # + def mouse_down(self, detail): + x = self.editor.event(WE_MOUSE_DOWN, self.window, detail) + def mouse_move(self, detail): + x = self.editor.event(WE_MOUSE_MOVE, self.window, detail) + def mouse_up(self, detail): + x = self.editor.event(WE_MOUSE_UP, self.window, detail) + # + def keybd(self, (type, detail)): + x = self.editor.event(type, self.window, detail) + # diff --git a/Lib/lib-stdwin/WindowSched.py b/Lib/lib-stdwin/WindowSched.py new file mode 100644 index 0000000..19be2b1 --- /dev/null +++ b/Lib/lib-stdwin/WindowSched.py @@ -0,0 +1,57 @@ +# Combine a real-time scheduling queue and stdwin event handling. +# Uses the millisecond timer. + +import stdwin +from stdwinevents import WE_TIMER +import WindowParent +import sched +import time + +# Delay function called by the scheduler when it has nothing to do. +# Return immediately when something is done, or when the delay is up. +# +def delayfunc(msecs): + # + # Check for immediate stdwin event + # + event = stdwin.pollevent() + if event: + WindowParent.Dispatch(event) + return + # + # Use millisleep for very short delays or if there are no windows + # + if msecs < 100 or WindowParent.CountWindows() = 0: + time.millisleep(msecs) + return + # + # Post a timer event on an arbitrary window and wait for it + # + window = WindowParent.AnyWindow() + window.settimer(msecs/100) + event = stdwin.getevent() + window.settimer(0) + if event[0] <> WE_TIMER: + WindowParent.Dispatch(event) + +q = sched.scheduler().init(time.millitimer, delayfunc) + +# Export functions enter, enterabs and cancel just like a scheduler +# +enter = q.enter +enterabs = q.enterabs +cancel = q.cancel + +# Emptiness check must check both queues +# +def empty(): + return q.empty() and WindowParent.CountWindows() = 0 + +# Run until there is nothing left to do +# +def run(): + while not empty(): + if q.empty(): + WindowParent.Dispatch(stdwin.getevent()) + else: + q.run() diff --git a/Lib/persist.py b/Lib/persist.py new file mode 100755 index 0000000..bc91e4d --- /dev/null +++ b/Lib/persist.py @@ -0,0 +1,297 @@ +# persist.py +# +# Implement limited persistence. +# +# Simple interface: +# persist.save() save __main__ module on file (overwrite) +# persist.load() load __main__ module from file (merge) +# +# These use the filename persist.defaultfile, initialized to 'wsrestore.py'. +# +# A raw interface also exists: +# persist.writedict(dict, fp) save dictionary to open file +# persist.readdict(dict, fp) read (merge) dictionary from open file +# +# Internally, the function dump() and a whole bunch of support of functions +# traverse a graph of objects and print them in a restorable form +# (which happens to be a Python module). +# +# XXX Limitations: +# - Volatile objects are dumped as strings: +# - open files, windows etc. +# - Other 'obscure' objects are dumped as strings: +# - classes, instances and methods +# - compiled regular expressions +# - anything else reasonably obscure (e.g., capabilities) +# - type objects for obscure objects +# - It's slow when there are many of lists or dictionaries +# (This could be fixed if there were a quick way to compute a hash +# function of any object, even if recursive) + +defaultfile = 'wsrestore.py' + +def save(): + import __main__ + import posix + # XXX On SYSV, if len(defaultfile) >= 14, this is wrong! + backup = defaultfile + '~' + try: + posix.unlink(backup) + except posix.error: + pass + try: + posix.rename(defaultfile, backup) + except posix.error: + pass + fp = open(defaultfile, 'w') + writedict(__main__.__dict__, fp) + fp.close() + +def load(): + import __main__ + fp = open(defaultfile, 'r') + readdict(__main__.__dict__, fp) + +def writedict(dict, fp): + import sys + savestdout = sys.stdout + try: + sys.stdout = fp + dump(dict) # Writes to sys.stdout + finally: + sys.stdout = savestdout + +def readdict(dict, fp): + contents = fp.read() # Or: util.readopenfile(fp) + globals = {} + exec(contents, globals) + top = globals['top'] + for key in top.keys(): + if dict.has_key(key): + print 'warning:', key, 'not overwritten' + else: + dict[key] = top[key] + + +# Function dump(x) prints (on sys.stdout!) a sequence of Python statements +# that, when executed in an empty environment, will reconstruct the +# contents of an arbitrary dictionary. + +import sys + +# Name used for objects dict on output. +# +FUNNYNAME = FN = 'A' + +# Top-level function. Call with the object you want to dump. +# +def dump(x): + types = {} + stack = [] # Used by test for recursive objects + print FN, '= {}' + topuid = dumpobject(x, types, stack) + print 'top =', FN, '[', `topuid`, ']' + +# Generic function to dump any object. +# +dumpswitch = {} +# +def dumpobject(x, types, stack): + typerepr = `type(x)` + if not types.has_key(typerepr): + types[typerepr] = {} + typedict = types[typerepr] + if dumpswitch.has_key(typerepr): + return dumpswitch[typerepr](x, typedict, types, stack) + else: + return dumpbadvalue(x, typedict, types, stack) + +# Generic function to dump unknown values. +# This assumes that the Python interpreter prints such values as +# <foo object at xxxxxxxx>. +# The object will be read back as a string: '<foo object at xxxxxxxx>'. +# In some cases it may be possible to fix the dump manually; +# to ease the editing, these cases are labeled with an XXX comment. +# +def dumpbadvalue(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] =', `xrepr`, '# XXX' + return uid + +# Generic function to dump pure, simple values, except strings +# +def dumpvalue(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] =', `x` + return uid + +# Functions to dump string objects +# +def dumpstring(x, typedict, types, stack): + # XXX This can break if strings have embedded '\0' bytes + # XXX because of a bug in the dictionary module + if typedict.has_key(x): + return typedict[x] + uid = genuid() + typedict[x] = uid + print FN, '[', `uid`, '] =', `x` + return uid + +# Function to dump type objects +# +typeswitch = {} +class some_class(): + def method(self): pass +some_instance = some_class() +# +def dumptype(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + if typeswitch.has_key(xrepr): + print FN, '[', `uid`, '] =', typeswitch[xrepr] + elif x = type(sys): + print 'import sys' + print FN, '[', `uid`, '] = type(sys)' + elif x = type(sys.stderr): + print 'import sys' + print FN, '[', `uid`, '] = type(sys.stderr)' + elif x = type(dumptype): + print 'def some_function(): pass' + print FN, '[', `uid`, '] = type(some_function)' + elif x = type(some_class): + print 'class some_class(): pass' + print FN, '[', `uid`, '] = type(some_class)' + elif x = type(some_instance): + print 'class another_class(): pass' + print 'some_instance = another_class()' + print FN, '[', `uid`, '] = type(some_instance)' + elif x = type(some_instance.method): + print 'class yet_another_class():' + print ' def method(): pass' + print 'another_instance = yet_another_class()' + print FN, '[', `uid`, '] = type(another_instance.method)' + else: + # Unknown type + print FN, '[', `uid`, '] =', `xrepr`, '# XXX' + return uid + +# Initialize the typeswitch +# +for x in None, 0, 0.0, '', (), [], {}: + typeswitch[`type(x)`] = 'type(' + `x` + ')' +for s in 'type(0)', 'abs', '[].append': + typeswitch[`type(eval(s))`] = 'type(' + s + ')' + +# Dump a tuple object +# +def dumptuple(x, typedict, types, stack): + item_uids = [] + xrepr = '' + for item in x: + item_uid = dumpobject(item, types, stack) + item_uids.append(item_uid) + xrepr = xrepr + ' ' + item_uid + del stack[-1:] + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] = (', + for item_uid in item_uids: + print FN, '[', `item_uid`, '],', + print ')' + return uid + +# Dump a list object +# +def dumplist(x, typedict, types, stack): + # Check for recursion + for x1, uid1 in stack: + if x is x1: return uid1 + # Check for occurrence elsewhere in the typedict + for uid1 in typedict.keys(): + if x is typedict[uid1]: return uid1 + # This uses typedict differently! + uid = genuid() + typedict[uid] = x + print FN, '[', `uid`, '] = []' + stack.append(x, uid) + item_uids = [] + for item in x: + item_uid = dumpobject(item, types, stack) + item_uids.append(item_uid) + del stack[-1:] + for item_uid in item_uids: + print FN, '[', `uid`, '].append(', FN, '[', `item_uid`, '])' + return uid + +# Dump a dictionary object +# +def dumpdict(x, typedict, types, stack): + # Check for recursion + for x1, uid1 in stack: + if x is x1: return uid1 + # Check for occurrence elsewhere in the typedict + for uid1 in typedict.keys(): + if x is typedict[uid1]: return uid1 + # This uses typedict differently! + uid = genuid() + typedict[uid] = x + print FN, '[', `uid`, '] = {}' + stack.append(x, uid) + item_uids = [] + for key in x.keys(): + val_uid = dumpobject(x[key], types, stack) + item_uids.append(key, val_uid) + del stack[-1:] + for key, val_uid in item_uids: + print FN, '[', `uid`, '][', `key`, '] =', + print FN, '[', `val_uid`, ']' + return uid + +# Dump a module object +# +def dumpmodule(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + from string import split + # `x` has the form <module 'foo'> + name = xrepr[9:-2] + uid = genuid() + typedict[xrepr] = uid + print 'import', name + print FN, '[', `uid`, '] =', name + return uid + + +# Initialize dumpswitch, a table of functions to dump various objects, +# indexed by `type(x)`. +# +for x in None, 0, 0.0: + dumpswitch[`type(x)`] = dumpvalue +for x, f in ('', dumpstring), (type(0), dumptype), ((), dumptuple), \ + ([], dumplist), ({}, dumpdict), (sys, dumpmodule): + dumpswitch[`type(x)`] = f + + +# Generate the next unique id; a string consisting of digits. +# The seed is stored as seed[0]. +# +seed = [0] +# +def genuid(): + x = seed[0] + seed[0] = seed[0] + 1 + return `x` diff --git a/Lib/sched.py b/Lib/sched.py new file mode 100644 index 0000000..b70a998 --- /dev/null +++ b/Lib/sched.py @@ -0,0 +1,96 @@ +# Module sched -- a generally useful event scheduler class + +# Each instance of this class manages its own queue. +# No multi-threading is implied; you are supposed to hack that +# yourself, or use a single instance per application. +# +# Each instance is parametrized with two functions, one that is +# supposed to return the current time, one that is supposed to +# implement a delay. You can implement fine- or course-grained +# real-time scheduling by substituting time and sleep or millitimer +# and millisleep from the built-in module time, or you can implement +# simulated time by writing your own functions. This can also be +# used to integrate scheduling with STDWIN events; the delay function +# is allowed to modify the queue. Time can be expressed +# as integers or floating point numbers, as long as it is consistent. + +# Events are specified by tuples (time, priority, action, argument). +# As in UNIX, lower priority numbers mean higher priority; in this +# way the queue can be maintained fully sorted. Execution of the +# event means calling the action function, passing it the argument. +# Remember that in Python, multiple function arguments can be packed +# in a tuple. The action function may be an instance method so it +# has another way to reference private data (besides global variables). +# Parameterless functions or methods cannot be used, however. + +class scheduler(): + # + # Initialize a new instance, passing the time and delay functions + # + def init(self, (timefunc, delayfunc)): + self.queue = [] + self.timefunc = timefunc + self.delayfunc = delayfunc + return self + # + # Enter a new event in the queue at an absolute time. + # Returns an ID for the event which can be used + # to remove it, if necessary. + # + def enterabs(self, event): + time, priority, action, argument = event + q = self.queue + # XXX Could use bisection or linear interpolation? + for i in range(len(q)): + qtime, qpri, qact, qarg = q[i] + if time < qtime: break + if time = qtime and priority < qpri: break + else: + i = len(q) + q.insert(i, event) + return event # The ID + # + # A variant that specifies the time as a relative time. + # This is actually the more commonly used interface. + # + def enter(self, (delay, priority, action, argument)): + time = self.timefunc() + delay + return self.enterabs(time, priority, action, argument) + # + # Remove an event from the queue. + # This must be presented the ID as returned by enter(). + # If the event is not in the queue, this raises RuntimeError. + # + def cancel(self, event): + self.queue.remove(event) + # + # Check whether the queue is empty. + # + def empty(self): + return len(self.queue) = 0 + # + # Run: execute events until the queue is empty. + # + # When there is a positive delay until the first event, the + # delay function is called and the event is left in the queue; + # otherwise, the event is removed from the queue and executed + # (its action function is called, passing it the argument). + # If the delay function returns prematurely, it is simply + # restarted. + # + # It is legal for both the delay function and the action + # function to to modify the queue or to raise an exception; + # exceptions are not caught but the scheduler's state + # remains well-defined so run() may be called again. + # + def run(self): + q = self.queue + while q: + time, priority, action, argument = q[0] + now = self.timefunc() + if now < time: + self.delayfunc(time - now) + else: + del q[0] + void = action(argument) + # diff --git a/Lib/stdwin/DirList.py b/Lib/stdwin/DirList.py new file mode 100755 index 0000000..fb0ae99 --- /dev/null +++ b/Lib/stdwin/DirList.py @@ -0,0 +1,63 @@ +# DirList -- Directory Listing widget + +try: + import posix, path + os = posix +except NameError: + import mac, macpath + os = mac + path = macpath + +import stdwin, rect +from stdwinevents import * +from Buttons import PushButton +from WindowParent import WindowParent +from HVSplit import HSplit, VSplit + +class DirList() = VSplit(): + # + def create(self, (parent, dirname)): + self = VSplit.create(self, parent) + names = os.listdir(dirname) + for name in names: + if path.isdir(path.cat(dirname, name)): + fullname = path.cat(dirname, name) + btn = SubdirButton().definetext(self, fullname) + elif name[-3:] = '.py': + btn = ModuleButton().definetext(self, name) + else: + btn = FileButton().definetext(self, name) + return self + # + +class DirListWindow() = WindowParent(): + # + def create(self, dirname): + self = WindowParent.create(self, (dirname, (0, 0))) + child = DirList().create(self, dirname) + self.realize() + return self + # + +class SubdirButton() = PushButton(): + # + def drawpict(self, d): + PushButton.drawpict(self, d) + d.box(rect.inset(self.bounds, (3, 1))) + # + def up_trigger(self): + window = DirListWindow().create(self.text) + # + +class FileButton() = PushButton(): + # + def up_trigger(self): + stdwin.fleep() + # + +class ModuleButton() = FileButton(): + # + def drawpict(self, d): + PushButton.drawpict(self, d) + d.box(rect.inset(self.bounds, (1, 3))) + # diff --git a/Lib/stdwin/FormSplit.py b/Lib/stdwin/FormSplit.py new file mode 100755 index 0000000..4f0bd01 --- /dev/null +++ b/Lib/stdwin/FormSplit.py @@ -0,0 +1,56 @@ +# A FormSplit lets you place its children exactly where you want them +# (including silly places!). +# It does no explicit geometry management except moving its children +# when it is moved. +# The interface to place children is as follows. +# Before you add a child, you may specify its (left, top) position +# relative to the FormSplit. If you don't specify a position for +# a child, it goes right below the previous child; the first child +# goes to (0, 0) by default. +# NB: This places data attributes named form_* on its children. +# XXX Yes, I know, there should be options to do all sorts of relative +# placement, but for now this will do. + +from Split import Split + +class FormSplit() = Split(): + # + def create(self, parent): + self.next_left = self.next_top = 0 + self.last_child = None + return Split.create(self, parent) + # + def minsize(self, m): + max_width, max_height = 0, 0 + for c in self.children: + c.form_width, c.form_height = c.minsize(m) + max_width = max(max_width, c.form_width + c.form_left) + max_height = max(max_height, c.form_height + c.form_top) + return max_width, max_height + # + def getbounds(self): + return self.bounds + # + def setbounds(self, bounds): + self.bounds = bounds + fleft, ftop = bounds[0] + for c in self.children: + left, top = c.form_left + fleft, c.form_top + ftop + right, bottom = left + c.form_width, top + c.form_height + c.setbounds((left, top), (right, bottom)) + # + def placenext(self, (left, top)): + self.next_left = left + self.next_top = top + self.last_child = None + # + def addchild(self, child): + if self.last_child: + width, height = \ + self.last_child.minsize(self.beginmeasuring()) + self.next_top = self.next_top + height + child.form_left = self.next_left + child.form_top = self.next_top + Split.addchild(self, child) + self.last_child = child + # diff --git a/Lib/stdwin/TextEdit.py b/Lib/stdwin/TextEdit.py new file mode 100755 index 0000000..8d12465 --- /dev/null +++ b/Lib/stdwin/TextEdit.py @@ -0,0 +1,58 @@ +# Text editing widget + +from stdwinevents import * + +class TextEdit(): + # + def create(self, (parent, (cols, rows))): + parent.addchild(self) + self.parent = parent + self.cols = cols + self.rows = rows + self.text = '' + # Creation of the editor is done in realize() + self.editor = 0 + return self + # + # Downcalls from parent to child + # + def destroy(self): + del self.parent + del self.editor + del self.window + # + def minsize(self, m): + return self.cols*m.textwidth('n'), self.rows*m.lineheight() + def setbounds(self, bounds): + self.bounds = bounds + if self.editor: + self.editor.move(bounds) + def getbounds(self, bounds): + if self.editor: + return self.editor.getrect() + else: + return self.bounds + def realize(self): + self.window = self.parent.getwindow() + self.editor = self.window.textcreate(self.bounds) + self.editor.replace(self.text) + self.parent.need_mouse(self) + self.parent.need_keybd(self) + self.parent.need_altdraw(self) + def draw(self, (d, area)): + pass + def altdraw(self, area): + self.editor.draw(area) + # + # Event downcalls + # + def mouse_down(self, detail): + x = self.editor.event(WE_MOUSE_DOWN, self.window, detail) + def mouse_move(self, detail): + x = self.editor.event(WE_MOUSE_MOVE, self.window, detail) + def mouse_up(self, detail): + x = self.editor.event(WE_MOUSE_UP, self.window, detail) + # + def keybd(self, (type, detail)): + x = self.editor.event(type, self.window, detail) + # diff --git a/Lib/stdwin/WindowSched.py b/Lib/stdwin/WindowSched.py new file mode 100755 index 0000000..19be2b1 --- /dev/null +++ b/Lib/stdwin/WindowSched.py @@ -0,0 +1,57 @@ +# Combine a real-time scheduling queue and stdwin event handling. +# Uses the millisecond timer. + +import stdwin +from stdwinevents import WE_TIMER +import WindowParent +import sched +import time + +# Delay function called by the scheduler when it has nothing to do. +# Return immediately when something is done, or when the delay is up. +# +def delayfunc(msecs): + # + # Check for immediate stdwin event + # + event = stdwin.pollevent() + if event: + WindowParent.Dispatch(event) + return + # + # Use millisleep for very short delays or if there are no windows + # + if msecs < 100 or WindowParent.CountWindows() = 0: + time.millisleep(msecs) + return + # + # Post a timer event on an arbitrary window and wait for it + # + window = WindowParent.AnyWindow() + window.settimer(msecs/100) + event = stdwin.getevent() + window.settimer(0) + if event[0] <> WE_TIMER: + WindowParent.Dispatch(event) + +q = sched.scheduler().init(time.millitimer, delayfunc) + +# Export functions enter, enterabs and cancel just like a scheduler +# +enter = q.enter +enterabs = q.enterabs +cancel = q.cancel + +# Emptiness check must check both queues +# +def empty(): + return q.empty() and WindowParent.CountWindows() = 0 + +# Run until there is nothing left to do +# +def run(): + while not empty(): + if q.empty(): + WindowParent.Dispatch(stdwin.getevent()) + else: + q.run() |