root/gaphor-doc/trunk/gaphor-internals.xml

Revision 742, 16.9 kB (checked in by wrobell, 3 years ago)

- do not copy diagrams into html without their directory hierarchy
- convert into png using svg2png instead of rsvg
- keep svg files with manual
- list of examples css stylesheets improvements
- save some of diagrams with new version of gaphor

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?xml version="1.0"?>
2 <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
3     "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
4 >
5 <chapter id="chap-internals">
6   <title>Working with <application>Gaphor</application>'s internals</title>
7
8   <para>As stated before, <application>Gaphor</application> is completely
9   written in <ulink
10   url="http://www.python.org"><application>Python</application></ulink>. Well,
11   completely... It makes use of the <ulink
12   url="http://www.gnome.org">GNOME2</ulink> desktop environment. GNOME has
13   powerful bindings for <application>Python</application>.</para>
14
15   <section id="sect-datamodel">
16     <title>Data model</title>
17
18     <para>Lots of effort has been put in the creation of an easy to use and
19     extensible data model. The data model supports:
20       <orderedlist>
21         <listitem><para>attributes, such as strings and integers</para></listitem>
22         <listitem><para>associations, both uni- and bi-directional</para></listitem>
23         <listitem><para>enumerations</para></listitem>
24         <listitem><para>derived unions and redefined values</para></listitem>
25       </orderedlist>
26     These items will be refered to as <emphasis>properties</emphasis>.
27     </para>
28
29     <para>When a property changes, a signal is be emited. Other objects can
30     attach signal handlers to the property owners (data objects) in order to
31     be notified.</para>
32
33     <note><simpara>When a property emits a signal, all redefine properties and derived
34     unions also send a signal.</simpara></note>
35
36     <para><application>Gaphor</application>'s data model is build from properties. This makes it easy to
37     see which properties can be set on a model element and which can not.
38     </para>
39
40     <section>
41       <title>Working with the data model</title>
42
43       <para>The data model is designed so it is easy to retrieve all kinds of
44       information from the data model. One can retrieve all possible properties
45       of a class by simply typing (output is slightly modified to remove default
46       properties):
47         <screen>
48           &gt;&gt;&gt; dir(Class)
49           ['connect', 'attribute', 'clientDependency', 'disconnect', 'elementImport',
50           'feature', 'general', 'generalization', 'importedMember',
51           'inheritedMember', 'isAbstract', 'isKindOf', 'isLeaf', 'isTypeOf',
52           'load', 'member', 'name', 'namespace', 'nestedClassifier', 'notify',
53           'ownedAttribute', 'ownedComment', 'ownedElement', 'ownedMember',
54           'ownedOperation', 'ownedRule', 'ownedUseCase', 'owner', 'package',
55           'packageImport', 'postload', 'redefinedClassifier', 'redefinedElement',
56           'redefinitionContext', 'save', 'substitution', 'superClass',
57           'supplierDependency', 'unlink', 'visibility']
58         </screen>
59       Besides properties also a few default methods can be found:
60         <variablelist>
61           <varlistentry>
62             <term>connect</term>
63             <listitem><para>Attach (connect) a signal handler to an
64             instance</para></listitem>
65           </varlistentry>
66           <varlistentry>
67             <term>disconnect</term>
68             <listitem><para>Detach (disconnect) a signal handler.
69             </para></listitem>
70           </varlistentry>
71           <varlistentry>
72             <term>notify</term>
73             <listitem><para>Notify all objects that attached to a specific
74             signal. This is automatically done when a property changes.
75             </para></listitem>
76           </varlistentry>
77           <varlistentry>
78             <term>isKindOf</term>
79             <listitem><para>OCL method. Same as <literal>isinstance()</literal>.
80             </para></listitem>
81           </varlistentry>
82           <varlistentry>
83             <term>isTypeOf</term>
84             <listitem><para>Check if the <literal>type()</literal> is equal.
85             </para></listitem>
86           </varlistentry>
87           <varlistentry>
88             <term>load</term>
89             <listitem><para>Load a given name-value pair. This is used for
90             loading a model.
91             </para></listitem>
92           </varlistentry>
93           <varlistentry>
94             <term>postload</term>
95             <listitem><para>Postload is called after all information is loaded.
96             </para></listitem>
97           </varlistentry>
98           <varlistentry>
99             <term>save</term>
100             <listitem><para>Save a name-value pair. This is used to serialize
101             (save) the object.</para></listitem>
102           </varlistentry>
103           <varlistentry>
104             <term>unlink</term>
105             <listitem><para>End the life cycle of an object. Unlink removed all
106             relations with the object.
107             </para></listitem>
108           </varlistentry>
109         </variablelist>
110       Exept for those methods, all other fields are properties of the data
111       object.
112       </para>
113
114       <para>It is pretty easy to find out what type of object could be
115       connected to an attribute:
116         <screen>
117           &gt;&gt;&gt; print Class.name
118           &lt;attribute name: &lt;type 'str'&gt;[0..1] = None&gt;
119         </screen>
120       </para>
121       <para>An association displays similar results:
122         <screen>
123           &gt;&gt;&gt; print Class.ownedAttribute
124           &lt;association ownedAttribute: Property[0..*]&gt;
125         </screen>
126       This shows the definition of <property>Class.ownedAttribute</property>. It
127       has a uni-directional to zero or more <classname>Property</classname>
128       objects.
129       The following code shows a bi-directional association:
130         <screen>
131           &gt;&gt;&gt; print Class.generalization
132           &lt;association generalization: Generalization[0..*] -&gt; specific&gt;
133           &gt;&gt;&gt; print Generalization.specific
134           &lt;association specific: Classifier[1] -&gt; generalization&gt;
135         </screen>
136       </para>
137
138       <para>One of the more advanced features of the data model are derived
139       unions (which show a <literal>/</literal> before their name and
140       redefined properties (which carry a <literal>{redefine}</literal> tag).
141       A derived union is a gathering of one or more other properties. a
142       redefined property is some sort of alias.  In the following example a
143       <classname>Class</classname> object is added to a
144       <classname>Package</classname>.
145         <screen>
146           &gt;&gt;&gt; p = Package()
147           &gt;&gt;&gt; c = Class()
148           &gt;&gt;&gt; c.package = p
149           &gt;&gt;&gt; c.package
150           &lt;uml2.Package object at 0x00815020&gt;
151           &gt;&gt;&gt; c.owner
152           &lt;uml2.Package object at 0x00815020&gt;
153           &gt;&gt;&gt; c.namespace
154           &lt;uml2.Package object at 0x00815020&gt;
155           &gt;&gt;&gt; p.ownedClassifier
156           [&lt;uml2.Class object at 0x008166F0&gt;]
157           &gt;&gt;&gt; p.ownedElement
158           [&lt;uml2.Class object at 0x008166F0&gt;]
159           &gt;&gt;&gt; p.ownedMember
160           [&lt;uml2.Class object at 0x008166F0&gt;]
161         </screen>
162       Besides <property>package</property> also the
163       <property>namespace</property> and <property>owner</property> properties
164       of the <classname>Class</classname> are set. Well, they are not actually
165       set, they are derived values from <property>c.package</property>.  The
166       same thing happens for <property>Package.ownedClassifier</property> (the
167       opposite side of <property>package</property>).</para>
168     </section>
169
170     <section>
171       <title>Extensions to the data model</title>
172
173       <para>A few changes have been made to <application>Gaphor</application>s implementation of
174       the Metamodel. First of all some relationships have to be modified
175       since the same name is used for different relationships.
176       These are all small changed and
177       should not restrict the usability of <application>Gaphor</application>s model.</para>
178
179       <para>The biggest change is the addition of a whole new class:
180       <classname>Diagram</classname>.
181       <classname>Diagram</classname> is inherited from
182       <classname>Namespace</classname> and is used to hold a
183       diagram.  It contains a <classname>diacanvas.Canvas</classname> object
184       which can be displayed on screen by a <classname>DiagramView</classname>
185       class.</para>
186
187     </section>
188 <!--
189     <figure id="fig-uml" float="1">
190       <title>gaphor.UML</title>
191       <graphic fileref="uml.png"/>
192     </figure>
193  -->
194   </section>
195
196   <section>
197     <title>User interface</title>
198
199     <para>For the user interface (UI), <application>Gaphor</application> makes use of the GTK+ 2.0 libraries.
200     All UI stuff is found in the <classname>gaphor.ui</classname> module.
201     </para>
202
203     <para>All windows are inherited from
204     <classname>AbstractWindow</classname>. This class provides some basic
205     stuff such as window construction and popup menu handling. The classes
206     that are inherited from <classname>AbstractWindow</classname> all use
207     the <classname>gtk.Window</classname> and provide some content of
208     themselves. Each <classname>AbstractWindow</classname> manages a set of
209     <classname>Action</classname>s. Those actions are kept in an
210     <classname>ActionPool</classname>.</para>
211
212     <para><classname>AbstractWindow</classname> also provides an
213     interface for all windows. A window can have three states: INIT, ACTIVE
214     and CLOSED. If a new <classname>AbstractWindows</classname>
215     deriviate is instantiated it has the
216     state INIT. The <function>construct()</function> method is called to
217     create the actual window on the screen, the state changes to ACTIVE. You
218     can now invoke all operations on the window (such as getting the real
219     ui components and setting a message in the status bar). The last state,
220     CLOSED, is reached when the <function>close()</function> method has
221     been called. The UI components are destroyed and the object is basically
222     useless.</para>
223
224     <para>Note that the <classname>AbstractWindow</classname> is itself no a
225     ui component, it contains the graphical objects and observes their
226     activities. It is a Mediator.</para>
227    
228     <figure id="fig-ui-uml" float="1">
229       <title>gaphor.ui</title>
230       <graphic fileref="Diagrams/Gaphor/ui/ui.png"/>
231     </figure>
232
233     <para>As of this writing three windows are provided: a main window,
234     an editor window and a console window. The main window is what you get when
235     <application>Gaphor</application> is started (see <xref linkend="chap-usage"/>).</para>
236
237     <para>Diagrams are shown in the main window, in a notebook (tab). Each tab
238     contains a <classname>DiagramView</classname> object, which is a
239     specialized version of a <classname>DiaCanvasView</classname> (which in
240     his turn is a subclass of a <classname>GnomeCanvas</classname>). The tabs,
241     the currently shown tab, diagram and view can be queried through the
242     <classname>MainWindow</classname> interface.</para>
243    
244     <para>One of the more neat features are the Console window and the Editor
245     window. They allow you to run python scripts directly within
246     <application>Gaphor</application>. This
247     is also a nice way to try new peaces of code and debug the data model.
248     </para>
249
250     <section>
251       <title>Actions</title>
252
253       <para>Every (well almost every) action that can be done in
254       <application>Gaphor</application> is
255       represented by an Action
256       (<classname>gaphor.misc.action.Action</classname>). An action is created
257       in the context of a window (<classname>AbstractWindow</classname>) and
258       represents an action that can be executed through the user interface
259       (for example: there is an action that opens a model, an action that
260       switches the grid on a diagram, etc.).</para>
261      
262       <para>When you create a plugin you should register actions.</para>
263     </section>
264   </section>
265
266   <section id="sect-plugin">
267     <title>Plugins</title>
268     <para>As of version 0.5.0, <application>Gaphor</application> provides some code for plugins. They are
269     loaded on application startup (as <application>Python</application>
270     modules.</para>
271
272     <para>Plugins can be stored in two places:
273       <variablelist>
274         <varlistentry>
275           <term><filename>${datadir}/plugins</filename></term>
276           <listitem>
277             <para>The system wide location. <literal>${datadir}</literal> is
278             usually something like <filename>/usr/share/gaphor</filename> or
279             <filename>/usr/local/share/gaphor</filename>.
280             </para>
281           </listitem>
282         </varlistentry>
283         <varlistentry>
284           <term><filename>${HOME}/.gaphor/plugins</filename></term>
285           <listitem>
286             <para>Plugins can also be stored in a directory in the users home
287             directory.
288             </para>
289           </listitem>
290         </varlistentry>
291       </variablelist>
292     </para>
293     <para>A plugin consists of a directory with at least the following files
294     present:
295       <variablelist>
296         <varlistentry>
297           <term><filename>plugin.xml</filename></term>
298           <listitem>
299             <para>This is an XML file that contains a description of the
300             plugin as well as a list of prerequisites and provided actions.
301             Requirements can be either modules (e.g.
302             <literal>os.path</literal>) or other plugins that a plugin depends
303             on. The provided actions are shown as menu items in the
304             application.</para>
305           </listitem>
306         </varlistentry>
307         <varlistentry>
308           <term><filename>__init__.py</filename></term>
309           <listitem>
310             <para>This file is required by <application>Python</application>,
311             otherwise it wouldn't be recognised as a module (the plugin is
312             loaded as a module).</para>
313           </listitem>
314         </varlistentry>
315       </variablelist>
316     </para>
317
318
319     <para>Before a plugin is loaded <filename>plugin.xml</filename> is read
320     which contains some definitions for the plugin:
321       <orderedlist>
322         <listitem>
323           <para>Information such as author, version, and of course the name.
324           </para>
325         </listitem>
326         <listitem>
327           <para>Requirements: other plugins this one depends on, modules.
328           </para>
329         </listitem>
330         <listitem>
331           <para>definitions of <classname>Action</classname>s.
332           </para>
333         </listitem>
334         <!--
335         <listitem>
336           <para>Code that should be ran before and after installation, before
337           and after removal.
338           </para>
339         </listitem>
340         -->
341       </orderedlist>
342     </para>
343
344     <para>A typical <filename>plugin.xml</filename> file looks like this:
345     <programlisting>
346 <![CDATA[<?xml version="1.0"?>
347 <plugin name="UML metamodel sanity check"
348         version="0.1"
349         author="Arjan Molenaar">
350   <description>
351     A description of what this thing does.
352   </description>
353
354   <require>
355     <!--
356       Define modules and plugins that are needed for this plugin to function
357       properly.
358     -->
359     <module name="os.path"/>
360     <plugin name="anotherPlugin"/>
361   </require>
362
363   <provide>
364     <!--
365       Actions should be defined on the module's toplevel (like in __init__.py).
366     -->
367     <action id="MyPlugin"
368             label="Do a typical thing with the plugin"
369             icon-file="myicon.png"
370             tooltip="bla bla"
371             class="MyPluginAction" slot="WindowSlot">
372       <!--
373         Add optional dependencies to this action. The action is then updated
374         when actions defined in the depends tag are executed.
375       -->
376       <depends action="ItemFocus"/>
377     </action>
378   </provide>
379 </plugin>]]>
380       </programlisting>
381     </para>
382
383     <para>A plugin contains three sections:
384       <variablelist>
385         <varlistentry>
386           <term>description</term>
387           <listitem>
388             <para>A description of the plugin (could be shown in a plugin
389             browser for example) This is just a text field.</para>
390           </listitem>
391         </varlistentry>
392         <varlistentry>
393           <term>require</term>
394           <listitem>
395             <para>The require section can contain modules and plugins that are
396             needed for this plugin to work.</para>
397           </listitem>
398         </varlistentry>
399         <varlistentry>
400           <term>provide</term>
401           <listitem>
402             <para>Actions that are provided by this plugin.</para>
403           </listitem>
404         </varlistentry>
405       </variablelist>
406     </para>
407
408     <para>The <literal>provide</literal> section contains the actions that can
409     be added to the application.  An action is a
410     <application>Python</application> class that extends
411     <classname>gaphor.plugin.Action</classname> (or
412     <classname>CheckAction</classname> for checkbox actions or
413     <classname>RadioAction</classname> for radiobutton actions). An action has
414     an:
415       <variablelist>
416         <varlistentry>
417           <term><literal>id</literal></term>
418           <listitem>
419             <para>Id is a unique identifier for the action.
420             </para>
421           </listitem>
422         </varlistentry>
423         <varlistentry>
424           <term><literal>label</literal></term>
425           <listitem>
426             <para>A label that is shown in the menu.
427             </para>
428           </listitem>
429         </varlistentry>
430         <varlistentry>
431           <term><literal>tooltip</literal></term>
432           <listitem>
433             <para>A short description of the action that can be displayed in a
434             status bar or as a tooltip.
435             </para>
436           </listitem>
437         </varlistentry>
438         <varlistentry>
439           <term><literal>icon-file</literal></term>
440           <listitem>
441             <para>A file containing a nice (24x24) image, preferbly a PNG image.
442             </para>
443           </listitem>
444         </varlistentry>
445         <varlistentry>
446           <term><literal>class</literal></term>
447           <listitem>
448             <para>The <classname>Action</classname> class to load from the
449             module. The <classname>Action</classname> class should be visible
450             through <filename>__init__.py</filename> (class names like
451             <classname>test.TestAction</classname> do not work).</para>
452           </listitem>
453         </varlistentry>
454         <varlistentry>
455           <term><literal>slot</literal></term>
456           <listitem>
457             <para>Slots are predefined places in a menu where new actions can
458             be added. For a list of slots take a look at the documentation of
459             <application>Gaphor</application>.</para>
460           </listitem>
461         </varlistentry>
462       </variablelist>
463     </para>
464   </section>
465 </chapter>
466 <!-- vi:sw=2:tw=78:tabstop=2:sts=2
467 -->
Note: See TracBrowser for help on using the browser.