Source code for GLXCurses.MainLoop

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# It script it publish under GNU GENERAL PUBLIC LICENSE
# http://www.gnu.org/licenses/gpl-3.0.en.html
# Author: the Galaxie Curses Team, all rights reserved
import sys
import logging

import threading

lock = threading.Lock()
from GLXCurses.EventList import EventList


[docs]class Singleton(type): def __init__(cls, name, bases, dictionary): super(Singleton, cls).__init__(name, bases, dictionary) cls.instance = None def __call__(cls, *args, **kw): if cls.instance is None: cls.instance = super(Singleton, cls).__call__(*args, **kw) return cls.instance
# https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
[docs]class MainLoop(object, metaclass=Singleton): """ :Description: The MainLoop is something close to a infinity loop with a start() and stop() method Methods: start() -- start the mainloop stop() -- stop the mainloop emit() -- emit a signal .. warning:: you have to start the mainloop from you application via MainLoop().start() """ def __init__(self, application=None, debug=None): """ Creates a new MainLoop structure. """ self.__application = None self.__debug = None self.__event_list = None self.__running = None self.__glxcurses_support = None # First init self.application = application self.debug = debug self.event_list = None self.glxcurses_support = None self.running = None self.debug = True @property def debug(self): return self.__debug @debug.setter def debug(self, debug=None): """ Set the debugging level of information's display on the stdscr. Generally it highly stress the console and is here for future maintenance of that Application. Enjoy future dev it found it function ;) :param debug: True is debugging mode is enable, False for disable it. :type debug: bool :raise TypeError: when "debug" argument is not a :py:__area_data:`bool` """ if debug is None: debug = False if type(debug) != bool: raise TypeError('"debug" must be a boolean type') if self.debug != debug: self.__debug = debug @property def application(self): return self.__application @application.setter def application(self, value): if value != self.application: self.__application = value @property def event_list(self): return self.__event_list @event_list.setter def event_list(self, value): if value is None: value = EventList(debug=self.debug) if not isinstance(value, EventList): raise TypeError( "'event_list' property value must be a EventList instance or None" ) if value != self.event_list: self.__event_list = value @property def running(self): return self.__running @running.setter def running(self, value): """ Set the is_running attribute :param value: False or True :type value: Boolean """ if value is None: value = False if type(value) != bool: raise TypeError("'running' property value must be bool type or None") if self.running != value: self.__running = value @property def glxcurses_support(self): return self.__glxcurses_support @glxcurses_support.setter def glxcurses_support(self, value): """ Set the glxcurses_support attribute :param value: If ``True`` the support for GLXCurses is enable :type value: Boolean """ if value is None: value = False if type(value) != bool: raise TypeError( "'glxcurses_support' property value must be bool type or None" ) if self.glxcurses_support != value: self.__glxcurses_support = value
[docs] def start(self): """ Runs a MainLoop until quit() is called on the loop. If this is called for the thread of the loop's , it will process events from the loop, otherwise it will simply wait. """ if self.debug: logging.debug("Starting " + self.__class__.__name__) self.running = True # Normally it the first refresh of the application, it can be considered as the first stdscr display. # Consider a chance to crash before the start of the loop try: self.handle_event() if hasattr(self.application, "eveloop_precmd"): self.application.eveloop_precmd() if hasattr(self.application, "eveloop_cmd"): self.application.eveloop_cmd() if hasattr(self.application, "eveloop_postcmd"): self.application.eveloop_postcmd() except Exception: self.stop() sys.stdout.write("{0}\n".format(sys.exc_info()[0])) sys.stdout.flush() raise # A bit light for notify about we are up and running, but we are really inside the main while(1) loop if self.debug: logging.debug(self.__class__.__name__ + ": Started") # The loop while self.running: # Parse user input into a Statement object # Start timer # Call loop_precmd method # Add statement to History # Call loop_cmd method # Call loop_postcmd method # Stop timer and display the elapsed time # In Case of Exit # Call methods loop_finalization try: # Parse input event if hasattr(self.application, "eveloop_input_event"): self.handle_event(self.application.eveloop_input_event()) if hasattr(self.application, "eveloop_precmd"): self.application.eveloop_precmd() if hasattr(self.application, "eveloop_cmd"): self.application.eveloop_cmd() if hasattr(self.application, "eveloop_postcmd"): self.application.eveloop_postcmd() except KeyboardInterrupt: # Default EvLoop Keyboard Interruption if hasattr(self.application, "permit_keyboard_interruption"): if self.application.permit_keyboard_interruption: self.stop() sys.stdout.write( "{0}: {1}\n".format("KeyboardInterrupt", sys.exc_info()[2]) ) sys.stdout.flush() else: if self.glxcurses_support: self.event_list.add("CURSES", 3) else: self.stop() sys.stdout.write( "{0}: {1}\n".format("KeyboardInterrupt", sys.exc_info()[2]) ) sys.stdout.flush() except Exception: self.stop() sys.stdout.write("{0}\n".format(sys.exc_info()[0])) sys.stdout.flush() raise # running property have been set to False during a loop iteration if hasattr(self.application, "eveloop_finalization"): if self.debug: logging.debug(self.__class__.__name__ + ": Call finalization method") self.application.eveloop_finalization() if self.debug: logging.debug(self.__class__.__name__ + ": All operations is stop")
[docs] def stop(self): """ Stops a MainLoop from running. Any calls to run() for the loop will return. Note that sources that have already been dispatched when quit() is called will still be executed. .. :warning: A MainLoop quit() call will certainly cause the end of you programme """ if self.debug: logging.debug(self.__class__.__name__ + ": Stopping") self.running = False
[docs] def handle_event(self, event=None): # GLXCurses Support if self.glxcurses_support: # curses.getch() event support if event != -1: # curses.mouse event if event == 409: # get mouse position from GLXCurses.Application() class if hasattr(self.application, "get_mouse"): self.event_list.add("MOUSE_EVENT", self.application.get_mouse()) # curses event else: self.event_list.add("CURSES", event) # Default EvLoop mode else: if event: self.event_list.add("EVENT", event) # Do something with events event = self.event_list.pop() try: if event: while event: # If it have event dispatch it if self.debug: logging.debug( "{0}: Dispatch {1}: {2} to self.application".format( self.__class__.__name__, event[0], event[1] ) ) # Dispatch to the application if hasattr(self.application, "events_dispatch"): self.application.events_dispatch(event[0], event[1]) # Delete the last event inside teh event list event = self.event_list.pop() except KeyError as the_error: # Permit to have error logs about unknown event logging.error( "{0}._handle_event(): KeyError:{1} event:{2}".format( self.__class__.__name__, the_error, event ) )