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
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="STYLESHEET" href="howto.css" type='text/css' />
<link rel="first" href="howto.html" title='Developing applications with Kiwi' />
<link rel='last' href='about.html' title='About this document...' />
<link rel='help' href='about.html' title='About this document...' />
<link rel="next" href="node21.html" />
<link rel="prev" href="node19.html" />
<link rel="parent" href="node16.html" />
<link rel="next" href="node21.html" />
<meta name='aesop' content='information' />
<title>2.9.4 Customizing Proxies and Models</title>
</head>
<body>
<DIV CLASS="navigation">
<div id='top-navigation-panel' xml:id='top-navigation-panel'>
<table align="center" width="100%" cellpadding="0" cellspacing="2">
<tr>
<td class='online-navigation'><a rel="prev" title="2.9.3 Propagating changes from"
href="node19.html"><img src='previous.png'
border='0' height='32' alt='Previous Page' width='32' /></A></td>
<td class='online-navigation'><a rel="parent" title="2.9 Proxies and Models"
href="node16.html"><img src='up.png'
border='0' height='32' alt='Up One Level' width='32' /></A></td>
<td class='online-navigation'><a rel="next" title="2.9.5 Using Signal Handlers"
href="node21.html"><img src='next.png'
border='0' height='32' alt='Next Page' width='32' /></A></td>
<td align="center" width="100%">Developing applications with Kiwi</td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
</tr></table>
<div class='online-navigation'>
<b class="navlabel">Previous:</b>
<a class="sectref" rel="prev" href="node19.html">2.9.3 Propagating changes from</A>
<b class="navlabel">Up:</b>
<a class="sectref" rel="parent" href="node16.html">2.9 Proxies and Models</A>
<b class="navlabel">Next:</b>
<a class="sectref" rel="next" href="node21.html">2.9.5 Using Signal Handlers</A>
</div>
<hr /></div>
</DIV>
<!--End of Navigation Panel-->
<H3><A NAME="SECTION000294000000000000000">
2.9.4 Customizing Proxies and Models</A>
</H3>
<P>
From our example, proxy and instance appear to be completely coupled -
even the names of the components and attributes are tied. This is a
precise interpretation of the situation, but in my opinion, this
coupling is not only unavoidable, it is the essence of UI architecture.
Treating the interface as a completely separate entity from the object
it manipulates is, honestly, a bad idea, because <B>the interface is a
representation of the object</B>, and as such, essentially coupled to it.
Would it make sense to remove the <code>url</code> attribute from the model
and <I>not</I> remove it from the interface in question?
<P>
Because the UI is really a representation, however, there are times
where the contents of the widget and its attached proxy attribute must
differ in some way. Often, it is a matter of cardinality: more than one
widget defines a single proxy attribute, or vice-versa; at other times,
the data format in the widget does not match the format in the model
(think of dates, represented by strings in the interface, but stored as
DateTime objects in the object)<A NAME="tex2html7"
HREF="#foot294"><SUP>7</SUP></A>. The Kiwi Proxy was
designed to cater to these different requirements, using accessor
functions when available.
<P>
Accessors provide an easy way to translate the model value to the
interface value: a pair of <code>get_*()</code> and <code>set_*()</code> functions
implemented in the model that perform internal manipulation of its
variables, removing the need to directly manipulate the instance
variable. You can define accessors for as few or as many model
attributes you want.
<P>
To make the process clearer, it is worth discussing how the model and
the UI are updated. The heuristics for updating a model are:
<P>
<OL>
<LI>If a change happens in the Proxy's widget <code>X</code>, it looks at
the model attached to it.
</LI>
<LI>If the model offers a <code>set_X()</code> method, it is called, with
the new value as its only parameter.
</LI>
<LI>If not, it manipulates the model directly by using
<code>setattr()</code>, which is the equivalent of <code>model.X = value</code>. If
<code>Proxies.set_attr_warnings(True)</code> has been called, a warning like
the following will be printed:
<P>
<div class="verbatim"><pre>
Kiwi warning: could not find method set_title in model
<__main__.NewsItem instance at 0x82011ac>, using setattr()
</pre></div>
<P>
</LI>
<LI>The model is updated (If multiple proxies are attached to the
model, special things happen, additionally, as you will see in section
<A href="multipleproxies.html#multipleproxies">2.9</A>).
</LI>
</OL>
<P>
The heuristics for updating the interface widget are:
<P>
<OL>
<LI>On startup, the proxy queries the model for the value for
attribute <code>X</code>.
</LI>
<LI>It tries first using an accessor method <code>get_X()</code>
(which should return a single value). If the accessor does not exist, it
will attempt to access the model's variable directly (using
<code>getattr()</code>). As with <code>setattr()</code> above, a warning will be
printed if attribute warnings are enabled.
</LI>
<LI>The interface is updated with this initial value, and normal
event processing begins.
</LI>
<LI>If a model's state for <code>X</code> is altered (<code>item.X =
"foo"</code>), a notification is sent to the proxy, with the attribute that
was altered. If a callback calls <code>self.update("X")</code>, a notification
is sent to the proxy, with the attribute that was altered.
</LI>
<LI>The proxy receives the notification; it gets the value of <code>X</code>
from the model using the same method as in step 2, and updates the
widget contents accordingly.
</LI>
</OL>
<P>
Summarizing: if you would like to customize the connection between model
and proxy for an attribute, implement accessor functions
(<code>get_foo()</code> and <code>set_foo()</code>) for it. If you would like to
verify that no direct instance manipulations are happening, use the
module function <code>set_attr_warnings()</code> and check the output
printed to the console's standard error.
<P>
Let's extend our previous example to provide an accessor and explain how
things work out (<span class="file">newsform3.py</span>).
<P>
<div class="verbatim"><pre>
class NewsItem(FrameWork.Model):
"""An instance representing an item of news.
Attributes: title, author, url"""
def set_url(self, url):
"""sets the url, prefixing "http://" if missing"""
http = "http://"
if len(url) > len(http) and string.find(url, http) != 0:
url = http + url
self.url = url
</pre></div>
<P>
In this example, we provide an accessor for setting the url (a "setter")
prefixed by "http://". The accessor is called when the entry's text
changes (for each character inserted or deleted), which is why I have to
check for the length of the url typed in. Note that I don't provide a
<code>get_url()</code> method (a "getter"), which means the raw url would be
loaded from the instance into the interface. In this specific case, this
is not an issue, because data is only loaded from the instance at
instantiation time and when the model attribute is changed. However, for
most cases both setter and getter need to convert to and from the model
format.
<P>
<BR><HR><H4>Footnotes</H4>
<DL>
<DT><A NAME="foot294">... object)</A><A
HREF="node20.html#tex2html7"><SUP>7</SUP></A></DT>
<DD> Apart from these reasons, some
people insist that it is bad or wrong to manipulate the instance
directly, and that accessors should always be used.
</DD>
</DL>
<DIV CLASS="navigation">
<div class='online-navigation'>
<p></p><hr />
<table align="center" width="100%" cellpadding="0" cellspacing="2">
<tr>
<td class='online-navigation'><a rel="prev" title="2.9.3 Propagating changes from"
href="node19.html"><img src='previous.png'
border='0' height='32' alt='Previous Page' width='32' /></A></td>
<td class='online-navigation'><a rel="parent" title="2.9 Proxies and Models"
href="node16.html"><img src='up.png'
border='0' height='32' alt='Up One Level' width='32' /></A></td>
<td class='online-navigation'><a rel="next" title="2.9.5 Using Signal Handlers"
href="node21.html"><img src='next.png'
border='0' height='32' alt='Next Page' width='32' /></A></td>
<td align="center" width="100%">Developing applications with Kiwi</td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
<td class='online-navigation'><img src='blank.png'
border='0' height='32' alt='' width='32' /></td>
</tr></table>
<div class='online-navigation'>
<b class="navlabel">Previous:</b>
<a class="sectref" rel="prev" href="node19.html">2.9.3 Propagating changes from</A>
<b class="navlabel">Up:</b>
<a class="sectref" rel="parent" href="node16.html">2.9 Proxies and Models</A>
<b class="navlabel">Next:</b>
<a class="sectref" rel="next" href="node21.html">2.9.5 Using Signal Handlers</A>
</div>
</div>
<hr />
<span class="release-info">Release 1.9.22, documentation updated on August, 2006.</span>
</DIV>
<!--End of Navigation Panel-->
</BODY>
</HTML>
|