[349] | 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() |
---|