1 | #! /usr/env/python |
---|
2 | |
---|
3 | """ |
---|
4 | AppShell provides a GUI application framework. |
---|
5 | |
---|
6 | This is a streamlined adaptation of GuiAppD.py, originally |
---|
7 | created by Doug Hellmann (doughellmann@mindspring.com). |
---|
8 | |
---|
9 | Pmw copyright |
---|
10 | |
---|
11 | Copyright 1997-1999 Telstra Corporation Limited, |
---|
12 | Australia Copyright 2000-2002 Really Good Software Pty Ltd, Australia |
---|
13 | |
---|
14 | Permission is hereby granted, free of charge, to any person obtaining |
---|
15 | a copy of this software and associated documentation files (the |
---|
16 | "Software"), to deal in the Software without restriction, including |
---|
17 | without limitation the rights to use, copy, modify, merge, publish, |
---|
18 | distribute, sublicense, and/or sell copies of the Software, and to |
---|
19 | permit persons to whom the Software is furnished to do so, subject to |
---|
20 | the following conditions: |
---|
21 | |
---|
22 | The above copyright notice and this permission notice shall be |
---|
23 | included in all copies or substantial portions of the Software. |
---|
24 | |
---|
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
---|
26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
---|
27 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
---|
28 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
---|
29 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
---|
30 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
---|
31 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
---|
32 | |
---|
33 | """ |
---|
34 | |
---|
35 | |
---|
36 | from Tkinter import * |
---|
37 | import Pmw |
---|
38 | import sys, string |
---|
39 | import ProgressBar |
---|
40 | |
---|
41 | class AppShell(Pmw.MegaWidget): |
---|
42 | appversion = '1.0' |
---|
43 | appname = 'Generic Application Frame' |
---|
44 | copyright = 'Copyright YYYY Your Company. All Rights Reserved' |
---|
45 | contactname = 'Your Name' |
---|
46 | contactphone = '(999) 555-1212' |
---|
47 | contactemail = 'youremail@host.com' |
---|
48 | |
---|
49 | frameWidth = 450 |
---|
50 | frameHeight = 320 |
---|
51 | padx = 5 |
---|
52 | pady = 5 |
---|
53 | usecommandarea = 0 |
---|
54 | balloonhelp = 1 |
---|
55 | |
---|
56 | busyCursor = 'watch' |
---|
57 | |
---|
58 | def __init__(self, **kw): |
---|
59 | optiondefs = ( |
---|
60 | ('padx', 1, Pmw.INITOPT), |
---|
61 | ('pady', 1, Pmw.INITOPT), |
---|
62 | ('framewidth', 1, Pmw.INITOPT), |
---|
63 | ('frameheight', 1, Pmw.INITOPT), |
---|
64 | ('usecommandarea', self.usecommandarea, Pmw.INITOPT)) |
---|
65 | self.defineoptions(kw, optiondefs) |
---|
66 | |
---|
67 | self.root = Tk() |
---|
68 | self.initializeTk(self.root) |
---|
69 | Pmw.initialise(self.root) |
---|
70 | self.root.title(self.appname) |
---|
71 | self.root.geometry('%dx%d' % (self.frameWidth, self.frameHeight)) |
---|
72 | |
---|
73 | # Initialize the base class |
---|
74 | Pmw.MegaWidget.__init__(self, parent=self.root) |
---|
75 | |
---|
76 | # initialize the application |
---|
77 | self.appInit() |
---|
78 | |
---|
79 | # create the interface |
---|
80 | self.__createInterface() |
---|
81 | |
---|
82 | # create a table to hold the cursors for |
---|
83 | # widgets which get changed when we go busy |
---|
84 | self.preBusyCursors = None |
---|
85 | |
---|
86 | # pack the container and set focus |
---|
87 | # to ourselves |
---|
88 | self._hull.pack(side=TOP, fill=BOTH, expand=YES) |
---|
89 | self.focus_set() |
---|
90 | |
---|
91 | # initialize our options |
---|
92 | self.initialiseoptions(AppShell) |
---|
93 | |
---|
94 | def appInit(self): |
---|
95 | # Called before interface is created (should be overridden). |
---|
96 | pass |
---|
97 | |
---|
98 | def initializeTk(self, root): |
---|
99 | # Initialize platform-specific options |
---|
100 | if sys.platform == 'mac': |
---|
101 | self.__initializeTk_mac(root) |
---|
102 | elif sys.platform == 'win32': |
---|
103 | self.__initializeTk_win32(root) |
---|
104 | else: |
---|
105 | self.__initializeTk_unix(root) |
---|
106 | |
---|
107 | def __initializeTk_colors_common(self, root): |
---|
108 | root.option_add('*background', 'grey') |
---|
109 | root.option_add('*foreground', 'black') |
---|
110 | root.option_add('*EntryField.Entry.background', 'white') |
---|
111 | root.option_add('*Entry.background', 'white') |
---|
112 | root.option_add('*MessageBar.Entry.background', 'gray85') |
---|
113 | root.option_add('*Listbox*background', 'white') |
---|
114 | root.option_add('*Listbox*selectBackground', 'dark slate blue') |
---|
115 | root.option_add('*Listbox*selectForeground', 'white') |
---|
116 | |
---|
117 | def __initializeTk_win32(self, root): |
---|
118 | self.__initializeTk_colors_common(root) |
---|
119 | root.option_add('*Font', 'Verdana 10 bold') |
---|
120 | root.option_add('*EntryField.Entry.Font', 'Courier 10') |
---|
121 | root.option_add('*Listbox*Font', 'Courier 10') |
---|
122 | |
---|
123 | def __initializeTk_mac(self, root): |
---|
124 | self.__initializeTk_colors_common(root) |
---|
125 | |
---|
126 | def __initializeTk_unix(self, root): |
---|
127 | self.__initializeTk_colors_common(root) |
---|
128 | |
---|
129 | def busyStart(self, newcursor=None): |
---|
130 | if not newcursor: |
---|
131 | newcursor = self.busyCursor |
---|
132 | newPreBusyCursors = {} |
---|
133 | for component in self.busyWidgets: |
---|
134 | newPreBusyCursors[component] = component['cursor'] |
---|
135 | component.configure(cursor=newcursor) |
---|
136 | component.update_idletasks() |
---|
137 | self.preBusyCursors = (newPreBusyCursors, self.preBusyCursors) |
---|
138 | |
---|
139 | def busyEnd(self): |
---|
140 | if not self.preBusyCursors: |
---|
141 | return |
---|
142 | oldPreBusyCursors = self.preBusyCursors[0] |
---|
143 | self.preBusyCursors = self.preBusyCursors[1] |
---|
144 | for component in self.busyWidgets: |
---|
145 | try: |
---|
146 | component.configure(cursor=oldPreBusyCursors[component]) |
---|
147 | except KeyError: |
---|
148 | pass |
---|
149 | component.update_idletasks() |
---|
150 | |
---|
151 | def __createAboutBox(self): |
---|
152 | Pmw.aboutversion(self.appversion) |
---|
153 | Pmw.aboutcopyright(self.copyright) |
---|
154 | Pmw.aboutcontact( |
---|
155 | 'For more information, contact:\n %s\n Phone: %s\n Email: %s' %\ |
---|
156 | (self.contactname, self.contactphone, |
---|
157 | self.contactemail)) |
---|
158 | self.about = Pmw.AboutDialog(self._hull, |
---|
159 | applicationname=self.appname) |
---|
160 | self.about.withdraw() |
---|
161 | return None |
---|
162 | |
---|
163 | def showAbout(self): |
---|
164 | # Create the dialog to display about and contact information. |
---|
165 | self.about.show() |
---|
166 | self.about.focus_set() |
---|
167 | |
---|
168 | def toggleBalloon(self): |
---|
169 | if self.toggleBalloonVar.get(): |
---|
170 | self.__balloon.configure(state = 'both') |
---|
171 | else: |
---|
172 | self.__balloon.configure(state = 'status') |
---|
173 | |
---|
174 | def __createMenuBar(self): |
---|
175 | self.menuBar = self.createcomponent('menubar', (), None, |
---|
176 | Pmw.MenuBar, |
---|
177 | (self._hull,), |
---|
178 | hull_relief=RAISED, |
---|
179 | hull_borderwidth=1, |
---|
180 | balloon=self.balloon()) |
---|
181 | |
---|
182 | self.menuBar.pack(fill=X) |
---|
183 | self.menuBar.addmenu('Help', 'About %s' % self.appname, side='right') |
---|
184 | self.menuBar.addmenu('File', 'File commands and Quit') |
---|
185 | |
---|
186 | def createMenuBar(self): |
---|
187 | self.menuBar.addmenuitem('Help', 'command', |
---|
188 | 'Get information on application', |
---|
189 | label='About...', command=self.showAbout) |
---|
190 | self.toggleBalloonVar = IntVar() |
---|
191 | self.toggleBalloonVar.set(1) |
---|
192 | self.menuBar.addmenuitem('Help', 'checkbutton', |
---|
193 | 'Toggle balloon help', |
---|
194 | label='Balloon help', |
---|
195 | variable = self.toggleBalloonVar, |
---|
196 | command=self.toggleBalloon) |
---|
197 | |
---|
198 | self.menuBar.addmenuitem('File', 'command', 'Quit this application', |
---|
199 | label='Quit', |
---|
200 | command=self.quit) |
---|
201 | |
---|
202 | def __createBalloon(self): |
---|
203 | # Create the balloon help manager for the frame. |
---|
204 | # Create the manager for the balloon help |
---|
205 | self.__balloon = self.createcomponent('balloon', (), None, |
---|
206 | Pmw.Balloon, (self._hull,)) |
---|
207 | |
---|
208 | def balloon(self): |
---|
209 | return self.__balloon |
---|
210 | |
---|
211 | def __createDataArea(self): |
---|
212 | # Create data area where data entry widgets are placed. |
---|
213 | self.dataArea = self.createcomponent('dataarea', |
---|
214 | (), None, |
---|
215 | Frame, (self._hull,), |
---|
216 | relief=GROOVE, |
---|
217 | bd=1) |
---|
218 | self.dataArea.pack(side=TOP, fill=BOTH, expand=YES, |
---|
219 | padx=self['padx'], pady=self['pady']) |
---|
220 | |
---|
221 | def __createCommandArea(self): |
---|
222 | # Create a command area for application-wide buttons. |
---|
223 | self.__commandFrame = self.createcomponent('commandframe', (), None, |
---|
224 | Frame, |
---|
225 | (self._hull,), |
---|
226 | relief=SUNKEN, |
---|
227 | bd=1) |
---|
228 | self.__buttonBox = self.createcomponent('buttonbox', (), None, |
---|
229 | Pmw.ButtonBox, |
---|
230 | (self.__commandFrame,), |
---|
231 | padx=0, pady=0) |
---|
232 | self.__buttonBox.pack(side=TOP, expand=NO, fill=X) |
---|
233 | if self['usecommandarea']: |
---|
234 | self.__commandFrame.pack(side=TOP, |
---|
235 | expand=NO, |
---|
236 | fill=X, |
---|
237 | padx=self['padx'], |
---|
238 | pady=self['pady']) |
---|
239 | |
---|
240 | |
---|
241 | def __createMessageBar(self): |
---|
242 | # Create the message bar area for help and status messages. |
---|
243 | frame = self.createcomponent('bottomtray', (), None, |
---|
244 | Frame,(self._hull,), relief=SUNKEN) |
---|
245 | self.__messageBar = self.createcomponent('messagebar', |
---|
246 | (), None, |
---|
247 | Pmw.MessageBar, |
---|
248 | (frame,), |
---|
249 | #entry_width = 40, |
---|
250 | entry_relief=SUNKEN, |
---|
251 | entry_bd=1, |
---|
252 | labelpos=None) |
---|
253 | self.__messageBar.pack(side=LEFT, expand=YES, fill=X) |
---|
254 | |
---|
255 | self.__progressBar = ProgressBar.ProgressBar(frame, |
---|
256 | fillColor='slateblue', |
---|
257 | doLabel=1, |
---|
258 | width=150) |
---|
259 | self.__progressBar.frame.pack(side=LEFT, expand=NO, fill=NONE) |
---|
260 | |
---|
261 | self.updateProgress(0) |
---|
262 | frame.pack(side=BOTTOM, expand=NO, fill=X) |
---|
263 | |
---|
264 | self.__balloon.configure(statuscommand = \ |
---|
265 | self.__messageBar.helpmessage) |
---|
266 | |
---|
267 | def messageBar(self): |
---|
268 | return self.__messageBar |
---|
269 | |
---|
270 | def updateProgress(self, newValue=0, newMax=0): |
---|
271 | self.__progressBar.updateProgress(newValue, newMax) |
---|
272 | |
---|
273 | def bind(self, child, balloonHelpMsg, statusHelpMsg=None): |
---|
274 | # Bind a help message and/or status message to a widget. |
---|
275 | self.__balloon.bind(child, balloonHelpMsg, statusHelpMsg) |
---|
276 | |
---|
277 | def interior(self): |
---|
278 | # Retrieve the interior site where widgets should go. |
---|
279 | return self.dataArea |
---|
280 | |
---|
281 | def buttonBox(self): |
---|
282 | # Retrieve the button box. |
---|
283 | return self.__buttonBox |
---|
284 | |
---|
285 | def buttonAdd(self, buttonName, helpMessage=None, |
---|
286 | statusMessage=None, **kw): |
---|
287 | # Add a button to the button box. |
---|
288 | newBtn = self.__buttonBox.add(buttonName) |
---|
289 | newBtn.configure(kw) |
---|
290 | if helpMessage: |
---|
291 | self.bind(newBtn, helpMessage, statusMessage) |
---|
292 | return newBtn |
---|
293 | |
---|
294 | def __createInterface(self): |
---|
295 | self.__createBalloon() |
---|
296 | self.__createMenuBar() |
---|
297 | self.__createDataArea() |
---|
298 | self.__createCommandArea() |
---|
299 | self.__createMessageBar() |
---|
300 | self.__createAboutBox() |
---|
301 | # |
---|
302 | # Create the parts of the interface |
---|
303 | # which can be modified by subclasses |
---|
304 | # |
---|
305 | self.busyWidgets = ( self.root, ) |
---|
306 | self.createMenuBar() |
---|
307 | self.createInterface() |
---|
308 | |
---|
309 | def createInterface(self): |
---|
310 | # Override this method to create the interface for the app. |
---|
311 | pass |
---|
312 | |
---|
313 | def main(self): |
---|
314 | # This method should be left intact! |
---|
315 | self.pack() |
---|
316 | self.mainloop() |
---|
317 | |
---|
318 | def run(self): |
---|
319 | self.main() |
---|
320 | |
---|
321 | class TestAppShell(AppShell): |
---|
322 | usecommandarea=1 |
---|
323 | |
---|
324 | def createButtons(self): |
---|
325 | self.buttonAdd('Ok', |
---|
326 | helpMessage='Exit', |
---|
327 | statusMessage='Exit', |
---|
328 | command=self.quit) |
---|
329 | |
---|
330 | def createMain(self): |
---|
331 | self.label = self.createcomponent('label', (), None, |
---|
332 | Label, |
---|
333 | (self.interior(),), |
---|
334 | text='Data Area') |
---|
335 | self.label.pack() |
---|
336 | self.bind(self.label, 'Space taker') |
---|
337 | |
---|
338 | def createInterface(self): |
---|
339 | AppShell.createInterface(self) |
---|
340 | self.createButtons() |
---|
341 | self.createMain() |
---|
342 | |
---|
343 | if __name__ == '__main__': |
---|
344 | test = TestAppShell(balloon_state='both') |
---|
345 | test.run() |
---|