#!/usr/bin/env python ############################################################################# # Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999 # All Rights Reserved. # # The software contained on this media is the property of the DSTC Pty # Ltd. Use of this software is strictly in accordance with the # license agreement in the accompanying LICENSE.HTML file. If your # distribution of this software does not contain a LICENSE.HTML file # then you have no rights to use this software in any manner and # should contact DSTC at the address below to determine an appropriate # licensing arrangement. # # DSTC Pty Ltd # Level 7, GP South # Staff House Road # University of Queensland # St Lucia, 4072 # Australia # Tel: +61 7 3365 4310 # Fax: +61 7 3365 4311 # Email: enquiries@dstc.edu.au # # This software is being provided "AS IS" without warranty of any # kind. In no event shall DSTC Pty Ltd be liable for damage of any # kind arising out of or in connection with the use or performance of # this software. # # Project: Fnorb # File: $Source: /cvsroot/fnorb/fnorb/orb/TkReactor.py,v $ # Version: @(#)$RCSfile: TkReactor.py,v $ $Revision: 1.15 $ # ############################################################################# """ A Reactor for use with 'tkinter'. 'tkinter' (and 'Tk' itself) are NOT thread-safe, and therefore this module assumes a single-threaded environment. """ # The Python module for Tk 4.1 and upwards is "_tkinter", for Tk 4.0 it is # "tkinter" 8^( try: import _tkinter except ImportError: import tkinter; _tkinter = tkinter # Fnorb modules. import Reactor def TkReactor_init(): """ Initialise the TkReactor. There can only be one instance of any concrete reactor class per process. """ try: reactor = TkReactor() except Reactor.Reactor, reactor: pass return reactor class TkReactor(Reactor.Reactor): """ The TkReactor. The TkReactor uses the Tk event loop to dispatch Fnorb events. """ def __init__(self): """ Constructor. """ # There can be at most one instance of any concrete reactor class per # process. if Reactor.Reactor._instance is not None: # A reactor already exists. raise Reactor.Reactor._instance Reactor.Reactor._instance = self # A dictionary of all registered event handlers. All handles in the # TkReactor are actually just file descriptors. self.__handlers = {} # {Handle: (EventHandler, Mask)} return ######################################################################### # Reactor interface. ######################################################################### def register_handler(self, handler, mask): """ Register an event handler. """ # Get the event handler's underlying I/O handle. handle = handler.handle() # If the handler is already registered then update the mask. try: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Update the mask. handler_mask = handler_mask | mask # Otherwise, add the new handler. except KeyError: handler_mask = mask # Do the add/update. self.__handlers[handle] = (handler, handler_mask) # Register with Tk. self.__register_Tk_handler(handle, handler_mask) return def unregister_handler(self, handler, mask): """ Withdraw a handler's registration. """ # Get the event handler's underlying I/O handle. handle = handler.handle() try: # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Update the mask. handler_mask = handler_mask & ~mask # If the mask is now zero (ie. the handler is no longer interested # in *any* events) then delete the handler. if handler_mask == 0: # Remove the handler from Tk. _tkinter.deletefilehandler(handle) # Delete it! del self.__handlers[handle] # Otherwise, update the handler's mask and re-register with Tk. else: # Update the handler's mask. self.__handlers[handle] = (handler, handler_mask) # Re-register with Tk. self.__register_Tk_handler(handle, handler_mask) # Ignore any attempts to un-register something that wasn't registerd! except KeyError: pass return def start_event_loop(self, timeout=None): """ Start the event loop. """ # Enter the Tk mainloop (ie. Tk will dispatch the events). _tkinter.mainloop() # Close all registered handlers. self.__close_all_handlers() return def stop_event_loop(self): """ Stop the event loop. """ # Quit the Tk mainloop. _tkinter.quit() return def do_one_event(self): """ Dispatch a single event. """ _tkinter.dooneevent() return # The following two methods are provided to allow Fnorb events to be # handled in other event loops. def handles(self): """ Return the read/write/exception handles for registered handlers. The return value is a tuple in the form:- ([ReadHandles], [WriteHandles], [ExceptionHandles]) """ return self.__get_handles() def handle_one_event(self, handle, tk_mask): """ Handle a single Tk event. """ self.__handle_Tk_event(handle, tk_mask) return ######################################################################### # Private interface. ######################################################################### def __get_handles(self): """ Return the read/write/exception handles for registered handlers. The return value is a tuple in the form:- ([ReadHandles], [WriteHandles], [ExceptionHandles]) """ iwtd = {} owtd = {} ewtd = {} for handle in self.__handlers.keys(): # Find the event handler associated with the handle. (handler, mask) = self.__handlers[handle] if mask & Reactor.READ: iwtd[handle] = None if mask & Reactor.WRITE: owtd[handle] = None # Listen for errors on all handles. ewtd[handle] = None return (iwtd.keys(), owtd.keys(), ewtd.keys()) def __handle_Tk_event(self, handle, tk_mask): """ Handle an event. """ # Translate between Tk and Fnorb event masks. mask = 0x0 if tk_mask & _tkinter.READABLE: mask = mask | Reactor.READ if tk_mask & _tkinter.WRITABLE: mask = mask | Reactor.WRITE if tk_mask & _tkinter.EXCEPTION: mask = mask | Reactor.EXCEPTION # Find the event handler associated with the handle. (handler, handler_mask) = self.__handlers[handle] # Handler callback. handler.handle_event(mask) return def __register_Tk_handler(self, handle, mask): """ Register a file handler with Tk. """ # Translate between Fnorb and Tk event masks (we are always interested # in exceptions!). tk_mask = _tkinter.EXCEPTION if mask & Reactor.READ: tk_mask = tk_mask | _tkinter.READABLE if mask & Reactor.WRITE: tk_mask = tk_mask | _tkinter.WRITABLE # Register with Tk. _tkinter.createfilehandler(handle, tk_mask, self.__handle_Tk_event) return def __close_all_handlers(self): """ Close all registered handlers. """ # We work on a copy of the list, 'cos the handlers will most likely # call 'unregister_handler' in their 'close' method. for (handler, mask) in self.__handlers.values(): # Handler callback. handler.handle_close() return #############################################################################