/PythonCard-0.8.2/docs/html/walkthrough3.html
HTML | 252 lines | 248 code | 4 blank | 0 comment | 0 complexity | fb30da2b19e3363c28a76eed4cc2f7d5 MD5 | raw file
Possible License(s): GPL-2.0
- <!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="David Primmer" />
- <link rel="stylesheet" href="PythonCard.css" type="text/css" />
- <title>How to Add a Child Window to a PythonCard Application</title>
- </head>
- <body>
- <div id="banner">
- <h1>PythonCard Documentation</h1>
- </div>
- <?php include "sidebar.php" ?>
- <div id="content">
- <h1>How to add a child window (or non-modal dialog)</h1>
- <h4>by David Primmer</h4>
- <p>This is the third walkthrough in a series of tutorial-style walkthroughs
- to help newcomers get started using PythonCard.</p>
- <h2>Overview, Scope and Purpose</h2>
- <p>This walkthrough covers PythonCard Version 0.8.</p>
- <p>The first two PythonCard walkthrough tutorials (which can be found, like
- this one, in the docs/html directory of your PythonCard installation)
- have created single-window applications. It is, of course, often necessary
- to create applications with multiple windows. Adding another background as
- a child window will allow you to modularize your application. You can allow
- users to hide and show windows (by using the visible attribute), and you can clean up your
- user interface and split widgets off into logical groups.</p>
- <p>In this walkthrough, we will extend our simple Counter application (the
- subject of walkthrough2) to add a child window which launches when the
- application opens, interacts with it, and exhibits some demonstrative
- behavior when the child window is closed. This walkthrough will make more
- sense if you've worked through walkthrough2.</p>
- <h2>Modal vs. Non-modal</h2>
- <p>There are two types of child windows: those that require the user to stop
- any other activity and pay attention only to the current window (modal) and
- windows that allow simultaneous interaction with the main window (non-modal
- or modeless). The first thing to decide when creating a child window is
- whether you require the window to be dismissed before the user can continue
- to work with the main application window. With a modal window, users can
- choose to cancel, they can respond to a message, or they can set parameters
- and assign values to content in the active application. An example of a
- simple modal window is a message box or alert.</p>
- <p>You may want your window to stick around while the user is working in
- another window. The radioclient sample, for example, has a child window that
- displays a view of the current document as it would be rendered in a Web
- browser. It's important to decide between modal and non-modal before you
- begin creating your child window because the standard window type in
- PythonCard, the background, cannot be modal.</p>
- <h2>model.Background</h2>
- <p>This is the standard class definition for a PythonCard app:</p>
- <pre>
- class Minimal(model.Background):
- </pre>
- <p>One of the most important concepts when dealing with child windows in
- PythonCard is the background. The background is unique to PythonCard and it
- basically encapsulates a wxFrame and a wxPanel. Each PythonCard application
- is a class that derives from model.Background. You can't make a class derived
- from model.Background modal. That is a wxWidgets limitation, not PythonCard.
- If you want a modal dialog then your class has to be derived from
- model.CustomDialog. They are similar, but different and the resource format
- is slightly different as well. dbBrowser, resourceEditor, textEditor, and
- textRouter all use custom modal dialogs.</p>
- <h2>Overview of Designing a Child Window</h2>
- <p>Making a non-modal child window using resourceEditor is simple, following
- these steps:</p>
- <ol>
- <li>Create a window with resourceEditor. In this walkthrough, we'll do so
- by starting with the minimal sample and adding features.</li>
- <li>In your main application, import the module you created at step 1 and
- add code to the main application's startup routine to create an instance of
- your imported child window class.</li>
- <li>Modify the attributes of the new object, calling its methods and using
- its components. It is now part of the main application.</li>
- </ol>
- <h3>Create a stand-alone PythonCard application</h3>
- <p>Each PythonCard .py file contains a class definition derived from
- PythonCard's model.Background as well as a stub to instantiate that class. A
- child window is simply an instantiation of a class from within another
- application. Tutorials in walkthrough1 and walkthrough2 cover how to create a
- stand-alone PythonCard application so we won't cover that here.</p>
- <p>It is possible that your child window will be interacting with the data
- in your main application, so there may be limits to how much design can be
- done independently of your main application. But it is still a good idea to
- try to separate their functions as much as possible to allow code re-use and
- to simplify debugging. In the current example, we will use the minimal sample
- as the basis of our child window while using the counter sample as our main
- application.</p>
- <p>Minimal.py has one control, a text field called 'field1'. We will add a
- button to minimal.py to reset the value of the field in the main counter
- application to 0 when it is pressed. We will also connect the buttons in the
- main counter application to update field1's contents in the child window
- (minimal.py) to match those of the field in counter's window.</p>
- <p>Even though these are relatively trivial interactions, they serve to
- demonstrate the techniques involved in getting two (or more) windows
- communicating with one another in a PythonCard application.</p>
- <p>Start by creating a new folder to hold your two resource files and your two
- PythonCard script files. Copy minimal.py, minimal.rsrc.py, counter.py, and
- counter.rsrc.py from their respective folders in the samples directory into
- this new folder. You can leave their names the same, though in practice you
- will generally change the names of files to match the application you are
- constructing.</p>
- <p>Open minimal.rsrc.py in the PythonCard resourceEditor and add a button to
- it as shown in Figure 1.</p>
- <p class="imageCaption"><img src="images/wt3fig1.png" alt="button added to minimal application" width="200" height="100" /><br />
- Figure 1. Button Added to minimal Sample Window</p>
- <p>Label the button "Clear Counter" and name it btnReset. Save the
- resource file.</p>
- <p>We'll get back to scripting this button shortly.</p>
- <h2>Launching the Child Window From the Parent Application</h2>
- <p>Opening the child window in our main application's code is simply a matter
- of importing the class and creating an object of that class, attached to the
- current background.</p>
- <p>In addition to the standard imports for Counter, we'll import minimal:</p>
- <pre>
- from PythonCard import model
- import minimal
- </pre>
- <p>Next we'll add an event handler to be executed when the Counter application
- is started. This handler acts something like autoexec.bat on a PC or .login
- in a Unix shell. Place an on_initialize handler right below the class
- definition. (Placement isn't important but following this convention will
- make it easier for you to work through and maintain multi-window
- applications.) Here is the class declaration of our Counter application with
- on_initialize added:</p>
- <pre>
- class Counter(model.Background):
- def on_initialize(self, event):
- </pre>
- <p>and here is the code that we will add to the on_initialize method:</p>
- <pre>
- self.minimalWindow = model.childWindow(self, minimal.Minimal)
- </pre>
- <p>We create a minimal window object uisng the <span class="code">childWindow
- </span> function and give it the name minimalWindow by passing two parameters
- to the function: the parent window (<span class="code">self</span>), and the
- background class (<span class="code">minimal.Minimal</span>) we want to use.</p>
- <p>That is all is needed to create a minimal window object, but at this point,
- it is still hidden and not much good to us.</p>
- <h2>Communicating With Your Child Window</h2>
- <p>Continuing in the on_initialize handler, we make calls to set the
- position and visibility of the new window:</p>
- <pre>
- # override resource position
- self.minimalWindow.position = (200, 5)
- self.minimalWindow.visible = True
- </pre>
- <p>We now have a window that is an attribute of our main background, just like
- any of the menus or buttons that are already a part of Counter.</p>
- <p>As constructed before we began this project, the increment and decrement
- buttons in Counter modify the value of the text field in Counter. To cause the
- Counter application's buttons to update the text value in the minimal child
- window minimalWindow, we simply add one more call to update the control in
- that window as well (the new lines are in bold type):</p>
- <pre>
- def on_incrBtn_mouseClick(self, event):
- startValue = int(self.components.field1.text)
- endValue = startValue + 1
- self.components.field1.text = str(endValue)
- <strong>self.minimalWindow.components.field1.text = str(endValue)</strong>
- def on_decrBtn_mouseClick(self, event):
- startValue = int(self.components.field1.text)
- endValue = startValue - 1
- self.components.field1.text = str(endValue)
- <strong>self.minimalWindow.components.field1.text = str(endValue)</strong>
- </pre>
- <p>Notice that we reference components in the child window by a collection of
- objects starting with the main application (self) and then pointing first to
- the child window, then to its components property, then to the specific
- component, then to the property of that component we wish to change. If we
- wanted to execute a method of that component or the background, we would use
- a similar construct.</p>
- <p>This is obviously very simplistic, (not to mention somewhat redundant
- coding). Many times, you will be using a child window to modify components or
- data associated with the parent window. PythonCard is not passing events
- between windows in this release, but in many cases you can simply call the
- event handler directly. If you are interested in passing an arbitrary event
- such as a mouseClick, that will require creating the event and then posting
- it using wx.PostEvent. Custom events are beyond the scope of this tutorial.
- However, the child window is able to access the parent window directly by
- traversing up the stack of windows.</p>
- <p>For example we can place a control on our child window that updates a
- control on our main background. Let's connect the Reset Counter button we
- added to the minimal application above. Add the following code to
- minimal.py</p>
- <pre>
- def on_initialize(self, event):
- self.parent = self.getParent()
- def on_btnReset_mouseClick(self, event):
- self.parent.components.field1.text = "0"
- </pre>
- <p>When our child window it initialized, it calls getParent() to get a
- reference to its parent window, and then stores that reference. We place the
- code that handles this task in the on_initialize handler so that the
- reference is available to all handlers in the application.</p>
- <p>You'll notice that the text field on the Counter background is reset to
- zero but the text field on the Minimal background is not. (This might be a
- little confusing because both fields are called 'field1'. In a real
- application, the fields should be named something more descriptive.) At this
- point, our minimal sample is no longer a stand-alone. If you run Minimal by
- itself, self.parent is set to None. If you were using the child window in
- other roles or wanted to make it multi-purpose, you could place the
- self.getParent() call in a try...except block.</p>
- <h2>Closing the Child Window</h2>
- <p>Your main application window will clean up the child window when your app
- is closed. If the user has the ability to close the child window before the
- main window closes (by using the the child's File->Exit menu or by
- clicking the close box on the window) it's a good idea to just hide the
- window instead of destroying it. This will allow you to unhide the window
- without re-initializing it and also permits you to communicate with the
- window while it is hidden. In the process, you avoid runtime errors that
- could result from the child window being non-existent as far as the
- application is concerned.</p>
- <p>We do this by overriding the on_close event handler. on_close would
- normally destroy the window but we change it so it hides the window by
- setting the visible attribute to False, hiding the window. Just to
- make things a little more interesting, we've also added a custom doExit
- function that sets the counter's field1 to an arbitrary value just to confirm
- the connection between the two windows visibly:</p>
- <pre>
- def doExit(self):
- self.parent.components.field1.text = "99"
- def on_close(self, event):
- self.doExit()
- self.visible = False
- def on_exit_command(self, event):
- self.close()
- </pre>
- <p>The resource file (minimal.rsrc.py) defines an exit command for the File->Exit menu
- so we use a command handler to override the default behavior.
- As the above code shows, the File->Exit menu item just calls the
- close() method to close the window. That is the same as clicking the
- close box on the window and triggers the close window event, so that on_close
- is called. We placed the work to be done when the document is closing in the
- doExit method. In this case it just sets the counter field in the parent to
- "99".</p>
- <p>In addition, in doExit() you could modify some properties of the parent
- window to keep track of the state of your child window. For example, assuming
- you have a View menu with an item that hides or unhides your child window, you
- could use doExit() to check or uncheck the 'View Minimal Window' menu item on
- the parent. The code would look something like this:</p>
- <pre>
- self.parent.menuBar.setChecked('menuViewMinimalWindow', False)
- </pre>
- <?php include "footer.php" ?>
- <p>$Revision: 1.11 $ : $Author: kasplat $ : Last update $Date: 2005/12/30 06:29:36 $</p>
- </div> <!-- end of content -->
- </body>
- </html>