File: crash_course.rst

package info (click to toggle)
python-enaml 0.19.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,284 kB
  • sloc: python: 31,443; cpp: 4,499; makefile: 140; javascript: 68; lisp: 53; sh: 20
file content (671 lines) | stat: -rw-r--r-- 26,501 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
Enaml Workbench Developer Crash Course
======================================
This document is a short introduction to the Enaml Workbench plugin framework.
It is intended for developers of plugin applications that need to get up and
running with the framework in a short amount of time. The Workbench framework
is not large, and a good developer can be comfortable with it in an afternoon.

This document covers the concepts, terminology, workflow, and the core plugins
and classes of the framework. The accompanying example demonstrates the various
parts of the framework with a simple plugin application which allows the user
to toggle between a handful of sample views.

Concepts
--------
Writing large applications is hard. Writing large UI applications is harder.
Writing large UI applications which can be *safetly extended at runtime* by
other developers is a recipe for hair loss. There are several difficult issues
which  must be addressed when developing such applications, some of the most
notable are:

Registration
	How does user code get dynamically registered and unregistered at runtime?

Life Cyle
	When and how should user code be loaded and run? How and when and how
	should it be unloaded and stopped?

Dependencies
	How does the application get started without requiring all user code to
	be available at startup? How does the application avoid loading external
	dependencies until they are actually required to do work?

Notifications
	How can various parts of the application be notified when user code is
	registered and unregistered?

User Interfaces
	How can the application be flexible enough to allow user code to add
	user interface elements to the window at runtime, without clobbering
	or interfering with the existing user interface elements?

Flexibility
	How can an application be designed in a way where it may be extended
	to support future use cases which have not yet been conceived?

Ease of Use
	How can all of these difficult problems be solved in such a way that
	a good developer can be comfortable developing with the application
	in an afternoon?

The Enaml Workbench framework attempts to solve these problems by providing
a set of low-level components which can be used to develop high-level plugin
applications. Think of it as a mini-Eclipse framework for Enaml.

Unlike Eclipse however, the Enaml Workbench framework strives to be compact
and efficient. Following the "less is more" mantra, it seeks to provide only
the core low-level features required for generic plugin applications. It is
intended that the core development team for a large application will build
domain specific abstractions on top of the core workbench pieces which will
then used to assemble the final application.

Terminology
-----------
Before continuuing with the crash course, the following terminology is
introduced and used throughout the rest of the document.

Workbench
	The core framework object which manages the registration of plugin
	manifests and the creation of plugin objects. It acts as the central
	registry and primary communication hub for the various parts of a
	plugin application.

Plugin Manifest
	An object which declares a plugin and its public behavior. It does
	not provide an implementation of that behavior.

Plugin
	An object which can be dynamically loaded and unloaded from a workbench.
	It is the implementation of the behavior defined by its plugin manifest.
	This term is often overload to also indicate the collection of manifest,
	plugin, extension points, and extensions. That is, 'plugin' can refer to
	the actual plugin instance, or the entire package of related objects
	written by the developer.

Extension Point
	A declaration in a plugin manifest which advertises that other plugins
	may contribute functionality to this plugin through extensions. It
	defines the interface to which an extension must conform in order to
	be useful to the plugin which declares the extension point.

Extension
	A contribution to the extension point of a plugin. An extension adds
	functionality and behavior to an existing application by implementing
	the interface required by a given extension point.

Workflow
--------
Using the workbench framework is relatively straightforward and has only
a few conceptual steps.

0. Define the classes which implement your application business logic.
1. If your application will create a plugin which contribute extensions
   to an extension point, define the extension classes and ensure that
   they implement the interface required by the extension point. The
   extension classes should interact with the business logic classes to
   expose their functionality to the rest of the application.
2. If your application will create a plugin which defines new extension
   points, define a Plugin subclasses which will implement the extension
   point behavior by interacting with the extensions contributed to the
   extension point by other plugins.
3. Create a PluginManifest for each plugin defined by your application.
   The manifest will declare the extension points provided by the plugin
   as well as the extensions it contributes to other extension points. If
   needed, it will supply a factory to create the custom Plugin object.
4. Create an instance of Workbench or one of its subclasses.
5. Register the plugin manifests required by your application with the
   workbench. Only the plugins required for startup need to be registered.
   Additional manifest can be added and removed dynamically at runtime.
6. Start the application. How this is done is application dependent.

Points 0 - 3 require the most mental effort. The framework provides a few pre-
defined plugins and Workbench subclasses (described later) which make the last
few steps of the process more-or-less trivial.

The important takeaway here is that the application business logic should be
defined first, and then be bundled up as extensions and extension points to
expose that logic to various other parts of the application. This design
pattern forces a strong separation between logical components. And while it
requires a bit more up-front work, it results in better code reuse and a more
maintainable and extensible code base.

Core Classes
------------
This section covers the core classes of the workbench framework.

Workbench
~~~~~~~~~
The Workbench class acts as the fundamental registry and manager object for
all the other parts of the plugin framework. As a central hub, it's usually
possible to access any object of interest in the application by starting with
a reference to the workbench object.

The core `Workbench` class can be imported from `enaml.workbench.api`.

The core `Workbench` class may be used directly, though application developers
will typically create a subclass to register default plugins on startup. A
perfect example of this is the `UIWorkbench` subclass which registers the
'enaml.workbench.core' and 'enaml.workbench.ui' plugins when started.

The following methods on a Workbench are of particular interest:

register
	This method is used to register a `PluginManifest` instance with the
	workbench. This is the one-and-only way to contribute plugins to an
	application, whether during initialization or later at runtime.

unregister
	This method is used to unregister a plugin manifest which was previously
	added to the workbench with a call to `register`. This is the one-and-
	only way to remove plugins from the workbench application.

get_plugin
	This method is used to query for, and lazily create, the plugin object
	for a given manifest. The plugin object will be created the *first* time
 	this method is called. Future calls will return the cached plugin object.

get_extension_point
	This method will return the extension point declared by a plugin. The
	extension point can be queried for contributed extensions at runtime.

PluginManifest
~~~~~~~~~~~~~~
The PluginManifest class is used to describe a plugin in terms of its
extension points and extensions. It also defines a globally unique
identifier for the plugin along with an optional factory function which
can be used to create the underlying plugin instance when needed.

The `PluginManifest` class can be imported from `enaml.workbench.api`.

The PluginManifest class is a declarative class and defines the following
attributes of interest:

id
	This is a globally unique identifier which identifies both the manifest
	and the plugin which will be created for it. It should be a string in
	dot-separated form, typically 'org.pkg.module.name'. It also serves as
	the enclosing namespace for the identifiers of its extension points and
	extensions. The global uniqueness of this identifier is enforced.

factory
	A callable which takes no arguments and returns an instance of Plugin.
	For most use-cases, this factory can be ignored. The default factory
	will create an instance of the default Plugin class which is suitable
	for the frequent case of a plugin providing nothing but extensions to
	the extension points of other plugins.

Since this class is declarative, children may be defined on it. In particular,
a plugin's extension points and extensions are defined by declaring children
of type `ExtensionPoint` and `Extension` on the plugin manifest.

Plugin
~~~~~~
The Plugin class is what does the actual work for implementing the behaviors
defined by extension points. It acts as a sort of manager, ensuring that the
extensions which were contributed to a given extension point are invoked
properly and in accordance with interface defined by the extension point.

Well-behaved plugins also react appropriately when extensions are added or
removed from one of their extension points at runtime.

The `Plugin` class can be imported from `enaml.workbench.api`.

It will be uncommon for most end-user developers to ever need to create a
custom plugin class. That job is reserved for core application developers
which actually define how the application can be extended. That said, there
are two methods on a plugin which will be of interest to developers:

start
    This method will be called by the workbench after it creates the
    plugin. The default implementation does nothing and can be ignored
    by subclasses which do not need life-cycle behavior.

stop
	This method will be called by the workbench when the plugin is
	removed. The default implementation does nothing and can be
	ignored by subclasses which do not need life-cycle behavior.

ExtensionPoint
~~~~~~~~~~~~~~
The ExtensionPoint class is used to publicly declare a point to which
extensions can be contributed to the plugin. Is is declared as the
child of a PluginManifest.

The `ExtensionPoint` class can be imported from `enaml.workbench.api`.

The ExtensionPoint class is a declarative class and defines the following
attributes of interest:

id
	The unique identifier for the extension point. It should be simple
	string with no dots. The fully qualified id of the extension point
	will be formed by dot-joining the id of the parent plugin manifest
	with this id.

Declarative children of an extension point do not have any meaning as
far as the workbench framework is concerned.

Extension
~~~~~~~~~
The Extension class is used to pubclicly declare the contribution a plugin
provides to the extension point of another plugin. It is declared as the
child of a PluginManifest.

The `Extension` class can be imported from `enaml.workbench.api`.

The Extension class is a declarative class and defines the following
attributes of interest:

id
	The unique identifier for the extension. It should be simple string
	with no dots. The fully qualified id of the extension will be formed
	by dot-joining the id of the parent plugin manifest with this id.

point
	The fully qualified id of the extension point to which the extension
	is contributing.

rank
	An optional integer to rank the extension among other extensions
	contributed to the same extension point. The semantics of how the
	rank value is used is specified by a given extension point.

factory
	An optional callable which is used to create the implementation
	object for an extension. The semantics of the call signature and
	return value are specified by a given extension point.

Declarative children of an Extension are allowed, and their semantic meaning
are defined by a given extension point. For example, the extension point
'enaml.workbench.core.commands' allows extension commands to be defined as
declarative children of the extension.

Core Plugin
-----------
The section covers the workbench core plugin.

The core plugin is a pre-defined plugin supplied by the workbench framework.
It provides non-ui related functionality that is useful across a wide variety
of applications. It must be explicitly registered with a workbench in order
to be used.

The `CoreManifest` class can be imported from `enaml.workbench.core.api`. It
is a declarative enamldef and so must be imported from within an Enaml imports
context.

The id for the core plugin is 'enaml.workbench.core' and it declares the
following extension points:

'commands'
	Extensions to this point may contribute `Command` objects which can
	be invoked via the `invoke_command` method of the CorePlugin instance.
	Commands can be provided by declaring them as children of the Extension
	and/or by declaring a factory function which takes the workbench as an
	argument and returns a list of Command instances.

Command
~~~~~~~
A Command object is used to declare that a plugin can take some action when
invoked by a user. It is declared as the child of an Extension which
contributes to the 'enaml.workbench.core.commands' extension point.

The `Command` class can be imported from `enaml.workbench.core.api`.

The Command class is a declarative class and defines the following
attributes of interest:

id
	The globally unique identifier for the command. This should be a
	dot-separated string. The global uniqueness is enforced.

handler
	A callable object which implements the command behavior. It must
	accept a single argument which is an instance of `ExecutionEvent`.

ExecutionEvent
~~~~~~~~~~~~~~
An ExecutionEvent is an object which is passed to a Command handler when
it is invoked by the framework. User code will never directly create an
ExecutionEvent.

An ExecutionEvent has the following attributes of interest:

command
	The Command object which is being invoked.

workbench
	A reference to the workbench which owns the command.

parameters
	A dictionary of user-supplied parameters to the command.

trigger
	The user object which triggered the command.

UI Plugin
---------
This section covers the workbench ui plugin.

The ui plugin is a pre-defined plugin supplied by the workbench framework.
It provides ui-related functionality which is common to a large swath of
UI applications. It must be explicity registered with a workbench in order
to be used.

The `UIManifest` class can be imported from `enaml.workbench.ui.api`. It is
a declarative enamldef and so must be imported from within an Enaml imports
context.

The id of the ui plugin is 'enaml.workbench.ui' and it declares the following
extension points:

'application_factory'
	An Extension to this point can be used to provide a custom
	application object for the workbench. The extension factory should
	accept no arguments and return an Application instance. The highest
	ranking extension will be chosen to create the application.

'window_factory'
	An Extension to this point can be used to provide a custom main
	window for the workbench. The extension factory should accept the
	workbench as an argument and return a WorkbenchWindow instance. The
	highest ranking extension will be chosen to create the window.

'branding'
	An Extension to this point can be used to provide a custom window
	title and icon to the primary workbench window. A Branding object can
	be declared as the child of the extension, or created by the extension
	factory function which accepts the workbench as an argument. The
	highest ranking extension will be chosen to provide the branding.

'actions'
	Extensions to this point can be used to provide menu items and
	action items to be added to the primary workbench window menu bar. The
	extension can declare child MenuItem and ActionItem instances as well
	as provide a factory function which returns a list of the same.

'workspaces'
	Extensions to this point can be used to provide workspaces which
	can be readily swapped to provide the main content for the workbench
	window. The extension factory function should accep the workbench as
	an argument and return an instance of Workspace.

'autostart'
	Extensions to this point can be used to provide the id of a plugin
	which should be started preemptively on application startup. The
	extension should declare children of type Autostart. The plugins will
	be started in order of extension rank. Warning - abusing this facility
	can cause drastic slowdowns in application startup time. Only use it
	if you are *absolutely* sure your plugin must be loaded on startup.

The plugin declares the following extensions:

'default_application_factory'
	This contributes to the 'enaml.workbench.ui.application_factory'
	extension point and provides a default instance of a QtApplication.

'default_window_factory'
	This contributes to the 'enaml.workbench.ui.window_factory' extension
	point and provides a default instance of a WorkbenchWindow.

'default_commands'
	This contributes to the 'enaml.workbench.core.commands' extension point
	and provides the default command for the plugin (described later).

The plugin provides the following commands:

'enaml.workbench.ui.close_window'
	This command will close the primary application window. It takes
	no parameters.

'enaml.workbench.ui.close_workspace'
	This command will close the currently active workspace. It takes
	no parameters.

'enaml.workbench.ui.select_workspace'
	This command will select and activate a new workspace. It takes
	a single 'workspace' parameter which is the fully qualified id of
	the extension point which contributes the workspace of interest.

WorkbenchWindow
~~~~~~~~~~~~~~~
The WorkbenchWindow is an enamldef subclass of the Enaml MainWindow widget.
It is used by the ui plugin to bind to the internal ui window model which
drives the runtime dynamism of the window.

The will be cases where a developer wishes to create a custom workbench
window for one reason or another. This can be done subclassing the plain
WorkbenchWindow and writing a plugin which contributes a factory to the
'enaml.workbench.ui.window_factory' class.

The WorkbenchWindow class can be imported from `enaml.workbench.ui.api`.

Branding
~~~~~~~~
The Branding class is a declarative class which can be used to apply a
custom window title and window icon to the primary application window. This
is a declarative class which can be defined as the child of an extension, or
returned from the factory of an extension which contributes to the
'enaml.workbench.ui.branding' extension point.

The Branding class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest:

title
	The string to use as the primary title of the main window.

icon
	The icon to use for the icon of the main window and taskbar.

MenuItem
~~~~~~~~
The MenuItem class is a declarative class which can be used to declare a
menu in the primary window menu bar.

The MenuItem class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest:

path
	A "/" separated path to the location of this item in the menu bar.
	This path must be unique for the menu bar, and the parent path must
	exist in the menu bar. The last token in the path is the id of this
	menu item with respect to its siblings. For example, if the path for
	the item is '/foo/bar/baz', then '/foo/bar' is the path for the parent
	menu, and 'baz' is the id of the menu with respect to its siblings.
	*The parent menu need not be defined by the same extension which
	defines the menu. That is, one plugin can contribute a sub-menu to
	a menu defined by another plugin.*

group
	The name of the item group defined by the parent menu to which this
	menu item should be added. For a top-level menu item, the empty group
	is automatically implied.

before
	The id of the sibling item before which this menu item should appear.
	The sibling must exist in the same group as this menu item.

after
	The id of the sibling item after which this menu item should appear.
	This sibling must exist in the same group as this menu item.

label
	The text to diplay as the label for the menu.

visible
	Whether or not the menu is visible.

enabled
	Whether or not the menu is enabled.

A MenuItem can define conceptual groups in which other plugins may contribute
other menu items and action items. A group is defined by declaring a child
ItemGroup object on the menu item. The group will appear on screen in the
order in which they were declared. There is an implicit group with an empty
identifier into which all unclassified items are added. The implicit group
will always appear visually last on the screen.

ItemGroup
~~~~~~~~~
The ItemGroup class is a declarative class used to form a logical and
visual group of items in a menu. It is declared as a child of a MenuItem
and provides a concrete advertisement by the author of a MenuItem that it
expects other MenuItem and ActionItem instances to be added to that point
in the Menu.

The ItemGroup class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest:

id
	The identifier of the group within the menu. It must be unique among
	all other group siblings defined for the menu item.

visible
	Whether or not the items in the group are visible.

enabled
	Whether or not the items in the group are enabled.

exclusive
	Whether or not neighboring checkable action items in the group
	should behave as exclusive checkable items.

ActionItem
~~~~~~~~~~
The ActionItem class is used to declare a triggerable item in a menu. It
is declared as a child of a plugin Extension object.

The ActionItem class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest:

path
	A "/" separated path to the location of this item in the menu bar.
	This path must be unique for the menu bar, and the parent path must
	exist in the menu bar. The last token in the path is the id of this
	action item with respect to its siblings. For example, if the path for
	the item is '/foo/bar/baz', then '/foo/bar' is the path for the parent
	menu, and 'baz' is the id of the action with respect to its siblings.
	*The parent menu need not be defined by the same extension which
	defines the action. That is, one plugin can contribute an action to a
	menu defined by another plugin.*

group
	The name of the item group defined by the parent menu to which this
	action item should be added.

before
	The id of the sibling item before which this action item should appear.
	The sibling must exist in the same group as this action item.

after
	The id of the sibling item after which this action item should appear.
	This sibling must exist in the same group as this action item.

command
	The identifier of the Command object which should be invoked when
	this action item is triggered by the user.

parameters
	The dictionary of parameters which should be passed to the command
	when it is invoked.

label
	The text to diplay as the label for the action.

shortcut
	The keyboard shortcut which should be bound to trigger action item.

visible
	Whether or not the action is visible.

enabled
	Whether or not the action is enabled.

checkable
	Whether or not the action is checkable.

checked
	Whether or not the action is checked.

icon
	The icon to display next to the action.

tool_tip
	The tool tip text to display when the user hovers over the action.

status_tip
	The text to display in the status bar when the user hovers over the
	action.

Workspace
~~~~~~~~~
The Workspace class is a declarative class which is used to supply the
central window content for a ui workbench application. It contains the
attributes and method which are necessary for the ui plugin to be able
to dynamically switch workspaces at runtime. The application developer
will typically create a custom workspace class for each one of the views
that will be shown in the workbench.

The Workspace class is declarative to allow the developer to fully
leverage the Enaml language in the course of defining their workspace.
It will typically be declared as the child of any object.

The Workspace class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest:

window_title
	This is text which will be added to the window title *in addition*
	to the title text which is supplied by a branding extension.

content
	This is an Enaml Container widget which will be used as the primary
	window content. It should be created during the workspace 'start'
	method and will be destroyed by the framework automatically when
	the workspace is stopped.

It has the following methods of interest:

start
    This method is called when the UI plugin starts the workspace. This
    can be used to load content or any other resource which should exist
    for the life of the workspace.

stop
    This method is called when the UI plugin closes the workspace. This
    should be used to release any resources acquired during the lifetime
    of the workspace. The content Container will be destroyed automatically
    after this method returns.

Autostart
~~~~~~~~~
The Autostart class is a declarative class which is used to supply the
plugin id for a plugin which should be automatically started on application
startup.

The Autostart class can be imported from `enaml.workbench.ui.api`.

It has the following attributes of interest.

plugin_id
	This is the id of the plugin to start on application startup. The
	manifest for the plugin must be registered before the ui plugin is
	started.

UI Workbench
------------
The UIWorkbench class is a simple sublass of Workbench for creating ui
applications. This class will automatically register the pre-defined
'core' and 'ui' workbench plugins when it is started.

The UIWorkbench class can be imported from `enaml.workbench.ui.api`.

It has the following methods of interest:

run
    This method will load the core and ui plugins and start the
    main application event loop. This is a blocking call which
    will return when the application event loop exits.