Package teamwork :: Package widgets :: Module MultiWin
[hide private]
[frames] | no frames]

Source Code for Module teamwork.widgets.MultiWin

  1  ## 
  2  ## <Multiple_window> and <Inner_Window> classes  
  3  ## 
  4   
  5  ##  
  6  ## 
  7  ## Idea of multiples windows comes from a Tcl package called mdw_lib.tk 
  8  ## (a classlib for [m]ulti_[d]ocument_[w]indow applications). 
  9  ## The mdw_lib is a number of Tcl/Tk procedures to create and manipulate  
 10  ## multiple childwindows in one Tk application window  
 11  ## The  mdw_lib author is Thomas Schwarze <swz@rtws18.ee.tu_berlin.de> 
 12  ## Original package is GPL'ed. However, code and way to implement 
 13  ## multiple windows and mdw_lib are completely different. 
 14  ## 
 15  ## 
 16  ##           Author: Erick Gallesio [eg@unice.fr] 
 17  ##    Creation date:  5_Apr_1996 18:04 
 18  ## Last file update:  3_Sep_1999 21:34 (eg) 
 19   
 20  # (require "Basics") 
 21  # (select-module STklos+Tk) 
 22   
 23  # (export place) # Since it it redefined as a generic ... 
 24   
 25  from Tkinter import * 
 26  import Pmw 
 27   
 28   
 29  ####################################### 
 30  ## 
 31  ## <Multiple_window> class 
 32  ## 
 33  ####################################### 
 34   
 35   
 36  # Consider forwarding methods eg 
 37  # Pmw.forwardmethods(MyClass, TargetClass, 
 38  #    MyClass.findtarget, ['dangerous1', 'dangerous2']) 
 39  # In both cases, all TargetClass methods will be forwarded from MyClass 
 40  # except for dangerous1, dangerous2, special methods like __str__, 
 41  # and pre-existing methods like foo. 
 42   
 43  # so something like  
 44  # Pmw.forwardmethods(InnerWindow, Frame, InnerWindow.interior) 
 45  # and it MUST be placed afer class definition 
 46   
 47   
48 -class MultipleWin (Pmw.MegaWidget):
49
50 - def __init__ (self, parent, **kw):
51 optiondefs = ( 52 ('relief', 'ridge', None), 53 ('width', 640, None), 54 ('height', 480, None), 55 ('background', 'gray50', None), 56 ('borderwidth', 2, None), 57 ('foreground', 'navy', None), 58 ('readyText',"Windows",Pmw.INITOPT), 59 ) 60 self.defineoptions(kw, optiondefs) 61 62 # Initialise the base class (after defining the options). 63 Pmw.MegaWidget.__init__(self, parent) 64 65 self.propagate(0) 66 self.parent = parent 67 self.children = [] 68 self.stack = [] 69 ## self.current = 0 70 71 # Ignore the border-width or highlight-thickness given (or implied 72 # by a .Xdefaults configuration) to ease window movement in. This 73 # is clearly not correct but user should not see the trick 74 self.configure(hull_width = self['width']) 75 self.configure(hull_height = self['height']) 76 self.configure(hull_relief = self['relief']) 77 self.configure(hull_background = self['background']) 78 # self.borderwidth = 0 79 # self.highlight_thickness = 0 80 81 # Create the bottom bar 82 self.BottomBar = self.createcomponent('BottomBar', 83 (), 'Mygroup', 84 Frame, (self.interior(),), relief = "groove", 85 ## background = 'blue', 86 borderwidth = 3) 87 self.Btitle = self.createcomponent('Btitle', 88 (), 'Mygroup', 89 Label, (self.BottomBar,), 90 text = self['readyText'], 91 width=10, 92 ## background = 'lightblue', 93 borderwidth=3, relief="ridge", anchor="w") 94 self.BottomBar.pack(side='bottom', expand = 0, fill='x') 95 self.Btitle.pack(side='left', expand = 0, padx = 3, pady = 3) 96 97 self.initialiseoptions(MultipleWin)
98
99 - def setTitleColor(self):
100 """Updates the color of all L{InnerWindow} children 101 """ 102 for w in self.children: 103 w.setTitleColor()
104
105 - def find_current_window (self, avoid):
106 """Find a plausible current window (i.e one which is different than avoid and which is not iconified). If no window is available return avoid. 107 """ 108 try: 109 return self.stack[-1] 110 except IndexError: 111 l = self.children[:] 112 while True: 113 if len(l) == 0: return avoid 114 if l[0] == avoid or l[0].iconified: 115 del l[0] 116 else: 117 return l[0]
118
119 - def height(self):
120 widgets = [self.BottomBar,self.Btitle] 121 total = self.winfo_height() 122 for win in widgets: 123 total -= win.winfo_height() 124 return total
125 126 ####################################### 127 ## 128 ## <Inner-window> class 129 ## 130 ###############################
131 -class InnerWindow (Pmw.MegaWidget):
132 ## InnerWindow contol icons 133 mycursors = ["top_left_corner", "top_side", "top_right_corner", 134 "left_side", "crosshair", "right_side", 135 "bottom_left_corner", "bottom_side", "bottom_right_corner"] 136 137 ## bitmap_cross = None 138 ## bitmap_icon = None 139 ## bitmap_max = None 140 ## bitmap_min = None
141 - def createBitmaps( self ) :
142 if not hasattr( InnerWindow, 'bitmap_cross' ) : 143 InnerWindow.bitmap_cross = BitmapImage(data = """ 144 #define im_width 10 145 #define im_height 10 146 static unsigned char im_bits[] = { 147 0,0,0,0,0xc,3,0x9c,3,0xf8, 148 1,0xf0,0,0xf0,0,0xf8,1,0x9c,3,0x0c,3 149 };""" ) 150 if not hasattr( InnerWindow, 'bitmap_icon' ) : 151 InnerWindow.bitmap_icon = BitmapImage(data = """ 152 #define im_width 10 153 #define im_height 10 154 static unsigned char im_bits[] = { 155 0,0,0,0,0,0,0,0, 156 0,0,0,0,0,0,0xfc,3,0xfc,3,0,0 157 };""" ) 158 if not hasattr( InnerWindow, 'bitmap_max' ) : 159 InnerWindow.bitmap_max = BitmapImage(data = """ 160 #define im_width 10 161 #define im_height 10 162 static unsigned char im_bits[] = { 163 0,0,0,0,0xfc,3,0xfc, 164 3,0xfc,3,0xfc,3,0xfc,3,0xfc,3,0xfc,3,0xfc,3 165 };""" ) 166 if not hasattr( InnerWindow, 'bitmap_min' ) : 167 InnerWindow.bitmap_min = BitmapImage(data = """ 168 #define im_width 10 169 #define im_height 10 170 static unsigned char im_bits[] = { 171 0,0,0,0,0,0,0,0,0xf0, 172 0,0xf0,0,0xf0,0,0xf0,0,0,0,0,0 173 };""" )
174 175 176 bd = 15 # # of pixels for detecting a border 177
178 - def __init__ (self, parent, **kw):
179 palette = Pmw.Color.getdefaultpalette(parent.parent) 180 optiondefs = ( 181 ('relief', 'ridge', None), 182 ('title','Inner Window',self.setTitle), 183 ('width', 320, None), 184 ('height', 240, None), 185 ('x', 20, None), 186 ('y', 20, None), 187 ('background', palette['background'], None), 188 ('borderwidth', 2, None), 189 ('foreground', palette['foreground'], None), 190 ('destroycommand',None,Pmw.INITOPT), 191 ) 192 self.defineoptions(kw, optiondefs) 193 194 # Initialise the base class (after defining the options). 195 Pmw.MegaWidget.__init__(self, parent.interior()) 196 self.propagate(0) 197 self.width = self['width'] 198 self.height = self['height'] 199 self.configure(hull_width = self['width']) 200 self.configure(hull_height = self['height']) 201 self.configure(hull_relief = self['relief']) 202 self.configure(hull_borderwidth = 3) 203 self.parent = parent 204 self.frame = self.createcomponent('frame',(), 'Mygroup',Frame, 205 (self.interior(),), cursor ="left_ptr", 206 background = self['background'], 207 borderwidth = 0, 208 ) 209 self.frame.pack(fill='both',expand=1) 210 self.createcomponent('title',(), 'Mygroup',Frame, 211 (self.component('frame'),), 212 borderwidth=2, relief='raised', 213 ).pack(side = 'top', fill = 'x', expand = 0) 214 215 self.createcomponent('label',(), 'Mygroup',Label, 216 (self.component('title'),), 217 cursor = "fleur", 218 ).pack(side='left', expand = 1, fill = 'x') 219 self.createBitmaps() 220 if self['destroycommand']: 221 self.createcomponent('destroy',(), 'Mygroup',Button, 222 (self.component('title'),), 223 image = InnerWindow.bitmap_cross, 224 command = self.destroy, 225 ).pack(side = 'right') 226 self.createcomponent('maximize',(), 'Mygroup',Button, 227 (self.component('title'),), 228 image = InnerWindow.bitmap_max, 229 command = self.minormax, 230 ).pack(side = 'right') 231 self.createcomponent('iconify',(), 'Mygroup', 232 Button, (self.component('title'),), 233 image = InnerWindow.bitmap_icon, 234 command = self.iconify_window, 235 ).pack(side = 'right') 236 237 self.maximized = False 238 self.visible = IntVar() 239 self.visible.set(1) 240 self.iconified = 0 241 self.myindex = 0 242 self.resizing = None 243 244 # Associate buttons callbacks 245 self.make_inner_window_bindings(None) 246 # Add the new window to the multiple-window children list 247 parent.children.append(self) 248 # Initialise instance variables. 249 self.initialiseoptions(InnerWindow)
250
251 - def setTitle(self):
252 self.component('label').configure(text=self['title']) 253 try: 254 widget = self.component('icon') 255 widget.configure(text=self['title'], 256 width=min(len(self['title']),15)) 257 except KeyError: 258 pass
259
260 - def minormax(self):
261 if self.maximized: 262 self.minimize_window() 263 else: 264 self.maximize_window()
265
266 - def unselect_window (self):
267 if self in self.parent.stack: 268 self.parent.stack.remove(self) 269 win = self.parent.find_current_window(self) 270 if win is not self: 271 win.mwraise() 272 win.setTitleColor() 273 self.setTitleColor()
274
275 - def select_window (self):
276 if self in self.parent.stack: 277 self.parent.stack.remove(self) 278 self.parent.stack.append(self) 279 if len(self.parent.stack) > 1: 280 self.parent.stack[-2].setTitleColor() 281 self.setTitleColor()
282
283 - def setTitleColor(self):
284 """Updates the title color of this window 285 """ 286 palette = Pmw.Color.getdefaultpalette(self.parent.parent) 287 if len(self.parent.stack) > 0 and self is self.parent.stack[-1]: 288 self.component('label').configure(fg=palette['selectForeground'], 289 bg=palette['selectBackground']) 290 else: 291 self.component('label').configure(fg=palette['foreground'], 292 bg=palette['background'])
293
294 - def mwraise (self,event=None):
295 if len(self.parent.stack) == 0 or self is not self.parent.stack[-1]: 296 self.select_window() 297 Frame.tkraise(self.interior()) 298 self.parent.BottomBar.tkraise()
299
300 - def lower (self):
301 self.unselect_window 302 self.interior().lower()
303
304 - def destroy (self):
305 if self['destroycommand']: 306 self['destroycommand'](self) 307 parent = self.parent 308 if parent.children.count(self): parent.children.remove(self) 309 self.unselect_window() 310 if self.iconified: 311 self.destroycomponent('icon') 312 else: 313 try: 314 self.parent.stack.remove(self) 315 except ValueError: 316 # No big deal 317 pass 318 Pmw.MegaWidget.destroy(self)#.interior())
319
320 - def maximize_window (self):
321 if not self.maximized : 322 self.mwraise() 323 self.maximized = True 324 self['x'] = self.winfo_x() 325 self['y'] = self.winfo_y() 326 self.width = self.winfo_width() 327 self.height = self.winfo_height() 328 # Use pack to fill the cavity 329 self.pack(fill = 'both', expand = 1) 330 # Change maximize button 331 but = self.component('maximize') 332 but.configure(image = InnerWindow.bitmap_min)
333
334 - def minimize_window (self):
335 if self.maximized: 336 self.maximized = False 337 # Forget previous pack 338 self.interior().pack_forget() 339 self.interior().place(x = self['x'],y = self['y'], 340 width = self.width,height = self.height) 341 # Change maximize button 342 self.component('maximize').configure(image=InnerWindow.bitmap_max)
343
344 - def iconify_window (self):
345 if not self.iconified: 346 parent = self.parent 347 self.visible.set(0) 348 self.iconified = True 349 self.unselect_window() 350 self.minimize_window() 351 # Retain window position before deleting it 352 if self.winfo_y() > 2 and self.winfo_width() > 20 and \ 353 self.winfo_height() > 20: 354 self['x'] = self.winfo_x() 355 self['y'] = self.winfo_y() 356 self.width = self.winfo_width() 357 self.height = self.winfo_height() 358 # Create Icon button 359 bar = parent.BottomBar 360 title = self.component('label').cget('text') 361 b = self.createcomponent('icon',(),None,Button,(bar,), 362 text = title, 363 font = "-adobe-helvetica-*-r-*-*-*-100-*-*-*-*-*-*", 364 width = min(len(title), 15), 365 anchor = "w", 366 command = self.show_window) 367 b.pack(side = 'left', expand = 0, fill = 'none') 368 self.place_forget()
369
370 - def show_window (self) :
371 if self.iconified: 372 self.iconified = None 373 self.visible.set(1) 374 self.place( x = self['x'], 375 y = self['y'], 376 width = self.width, 377 height = self.height ) 378 # Destroy icon 379 self.destroycomponent('icon') 380 else: 381 # raise the window 382 self.mwraise()
383 384
385 - def iconify_or_show (self) :
386 if self.iconified: 387 self.show_window() 388 else: 389 self.iconify_window()
390
391 - def place (self, **kw):
392 Frame.place(self.interior(), kw) 393 self.mwraise()
394
395 - def inner_raise_set(self, event):
396 x = event.x_root 397 y = event.y_root 398 self.mwraise() 399 self['x'] = x 400 self['y'] = y
401
402 - def inner_place_set(self, event):
403 x = event.x_root 404 y = event.y_root 405 if not self.maximized: 406 self.update_idletasks() 407 myy = self.encloseY(y) 408 myx = self.encloseX(x,25) 409 self.place(x = myx, y = myy) 410 self['x'] = x 411 self['y'] = y
412
413 - def encloseX(self,x,pad=0):
414 return min(max(self.winfo_x() + (x - self['x']),0), 415 self.parent.winfo_width()-pad)
416
417 - def encloseY(self,y,pad=0):
418 return min(max(self.winfo_y() + (y - self['y']),0), 419 self.parent.height()-pad)
420
421 - def inner_lower_set(self, event):
422 if self is self.parent.stack[-1]: 423 self.lower()
424
425 - def in_inner_window (self, event):
426 x = event.x 427 y = event.y 428 w = self.winfo_width() 429 h = self.winfo_height() 430 bd = self.bd 431 ci = 0 432 if y < bd : 433 ci = 0 434 elif y < (h - bd) : 435 ci = 3 436 else: 437 ci = 6 438 if x < bd : 439 ci = ci 440 elif x < (w - bd) : 441 ci = ci + 1 442 else : 443 ci = ci + 2 444 self.myindex = ci 445 self.interior().config(cursor = self.mycursors[ci])
446
447 - def resize_inner_window (self, event):
448 x = event.x_root 449 y = event.y_root 450 if (not self.resizing) and (not self.maximized) : 451 self.resizing = True 452 index = self.myindex 453 width = self.winfo_width() 454 height = self.winfo_height() 455 parent = self.parent 456 parentHeight = parent.height() 457 parentWidth = parent.winfo_width() 458 off_x = parent.winfo_rootx() 459 off_y = parent.winfo_rooty() 460 x1 = self.winfo_rootx() - off_x 461 y1 = self.winfo_rooty() - off_y 462 x2 = x1 + width 463 y2 = y1 + height 464 if index == 0: 465 x1 = min(max(x - off_x,0),x2-25,parentWidth-25) 466 y1 = min(max(y - off_y,0),y2-25,parentHeight) 467 elif index == 1: 468 y1 = min(max(y - off_y,0),y2-25,parentHeight) 469 elif index == 2: 470 x2 = min(max(x - off_x,x1+25),parentWidth) 471 elif index == 3: 472 x1 = min(max(x - off_x,0),x2-25,parentWidth-25) 473 elif index == 5: 474 x2 = min(max(x - off_x,x1+25),parentWidth) 475 elif index == 6: 476 x1 = min(max(x - off_x,0),x2-25,parentWidth-25) 477 y2 = min(max(y - off_y,y1+25),parentHeight) 478 elif index == 7: 479 y2 = min(max(y - off_y,y1+25),parentHeight) 480 elif index == 8: 481 x2 = min(max(x - off_x,x1+25),parentWidth) 482 y2 = min(max(y - off_y,y1+25),parentHeight) 483 self.place(in_ = parent, x = x1, y = y1, 484 width = (x2 - x1), height = (y2 - y1)) 485 self.resizing = False
486 487 488 ## 489 ## The hard part: associate bindings to the window corners and to 490 ## the title bar. This is very long and very ugly; but can it be done 491 ## in another way? 492 ##
493 - def make_inner_window_bindings (self, button):
494 # The frame 495 f = self.interior() 496 f.bind("<1>", self.mwraise ) 497 f.bind("<Enter>", self.in_inner_window) 498 f.bind("<Motion>", self.in_inner_window) 499 f.bind("<B1-Motion>", self.resize_inner_window) 500 501 # Title label 502 self.component('label').bind("<1>", self.inner_raise_set) 503 self.component('label').bind("<B1-Motion>", self.inner_place_set) 504 self.component('label').bind("<2>", self.inner_lower_set) 505 if button: 506 # The Destroy button 507 button.bind("<Double-1>", 508 lambda event: self.after_idle(self.destroy))
509 510 511 512 513 Pmw.forwardmethods(MultipleWin, Frame, MultipleWin.interior) 514 Pmw.forwardmethods(InnerWindow, Frame, InnerWindow.interior) 515