| # Standard main loop for *all* STDWIN applications. |
| # This requires that applications: |
| # - register their windows on creation and unregister them when closed |
| # - have a 'dispatch' function as a window member |
| |
| |
| import stdwin, stdwinq |
| from stdwinevents import * |
| |
| |
| # List of windows known to the main loop. |
| # |
| windows = [] |
| |
| |
| # Last window that ever received an event |
| # |
| last_window = None |
| |
| |
| # Function to register a window. |
| # |
| def register(win): |
| # First test the dispatch function by passing it a null event -- |
| # this catches registration of unconforming windows. |
| win.dispatch((WE_NULL, win, None)) |
| if win not in windows: |
| windows.append(win) |
| |
| |
| # Function to unregister a window. |
| # It is not an error to unregister an already unregistered window |
| # (this is useful for cleanup actions). |
| # |
| def unregister(win): |
| global last_window |
| if win == last_window: |
| last_window = None |
| if win in windows: |
| windows.remove(win) # Not in 0.9.1 |
| # 0.9.1 solution: |
| #for i in range(len(windows)): |
| # if windows[i] = win: |
| # del windows[i] |
| # break |
| |
| |
| # Interfaces used by WindowSched. |
| # |
| def countwindows(): |
| return len(windows) |
| # |
| def anywindow(): |
| if windows: |
| return windows[0] |
| else: |
| return None |
| |
| |
| # NEW: register any number of file descriptors |
| # |
| fdlist = [] |
| select_args = None |
| select_handlers = None |
| # |
| def registerfd(fd, mode, handler): |
| if mode not in ('r', 'w', 'x'): |
| raise ValueError, 'mode must be r, w or x' |
| if type(fd) <> type(0): |
| fd = fd.fileno() # If this fails it's not a proper select arg |
| for i in range(len(fdlist)): |
| if fdlist[i][:2] == (fd, mode): |
| raise ValueError, \ |
| '(fd, mode) combination already registered' |
| fdlist.append((fd, mode, handler)) |
| make_select_args() |
| # |
| def unregisterfd(fd, *args): |
| if type(fd) <> type(0): |
| fd = fd.fileno() # If this fails it's not a proper select arg |
| args = (fd,) + args |
| n = len(args) |
| for i in range(len(fdlist)): |
| if fdlist[i][:n] == args: |
| del fdlist[i] |
| make_select_args() |
| # |
| def make_select_args(): |
| global select_args, select_handlers |
| rlist, wlist, xlist = [], [], [] |
| rhandlers, whandlers, xhandlers = {}, {}, {} |
| for fd, mode, handler in fdlist: |
| if mode == 'r': |
| rlist.append(fd) |
| rhandlers[`fd`] = handler |
| if mode == 'w': |
| wlist.append(fd) |
| whandlers[`fd`] = handler |
| if mode == 'x': |
| xlist.append(fd) |
| xhandlers[`fd`] = handler |
| if rlist or wlist or xlist: |
| select_args = rlist, wlist, xlist |
| select_handlers = rhandlers, whandlers, xhandlers |
| else: |
| select_args = None |
| select_handlers = None |
| # |
| def do_select(): |
| import select |
| reply = apply(select.select, select_args) |
| for mode in 0, 1, 2: |
| list = reply[mode] |
| for fd in list: |
| handler = select_handlers[mode][`fd`] |
| handler(fd, 'rwx'[mode]) |
| |
| |
| # Event processing main loop. |
| # Return when there are no windows left, or when an unhandled |
| # exception occurs. (It is safe to restart the main loop after |
| # an unsuccessful exit.) |
| # Python's stdwin.getevent() turns WE_COMMAND/WC_CANCEL events |
| # into KeyboardInterrupt exceptions; these are turned back in events. |
| # |
| recursion_level = 0 # Hack to make it reentrant |
| def mainloop(): |
| global recursion_level |
| recursion_level = recursion_level + 1 |
| try: |
| stdwin_select_handler() # Process events already in queue |
| while 1: |
| if windows and not fdlist: |
| while windows and not fdlist: |
| try: |
| event = stdwinq.getevent() |
| except KeyboardInterrupt: |
| event = (WE_COMMAND, \ |
| None, WC_CANCEL) |
| dispatch(event) |
| elif windows and fdlist: |
| fd = stdwin.fileno() |
| if recursion_level == 1: |
| registerfd(fd, 'r', stdwin_select_handler) |
| try: |
| while windows: |
| do_select() |
| stdwin_select_handler() |
| finally: |
| if recursion_level == 1: |
| unregisterfd(fd) |
| elif fdlist: |
| while fdlist and not windows: |
| do_select() |
| else: |
| break |
| finally: |
| recursion_level = recursion_level - 1 |
| |
| |
| # Check for events without ever blocking |
| # |
| def check(): |
| stdwin_select_handler() |
| # XXX Should check for socket stuff as well |
| |
| |
| # Handle stdwin events until none are left |
| # |
| def stdwin_select_handler(*args): |
| while 1: |
| try: |
| event = stdwinq.pollevent() |
| except KeyboardInterrupt: |
| event = (WE_COMMAND, None, WC_CANCEL) |
| if event is None: |
| break |
| dispatch(event) |
| |
| |
| # Run a modal dialog loop for a window. The dialog window must have |
| # been registered first. This prohibits most events (except size/draw |
| # events) to other windows. The modal dialog loop ends when the |
| # dialog window unregisters itself. |
| # |
| passthrough = WE_SIZE, WE_DRAW |
| beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU |
| # |
| def modaldialog(window): |
| if window not in windows: |
| raise ValueError, 'modaldialog window not registered' |
| while window in windows: |
| try: |
| event = stdwinq.getevent() |
| except KeyboardInterrupt: |
| event = WE_COMMAND, None, WC_CANCEL |
| etype, ewindow, edetail = event |
| if etype not in passthrough and ewindow <> window: |
| if etype in beeping: |
| stdwin.fleep() |
| continue |
| dispatch(event) |
| |
| |
| # Dispatch a single event. |
| # Events for the no window in particular are sent to the active window |
| # or to the last window that received an event (these hacks are for the |
| # WE_LOST_SEL event, which is directed to no particular window). |
| # Windows not in the windows list don't get their events: |
| # events for such windows are silently ignored. |
| # |
| def dispatch(event): |
| global last_window |
| if event[1] == None: |
| active = stdwin.getactive() |
| if active: last_window = active |
| else: |
| last_window = event[1] |
| if last_window in windows: |
| last_window.dispatch(event) |
| |
| |
| # Dialog base class |
| # |
| class Dialog: |
| # |
| def __init__(self, title): |
| self.window = stdwin.open(title) |
| self.window.dispatch = self.dispatch |
| register(self.window) |
| # |
| def close(self): |
| unregister(self.window) |
| del self.window.dispatch |
| self.window.close() |
| # |
| def dispatch(self, event): |
| etype, ewindow, edetail = event |
| if etype == WE_CLOSE: |
| self.close() |
| |
| |
| # Standard modal dialogs |
| # XXX implemented using stdwin dialogs for now |
| # |
| def askstr(prompt, default): |
| return stdwin.askstr(prompt, default) |
| # |
| def askync(prompt, yesorno): |
| return stdwin.askync(prompt, yesorno) |
| # |
| def askfile(prompt, default, new): |
| return stdwin.askfile(prompt, default, new) |
| # |
| def message(msg): |
| stdwin.message(msg) |