Ejemplo n.º 1
0
class CFoo(HasTraits):
    ints = CList(Int)
    strs = CList(Str)
Ejemplo n.º 2
0
class DataBox(AbstractOverlay):
    """
    An overlay that is a box defined by data space coordinates.  This can be
    used as a base class for various kinds of zoom boxes.  Unlike the
    "momentary" zoom box drawn for the ZoomTool, a ZoomBox is a more permanent
    visual component.
    """

    data_position = Property
    data_bounds = Property

    # Should the zoom box stay attached to the image or to the screen if the
    # component moves underneath it?
    # TODO: This basically works, but the problem is that it responds to both
    # changes in X and Y independently.  The DataRange2D needs to be updated
    # to reflect changes from its two DataRange1Ds.  The PanTool and ZoomTool
    # need to be improved that they change both dimensions at once.
    affinity = Enum("image", "screen")

    #-------------------------------------------------------------------------
    # Appearance properties (for Box mode)
    #-------------------------------------------------------------------------

    # The color of the selection box.
    color = ColorTrait("lightskyblue")

    # The alpha value to apply to **color** when filling in the selection
    # region.  Because it is almost certainly useless to have an opaque zoom
    # rectangle, but it's also extremely useful to be able to use the normal
    # named colors from Enable, this attribute allows the specification of a
    # separate alpha value that replaces the alpha value of **color** at draw
    # time.
    alpha = Trait(0.3, None, Float)

    # The color of the outside selection rectangle.
    border_color = ColorTrait("dodgerblue")

    # The thickness of selection rectangle border.
    border_size = Int(1)

    #-------------------------------------------------------------------------
    # Private Traits
    #-------------------------------------------------------------------------

    _data_position = CList([0, 0])
    _data_bounds = CList([0, 0])
    _position_valid = False
    _bounds_valid = False

    # Are we in the middle of an event handler or a property setter
    _updating = Bool(False)

    def __init__(self, *args, **kw):
        super(DataBox, self).__init__(*args, **kw)
        if hasattr(self.component, "range2d"):
            self.component.range2d._xrange.on_trait_change(
                self.my_component_moved, "updated")
            self.component.range2d._yrange.on_trait_change(
                self.my_component_moved, "updated")
        elif hasattr(self.component, "x_mapper") and hasattr(
                self.component, "y_mapper"):
            self.component.x_mapper.range.on_trait_change(
                self.my_component_moved, "updated")
            self.component.y_mapper.range.on_trait_change(
                self.my_component_moved, "updated")
        else:
            raise RuntimeError(
                "DataBox cannot find a suitable mapper on its component.")
        self.component.on_trait_change(self.my_component_resized, "bounds")
        self.component.on_trait_change(self.my_component_resized,
                                       "bounds_items")

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        if not self._position_valid:
            tmp = self.component.map_screen([self._data_position])
            if len(tmp.shape) == 2:
                tmp = tmp[0]
            self._updating = True
            self.position = tmp
            self._updating = False
            self._position_valid = True

        if not self._bounds_valid:
            data_x2 = self._data_position[0] + self._data_bounds[0]
            data_y2 = self._data_position[1] + self._data_bounds[1]
            tmp = self.component.map_screen((data_x2, data_y2))
            if len(tmp.shape) == 2:
                tmp = tmp[0]
            x2, y2 = tmp
            x, y = self.position
            self._updating = True
            self.bounds = [x2 - x, y2 - y]
            self._updating = False
            self._bounds_valid = True

        with gc:
            gc.set_antialias(0)
            gc.set_line_width(self.border_size)
            gc.set_stroke_color(self.border_color_)
            gc.clip_to_rect(component.x, component.y, component.width,
                            component.height)
            rect = self.position + self.bounds

            if self.color != "transparent":
                if self.alpha:
                    color = list(self.color_)
                    if len(color) == 4:
                        color[3] = self.alpha
                    else:
                        color += [self.alpha]
                else:
                    color = self.color_
                gc.set_fill_color(color)
                gc.rect(*rect)
                gc.draw_path()
            else:
                gc.rect(*rect)
                gc.stroke_path()

        return

    #-------------------------------------------------------------------------
    # Property setters/getters, event handlers
    #-------------------------------------------------------------------------

    def _get_data_position(self):
        return self._data_position

    def _set_data_position(self, val):
        self._data_position = val
        self._position_valid = False
        self.trait_property_changed("data_position", self._data_position)

    def _get_data_bounds(self):
        return self._data_bounds

    def _set_data_bounds(self, val):
        self._data_bounds = val
        self._bounds_valid = False
        self.trait_property_changed("data_bounds", self._data_bounds)

    @on_trait_change('position,position_items')
    def _update_position(self):
        if self._updating:
            return
        tmp = self.component.map_data(self.position)
        if len(tmp.shape) == 2:
            tmp = tmp.T[0]
        self._data_position = tmp
        self.trait_property_changed("data_position", self._data_position)

    @on_trait_change('bounds,bounds_items')
    def _update_bounds(self):
        if self._updating:
            return
        data_x2, data_y2 = self.component.map_data((self.x2, self.y2))
        data_pos = self._data_position
        self._data_bounds = [data_x2 - data_pos[0], data_y2 - data_pos[1]]
        self.trait_property_changed("data_bounds", self._data_bounds)

    def my_component_moved(self):
        if self.affinity == "screen":
            # If we have screen affinity, then we need to take our current position
            # and map that back down into data coords
            self._update_position()
            self._update_bounds()
        self._bounds_valid = False
        self._position_valid = False

    def my_component_resized(self):
        self._bounds_valid = False
        self._position_valid = False
Ejemplo n.º 3
0
class OpenFileDialog ( Handler ):
    """ Defines the model and handler for the open file dialog.
    """

    # The starting and current file path:
    file_name = File

    # The list of file filters to apply:
    filter = CList( Str )

    # Number of history entries to allow:
    entries = Int( 10 )

    # The file dialog title:
    title = Str( 'Open File' )

    # The Traits UI persistence id to use:
    id = Str( 'traitsui.file_dialog.OpenFileDialog' )

    # A list of optional file dialog extensions:
    extensions = CList( IFileDialogModel )

    #-- Private Traits ---------------------------------------------------------

    # The UIInfo object for the view:
    info = Instance( UIInfo )

    # Event fired when the file tree view should be reloaded:
    reload = Event

    # Event fired when the user double-clicks on a file name:
    dclick = Event

    # Allow extension models to be added dynamically:
    extension__ = Instance( IFileDialogModel )

    # Is the file dialog for saving a file (or opening a file)?
    is_save_file = Bool( False )

    # Is the currently specified file name valid?
    is_valid_file = Property( depends_on = 'file_name' )

    # Can a directory be created now?
    can_create_dir = Property( depends_on = 'file_name' )

    # The OK, Cancel and create directory buttons:
    ok      = Button( 'OK' )
    cancel  = Button( 'Cancel' )
    create  = Button( image = '@icons:folder-new',
                      style = 'toolbar' )

    #-- Handler Class Method Overrides -----------------------------------------

    def init_info ( self, info ):
        """ Handles the UIInfo object being initialized during view start-up.
        """
        self.info = info

    #-- Property Implementations -----------------------------------------------

    def _get_is_valid_file ( self ):
        if self.is_save_file:
            return (isfile( self.file_name ) or (not exists( self.file_name )))

        return isfile( self.file_name )

    def _get_can_create_dir ( self ):
        dir = dirname( self.file_name )
        return (isdir( dir ) and access( dir, R_OK | W_OK ))

    #-- Handler Event Handlers -------------------------------------------------

    def object_ok_changed ( self, info ):
        """ Handles the user clicking the OK button.
        """
        if self.is_save_file and exists( self.file_name ):
            do_later( self._file_already_exists )
        else:
            info.ui.dispose( True )

    def object_cancel_changed ( self, info ):
        """ Handles the user clicking the Cancel button.
        """
        info.ui.dispose( False )

    def object_create_changed ( self, info ):
        """ Handles the user clicking the create directory button.
        """
        if not isdir( self.file_name ):
            self.file_name = dirname( self.file_name )

        CreateDirHandler().edit_traits( context = self,
                                        parent  = info.create.control )

    #-- Traits Event Handlers --------------------------------------------------

    def _dclick_changed ( self ):
        """ Handles the user double-clicking a file name in the file tree view.
        """
        if self.is_valid_file:
            self.object_ok_changed( self.info )

    #-- Private Methods --------------------------------------------------------

    def open_file_view ( self ):
        """ Returns the file dialog view to use.
        """
        # Set up the default file dialog view and size information:
        item = Item( 'file_name',
                     id         = 'file_tree',
                     style      = 'custom',
                     show_label = False,
                     width      = 0.5,
                     editor     = FileEditor( filter      = self.filter,
                                              allow_dir   = True,
                                              reload_name = 'reload',
                                              dclick_name = 'dclick' ) )
        width = height = 0.20

        # Check to see if we have any extensions being added:
        if len( self.extensions ) > 0:

            # fixme: We should use the actual values of the view's Width and
            # height traits here to adjust the overall width and height...
            width *= 2.0

            # Assume we can used a fixed width Group:
            klass = HGroup

            # Set up to build a list of view Item objects:
            items = []

            # Add each extension to the dialog:
            for i, extension in enumerate( self.extensions ):

                # Save the extension in a new trait (for use by the View):
                name = 'extension_%d' % i
                setattr( self, name, extension )

                extension_view = extension

                # Sync up the 'file_name' trait with the extension:
                self.sync_trait( 'file_name', extension, mutual = True )

                # Check to see if it also defines the optional IFileDialogView
                # interface, and if not, use the default view information:
                if not extension.has_traits_interface( IFileDialogView ):
                    extension_view = default_view

                # Get the view that the extension wants to use:
                view = extension.trait_view( extension_view.view )

                # Determine if we should use a splitter for the dialog:
                if not extension_view.is_fixed:
                    klass = HSplit

                # Add the extension as a new view item:
                items.append(
                    Item( name,
                          label = user_name_for( extension.__class__.__name__ ),
                          show_label = False,
                          style      = 'custom',
                          width      = 0.5,
                          height     = 0.5,
                          dock       = 'horizontal',
                          resizable  = True,
                          editor     = InstanceEditor( view = view, id = name )
                    ) )

            # Finally, combine the normal view element with the extensions:
            item = klass( item,
                          VSplit( id = 'splitter2', springy = True, *items ),
                          id = 'splitter' )
        # Return the resulting view:
        return View(
            VGroup(
                VGroup( item ),
                HGroup(
                    Item( 'create',
                          id           = 'create',
                          show_label   = False,
                          style        = 'custom',
                          defined_when = 'is_save_file',
                          enabled_when = 'can_create_dir',
                          tooltip      = 'Create a new directory'
                    ),
                    Item( 'file_name',
                          id      = 'history',
                          editor  = HistoryEditor( entries  = self.entries,
                                                   auto_set = True ),
                          springy = True
                    ),
                    Item( 'ok',
                          id           = 'ok',
                          show_label   = False,
                          enabled_when = 'is_valid_file'
                    ),
                    Item( 'cancel',
                          show_label = False
                    )
                )
            ),
            title        = self.title,
            id           = self.id,
            kind         = 'livemodal',
            width        = width,
            height       = height,
            close_result = False,
            resizable    = True
        )

    def _file_already_exists ( self ):
        """ Handles prompting the user when the selected file already exists,
            and the dialog is a 'save file' dialog.
        """
        FileExistsHandler( message = ("The file '%s' already exists.\nDo "
                                      "you wish to overwrite it?") %
                                      basename( self.file_name )
            ).edit_traits( context = self,
                           parent  = self.info.ok.control ).set(
                           parent  = self.info.ui )
Ejemplo n.º 4
0
class System(SystemBase):
    #: Name of the system (shown in WEB UI for example)
    name = CStr

    #: Allow referencing objects by their names in Callables. If disabled, you can still refer to objects by names
    #: by Object('name')
    allow_name_referencing = CBool(True)

    #: Filename to where to dump the system state
    filename = Str

    # LOGGING
    ###########

    #: Name of the file where logs are stored
    logfile = CUnicode

    #: Reference to logger instance (read-only)
    logger = Instance(logging.Logger)

    #: Sentry: Raven DSN configuration (see http://sentry.io)
    raven_dsn = Str

    #: Raven client (is created automatically if raven_dsn is set and this is left empty)
    raven_client = Instance(raven.Client, transient=True)

    #: Format string of the log handler that writes to stdout
    log_format = Str('%(asctime)s %(log_color)s%(name)s%(reset)s %(message)s')

    #: Format string of the log handler that writes to logfile
    logfile_format = Str(
        '%(process)d:%(threadName)s:%(name)s:%(asctime)s:%(levelname)s:%(message)s'
    )

    #: Log level of System logger
    log_level = CInt(logging.INFO, transient=True)

    @on_trait_change('log_level', post_init=True)
    def log_level_changed(self, new):
        self.logger.setLevel(new)

    # SERVICES
    ###########

    #: Add here services that you want to be added automatically. This is meant to be re-defined in subclass.
    default_services = CList(trait=Str)

    #: List of services that are loaded in the initialization of the System.
    services = CList(trait=Instance(AbstractService))

    #: List of servicenames that are desired to be avoided (even if normally autoloaded).
    exclude_services = CSet(trait=Str)

    #: Reference to the worker thread (read-only)
    worker_thread = Instance(StatusWorkerThread, transient=True)

    #: System namespace (read-only)
    namespace = Instance(Namespace)

    # Set of all SystemObjects within the system. This is where SystemObjects are ultimately stored
    # in the System initialization. (read-only)
    objects = CSet(trait=SystemObject)

    #: Property giving objects sorted alphabetically (read-only)
    objects_sorted = Property(depends_on='objects')

    @cached_property
    def _get_objects_sorted(self):
        return sorted(list(self.objects), key=operator.attrgetter('_order'))

    #: Read-only property giving all sensors of the system
    sensors = Property(depends_on='objects[]')

    @cached_property
    def _get_sensors(self):
        return {
            i
            for i in self.objects_sorted if isinstance(i, AbstractSensor)
        }

    #: Read-only property giving all actuator of the system
    actuators = Property(depends_on='objects[]')

    @cached_property
    def _get_actuators(self):
        return {
            i
            for i in self.objects_sorted if isinstance(i, AbstractActuator)
        }

    #: Read-only property giving all objects that have program features in use
    programs = Property(depends_on='objects[]')

    @cached_property
    def _get_programs(self):
        from .program import Program, DefaultProgram
        return {
            i
            for i in self.objects_sorted
            if isinstance(i, (Program, DefaultProgram))
        }

    #: Read-only property giving all :class:`~program.Program` objects
    ordinary_programs = Property(depends_on='programs[]')

    @cached_property
    def _get_ordinary_programs(self):
        from . import program
        return {i for i in self.programs if isinstance(i, program.Program)}

    #: Start worker thread automatically after system is initialized
    worker_autostart = CBool(True)

    #: Trigger which is triggered after initialization is ready (used by Services)
    post_init_trigger = Event

    #: Trigger which is triggered before quiting (used by Services)
    pre_exit_trigger = Event

    #: Read-only property that gives list of all object tags
    all_tags = Property(depends_on='objects.tags[]')

    #: Number of state backup files
    num_state_backups = CInt(5)

    @cached_property
    def _get_all_tags(self):
        newset = set([])
        for i in self.system.objects:
            for j in i.tags:
                if j:
                    newset.add(j)
        return newset

    #: Enable experimental two-phase queue handling technique (not recommended)
    two_phase_queue = CBool(False)

    @classmethod
    def load_or_create(cls,
                       filename=None,
                       no_input=False,
                       create_new=False,
                       **kwargs):
        """
            Load system from a dump, if dump file exists, or create a new system if it does not exist.
        """
        parser = argparse.ArgumentParser()
        parser.add_argument('--no_input', action='store_true')
        parser.add_argument('--create_new', action='store_true')
        args = parser.parse_args()

        if args.no_input:
            print('Parameter --no_input was given')
            no_input = True
        if args.create_new:
            print('Parameter --create_new was given')
            create_new = True
            no_input = True

        def savefile_more_recent():
            time_savefile = os.path.getmtime(filename)
            time_program = os.path.getmtime(sys.argv[0])
            return time_savefile > time_program

        def load_pickle():
            with open(filename, 'rb') as of:
                statefile_version, data = pickle.load(of)

            if statefile_version != STATEFILE_VERSION:
                raise RuntimeError(
                    f'Wrong statefile version, please remove state file {filename}'
                )
            return data

        def load():
            print('Loading %s' % filename)
            obj_list, config = load_pickle()
            system = System(load_state=obj_list, filename=filename, **kwargs)

            return system

        def create():
            print('Creating new system')
            config = None
            if filename:
                try:
                    obj_list, config = load_pickle()
                except FileNotFoundError:
                    config = None
            return cls(filename=filename, load_config=config, **kwargs)

        if filename and os.path.isfile(filename):
            if savefile_more_recent() and not create_new:
                return load()
            else:
                if no_input:
                    print('Program file more recent. Loading that instead.')
                    return create()
                while True:
                    answer = input(
                        'Program file more recent. Do you want to load it? (y/n) '
                    )
                    if answer == 'y':
                        return create()
                    elif answer == 'n':
                        return load()
        else:
            return create()

    def save_state(self):
        """
            Save state of the system to a dump file :attr:`System.filename`
        """
        if not self.filename:
            self.logger.error('Filename not specified. Could not save state')
            return
        self.logger.debug('Saving system state to %s', self.filename)
        for i in reversed(range(self.num_state_backups)):
            fname = self.filename if i == 0 else '%s.%d' % (self.filename, i)
            new_fname = '%s.%d' % (self.filename, i + 1)
            try:
                os.rename(fname, new_fname)
            except FileNotFoundError:
                pass

        with open(self.filename, 'wb') as file, self.worker_thread.queue.mutex:
            obj_list = list(self.objects)
            config = {
                obj.name: obj.status
                for obj in obj_list if getattr(obj, 'user_editable', False)
            }
            data = obj_list, config
            pickle.dump((STATEFILE_VERSION, data), file,
                        pickle.HIGHEST_PROTOCOL)

    @property
    def cmd_namespace(self):
        """
            A read-only property that gives the namespace of the system for evaluating commands.
        """
        import automate
        ns = dict(
            list(automate.__dict__.items()) + list(self.namespace.items()))
        return ns

    def __getattr__(self, item):
        if self.namespace and item in self.namespace:
            return self.namespace[item]
        raise AttributeError

    def get_unique_name(self, obj, name='', name_from_system=''):
        """
            Give unique name for an Sensor/Program/Actuator object
        """
        ns = self.namespace
        newname = name
        if not newname:
            newname = name_from_system

        if not newname:
            newname = u"Nameless_" + obj.__class__.__name__

        if not newname in ns:
            return newname

        counter = 0
        while True:
            newname1 = u"%s_%.2d" % (newname, counter)
            if not newname1 in ns:
                return newname1
            counter += 1

    @property
    def services_by_name(self):
        """
            A property that gives a dictionary that contains services as values and their names as keys.
        """
        srvs = defaultdict(list)
        for i in self.services:
            srvs[i.__class__.__name__].append(i)
        return srvs

    @property
    def service_names(self):
        """
            A property that gives the names of services as a list
        """
        return set(self.services_by_name.keys())

    def flush(self):
        """
            Flush the worker queue. Usefull in unit tests.
        """
        self.worker_thread.flush()

    def name_to_system_object(self, name):
        """
            Give SystemObject instance corresponding to the name
        """
        if isinstance(name, str):
            if self.allow_name_referencing:
                name = name
            else:
                raise NameError(
                    'System.allow_name_referencing is set to False, cannot convert string to name'
                )
        elif isinstance(name, Object):
            name = str(name)
        return self.namespace.get(name, None)

    def eval_in_system_namespace(self, exec_str):
        """
            Get Callable for specified string (for GUI-based editing)
        """
        ns = self.cmd_namespace
        try:
            return eval(exec_str, ns)
        except Exception as e:
            self.logger.warning('Could not execute %s, gave error %s',
                                exec_str, e)
            return None

    def register_service_functions(self, *funcs):
        """
            Register function in the system namespace. Called by Services.
        """
        for func in funcs:
            self.namespace[func.__name__] = func

    def register_service(self, service):
        """
            Register service into the system. Called by Services.
        """
        if service not in self.services:
            self.services.append(service)

    def request_service(self, type, id=0):
        """
            Used by Sensors/Actuators/other services that need to use other services for their
            operations.
        """
        srvs = self.services_by_name.get(type)
        if not srvs:
            return

        ser = srvs[id]

        if not ser.system:
            ser.setup_system(self, id=id)
        return ser

    def cleanup(self):
        """
            Clean up before quitting
        """

        self.pre_exit_trigger = True

        self.logger.info("Shutting down %s, please wait a moment.", self.name)
        for t in threading.enumerate():
            if isinstance(t, TimerClass):
                t.cancel()
        self.logger.debug('Timers cancelled')

        for i in self.objects:
            i.cleanup()

        self.logger.debug('Sensors etc cleanups done')

        for ser in (i for i in self.services
                    if isinstance(i, AbstractUserService)):
            ser.cleanup_system()
        self.logger.debug('User services cleaned up')
        if self.worker_thread.is_alive():
            self.worker_thread.stop()
        self.logger.debug('Worker thread really stopped')

        for ser in (i for i in self.services
                    if isinstance(i, AbstractSystemService)):
            ser.cleanup_system()
        self.logger.debug('System services cleaned up')
        threads = list(t.name for t in threading.enumerate()
                       if t.is_alive() and not t.daemon)
        if threads:
            self.logger.info(
                'After cleanup, we have still the following threads '
                'running: %s', ', '.join(threads))

    def cmd_exec(self, cmd):
        """
            Execute commands in automate namespace
        """

        if not cmd:
            return
        ns = self.cmd_namespace
        import copy
        rval = True
        nscopy = copy.copy(ns)
        try:
            r = eval(cmd, ns)
            if isinstance(r, SystemObject) and not r.system:
                r.setup_system(self)
            if callable(r):
                r = r()
                cmd += "()"
            self.logger.info("Eval: %s", cmd)
            self.logger.info("Result: %s", r)
        except SyntaxError:
            r = {}
            try:
                exec(cmd, ns)
                self.logger.info("Exec: %s", cmd)
            except ExitException:
                raise
            except Exception as e:
                self.logger.info("Failed to exec cmd %s: %s.", cmd, e)
                rval = False
            for key, value in list(ns.items()):
                if key not in nscopy or not value is nscopy[key]:
                    if key in self.namespace:
                        del self.namespace[key]
                    self.namespace[key] = value
                    r[key] = value
            self.logger.info("Set items in namespace: %s", r)
        except ExitException:
            raise
        except Exception as e:
            self.logger.info("Failed to eval cmd %s: %s", cmd, e)
            return False

        return rval

    def __init__(self,
                 load_state: 'List[SystemObject]' = None,
                 load_config: 'Dict[str, Any]' = None,
                 **traits):
        super().__init__(**traits)
        if not self.name:
            self.name = self.__class__.__name__
            if self.name == 'System':
                self.name = os.path.split(sys.argv[0])[-1].replace('.py', '')

        # Initialize Sentry / raven client, if is configured
        if not self.raven_client and self.raven_dsn:
            self.raven_client = raven.Client(
                self.raven_dsn,
                release=__version__,
                tags={'automate-system': self.name})

        self._initialize_logging()
        self.worker_thread = StatusWorkerThread(name="Status worker thread",
                                                system=self)
        self.logger.info('Initializing services')
        self._initialize_services()
        self.logger.info('Initializing namespace')
        self._initialize_namespace(load_state)

        if load_config:
            self.logger.info('Loading config')
            for obj_name, status in load_config.items():
                if hasattr(self, obj_name):
                    getattr(self, obj_name).status = status

        self.logger.info('Initialize user services')
        self._setup_user_services()

        if self.worker_autostart:
            self.logger.info('Starting worker thread')
            self.worker_thread.start()

        self.post_init_trigger = True

    def _initialize_logging(self):
        root_logger = logging.getLogger('automate')
        self.logger = root_logger.getChild(self.name)

        # Check if root level logging has been set up externally.

        if len(root_logger.handlers) > 0:
            root_logger.info('Logging has been configured already, '
                             'skipping logging configuration')
            return

        root_logger.propagate = False
        root_logger.setLevel(self.log_level)
        self.logger.setLevel(self.log_level)

        if self.raven_client:
            sentry_handler = SentryHandler(client=self.raven_client,
                                           level=logging.ERROR)
            root_logger.addHandler(sentry_handler)

        if self.logfile:
            formatter = logging.Formatter(fmt=self.logfile_format)
            log_handler = logging.FileHandler(self.logfile)
            log_handler.setFormatter(formatter)
            root_logger.addHandler(log_handler)

        stream_handler = logging.StreamHandler()

        from colorlog import ColoredFormatter, default_log_colors
        colors = default_log_colors.copy()
        colors['DEBUG'] = 'purple'

        stream_handler.setFormatter(
            ColoredFormatter(self.log_format,
                             datefmt='%H:%M:%S',
                             log_colors=colors))
        root_logger.addHandler(stream_handler)

        self.logger.info('Logging setup ready')

    def _initialize_namespace(self, load_state=None):
        self.namespace = Namespace(system=self)
        self.namespace.set_system(load_state)

        self.logger.info('Setup loggers per object')
        for name, obj in self.namespace.items():
            if isinstance(obj, SystemObject):
                ctype = obj.__class__.__name__
                obj.logger = self.logger.getChild('%s.%s' % (ctype, name))

    def _initialize_services(self):
        # Add default_services, if not already
        for servname in self.default_services:
            if servname not in self.service_names | self.exclude_services:
                self.services.append(get_service_by_name(servname)())

        # Add autorun services if not already
        for servclass in get_autoload_services():
            if servclass.__name__ not in self.service_names | self.exclude_services:
                self.services.append(servclass())

    def _setup_user_services(self):
        for ser in (i for i in self.services
                    if isinstance(i, AbstractUserService)):
            self.logger.info('...%s', ser.__class__.__name__)
            ser.setup_system(self)
Ejemplo n.º 5
0
# A mapped trait for use in specification of line style attributes.
LineStyle = Map(
    _line_style_trait_values,
    default_value='solid',
    editor=LineStyleEditor,
)

# -----------------------------------------------------------------------------
#  Trait definitions:
# -----------------------------------------------------------------------------

# Font trait:
font_trait = KivaFont(default_font_name)

# Bounds trait
bounds_trait = CList([0.0, 0.0])  # (w,h)
coordinate_trait = CList([0.0, 0.0])  # (x,y)

# Component minimum size trait
# PZW: Make these just floats, or maybe remove them altogether.
ComponentMinSize = Range(0.0, 99999.0)
ComponentMaxSize = ComponentMinSize(99999.0)

# Pointer shape trait:
Pointer = PrefixList(pointer_shapes, default_value="arrow")

# Cursor style trait:
cursor_style_trait = PrefixMap(cursor_styles, default_value="default")

spacing_trait = Range(0, 63, value=4)
padding_trait = Range(0, 63, value=4)
Ejemplo n.º 6
0
STYLES = ('normal', 'italic', 'oblique')

#: Font variants. Currently only small caps variants are exposed in Qt, and
#: nothing in Wx.  In the future this could include things like swashes,
#: numeric variants, and so on, as exposed in the toolkit.
VARIANTS = ['small-caps']

#: Additional markings on or around the glyphs of the font that are not part
#: of the glyphs themselves.  Currently Qt and Wx support underline and
#: strikethrough, and Qt supports overline.  In the future overlines and other
#: decorations may be supported, as exposed in the toolkit.
DECORATIONS = ['underline', 'strikethrough', 'overline']

#: A trait for font families.
FontFamily = CList(Str, ['default'])

#: A trait for font weights.
FontWeight = Map(WEIGHTS, default_value='normal')

#: A trait for font styles.
FontStyle = Enum(STYLES)

#: A trait for font variant properties.
FontVariants = CSet(Enum(VARIANTS))

#: A trait for font decorator properties.
FontDecorations = CSet(Enum(DECORATIONS))


class FontStretch(BaseCFloat):
Ejemplo n.º 7
0
class Include(BaseComponent):
    """ A BaseComponent subclass which allows children to be dynamically
    included into a parent.

    """
    #: A read-only property which returns the toolkit widget for this
    #: component. This call is proxied to the parent and will return
    #: None if the parent does not have a toolkit widget defined.
    #: It is necessary to proxy the toolkit_widget so that any child
    #: Include components can access the proper toolkit widget to
    #: pass down to their dynamic components.
    toolkit_widget = Property

    #: The dynamic components of the Include. This is a component or
    #: list of components which should be included in the children of
    #: the parent of this Include. Changes made to this list in whole
    #: or in-place will be reflected as updates in the ui. A single
    #: component will be converted to a single element list.
    components = CList(Instance(BaseComponent))

    #: A private Boolean flag indicating if the dynamic components of
    #: this Include have been intialized for the first time. This should
    #: not be manipulated directly by the user.
    _components_initialized = Bool(False)

    #--------------------------------------------------------------------------
    # Property Getters and Setters
    #--------------------------------------------------------------------------
    def _get_toolkit_widget(self):
        """ The property getter for the 'toolkit_widget' attribute.

        """
        try:
            res = self.parent.toolkit_widget
        except AttributeError:
            res = None
        return res

    #--------------------------------------------------------------------------
    # Setup Methods
    #--------------------------------------------------------------------------
    def _setup_init_layout(self):
        """ A reimplemented parent class method which builds the initial
        list of include components during the layout initialization pass.
        The layout is performed bottom-up so that the tree is up-to-date
        before any parents compute their layout.

        """
        super(Include, self)._setup_init_layout()
        self._setup_components(self.components)
        self.on_trait_change(
            self._on_components_actual_updated,
            'components:_actual_updated',
        )
        self._components_initialized = True
        self._actual_updated()

    def _setup_components(self, components):
        """ Run the setup process for the provided list of components.

        This is a private internal method which is used to run the 
        setup process for a list of new components which should be
        parented by the parent widget of this Include.

        Parameters
        ----------
        components : sequence
            The sequence of BaseComponent instances which should be 
            setup as children of the parent widget for this Include.

        """
        # If we get an empty sequence, don't do any unnecessary work.
        if not components:
            return

        # The dynamic components of an Include are injected into the
        # parent's children so that they *appear* as if they are
        # siblings of the Include. To do this, we pass the reference
        # to the parent of the Include and the corresponding toolkit
        # widget where appropriate.
        parent = self.parent
        try:
            # If our parent is a BaseComponent, it won't have a
            # toolkit_widget attribute.
            toolkit_parent = parent.toolkit_widget
        except AttributeError:
            toolkit_parent = None

        # The following blocks perform roughly the same setup process
        # as BaseComponent.setup(), except that we don't need to perform
        # the setup for this Include instance (since it's already setup).
        # Also, since we don't need to recurse into any children (an
        # Include can't have children), there is no need to break these
        # blocks out into separate methods.

        # Need to explicitly assign the parent to the components
        # since they were not added via the add_subcomponent method.
        for child in components:
            child.parent = self

        for child in components:
            child._setup_create_widgets(toolkit_parent)

        for child in components:
            child._setup_init_widgets()

        for child in components:
            child._setup_eval_expressions()

        for child in components:
            child._setup_bind_widgets()

        for child in components:
            child._setup_listeners()

        for child in components:
            child._setup_init_visibility()

        for child in components:
            child._setup_init_layout()

        for child in components:
            child._setup_finalize()

        for child in components:
            child._setup_set_initialized()

    #--------------------------------------------------------------------------
    # Parent Class Overrides
    #--------------------------------------------------------------------------
    def add_subcomponent(self, component):
        """ An overriden parent class method which prevents subcomponents
        from being declared for an Include instance.

        """
        msg = ("Cannot add subcomponents to an Include. Assign a list of "
               "components to the 'components' attribute instead.")
        raise ValueError(msg)

    def get_actual(self):
        """ A reimplemented parent class method to include the dynamic
        children of this Include in our parent's list of children.

        """
        if self._components_initialized:
            res = sum([c.get_actual() for c in self.components], [])
        else:
            res = []
        return res

    def destroy(self):
        """ A re-implemented parent class method which destroys all of
        the underlying dynamic children.

        """
        super(Include, self).destroy()
        for item in self.components:
            item.destroy()

    #--------------------------------------------------------------------------
    # Change Handlers
    #--------------------------------------------------------------------------
    @on_trait_change('components')
    def _handle_components_changed(self, obj, name, old, new):
        """ A private trait change handler which reacts to changes to 
        the `components` list as a whole.

        """
        self._update_components_diff(set(old), set(new))

    @on_trait_change('components_items')
    def _handle_components_items_changed(self, obj, name, event):
        """ A private trait change handler which reacts to changes in
        the items of the `components` list.

        """
        self._update_components_diff(set(event.removed), set(event.added))

    def _update_components_diff(self, removed, added):
        """ Updates the UI components based on an item diff.

        A private method which will update the components by performing
        diff between the removed and added components. Components no
        longer in use will be destroyed. New components will be setup.

        Parameters
        ----------
        removed : set
            The set of components being removed. This is allowed to 
            overlap with `added` (e.g. a reverse operation).

        added : set
            The set of components being added. This is allowed to 
            overlap with `removed` (e.g. a reverse operation).

        """
        if self.initialized:
            to_destroy = removed - added
            to_setup = added - removed

            def closure():
                for item in to_destroy:
                    item.destroy()
                self._setup_components(to_setup)
                self._actual_updated()

            self.request_relayout_task(closure)

    # This notifier is hooked up in the '_setup_init_layout' method
    # due to issues surrounding trait_setq contexts.
    def _on_components_actual_updated(self):
        """ Handles a '_actual_updated' event being fired by one 
        the dynamic components. 

        The event is proxied up the tree by firing the same event on 
        this instance. This allows a nested Include to update its 
        contents independent of the Include in which it is nested.

        """
        self._actual_updated()