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