Beispiel #1
0
    def _execute_seek(self, controller, pattern):

        duration = pattern.duration
        total_duration = pattern.total_duration

        lm = self.laser_manager
        sm = lm.stage_manager
        ld = sm.lumen_detector

        ld.mask_kind = pattern.mask_kind
        ld.custom_mask = pattern.custom_mask_radius

        osdp = sm.canvas.show_desired_position
        sm.canvas.show_desired_position = False

        st = time.time()
        self.debug('Pre seek delay {}'.format(pattern.pre_seek_delay))
        time.sleep(pattern.pre_seek_delay)

        self.debug('starting seek')
        self.debug('total duration {}'.format(total_duration))
        self.debug('dwell duration {}'.format(duration))

        if pattern.kind == 'DragonFlyPeakPattern':
            try:
                self._dragonfly_peak(st, pattern, lm, controller)
            except BaseException as e:
                self.critical('Dragonfly exception. {}'.format(e))
        else:
            self._hill_climber(st, controller, pattern)

        sm.canvas.show_desired_position = osdp

        from pyface.gui import GUI
        GUI.invoke_later(self._info.dispose)
 def execute(self):
     url = 'http://' + self._host + self._url % self._tile_args
     try:
         r = requests.get(url)
         if r.status_code == 200:
             GUI.invoke_later(self.handler, self._tile_args, r.content)
     except requests.exceptions.RequestException as ex:
         print("Exception in request '{}': {}".format(self, ex))
Beispiel #3
0
def do_callback(dispatch, callback, *args):
    """Invoke the callback with a suitable dispatch.
    """
    if dispatch == 'ui':
        from pyface.gui import GUI
        GUI.invoke_later(callback, *args)
    else:
        callback(*args)
Beispiel #4
0
    def _calculation_done(self, future):
        """ Function which will return the result of the computation to
        the main thread

        Parameters
        ----------
        future
            Object containing the result of the calculation
        """
        GUI.invoke_later(self._computation_done, future.result())
Beispiel #5
0
    def progress_callback(self, datasets, current_iteration, total_iterations):
        """ Function called in the secondary thread. It will transfer the
        progress status of the calculation to the main thread

        Parameters
        ----------
        progress
            The progress of the calculation (Integer in the range [0, 100])
        """
        progress = current_iteration/total_iterations*100

        GUI.invoke_later(self._append_frame_and_continue, datasets)
        GUI.invoke_later(self.progress_dialog.update, progress)
Beispiel #6
0
    def _execute_seek(self, controller, pattern):

        duration = pattern.duration
        total_duration = pattern.total_duration

        lm = self.laser_manager
        sm = lm.stage_manager
        ld = sm.lumen_detector

        ld.mask_kind = pattern.mask_kind
        ld.custom_mask = pattern.custom_mask_radius

        osdp = sm.canvas.show_desired_position
        sm.canvas.show_desired_position = False

        st = time.time()
        self.debug('Pre seek delay {}'.format(pattern.pre_seek_delay))
        time.sleep(pattern.pre_seek_delay)

        self.debug('starting seek')
        self.debug('total duration {}'.format(total_duration))
        self.debug('dwell duration {}'.format(duration))

        if pattern.kind == 'DragonFlyPeakPattern':
            try:
                self._dragonfly_peak(st, pattern, lm, controller)
            except BaseException as e:
                self.critical('Dragonfly exception. {}'.format(e))
                self.debug_exception()
        else:
            self._hill_climber(st, controller, pattern)

        sm.canvas.show_desired_position = osdp

        from pyface.gui import GUI
        GUI.invoke_later(self._info.dispose)
Beispiel #7
0
                        error_message = arguments[1:]
                    logger.warning("Error status received from the server: " \
                                       "%s\n%s" % (error_status, error_message))
                    self.client.error = bool(int(error_status))

                # Handle other commands through Client interface
                else:
                    if self.client.ui_dispatch == 'off':
                        self.client.handle_command(command, arguments)
                    else:
                        if self.client.ui_dispatch == 'auto':
                            from pyface.gui import GUI
                        else:
                            exec('from pyface.ui.%s.gui import GUI' %
                                 self.client.ui_dispatch)
                        GUI.invoke_later(self.client.handle_command, command,
                                         arguments)
        finally:
            self.client.unregister()

    def stop(self):
        self._finished = True


class Client(HasTraits):
    """ An object that communicates with another object through a Server.
    """

    # The preferences file path and node path to use for spawning a Server. If
    # this is not specified it will not be possible for this Client to spawn
    # the server.
    server_prefs = Tuple(
 def handle_response(self):
     if self.response.status == 200:
         GUI.invoke_later(self.handler, self._tile_args, self.response.body)
     self.close()
Beispiel #9
0
def invoke_in_main_thread(fn, *args, **kw):
    from pyface.gui import GUI
    GUI.invoke_later(fn, *args, **kw)
 def handle_response(self):
     if self.response.status == 200:
         GUI.invoke_later(self.handler, self._tile_args, self.response.body)
     self.close()
Beispiel #11
0
    def run(self):
        # Get the server port, spawning it if necessary
        server_port = get_server_port()
        if server_port == -1 or not Server.ping(server_port, timeout=5):
            if len(self.client.server_prefs):
                # Spawn the server
                logger.info("Client spawning Server...")
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(5)
                sock.bind(('localhost', 0))
                sock.listen(1)
                port = sock.getsockname()[1]
                args = self.client.server_prefs + (port, )
                code = "from envisage.plugins.remote_editor.communication." \
                    "server import main; main(r'%s', '%s', %i)" % args
                spawn_independent([sys.executable, '-c', code])

                # Await a reponse from the server
                try:
                    server, address = accept_no_intr(sock)
                    try:
                        command, arguments = receive(server)
                        if command == "__port__":
                            self.client._server_port = int(arguments)
                        else:
                            raise socket.error
                    finally:
                        # Use try...except to handle timeouts
                        try:
                            server.shutdown(socket.SHUT_RD)
                        except:
                            pass
                except socket.error as e:
                    logger.error(repr(e))
                    logger.error("Client spawned a non-responsive Server! " \
                                     "Unregistering...")
                    self.client.error = True
                    self.client.unregister()
                    return
                finally:
                    sock.close()

            else:
                logger.error("Client could not contact the Server and no " \
                                 "spawn command is defined. Unregistering...")
                self.client.error = True
                self.client.unregister()
                return
        else:
            self.client._server_port = server_port

        # Create the socket that will receive commands from the server
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 0))
        sock.listen(1)
        self.client._port = sock.getsockname()[1]

        # Register with the server
        port = str(self.client._port)
        arguments = MESSAGE_SEP.join(
            (port, self.client.self_type, self.client.other_type))
        self.client.error = not send_port(self.client._server_port, 'register',
                                          arguments)
        self.client.registered = True

        # Send queued commands (these can only exist if we spawned the server)
        for command, args in self.client._queue:
            arguments = MESSAGE_SEP.join((port, command, args))
            self.client.error = not send_port(self.client._server_port, 'send',
                                              arguments)
        self.client._queue = []

        # Start the loop to listen for commands from the Server
        logger.info("Client listening on port %i..." % self.client._port)
        try:
            while not self._finished:
                server, address = accept_no_intr(sock)

                # Reject non-local connections
                if address[0] != '127.0.0.1':
                    msg = "Client on port %s received connection from a " \
                        "non-local party (port %s). Ignoring..."
                    logger.warning(msg % (port, address[0]))
                    continue

                # Receive the command from the server
                self.client.error = False
                command, arguments = receive(server)
                msg = r"Client on port %s received: %s %s"
                logger.debug(msg, port, command, arguments)

                # Handle special commands from the server
                if command == "__orphaned__":
                    self.client.orphaned = bool(int(arguments))

                elif command == "__error__":
                    error_status = arguments[0]
                    error_message = ''
                    if len(arguments) > 0:
                        error_message = arguments[1:]
                    logger.warning("Error status received from the server: " \
                                       "%s\n%s" % (error_status, error_message))
                    self.client.error = bool(int(error_status))

                # Handle other commands through Client interface
                else:
                    if self.client.ui_dispatch == 'off':
                        self.client.handle_command(command, arguments)
                    else:
                        if self.client.ui_dispatch == 'auto':
                            from pyface.gui import GUI
                        else:
                            exec('from pyface.ui.%s.gui import GUI' %
                                 self.client.ui_dispatch)
                        GUI.invoke_later(self.client.handle_command, command,
                                         arguments)
        finally:
            self.client.unregister()
Beispiel #12
0
class GUIApplication(Application):
    """ A basic Pyface GUI application. """

    # 'GUIApplication' traits -------------------------------------------------

    # Branding ---------------------------------------------------------------

    #: The splash screen for the application. No splash screen by default
    splash_screen = Instance(ISplashScreen)

    #: The about dialog for the application.
    about_dialog = Instance(IDialog)

    #: Icon for the application (used in window titlebars)
    icon = Image

    #: Logo of the application (used in splash screens and about dialogs)
    logo = Image

    # Window management ------------------------------------------------------

    #: The window factory to use when creating a window for the application.
    window_factory = Callable(default_window_factory)

    #: Default window size
    window_size = Tuple((800, 600))

    #: Currently active Window if any
    active_window = Instance(IWindow)

    #: List of all open windows in the application
    windows = List(Instance(IWindow))

    #: The Pyface GUI instance for the application
    gui = ReadOnly

    # Protected interface ----------------------------------------------------

    #: Flag if the exiting of the application was explicitely requested by user
    # An 'explicit' exit is when the 'exit' method is called.
    # An 'implicit' exit is when the user closes the last open window.
    _explicit_exit = Bool(False)

    # -------------------------------------------------------------------------
    # 'GUIApplication' interface
    # -------------------------------------------------------------------------

    # Window lifecycle methods -----------------------------------------------

    def create_window(self, **kwargs):
        """ Create a new application window.

        By default uses the :py:attr:`window_factory` to do this.  Subclasses
        can override if they want to do something different or additional.

        Parameters
        ----------
        **kwargs : dict
            Additional keyword arguments to pass to the window factory.

        Returns
        -------
        window : IWindow instance or None
            The new IWindow instance.
        """
        window = self.window_factory(application=self, **kwargs)

        if window.size == (-1, -1):
            window.size = self.window_size
        if not window.title:
            window.title = self.name
        if self.icon:
            window.icon = self.icon

        return window

    def add_window(self, window):
        """ Add a new window to the windows we are tracking. """

        # Keep a handle on all windows created so that non-active windows don't
        # get garbage collected
        self.windows.append(window)

        # Something might try to veto the opening of the window.
        opened = window.open()
        if opened:
            window.activate()

    # Action handlers --------------------------------------------------------

    def do_about(self):
        """ Display the about dialog, if it exists. """
        if self.about_dialog is not None:
            self.about_dialog.open()

    # -------------------------------------------------------------------------
    # 'Application' interface
    # -------------------------------------------------------------------------

    def start(self):
        """ Start the application, setting up things that are required

        Subclasses should open at least one ApplicationWindow or subclass in
        their start method, and should call the superclass start() method
        before doing any work themselves.
        """
        from pyface.gui import GUI

        ok = super(GUIApplication, self).start()
        if ok:
            # create the GUI so that the splash screen comes up first thing
            if self.gui is Undefined:
                self.gui = GUI(splash_screen=self.splash_screen)

            # create the initial windows to show
            self._create_windows()

        return ok

    # -------------------------------------------------------------------------
    # 'GUIApplication' Private interface
    # -------------------------------------------------------------------------

    def _create_windows(self):
        """ Create the initial windows to display.

        By default calls :py:meth:`create_window` once. Subclasses can
        override this method.
        """
        window = self.create_window()
        self.add_window(window)

    # -------------------------------------------------------------------------
    # 'Application' private interface
    # -------------------------------------------------------------------------

    def _run(self):
        """ Actual implementation of running the application: starting the GUI
        event loop.
        """
        # Fire a notification that the app is running.  This is guaranteed to
        # happen after all initialization has occurred and the event loop has
        # started.  A listener for this event is a good place to do things
        # where you want the event loop running.
        self.gui.invoke_later(self._fire_application_event,
                              "application_initialized")

        # start the GUI - script blocks here
        self.gui.start_event_loop()
        return True

    # Destruction methods -----------------------------------------------------

    def _can_exit(self):
        """ Check with each window to see if it can be closed

        The fires closing events for each window, and returns False if any
        listener vetos.
        """
        if not super(GUIApplication, self)._can_exit():
            return False

        for window in reversed(self.windows):
            window.closing = event = Vetoable()
            if event.veto:
                return False
        else:
            return True

    def _prepare_exit(self):
        """ Close each window """
        # ensure copy of list, as we modify original list while closing
        for window in list(reversed(self.windows)):
            window.destroy()
            window.closed = window

    def _exit(self):
        """ Shut down the event loop """
        self.gui.stop_event_loop()

    # Trait default handlers ------------------------------------------------

    def _window_factory_default(self):
        """ Default to ApplicationWindow

        This is almost never the right thing, but allows users to get off the
        ground with the base class.
        """
        from pyface.application_window import ApplicationWindow

        return lambda application, **kwargs: ApplicationWindow(**kwargs)

    def _splash_screen_default(self):
        """ Default SplashScreen """
        from pyface.splash_screen import SplashScreen

        dialog = SplashScreen()
        if self.logo:
            dialog.image = self.logo
        return dialog

    def _about_dialog_default(self):
        """ Default AboutDialog """
        from html import escape

        from pyface.about_dialog import AboutDialog

        additions = [
            "<h1>{}</h1>".format(escape(self.name)),
            "Copyright &copy; 2018 {}, all rights reserved".format(
                escape(self.company)),
            "",
        ]
        additions += [escape(line) for line in self.description.split("\n\n")]

        dialog = AboutDialog(title="About {}".format(self.name),
                             additions=additions)
        if self.logo:
            dialog.image = self.logo
        return dialog

    # Trait listeners --------------------------------------------------------

    @on_trait_change("windows:activated")
    def _on_activate_window(self, window, trait, old, new):
        """ Listener that tracks currently active window.
        """
        if window in self.windows:
            self.active_window = window

    @on_trait_change("windows:deactivated")
    def _on_deactivate_window(self, window, trait, old, new):
        """ Listener that tracks currently active window.
        """
        self.active_window = None

    @on_trait_change("windows:closed")
    def _on_window_closed(self, window, trait, old, new):
        """ Listener that ensures window handles are released when closed.
        """
        if window in self.windows:
            self.windows.remove(window)
Beispiel #13
0
    def run(self):
        # Get the server port, spawning it if necessary
        server_port = get_server_port()
        if server_port == -1 or not Server.ping(server_port, timeout=5):
            if len(self.client.server_prefs):
                # Spawn the server
                logger.info("Client spawning Server...")
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(5)
                sock.bind(('localhost', 0))
                sock.listen(1)
                port = sock.getsockname()[1]
                args = self.client.server_prefs + ( port, )
                code = "from envisage.plugins.remote_editor.communication." \
                    "server import main; main(r'%s', '%s', %i)" % args
                spawn_independent([sys.executable, '-c', code])

                # Await a reponse from the server
                try:
                    server, address = accept_no_intr(sock)
                    try:
                        command, arguments = receive(server)
                        if command == "__port__":
                            self.client._server_port = int(arguments)
                        else:
                            raise socket.error
                    finally:
                        # Use try...except to handle timeouts
                        try:
                            server.shutdown(socket.SHUT_RD)
                        except:
                            pass
                except socket.error as e:
                    logger.error(repr(e))
                    logger.error("Client spawned a non-responsive Server! " \
                                     "Unregistering...")
                    self.client.error = True
                    self.client.unregister()
                    return
                finally:
                    sock.close()

            else:
                logger.error("Client could not contact the Server and no " \
                                 "spawn command is defined. Unregistering...")
                self.client.error = True
                self.client.unregister()
                return
        else:
            self.client._server_port = server_port

        # Create the socket that will receive commands from the server
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 0))
        sock.listen(1)
        self.client._port = sock.getsockname()[1]

        # Register with the server
        port = str(self.client._port)
        arguments = MESSAGE_SEP.join((port, self.client.self_type,
                                      self.client.other_type))
        self.client.error = not send_port(self.client._server_port, 'register',
                                          arguments)
        self.client.registered = True

        # Send queued commands (these can only exist if we spawned the server)
        for command, args in self.client._queue:
            arguments = MESSAGE_SEP.join((port, command, args))
            self.client.error = not send_port(self.client._server_port, 'send',
                                              arguments)
        self.client._queue = []

        # Start the loop to listen for commands from the Server
        logger.info("Client listening on port %i..." % self.client._port)
        try:
            while not self._finished:
                server, address = accept_no_intr(sock)

                # Reject non-local connections
                if address[0] != '127.0.0.1':
                    msg = "Client on port %s received connection from a " \
                        "non-local party (port %s). Ignoring..."
                    logger.warning(msg % (port, address[0]))
                    continue

                # Receive the command from the server
                self.client.error = False
                command, arguments = receive(server)
                msg = r"Client on port %s received: %s %s"
                logger.debug(msg, port, command, arguments)

                # Handle special commands from the server
                if command == "__orphaned__":
                    self.client.orphaned = bool(int(arguments))

                elif command == "__error__":
                    error_status = arguments[0]
                    error_message = ''
                    if len(arguments) > 0:
                        error_message = arguments[1:]
                    logger.warning("Error status received from the server: " \
                                       "%s\n%s" % (error_status, error_message))
                    self.client.error = bool(int(error_status))

                # Handle other commands through Client interface
                else:
                    if self.client.ui_dispatch == 'off':
                        self.client.handle_command(command, arguments)
                    else:
                        if self.client.ui_dispatch == 'auto':
                            from pyface.gui import GUI
                        else:
                            exec('from pyface.ui.%s.gui import GUI' %
                                 self.client.ui_dispatch)
                        GUI.invoke_later(self.client.handle_command,
                                         command, arguments)
        finally:
            self.client.unregister()
Beispiel #14
0
def invoke_in_main_thread(fn, *args, **kw):
    from pyface.gui import GUI
    GUI.invoke_later(fn, *args, **kw)
Beispiel #15
0
                        error_message = arguments[1:]
                    logger.warning("Error status received from the server: " \
                                       "%s\n%s" % (error_status, error_message))
                    self.client.error = bool(int(error_status))

                # Handle other commands through Client interface
                else:
                    if self.client.ui_dispatch == 'off':
                        self.client.handle_command(command, arguments)
                    else:
                        if self.client.ui_dispatch == 'auto':
                            from pyface.gui import GUI
                        else:
                            exec('from pyface.ui.%s.gui import GUI' %
                                 self.client.ui_dispatch)
                        GUI.invoke_later(self.client.handle_command,
                                         command, arguments)
        finally:
            self.client.unregister()

    def stop(self):
        self._finished = True


class Client(HasTraits):
    """ An object that communicates with another object through a Server.
    """

    # The preferences file path and node path to use for spawning a Server. If
    # this is not specified it will not be possible for this Client to spawn
    # the server.
    server_prefs = Tuple((os.path.join(remote_editor.__path__[0],
 def _stderr_pattern_matched(self, pattern_index, match):
     pattern = match.group()
     self._errors += "\n%s" % pattern.strip()
     if pattern_index == 5:
         groupdict = match.groupdict()
         GUI.invoke_later(self.open_file_at_erroneous_line, groupdict)
Beispiel #17
0
class GUIApplication(Application):
    """ A basic Pyface GUI application. """

    # 'GUIApplication' traits -------------------------------------------------

    # Branding ---------------------------------------------------------------

    #: The splash screen for the application. No splash screen by default
    splash_screen = Instance(ISplashScreen)

    #: The about dialog for the application.
    about_dialog = Instance(IDialog)

    #: Icon for the application (used in window titlebars)
    icon = Image

    #: Logo of the application (used in splash screens and about dialogs)
    logo = Image

    # Window management ------------------------------------------------------

    #: The window factory to use when creating a window for the application.
    window_factory = Callable(default_window_factory)

    #: Default window size
    window_size = Tuple((800, 600))

    #: Currently active Window if any
    active_window = Instance(IWindow)

    #: List of all open windows in the application
    windows = List(Instance(IWindow))

    #: The Pyface GUI instance for the application
    gui = ReadOnly

    # Protected interface ----------------------------------------------------

    #: Flag if the exiting of the application was explicitely requested by user
    # An 'explicit' exit is when the 'exit' method is called.
    # An 'implicit' exit is when the user closes the last open window.
    _explicit_exit = Bool(False)

    # -------------------------------------------------------------------------
    # 'GUIApplication' interface
    # -------------------------------------------------------------------------

    # Window lifecycle methods -----------------------------------------------

    def create_window(self, **kwargs):
        """ Create a new application window.

        By default uses the :py:attr:`window_factory` to do this.  Subclasses
        can override if they want to do something different or additional.

        Parameters
        ----------
        **kwargs : dict
            Additional keyword arguments to pass to the window factory.

        Returns
        -------
        window : IWindow instance or None
            The new IWindow instance.
        """
        window = self.window_factory(application=self, **kwargs)

        if window.size == (-1, -1):
            window.size = self.window_size
        if not window.title:
            window.title = self.name
        if self.icon:
            window.icon = self.icon

        return window

    def add_window(self, window):
        """ Add a new window to the windows we are tracking. """

        # Keep a handle on all windows created so that non-active windows don't
        # get garbage collected
        self.windows.append(window)

        # Something might try to veto the opening of the window.
        opened = window.open()
        if opened:
            window.activate()

    # Action handlers --------------------------------------------------------

    def do_about(self):
        """ Display the about dialog, if it exists. """
        if self.about_dialog is not None:
            self.about_dialog.open()

    # -------------------------------------------------------------------------
    # 'Application' interface
    # -------------------------------------------------------------------------

    def start(self):
        """ Start the application, setting up things that are required

        Subclasses should open at least one ApplicationWindow or subclass in
        their start method, and should call the superclass start() method
        before doing any work themselves.
        """
        from pyface.gui import GUI

        ok = super(GUIApplication, self).start()
        if ok:
            # create the GUI so that the splash screen comes up first thing
            if self.gui is Undefined:
                self.gui = GUI(splash_screen=self.splash_screen)

            # create the initial windows to show
            self._create_windows()

        return ok

    # -------------------------------------------------------------------------
    # 'GUIApplication' Private interface
    # -------------------------------------------------------------------------

    def _create_windows(self):
        """ Create the initial windows to display.

        By default calls :py:meth:`create_window` once. Subclasses can
        override this method.
        """
        window = self.create_window()
        self.add_window(window)

    # -------------------------------------------------------------------------
    # 'Application' private interface
    # -------------------------------------------------------------------------

    def _run(self):
        """ Actual implementation of running the application: starting the GUI
        event loop.
        """
        # Fire a notification that the app is running.  This is guaranteed to
        # happen after all initialization has occurred and the event loop has
        # started.  A listener for this event is a good place to do things
        # where you want the event loop running.
        self.gui.invoke_later(
            self._fire_application_event, 'application_initialized'
        )

        # start the GUI - script blocks here
        self.gui.start_event_loop()
        return True

    # Destruction methods -----------------------------------------------------

    def _can_exit(self):
        """ Check with each window to see if it can be closed

        The fires closing events for each window, and returns False if any
        listener vetos.
        """
        if not super(GUIApplication, self)._can_exit():
            return False

        for window in reversed(self.windows):
            window.closing = event = Vetoable()
            if event.veto:
                return False
        else:
            return True

    def _prepare_exit(self):
        """ Close each window """
        # ensure copy of list, as we modify original list while closing
        for window in list(reversed(self.windows)):
            window.destroy()
            window.closed = window

    def _exit(self):
        """ Shut down the event loop """
        self.gui.stop_event_loop()

    # Trait default handlers ------------------------------------------------

    def _window_factory_default(self):
        """ Default to ApplicationWindow

        This is almost never the right thing, but allows users to get off the
        ground with the base class.
        """
        from pyface.application_window import ApplicationWindow
        return lambda application, **kwargs: ApplicationWindow(**kwargs)

    def _splash_screen_default(self):
        """ Default SplashScreen """
        from pyface.splash_screen import SplashScreen

        dialog = SplashScreen()
        if self.logo:
            dialog.image = self.logo
        return dialog

    def _about_dialog_default(self):
        """ Default AboutDialog """
        from sys import version_info
        if (version_info.major, version_info.minor) >= (3, 2):
            from html import escape
        else:
            from cgi import escape
        from pyface.about_dialog import AboutDialog

        additions = [
            u"<h1>{}</h1>".format(escape(self.name)),
            u"Copyright &copy; 2018 {}, all rights reserved".format(
                escape(self.company),
            ),
            u"",
        ]
        additions += [escape(line) for line in self.description.split('\n\n')]

        dialog = AboutDialog(
            title=u"About {}".format(self.name),
            additions=additions,
        )
        if self.logo:
            dialog.image = self.logo
        return dialog

    # Trait listeners --------------------------------------------------------

    @on_trait_change('windows:activated')
    def _on_activate_window(self, window, trait, old, new):
        """ Listener that tracks currently active window.
        """
        if window in self.windows:
            self.active_window = window

    @on_trait_change('windows:deactivated')
    def _on_deactivate_window(self, window, trait, old, new):
        """ Listener that tracks currently active window.
        """
        self.active_window = None

    @on_trait_change('windows:closed')
    def _on_window_closed(self, window, trait, old, new):
        """ Listener that ensures window handles are released when closed.
        """
        if window in self.windows:
            self.windows.remove(window)