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
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<meta name="author" content="Dan Shafer" />
<link rel="stylesheet" href="PythonCard.css" type="text/css" />
<title>PythonCard Walk-Through No. 2</title>
</head>
<body>
<div id="banner">
<h1>PythonCard Documentation</h1>
</div>
<?php include "sidebar.php" ?>
<div id="content">
<h1>Creating a Complete PythonCard Application from Scratch</h1>
<h4>by Dan Shafer (<a href="mailto:pydan@danshafer.com">pydan@danshafer.com</a>)</h4>
<h3>Background</h3>
<p>This is the second in a series of PythonCard walk-through documents. It
assumes you have either read and worked your way through
<a href="walkthrough1.html">PythonCard Walk-Through No. 1</a> which teaches
you the basics of working in the PythonCard environment or that you already
understand those basics.</p>
<h3>Purpose and Scope</h3>
<p>The purpose of this walk-through is to walk you, step by step, through the
process of creating a PythonCard application. By the time you have finished
this walk-through, you will:</p>
<ul>
<li>understand how PythonCard's GUI building facilities work</li>
<li>know how to create a starting point for a new PythonCard application</li>
<li>have a grasp of the basic event-handling architecture that gives
PythonCard applications life</li>
<li>know how to create menus and menu items in PythonCard and to hook those
items up to actions</li>
</ul>
<p>Over the years as I've played with various GUI tools and development
environments, I have traditionally created a simple counter application as a
way of getting familiar with the tool's basic operation and gaining some
nodding acquaintance with its language and architecture/API. When PythonCard
appeared in my life courtesy of my old colleague Kevin Altis, I decided that
I should do the same. Kevin encouraged me to document my efforts and this is
the result.</p>
<p>This document walks you step by step through the process of creating an
intentionally simplistic PythonCard application. I don't claim that this is
the best way to accomplish this objective, let alone the only one. It just
happens to be the way I approached it. At the end of this tutorial, I will
make a few observations about other things that I could have done that would
make the example more instructive or interesting. Note that this tutorial
describes how this process is handled in PythonCardPrototype 0.6.4.
Continuing enhancements to the UI, especially in the resourceEditor, will
make the process more and more streamlined over time.</p>
<h3>The Application</h3>
<p>This simple application, the finished result of which is shown in Figure 1,
consists of three buttons and a text field. The text field holds a numeric
value (represented as a string) which is manipulated by the three buttons.
The buttons add 1 to the current value displayed in the field, subtract 1
from that field's value, and reset the field's value to 0.</p>
<p class="imageCaption"><img src="images/wt2fig1.png" alt="Finished Counter Application" width="200" height="158" /><br />
Figure 1. Finished Counter Walk-Through Application</p>
<h3>The Application Creation Process</h3>
<p>Creating an application in PythonCard begins with the creation of a basic
structure. There are two ways to do this. One is to use the PythonCard
Resource Editor (called resourceEditor.py), start with the basic empty
window, and code from scratch. The other is to copy an existing application's
folder, rename a few basic things, and begin with a somewhat more complete
starting point. I'm taking the latter course here.</p>
<h3>The process in summary</h3>
<ol type="A">
<li>Run resourceEditor to create or modify an existing application.</li>
<li>Lay out the application's window in Resource Editor. </li>
<li>Script the components that will trigger actions (buttons and/or menus)</li>
<li>Cleaning up artifacts of the copied program. Let's go through those
steps with my simple Counter tutorial.</li>
</ol>
<h4>A. Run resourceEditor to modify an existing application. </h4>
<ol>
<li>Make a copy of the "minimal" project folder in the samples
folder of the PythonCard distribution. Put into its own folder called
"counter." (The folder name isn't important to PythonCard.)</li>
<li>Rename "minimal.py" to "counter.py" and "
minimal.rsrc.py" to "counter.rsrc.py."</li>
<li>Launch resourceEditor, which is found in the PythonCard distribution's
tools folder</li>
<li>Open the file counter.rsrc.py in the folder you just created. The
window looks identical to the minimal application when it is running,
except for the menu bar which remains the resourceEditor's menu bar since
we are running resourceEditor at the moment) rather than the Counter
application's menu bar. Figure 2 depicts this start-up situation.
resourceEditor is a "live" editor; the application is running
while you edit it.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig2.png" alt="Startup Screen for Walk-Through" width="199" height="101" /><br />
Figure 2. Startup Screen for Walk-Through Counter Application</p>
<h4>B. Laying out the window for the counter tutorial application.</h4>
<ol>
<li>Select the text field containing the words "Hello PythonCard"
by clicking anywhere in it. </li>
<li>In the Property Editor window, select the "text" property in
the right-hand list of properties for the field1 Text Field object. Select
the words in the field and delete them with the Backspace key.</li>
<li>Type the number "42" (or some other number; I just happen to
be a Douglas Adams fan) into the box.</li>
<li>Click Update.</li>
<li>In the Property Editor window, select the "font" property in
the right-hand list of properties for the field1 Text Field object. Click
on the "Font" button and set the font size to 24. [Note that
there is a bug in the GTK version of wxPython 2.3.2.1 that prevents the
font from being changed. This will be fixed in the next release of
wxPython.]</li>
<li>Click Update.</li>
<li>Use the resize handles to shape the field so that the entire value
"42" shows. Then position the field near the right edge of the
window and approximately centered vertically. (You will probably need to
resize the window itself; use the same technique for doing so on your
system as you'd use for any window.)</li>
<li>Select the "editable" property of the Text Field object
called field1 and uncheck the checkbox. (By making the field read-only, we
avoid the necessity of error-checking that would be required if we let the
user enter a value directly into the field.) </li>
<li>Click Update.</li>
<li>Select Save from the File menu to save the counter.rsrc.py resource
file.</li>
<li>From the "Components" menu, select "Add Button"</li>
<li>In the Property Editor window, select the "name" property in
the right-hand list of properties for the Button1 Button object. Change the
default name to incrBtn (for "increment button")</li>
<li>Click Update.</li>
<li>In the Property Editor window, select the "label" property in
the right-hand list of properties for the incrBtn Button object. Change the
label from Button1 to Increment.</li>
<li>Click Update.</li>
<li>Position this button in the upper left portion of the window.</li>
<li>Repeat steps 11-15, but this time change the default name to decrBtn
(for "decrement button" and the label from Button1 to Decrement.
Remember to click the "Update" button in the Property Editor
after setting each property.</li>
<li>Position the Decrement button below the Increment button, approximately
in the middle of the window.</li>
<li>Repeat steps 11-15 one more time. Change the default 'name' to
'resetBtn' and the 'label' to 'Reset'.</li>
<li>Position the Reset button to the bottom of the vertical row of three
buttons.</li>
<li>Save your work.</li>
</ol>
<p>Your project should now look like Figure 3.</p>
<p class="imageCaption"><img src="images/wt2fig3.png" alt="Project With Buttons Added" width="200" height="158" /><br />
Figure 3. Project With Buttons Added</p>
<h4>C. Scripting the Buttons</h4>
<p>Application scripts are stored in the Python (.py) file that represents
the application. In this case, that means they are in the file counter.py.</p>
<ol>
<li>Using PythonCard's built-in codeEditor or your favorite Python code
editor, open the file counter.py. It is a small file with a self-explanatory
comment and only one event-handling script right now (which responds to the
user selecting Exit from the File menu). codeEditor is found in the tools
directory of your PythonCard distribution.</li>
<li>Position your cursor below the last line of the <span class="code">on_menuFileExit_select(self, event):</span> handler.</li>
<li>Enter the following script, remembering that Python is white-space
aware so that indentations of lines are significant.</li>
</ol>
<p class="code"> def on_incrBtn_mouseClick(self, event):<br />
startValue = int(self.components.field1.text)<br />
endValue = startValue + 1<br />
self.components.field1.text
= str(endValue)</p>
<p>Let's examine this script because the others we will write are all but
identical.</p>
<p>The opening line of a PythonCard event handler always starts with the
keyword "def" which is standard Python for function and method
definition. The next expression in the line starts with the PythonCard
keyword "on_" and is followed by the name of the component we are
scripting. In this case, it's the Increment Button, whose name is "
incrBtn". After another connecting underscore, the last portion of the
handler definition line defines the event for which this handler is to be
called. All events take the same basic set of parameters as shown above.</p>
<p>The next lines are simple Python for the most part. The first line defines
a variable called "startValue" to which we assign the current
contents of the field, coerced to an integer so we can perform arithmetic on
it. The second line adds one to the value we just retrieved. The third line
assigns this new result to the field's text property after coercing it to a
string.</p>
<ol start="4">
<li>As long as we're in the application's main code file, let's also make
our program a little more internally consistent. Change the name of the
class we're creating from Minimal to Counter. At the end of the file,
replace "Minimal" with "Counter" in the line that
begins "app = ". The result should look like Figure 4.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig4.png" alt="Code Changes in counter.py" width="508" height="502" /><br />
Figure 4. Code Changes in counter.py</p>
<ol start="5">
<li>Save your work.</li>
<li>From the resourceEditor's File menu, select "Run."</li>
<li>When the Counter application appears, click on the Increment button and
watch the displayed value in the text field to confirm that it is
incrementing as expected.</li>
<li>Exit the application.</li>
<li>Back in your Python Editor, copy the function we just created for the
Increment button and paste it under that function, being sure indentation
remains correct.</li>
<li>Edit the new handler to change the name of the button from incrBtn to
decrBtn and the '+' sign to a minus (' - ') sign.</li>
<li>Create a final handler for the Reset button that looks like this:</li>
</ol>
<p class="code"> def on_resetBtn_mouseClick(self, event):<br />
self.components.field1.text = "0"</p>
<p>Figure 5 shows you what your editor window should look like now.</p>
<p class="imageCaption"><img src="images/wt2fig5.png" alt="Editor Showing Final Code Changes" width="521" height="619" /><br />
Figure 5. Editor Showing Final Code Changes</p>
<ol start="12">
<li>Save your work and test the application again to be sure it still
works.</li>
</ol>
<p>What if the application doesn't run? In that case, you can use the "
Run with interpreter" command under the "File" menu to get a
look at what errors, if any, are occurring. To see this in action, let's
introduce a typographical error into counter.py. (You can skip this
discussion if you either already know how to do this or are confident that
you'll never create a PythonCard bug that will cause the programs to fail.)</p>
<ol>
<li>Open counter.py in your Python editor if it isn't already open.</li>
<li>Change the word "class" to "classy" and save the
program.</li>
<li>Run the application as you have been doing. You will probably see a
brief console window appear and then disappear. Nothing else happens.</li>
<li>From the File menu in resourceEditor, choose "Run With Interpreter.
" This launches your PythonCard application with the Python interpreter
in a command console for your system so that you can see what error is being
generated.</li>
<li>You should see a syntax error indicated in the new window. It should be
displaying the line where we created the intentional typo. (See Figure 6)</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig6.png" alt="Error Shown in Console Window" width="457" height="320" /><br />
Figure 6. Error Shown in Console Window</p>
<ol start="6">
<li>Press Ctrl-Z and Enter to terminate the Python interpreter and close
the console window.</li>
<li>Go back to the counter.py file and fix the line. Save the file and then
re-run the application either from the resourceEditor's File menu or from
the command line.</li>
</ol>
<h4>D. Cleaning up artifacts of the original program</h4>
<p>We already took care of changing the class name and the runtime invocation
name of the application from Minimal to Counter. Now let's change the resource
file to reflect the program's new name.</p>
<p>In resourceEditor, go to the Edit menu and select "Background Info...
" Change the name of the application to "PythonCard Counter"
and click OK.</p>
<p>That ends the basic aspect of this second PythonCard walk-through. You now
have a finished and working PythonCard application.</p>
<h3>Optional Step: Adding a Menu</h3>
<p>We'll add one optional step for a program like Counter, one which you may
well need to take in any application of even a little greater complexity than
this one. We'll add a menu to the application.</p>
<ol>
<li>In the resourceEditor, go to the Edit menu and choose "Menu Editor
..."</li>
<li>A dialog box appears (see Figure 7) with the current menu structure
displayed on the left. As you can see, the Counter application, which was
started from the sample application called minimal., has a single menu with
a single menu choice.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig7.png" alt="Opening Screen of Menu Editor" width="480" height="300" /><br />
Figure 7. Opening Screen of Menu Editor</p>
<ol start="3">
<li>Click on the "New Menu" button. You should see a dialog box like the one
in Figure 8.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig8.png" alt="New Menu Dialog Box" width="480" height="300" /><br />
Figure 8. New Menu Item Dialog Box</p>
<ol start="4">
<li>In the editing area to the right of the display showing the menu, change
the name of the menu to menuCounterMenu and its label to Counter.</li>
<li>Now click on "New Menu Item" and add a new menu item named
"counterMenuIncrement". Make its label "Increment".</li>
<li>Click on "New Menu Item" again and do the same for new menu
items "Decrement" and "Reset". When you're done your
work should look something like Figure 9.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig9.png" alt="Menu Editor With All Menu Items Defined" width="480" height="300" /><br />
Figure 9. Menu Editor With All Menu Items Defined</p>
<ol start="7">
<li>Save your application in resourceEditor.</li>
<li>Open the counter.py Python code file in your Python editor. Add the
handler name shown here:</li>
</ol>
<p class="code"> def on_counterMenuIncrement_select(self, event):</p>
<ol start="9">
<li>Copy the three lines of the function in <span class="code">on_incrBtn_mouseClick</span>
and paste them into the definition of this menu function. (Be sure levels
of indentation are consistent.)</li>
<li>Follow the same procedure for hooking up the Decrement and Reset menu
options. When you're finished, your code window should look something like
Figure 10.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig10.png" alt="All Menu Items Programmed and Ready to Go" width="521" height="634" /><br />
Figure 10. All Menu Items Programmed and Ready to Go</p>
<ol start="11">
<li>Save your work.</li>
</ol>
<p>(You'll notice that the new menu doesn't appear in your application in
resourceEditor. Rest assured it will be there when you run the application
outside resourceEditor.)</p>
<ol start="12">
<li>Run the application (see Figure 11) and confirm everything works as
expected.</li>
</ol>
<p class="imageCaption"><img src="images/wt2fig11.png" alt="Finished C ounter Application With Counter Menu" width="200" height="158" /><br />
Figure 11. Finished Counter Application With Counter Menu</p>
<p>(<strong>NOTE</strong> that it would obviously be better design to factor
out the duplicated code into methods that handle the increment, decrement and
reset buttons and menus as processes and then to call those methods from
within the event handlers. We leave that you as an exercise for the reader.
Don't you hate when we do that to you?)</p>
<p><a href="walkthrough3.html">Continue on to Walk-Through 3</a></p>
<?php include "footer.php" ?>
<p>$Revision: 1.11 $ : $Author: kasplat $ : Last update $Date: 2004/07/26 15:35:32 $</p>
</div> <!-- end of content -->
</body>
</html>
|