File: workbench_application.py

package info (click to toggle)
python-envisage 7.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,880 kB
  • sloc: python: 8,696; makefile: 76; sh: 5
file content (179 lines) | stat: -rw-r--r-- 6,081 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# (C) Copyright 2007-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" The entry point for an Envisage Workbench application. """


# Standard library imports.
import logging

from pyface.api import AboutDialog, Dialog, GUI, ImageResource, SplashScreen
from pyface.workbench.api import IWorkbench
from traits.api import Callable, Instance, Str, Tuple

# Enthought library imports.
from envisage.api import Application

# Local imports.
from .workbench import Workbench

# Logging.
logger = logging.getLogger(__name__)


class WorkbenchApplication(Application):
    """The entry point for an Envisage Workbench application.

    i.e. a GUI application whose user interface is provided by the workbench
    plugin.

    This class handles the common case for Workbench applications, and it is
    intended to be subclassed to change start/stop behaviour etc. In fact, I
    generally create a subclass for every Workbench application I write since
    it is a good place to put branding information etc.

    """

    #### 'WorkbenchApplication' interface #####################################

    # The Pyface GUI for the application (this is here to make it easy for
    # parts of the application to get a reference to the GUI so they can get
    # system metrics, etc.
    gui = Instance(GUI)

    # The workbench.
    workbench = Instance(IWorkbench)

    # The factory for creating the workbench (used *instead* of providing a
    # workbench explicitly).
    workbench_factory = Callable(Workbench)

    # Branding information.
    #
    # The 'About' dialog.
    about_dialog = Instance(Dialog)

    # The icon used on window title bars etc.
    icon = Instance(ImageResource, ImageResource("application.ico"))

    # The name of the application (also used on window title bars etc).
    name = Str("Workbench")

    # The splash screen (None, the default, if no splash screen is required).
    splash_screen = Instance(SplashScreen)

    # The default position of the main window.
    window_position = Tuple((200, 200))

    # The default size of the main window.
    window_size = Tuple((800, 600))

    ###########################################################################
    # 'IApplication' interface.
    ###########################################################################

    def run(self):
        """Run the application.

        This does the following (so you don't have to ;^):-

        1) Starts the application
        2) Creates and opens a workbench window
        3) Starts the GUI event loop
        4) When the event loop terminates, stops the application

        """

        logger.debug("---------- workbench application ----------")

        # Make sure the GUI has been created (so that, if required, the splash
        # screen is shown).
        gui = self.gui

        # Start the application.
        if self.start():
            # Create and open the first workbench window.
            window = self.workbench.create_window(
                position=self.window_position, size=self.window_size
            )
            window.open()

            # We stop the application when the workbench has exited.
            self.workbench.on_trait_change(self._on_workbench_exited, "exited")

            # Start the GUI event loop.
            #
            # THIS CALL DOES NOT RETURN UNTIL THE GUI IS CLOSED.
            gui.start_event_loop()

    ###########################################################################
    # 'WorkbenchApplication' interface.
    ###########################################################################

    #### Initializers #########################################################

    def _about_dialog_default(self):
        """Trait initializer."""

        return AboutDialog(image=ImageResource("about"))

    def _gui_default(self):
        """Trait initializer."""

        return GUI(splash_screen=self.splash_screen)

    def _workbench_default(self):
        """Trait initializer."""

        return self.create_workbench()

    #### Methods ##############################################################

    def about(self):
        """Display the about dialog."""

        # fixme: We really need to create a new 'about dialog' every time so
        # that it can have the active window as its parent.
        self.about_dialog.open()

    # fixme: Is this needed on the public API? Why can't we just do this in
    # the default initializer (_workbench_default)?
    def create_workbench(self):
        """Create the workbench."""

        logger.debug("workbench factory %s", self.workbench_factory)

        return self.workbench_factory(application=self)

    def exit(self):
        """Exit the application.

        This closes all open windows and hence exits the GUI event loop.

        """

        self.workbench.exit()

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _on_workbench_exited(self):
        """Dynamic trait change handler."""

        # We don't invoke 'stop' directly because:-
        #
        # The workbench is often exited via a user action (either by closing
        # the last open window, or by choosing 'File/Exit'). If this happens
        # then the workbench 'exit' method is called from within an event
        # handler which would cause the 'stop' method to get called *before*
        # the handling of the window 'closed' event is complete. Hance, this
        # might mean that somebody listening for the window being closed would
        # get the event *after* the application had already stopped!
        self.gui.invoke_later(self.stop)