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 180 181 182 183 184 185 186 187 188 189 190
|
""" The entry point for an Envisage Workbench application. """
# Standard library imports.
import logging
# Enthought library imports.
#
# fixme: The ordering of these imports is critical. We don't use traits UI in
# this module, but it must be imported *before* any 'HasTraits' class whose
# instances might want to have 'edit_traits' called on them.
#
# fixme: Just importing the package is enought (see above).
import enthought.traits.ui
# Enthought library imports.
from enthought.envisage.api import Application
from enthought.pyface.api import AboutDialog, Dialog, GUI, ImageResource
from enthought.pyface.api import SplashScreen
from enthought.pyface.workbench.api import IWorkbench
from enthought.traits.api import Callable, Instance, Str, Tuple
# 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()
return
###########################################################################
# '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()
return
# 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()
return
###########################################################################
# 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)
return
#### EOF ######################################################################
|