<html> <head> <title>Developer's Guide: Message Passing</title> </head> <body bgcolor=white text=black link=blue vlink=navy alink=red> <TABLE WIDTH="100%"> <TR> <TH ALIGN="left" WIDTH="33%"><img SRC="Images/arrow-left.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Prev"></TH> <TH ALIGN="center" WIDTH="33%"><img SRC="Images/arrow-up.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Up"></TH> <TH ALIGN="right" WIDTH="33%"><img SRC="Images/arrow-right.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Next"></TH> </TR> <TR> <TD ALIGN="left"><A HREF="devguide-25.html">User Interface </A></TD> <TD ALIGN="center"><A HREF="devguide.html">Developer's Guide</A></TD> <TD ALIGN="right"><A HREF="devguide-27.html">File Format</A></TD> </TR> </TABLE> <HR NOSHADE> <H1><FONT face="Helvetica,Arial"><A NAME="N1"></A>Message Passing</font></H1> <P>In a Model-View-Controller architecture like the one used in Sketch, the model has to inform the views when the data has changed and the views have to be updated. Often there can be several views for one model object and it is desirable to implement the </P> <P>The connector provides a generic message passing mechanism similar to the signals used in Qt or GTK.</P> <H2><FONT face="Helvetica,Arial"><A NAME="N2"></A>Concepts</font></H2> <P><EM>Messages</EM> are generated by <EM>publishers</EM> and consist of a <EM>message type</EM> represented by a string and an arbitrary number of arguments. The message type is also called a <i>channel</i>.</P> <P>Program parts that want to receive messages subscribe to a channel of a particular publisher by passing a callable object, often a bound instance method, and optionally some additional arguments to the publisher.</P> <P>When the publisher issues a message all subscribers of the relevat channel of the publisher all called with the message arguments and the additional subscriber specific arguments.</P> <P>The heart of this mechanism is the <A HREF="#N4"><tt>Connector</tt></A> class, whose instances manage all connections between publishers and subscribers. This way, a publisher that has no subscribers does not use any resources.</P> <P>The connector module has one global <tt>Connector</tt> instance and some of its methods are available as globals.</P> <P>There are two additional classes for publisher objects. <A HREF="#N11"><tt>Publisher</tt></A> provides methods to subscribe to and unsubscribe from channels of the object. <A HREF="#N18"><tt>QueueingPublisher</tt></A> allows the publisher to accumulate messages in a queue with identical messages queued only once and release them all at once.</P> <P>Most objects that send messages are derived from one of these publisher objects and in most circumstances their methods are used to connect to their channels. The connector is rarely used directly.</P> <H2><FONT face="Helvetica,Arial"><A NAME="N3"></A>The Class <A NAME="N4"></A><tt>Connector</tt></font></H2> <P> <DL> <DT><B><A NAME="N5"></A><tt>Connect(<i>publisher</i>, <i>channel</i>, <i>subscriber</i>, <i>args</i>)</tt></B><DD> <P>Connect the <i>subscriber</i>, i.e. a callable object and additional arguments <i>args</i>, to a <i>channel</i> of a <i>publisher</i>.</P> <P>The <i>publisher</i> can be any object, though most of the time it's a <A HREF="#N11"><tt>Publisher</tt></A> instance. Sketch uses the <CODE>None</CODE> object as a publisher for global messages.</P> <P><i>args</i> are subscriber specific arguments as a tuple.</P> <P>The same subscriber may be registered multiple times but it is only stored once in the list of subscribers. Two subscribers are considered equal if the callable object and the arguments are equal.</P> <DT><B><A NAME="N6"></A><tt>Disconnect(<i>publisher</i>, <i>channel</i>, <i>subscriber</i>, <i>args</i>)</tt></B><DD> <P>Disconnect <i>subscriber</i> from <i>publisher</i>'s <i>channel</i>.</P> <P>The arguments have the same meaning as in <A HREF="#N5"><tt>Connect</tt></A> and again the combination of <i>subscriber</i> and <i>args</i> is used to identify one particular subscriber.</P> <P>If the subscriber isn't connected to the channel, raise a <CODE>ConnectorError</CODE> exception.</P> <DT><B><A NAME="N7"></A><tt>Issue(<i>publisher</i>, <i>channel</i>, *<i>args</i>)</tt></B><DD> <P>Issue a message on a <i>channel</i> of <i>publisher</i>.</P> <P>The third and all following arguments are passed through to all subscribers as message arguments. The arguments the subscribers are called with is the concatenation of the message arguments and the subscriber specific arguments.</P> <DT><B><A NAME="N8"></A><tt>RemovePublisher(<i>publisher</i>)</tt></B><DD> <P>Remove all subscribers from all channels of <EM>publisher</EM>. It is safe to call this in a __del__ method.</P> <DT><B><A NAME="N9"></A><tt>HasSubscribers(<i>publisher</i>)</tt></B><DD> <P>Return true iff <i>publisher</i> has any subscribers.</P> </DL> Note that the connector does not hold references to the publisher. Publishers are identified by their id. This makes it possible to automatically disconnect all subscribers of a publisher in a publisher's __del__ method. This does not work however, if e.g. the publisher is itself a subscriber because the connector has to own the references to the subcribers.</P> <H2><FONT face="Helvetica,Arial"><A NAME="N10"></A>The Class <A NAME="N11"></A><tt>Publisher</tt></font></H2> <P>The class <tt>Publisher</tt> serves as a base class for classes that send messages. It provides convenience methods to subscribe to and unsubscribe from channels. It also removes all subscriptions automatically when instances are deleted through the <CODE>__del__</CODE> method.</P> <P> <DL> <DT><B><A NAME="N12"></A><tt>Subscribe(<i>channel</i>, <i>subscriber</i>, *<i>args</i>)</tt></B><DD> <P>Subscribe <i>subscriber</i> to self's <i>channel</i> with the rest of the positional arguments as the subscriber specific parameters. See <A HREF="#N5"><tt>Connect</tt></A> for more details.</P> <DT><B><A NAME="N13"></A><tt>Unsubscribe(<i>channel</i>, <i>subscriber</i>, *<i>args</i>)</tt></B><DD> <P>Unsubscribe <i>subscriber</i> to self's <i>channel</i> with the rest of the positional arguments as the subscriber specific parameters. See <A HREF="#N6"><tt>Disconnect</tt></A> for more details.</P> <DT><B><A NAME="N14"></A><tt>issue(<i>channel</i>, *<i>args</i>)</tt></B><DD> <P>If the instance variable <tt>ignore_issue</tt> is false, issue the message described by the second and the following arguments on channel <i>channel</i>, otherwise, do nothing.</P> <P><tt>ignore_issue</tt> defaults to false. <tt>ignore_issue</tt> will be removed in 0.8.</P> <DT><B><A NAME="N15"></A><tt>Destroy()</tt></B><DD> <P>Remove all subscribers of all of self's channels. See <A HREF="#N8"><tt>RemovePublisher</tt></A> for more details.</P> <DT><B><A NAME="N16"></A><tt>__del__()</tt></B><DD> <P>Remove all subscribers of all of self's channels. See <A HREF="#N8"><tt>RemovePublisher</tt></A> for more details.</P> </DL> </P> <H2><FONT face="Helvetica,Arial"><A NAME="N17"></A>The Class <A NAME="N18"></A><tt>QueueingPublisher</tt></font></H2> <P>Sometimes it's useful to put messages in a queue and issue them all in one go after a complex operation is finished. The class <tt>QueueingPublisher</tt> provides such a queue and the following methods:</P> <P> <DL> <DT><B><A NAME="N19"></A><tt>queue_message(<i>channel</i>, *<i>args</i>)</tt></B><DD> <P>Put message for channel <i>channel</i> in the queue. The rest of the arguments are treated just as in <A HREF="#N14"><tt>issue</tt></A></P> <P>If the messge is already queued remove it and put it at the end. This is done to make certain that no channel gets called twice between two calls to flush_message_queue. If the order of channel invocation is important two or more queues should be used.</P> <P>Note, that this can be very inefficient if a lot of different messages are queued before the queue is flushed because each new message is compared with all messages in the queue sequentiually. A dictionary could speed this up considerably if the order in which the messages are issued isn't important and if the messages can be used as dictionary keys. Especially the latter is not always the case, so this isn't done by defalt.</P> <DT><B><A NAME="N20"></A><tt>flush_message_queue()</tt></B><DD> <P>Issue all queued messages until the queue is empty. The process is repeated because issueing messages might result in new messages being queued. This can theoretically result in an infinite loop.</P> <DT><B><A NAME="N21"></A><tt>clear_message_queue()</tt></B><DD> <P>Discard all queued messages.</P> </DL> </P> <HR NOSHADE> <TABLE WIDTH="100%"> <TR> <TD ALIGN="left"><A HREF="devguide-25.html">User Interface </A></TD> <TD ALIGN="center"><A HREF="devguide.html">Developer's Guide</A></TD> <TD ALIGN="right"><A HREF="devguide-27.html">File Format</A></TD> </TR> <TR> <TH ALIGN="left" WIDTH="33%"><img SRC="Images/arrow-left.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Prev"></TH> <TH ALIGN="center" WIDTH="33%"><img SRC="Images/arrow-up.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Up"></TH> <TH ALIGN="right" WIDTH="33%"><img SRC="Images/arrow-right.png" WIDTH="16" HEIGHT="16" ALIGN="top" ALT="Next"></TH> </TR> </TABLE> </body> </html>