Module SuperToolTip
[hide private]
[frames] | no frames]

Source Code for Module SuperToolTip

   1  # --------------------------------------------------------------------------------- # 
   2  # SUPERTOOLTIP wxPython IMPLEMENTATION 
   3  # 
   4  # Andrea Gavana, @ 07 October 2008 
   5  # Latest Revision: 07 October 2008, 22.00 GMT 
   6  # 
   7  # 
   8  # TODO List 
   9  # 
  10  # 1) Maybe add some more customization like multiline text 
  11  #    in the header and footer; 
  12  # 2) Check whether it's possible to use rounded corners and 
  13  #    shadows on the Mac 
  14  # 
  15  # 
  16  # For all kind of problems, requests of enhancements and bug reports, please 
  17  # write to me at: 
  18  # 
  19  # andrea.gavana@gmail.com 
  20  # gavana@kpo.kz 
  21  # 
  22  # Or, obviously, to the wxPython mailing list!!! 
  23  # 
  24  # 
  25  # End Of Comments 
  26  # --------------------------------------------------------------------------------- # 
  27   
  28  """ 
  29  Description 
  30  =========== 
  31   
  32  SuperToolTip is a class that mimics the behaviour of wx.TipWindow and generic tooltip 
  33  windows, although it is a custom-drawn widget.  
  34  This class supports: 
  35   
  36  * Blended triple-gradient for the tooltip background; 
  37  * Header text and header image, with possibility to set the header font indipendently; 
  38  * Footer text and footer image, with possibility to set the footer font indipendently; 
  39  * Multiline text message in the tooltip body, plus an optional image as "body image"; 
  40  * Bold lines and hyperlink lines in the tooltip body; 
  41  * A wide set of predefined drawing styles for the tooltip background; 
  42  * Drawing of separator lines after the header and/or before the footer; 
  43  * Rounded corners and shadows below the tooltip window (Windows XP only); 
  44  * Fade in/fade out effects (Windows XP only); 
  45  * User-settable delays for the delay after which the tooltip appears and the delay 
  46    after which the tooltip is destroyed. 
  47   
  48  And a lot more. Check the demo for an almost complete review of the functionalities. 
  49   
  50   
  51  Supported Platforms 
  52  =================== 
  53   
  54  SuperToolTip has been tested on the following platforms: 
  55    * Windows (Windows XP). 
  56   
  57   
  58  Latest Revision: Andrea Gavana @ 07 October 2008, 22.00 GMT 
  59  Version 0.1 
  60   
  61  """ 
  62   
  63  import wx 
  64  import webbrowser 
  65   
  66  # Let's see if we can add few nice shadows to our tooltips (Windows only) 
  67  _libimported = None 
  68   
  69  if wx.Platform == "__WXMSW__": 
  70      osVersion = wx.GetOsVersion() 
  71      # Shadows behind menus are supported only in XP 
  72      if osVersion[1] == 5 and osVersion[2] == 1: 
  73          try: 
  74              # Try Mark Hammond's win32all extensions 
  75              import win32api 
  76              import win32con 
  77              import win32gui 
  78              import winxpgui 
  79              _libimported = "MH" 
  80          except ImportError: 
  81              _libimported = None 
  82      else: 
  83          _libimported = None 
  84   
  85   
  86  # Define a bunch of predefined colour schemes... 
  87   
  88  _colorSchemes = {"Beige": (wx.Colour(255,255,255), wx.Colour(242,242,223), wx.Colour(198,195,160), wx.Colour(0,0,0)), 
  89                   "Blue": (wx.Colour(255,255,255), wx.Colour(202,220,246), wx.Colour(150,180,222), wx.Colour(0,0,0)), 
  90                   "Blue 2": (wx.Colour(255,255,255), wx.Colour(228,236,248), wx.Colour(198,214,235), wx.Colour(0,0,0)), 
  91                   "Blue 3": (wx.Colour(255,255,255), wx.Colour(213,233,243), wx.Colour(151,195,216), wx.Colour(0,0,0)), 
  92                   "Blue 4": (wx.Colour(255,255,255), wx.Colour(227,235,255), wx.Colour(102,153,255), wx.Colour(0,0,0)), 
  93                   "Blue Glass": (wx.Colour(182,226,253), wx.Colour(137,185,232), wx.Colour(188,244,253), wx.Colour(0,0,0)), 
  94                   "Blue Glass 2": (wx.Colour(192,236,255), wx.Colour(147,195,242), wx.Colour(198,254,255), wx.Colour(0,0,0)), 
  95                   "Blue Glass 3": (wx.Colour(212,255,255), wx.Colour(167,215,255), wx.Colour(218,255,255), wx.Colour(0,0,0)), 
  96                   "Blue Inverted": (wx.Colour(117,160,222), wx.Colour(167,210,240), wx.Colour(233,243,255), wx.Colour(0,0,0)), 
  97                   "Blue Shift": (wx.Colour(124,178,190), wx.Colour(13,122,153),  wx.Colour(0,89,116),    wx.Colour(255,255,255)), 
  98                   "CodeProject": (wx.Colour(255,250,172), wx.Colour(255,207,157), wx.Colour(255,153,0),   wx.Colour(0,0,0)), 
  99                   "Dark Gray": (wx.Colour(195,195,195), wx.Colour(168,168,168), wx.Colour(134,134,134), wx.Colour(255,255,255)), 
 100                   "Deep Purple": (wx.Colour(131,128,164), wx.Colour(112,110,143), wx.Colour(90,88,117),   wx.Colour(255,255,255)), 
 101                   "Electric Blue": (wx.Colour(224,233,255), wx.Colour(135,146,251), wx.Colour(99,109,233),  wx.Colour(0,0,0)), 
 102                   "Firefox": (wx.Colour(255,254,207), wx.Colour(254,248,125), wx.Colour(225,119,24),  wx.Colour(0,0,0)), 
 103                   "Gold": (wx.Colour(255,202,0),   wx.Colour(255,202,0),   wx.Colour(255,202,0),   wx.Colour(0,0,0)), 
 104                   "Gold Shift": (wx.Colour(178,170,107), wx.Colour(202,180,32),  wx.Colour(162,139,1),   wx.Colour(255,255,255)), 
 105                   "Gray": (wx.Colour(255,255,255), wx.Colour(228,228,228), wx.Colour(194,194,194), wx.Colour(0,0,0)), 
 106                   "Green": (wx.Colour(234,241,223), wx.Colour(211,224,180), wx.Colour(182,200,150), wx.Colour(0,0,0)), 
 107                   "Green Shift": (wx.Colour(129,184,129), wx.Colour(13,185,15),   wx.Colour(1,125,1), wx.Colour(255,255,255)), 
 108                   "Light Green": (wx.Colour(174,251,171), wx.Colour(145,221,146), wx.Colour(90,176,89),   wx.Colour(0,0,0)), 
 109                   "NASA Blue": (wx.Colour(0,91,134),    wx.Colour(0,100,150),   wx.Colour(0,105,160),   wx.Colour(255,255,255)), 
 110                   "Office 2007 Blue": (wx.Colour(255,255,255), wx.Colour(242,246,251), wx.Colour(202,218,239), wx.Colour(76,76,76)), 
 111                   "Orange Shift": (wx.Colour(179,120,80),  wx.Colour(183,92,19),   wx.Colour(157,73,1),    wx.Colour(255,255,255)), 
 112                   "Outlook Green": (wx.Colour(236,242,208), wx.Colour(219,230,187), wx.Colour(195,210,155), wx.Colour(0,0,0)), 
 113                   "Pale Green": (wx.Colour(249,255,248), wx.Colour(206,246,209), wx.Colour(148,225,155), wx.Colour(0,0,0)), 
 114                   "Pink Blush": (wx.Colour(255,254,255), wx.Colour(255,231,242), wx.Colour(255,213,233), wx.Colour(0,0,0)), 
 115                   "Pink Shift": (wx.Colour(202,135,188), wx.Colour(186,8,158),   wx.Colour(146,2,116),   wx.Colour(255,255,255)), 
 116                   "Pretty Pink": (wx.Colour(255,240,249), wx.Colour(253,205,217), wx.Colour(255,150,177), wx.Colour(0,0,0)), 
 117                   "Red": (wx.Colour(255,183,176), wx.Colour(253,157,143), wx.Colour(206,88,78),   wx.Colour(0,0,0)), 
 118                   "Red Shift": (wx.Colour(186,102,102), wx.Colour(229,23,9),    wx.Colour(182,11,1),    wx.Colour(255,255,255)), 
 119                   "Silver": (wx.Colour(255,255,255), wx.Colour(242,242,246), wx.Colour(212,212,224), wx.Colour(0,0,0)), 
 120                   "Silver 2": (wx.Colour(255,255,255), wx.Colour(242,242,248), wx.Colour(222,222,228), wx.Colour(0,0,0)), 
 121                   "Silver Glass": (wx.Colour(158,158,158), wx.Colour(255,255,255), wx.Colour(105,105,105), wx.Colour(0,0,0)), 
 122                   "Silver Inverted": (wx.Colour(161,160,186), wx.Colour(199,201,213), wx.Colour(255,255,255), wx.Colour(0,0,0)), 
 123                   "Silver Inverted 2": (wx.Colour(181,180,206), wx.Colour(219,221,233), wx.Colour(255,255,255), wx.Colour(0,0,0)), 
 124                   "Soylent Green": (wx.Colour(134,211,131), wx.Colour(105,181,106), wx.Colour(50,136,49),   wx.Colour(255,255,255)), 
 125                   "Spring Green": (wx.Colour(154,231,151), wx.Colour(125,201,126), wx.Colour(70,156,69),   wx.Colour(255,255,255)), 
 126                   "Too Blue": (wx.Colour(255,255,255), wx.Colour(225,235,244), wx.Colour(188,209,226), wx.Colour(0,0,0)), 
 127                   "Totally Green": (wx.Colour(190,230,160), wx.Colour(190,230,160), wx.Colour(190,230,160), wx.Colour(0,0,0)), 
 128                   "XP Blue": (wx.Colour(119,185,236), wx.Colour(81,144,223),  wx.Colour(36,76,171),   wx.Colour(255,255,255)), 
 129                   "Yellow": (wx.Colour(255,255,220), wx.Colour(255,231,161), wx.Colour(254,218,108), wx.Colour(0,0,0))} 
 130   
 131   
 132   
133 -def GetStyleKeys():
134 """ Returns the predefined styles keywords. """ 135 136 schemes = _colorSchemes.keys() 137 schemes.sort() 138 return schemes
139 140
141 -def MakeBold(font):
142 """ 143 Makes a font bold. Utility method. 144 145 @param font: the font to be made bold. 146 """ 147 148 newFont = wx.Font(font.GetPointSize(), font.GetFamily(), font.GetStyle(), 149 wx.BOLD, font.GetUnderlined(), font.GetFaceName()) 150 151 return newFont
152 153 168 169
170 -class ToolTipWindowBase(object):
171 """ Base class for the different Windows and Mac implementation. """ 172
173 - def __init__(self, parent, classParent):
174 """ 175 Default class constructor. 176 177 @param parent: the L{SuperToolTip} parent widget; 178 @param classParent: the L{SuperToolTip} class object. 179 """ 180 181 self._spacing = 6 182 self._wasOnLink = False 183 self._hyperlinkRect, self._hyperlinkWeb = [], [] 184 185 self._classParent = classParent 186 self._alphaTimer = wx.Timer(self, wx.ID_ANY) 187 188 # Initialize the fonts 189 self.InitFont() 190 191 # Bind the events 192 self.Bind(wx.EVT_PAINT, self.OnPaint) 193 self.Bind(wx.EVT_SIZE, self.OnSize) 194 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) 195 self.Bind(wx.EVT_MOTION, self.OnMouseMotion) 196 self.Bind(wx.EVT_TIMER, self.AlphaCycle) 197 self.Bind(wx.EVT_KILL_FOCUS, self.OnDestroy) 198 self.Bind(wx.EVT_LEFT_DOWN, self.OnDestroy) 199 self.Bind(wx.EVT_LEFT_DCLICK, self.OnDestroy)
200 201
202 - def InitFont(self):
203 """ Initalizes the fonts for L{SuperToolTip}. """ 204 205 self._messageFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) 206 self._headerFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) 207 self._headerFont.SetWeight(wx.BOLD) 208 self._footerFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) 209 self._footerFont.SetWeight(wx.BOLD) 210 self._hyperlinkFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) 211 self._hyperlinkFont.SetWeight(wx.BOLD) 212 self._hyperlinkFont.SetUnderlined(True)
213 214
215 - def OnPaint(self, event):
216 """ Handles the wx.EVT_PAINT event for L{SuperToolTip}. """ 217 218 # Go with double buffering... 219 dc = wx.BufferedPaintDC(self) 220 221 frameRect = self.GetClientRect() 222 x, y, width, height = frameRect 223 # Store the rects for the hyperlink lines 224 self._hyperlinkRect, self._hyperlinkWeb = [], [] 225 classParent = self._classParent 226 227 # Retrieve the colours for the blended triple-gradient background 228 topColour, middleColour, bottomColour = classParent.GetTopGradientColour(), \ 229 classParent.GetMiddleGradientColour(), \ 230 classParent.GetBottomGradientColour() 231 232 # Get the user options for header, bitmaps etc... 233 drawHeader, drawFooter = classParent.GetDrawHeaderLine(), classParent.GetDrawFooterLine() 234 topRect = wx.Rect(frameRect.x, frameRect.y, frameRect.width, frameRect.height/2) 235 bottomRect = wx.Rect(frameRect.x, frameRect.y+frameRect.height/2, frameRect.width, frameRect.height/2+1) 236 # Fill the triple-gradient 237 dc.GradientFillLinear(topRect, topColour, middleColour, wx.SOUTH) 238 dc.GradientFillLinear(bottomRect, middleColour, bottomColour, wx.SOUTH) 239 240 header, headerBmp = classParent.GetHeader(), classParent.GetHeaderBitmap() 241 xPos, yPos = self._spacing, 0 242 bmpXPos = bmpYPos = 0 243 bmpHeight = textHeight = bmpWidth = 0 244 245 if headerBmp and headerBmp.IsOk(): 246 # We got the header bitmap 247 bmpHeight, bmpWidth = headerBmp.GetHeight(), headerBmp.GetWidth() 248 bmpXPos = self._spacing 249 250 if header: 251 # We got the header text 252 dc.SetFont(self._headerFont) 253 textWidth, textHeight = dc.GetTextExtent(header) 254 255 # Calculate the header height 256 height = max(textHeight, bmpHeight) 257 if header: 258 dc.DrawText(header, bmpXPos+bmpWidth+self._spacing, (height-textHeight+self._spacing)/2) 259 if headerBmp and headerBmp.IsOk(): 260 dc.DrawBitmap(headerBmp, bmpXPos, (height-bmpHeight+self._spacing)/2) 261 262 if header or (headerBmp and headerBmp.IsOk()): 263 yPos += height 264 if drawHeader: 265 # Draw the separator line after the header 266 dc.SetPen(wx.GREY_PEN) 267 dc.DrawLine(self._spacing, yPos+self._spacing, width-self._spacing, yPos+self._spacing) 268 269 # Get the big body image (if any) 270 embeddedImage = classParent.GetBodyImage() 271 bmpWidth = bmpHeight = -1 272 if embeddedImage and embeddedImage.IsOk(): 273 bmpWidth, bmpHeight = embeddedImage.GetWidth(), embeddedImage.GetHeight() 274 275 # A bunch of calculations to draw the main body message 276 messageHeight = 0 277 textSpacing = (bmpWidth > 0 and [3*self._spacing] or [2*self._spacing])[0] 278 lines = classParent.GetMessage().split("\n") 279 yText = yPos 280 normalText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUTEXT) 281 hyperLinkText = wx.BLUE 282 283 for indx, line in enumerate(lines): 284 # Loop over all the lines in the message 285 isLink = False 286 dc.SetTextForeground(normalText) 287 if line.startswith("</b>"): # is a bold line 288 line = line[4:] 289 font = MakeBold(self._messageFont) 290 dc.SetFont(font) 291 elif line.startswith("</l>"): # is a link 292 dc.SetFont(self._hyperlinkFont) 293 isLink = True 294 line, hl = ExtractLink(line) 295 dc.SetTextForeground(hyperLinkText) 296 else: 297 # Is a normal line 298 dc.SetFont(self._messageFont) 299 300 textWidth, textHeight = dc.GetTextExtent(line) 301 if textHeight == 0: 302 textWidth, textHeight = dc.GetTextExtent("a") 303 304 messageHeight += textHeight 305 306 xText = (bmpWidth > 0 and [bmpWidth+2*self._spacing] or [self._spacing])[0] 307 yText += textHeight/2+self._spacing 308 309 dc.DrawText(line, xText, yText) 310 if isLink: 311 # Store the hyperlink rectangle and link 312 self._hyperlinkRect.append(wx.Rect(xText, yText, textWidth, textHeight)) 313 self._hyperlinkWeb.append(hl) 314 315 if indx == 0: 316 messagePos = yText 317 318 toAdd = 0 319 if bmpHeight > textHeight: 320 yPos += 2*self._spacing + bmpHeight 321 toAdd = self._spacing 322 else: 323 yPos += messageHeight + 2*self._spacing 324 325 yText = max(messageHeight, bmpHeight+2*self._spacing) 326 if embeddedImage and embeddedImage.IsOk(): 327 # Draw the main body image 328 dc.DrawBitmap(embeddedImage, self._spacing, messagePos) 329 330 footer, footerBmp = classParent.GetFooter(), classParent.GetFooterBitmap() 331 bmpHeight = bmpWidth = textHeight = textWidth = 0 332 bmpXPos = bmpYPos = 0 333 334 if footerBmp and footerBmp.IsOk(): 335 # Got the footer bitmap 336 bmpHeight, bmpWidth = footerBmp.GetHeight(), footerBmp.GetWidth() 337 bmpXPos = self._spacing 338 339 if footer: 340 # Got the footer text 341 dc.SetFont(self._footerFont) 342 textWidth, textHeight = dc.GetTextExtent(footer) 343 344 if textHeight or bmpHeight: 345 if drawFooter: 346 # Draw the separator line before the footer 347 dc.SetPen(wx.GREY_PEN) 348 dc.DrawLine(self._spacing, yPos-self._spacing/2+toAdd, width-self._spacing, yPos-self._spacing/2+toAdd) 349 350 # Draw the footer and footer bitmap (if any) 351 dc.SetTextForeground(normalText) 352 height = max(textHeight, bmpHeight) 353 yPos += toAdd 354 if footer: 355 dc.DrawText(footer, bmpXPos+bmpWidth+self._spacing, yPos + (height-textHeight+self._spacing)/2) 356 if footerBmp and footerBmp.IsOk(): 357 dc.DrawBitmap(footerBmp, bmpXPos, yPos + (height-bmpHeight+self._spacing)/2)
358 359
360 - def OnEraseBackground(self, event):
361 """ Handles the wx.EVT_ERASE_BACKGROUND event for L{SuperToolTip}. """ 362 363 # This is intentionally empty to reduce flicker 364 pass
365 366
367 - def OnSize(self, event):
368 """ Handles the wx.EVT_SIZE event for L{SuperToolTip}. """ 369 370 self.Refresh() 371 event.Skip()
372 373
374 - def OnMouseMotion(self, event):
375 """ Handles the wx.EVT_MOTION event for L{SuperToolTip}. """ 376 377 x, y = event.GetPosition() 378 for rect in self._hyperlinkRect: 379 if rect.Contains((x, y)): 380 # We are over one hyperlink... 381 self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) 382 self._wasOnLink = True 383 return 384 385 if self._wasOnLink: 386 # Restore the normal cursor 387 self._wasOnLink = False 388 self.SetCursor(wx.NullCursor)
389 390
391 - def OnDestroy(self, event):
392 """ 393 Handles the wx.EVT_LEFT_DOWN, wx.EVT_LEFT_DCLICK and wx.EVT_KILL_FOCUS 394 events for L{SuperToolTip}. All these events destroy the L{SuperToolTip}, 395 unless the user clicked on one hyperlink. 396 """ 397 398 if not isinstance(event, wx.MouseEvent): 399 # We haven't clicked a link 400 self.Destroy() 401 return 402 403 x, y = event.GetPosition() 404 for indx, rect in enumerate(self._hyperlinkRect): 405 if rect.Contains((x, y)): 406 # Run the webbrowser with the clicked link 407 webbrowser.open_new_tab(self._hyperlinkWeb[indx]) 408 return 409 410 if self._classParent.GetUseFade(): 411 # Fade out... 412 self.StartAlpha(False) 413 else: 414 self.Destroy()
415 416
417 - def StartAlpha(self, isShow):
418 """ 419 Start the timer which set the alpha channel for L{SuperToolTip}. 420 421 @param isShow: whether L{SuperToolTip} is being shown or deleted. 422 """ 423 424 if self._alphaTimer.IsRunning(): 425 return 426 427 # Calculate starting alpha value and its step 428 self.amount = (isShow and [0] or [255])[0] 429 self.delta = (isShow and [5] or [-5])[0] 430 # Start the timer 431 self._alphaTimer.Start(30)
432 433
434 - def SetFont(self, font):
435 """ 436 Sets the L{SuperToolTip} font globally. 437 438 @param font: the font to set. 439 """ 440 441 wx.PopupWindow.SetFont(self, font) 442 self.InitFont() 443 self.Invalidate()
444 445
446 - def Invalidate(self):
447 """ Invalidate L{SuperToolTip} size and repaint it. """ 448 449 if not self._classParent.GetMessage(): 450 # No message yet... 451 return 452 453 self.CalculateBestSize() 454 self.Refresh()
455 456
457 - def DropShadow(self, drop=True):
458 """ 459 Adds a shadow under the window (Windows XP only). 460 461 @param drop: whether to drop a shadow or not. 462 """ 463 464 if not _libimported: 465 # No Mark Hammond's win32all extension 466 return 467 468 if wx.Platform != "__WXMSW__": 469 # This works only on Windows XP 470 return 471 472 hwnd = self.GetHandle() 473 474 # Create a rounded rectangle region 475 size = self.GetSize() 476 if drop: 477 rgn = win32gui.CreateRoundRectRgn(0, 0, size.x, size.y, 9, 9) 478 win32gui.SetWindowRgn(hwnd, rgn, True) 479 480 CS_DROPSHADOW = 0x00020000 481 # Load the user32 library 482 if not hasattr(self, "_winlib"): 483 self._winlib = win32api.LoadLibrary("user32") 484 485 csstyle = win32api.GetWindowLong(hwnd, win32con.GCL_STYLE) 486 if drop: 487 if csstyle & CS_DROPSHADOW: 488 return 489 else: 490 csstyle |= CS_DROPSHADOW #Nothing to be done 491 else: 492 csstyle &= ~CS_DROPSHADOW 493 494 # Drop the shadow underneath the window 495 GCL_STYLE= -26 496 cstyle= win32gui.GetClassLong(hwnd, GCL_STYLE) 497 if drop: 498 if cstyle & CS_DROPSHADOW == 0: 499 win32api.SetClassLong(hwnd, GCL_STYLE, cstyle | CS_DROPSHADOW) 500 else: 501 win32api.SetClassLong(hwnd, GCL_STYLE, cstyle &~ CS_DROPSHADOW)
502 503
504 - def AlphaCycle(self, event):
505 """ Handles the wx.EVT_TIMER event for L{SuperToolTip}. """ 506 507 # Increase (or decrease) the alpha channel 508 self.amount += self.delta 509 510 if self.amount > 255 or self.amount < 0: 511 # We're done, stop the timer 512 self._alphaTimer.Stop() 513 if self.amount < 0: 514 # Destroy the SuperToolTip, we are fading out 515 self.Destroy() 516 return 517 518 # Make the SuperToolTip more or less transparent 519 self.MakeWindowTransparent(self.amount) 520 if not self.IsShown(): 521 self.Show()
522 523
524 - def MakeWindowTransparent(self, amount):
525 """ 526 Makes the L{SuperToolTip} window transparent. 527 528 @param amount: the alpha channel value. 529 """ 530 531 if not _libimported: 532 # No way, only Windows XP with Mark Hammond's win32all 533 return 534 535 # this API call is not in all SDKs, only the newer ones, so 536 # we will runtime bind this 537 if wx.Platform != "__WXMSW__": 538 return 539 540 hwnd = self.GetHandle() 541 542 if not hasattr(self, "_winlib"): 543 self._winlib = win32api.LoadLibrary("user32") 544 545 pSetLayeredWindowAttributes = win32api.GetProcAddress(self._winlib, 546 "SetLayeredWindowAttributes") 547 548 if pSetLayeredWindowAttributes == None: 549 return 550 551 exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) 552 if 0 == (exstyle & 0x80000): 553 win32api.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, exstyle | 0x80000) 554 555 winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
556 557
558 - def CalculateBestSize(self):
559 """ Calculates the L{SuperToolTip} window best size. """ 560 561 # See the OnPaint method for explanations... 562 maxWidth = maxHeight = 0 563 dc = wx.ClientDC(self) 564 565 classParent = self._classParent 566 header, headerBmp = classParent.GetHeader(), classParent.GetHeaderBitmap() 567 568 textHeight, bmpHeight = 0, 0 569 if header: 570 dc.SetFont(self._headerFont) 571 textWidth, textHeight = dc.GetTextExtent(header) 572 maxWidth = max(maxWidth, textWidth + 2*self._spacing) 573 maxHeight += self._spacing/2 574 if headerBmp and headerBmp.IsOk(): 575 maxWidth += headerBmp.GetWidth() + 2*self._spacing 576 bmpHeight = headerBmp.GetHeight() 577 if not header: 578 maxHeight += self._spacing/2 579 580 maxHeight += max(textHeight, bmpHeight) 581 if textHeight or bmpHeight: 582 maxHeight += self._spacing/2 583 584 # See the OnPaint method for explanations... 585 bmpWidth = bmpHeight = -1 586 embeddedImage = classParent.GetBodyImage() 587 if embeddedImage and embeddedImage.IsOk(): 588 bmpWidth, bmpHeight = embeddedImage.GetWidth(), embeddedImage.GetHeight() 589 590 messageHeight = 0 591 textSpacing = (bmpWidth and [3*self._spacing] or [2*self._spacing])[0] 592 lines = classParent.GetMessage().split("\n") 593 594 for line in lines: 595 if line.startswith("</b>"): # is a bold line 596 font = MakeBold(self._messageFont) 597 dc.SetFont(font) 598 line = line[4:] 599 elif line.startswith("</l>"): # is a link 600 dc.SetFont(self._hyperlinkFont) 601 line, hl = ExtractLink(line) 602 else: 603 dc.SetFont(self._messageFont) 604 605 textWidth, textHeight = dc.GetTextExtent(line) 606 if textHeight == 0: 607 textWidth, textHeight = dc.GetTextExtent("a") 608 609 maxWidth = max(maxWidth, textWidth + textSpacing + bmpWidth) 610 messageHeight += textHeight 611 612 # See the OnPaint method for explanations... 613 messageHeight = max(messageHeight, bmpHeight) 614 maxHeight += messageHeight 615 toAdd = 0 616 if bmpHeight > textHeight: 617 maxHeight += 2*self._spacing 618 toAdd = self._spacing 619 else: 620 maxHeight += 2*self._spacing 621 622 footer, footerBmp = classParent.GetFooter(), classParent.GetFooterBitmap() 623 textHeight, bmpHeight = 0, 0 624 625 # See the OnPaint method for explanations... 626 if footer: 627 dc.SetFont(self._footerFont) 628 textWidth, textHeight = dc.GetTextExtent(footer) 629 maxWidth = max(maxWidth, textWidth + 2*self._spacing) 630 maxHeight += self._spacing/2 631 632 if footerBmp and footerBmp.IsOk(): 633 bmpWidth, bmpHeight = footerBmp.GetWidth(), footerBmp.GetHeight() 634 maxWidth = max(maxWidth, textWidth + 3*self._spacing + bmpWidth) 635 if not footer: 636 maxHeight += self._spacing/2 637 638 if textHeight or bmpHeight: 639 maxHeight += self._spacing/2 + max(textHeight, bmpHeight) 640 641 maxHeight += toAdd 642 self.SetSize((maxWidth, maxHeight))
643 644 645 # Handle Mac and Windows/GTK differences... 646 647 if wx.Platform == "__WXMAC__": 648
649 - class ToolTipWindow(wx.Frame, ToolTipWindowBase):
650 """ Popup window that works on wxMac. """ 651
652 - def __init__(self, parent, classParent):
653 """ 654 Default class constructor. 655 656 @param parent: the L{SuperToolTip} widget parent; 657 @param classParent: the L{SuperToolTip} object parent. 658 """ 659 660 wx.Frame.__init__(self, parent, style=wx.NO_BORDER|wx.FRAME_FLOAT_ON_PARENT|wx.FRAME_NO_TASKBAR|wx.POPUP_WINDOW) 661 # Call the base class 662 ToolTipWindowBase.__init__(self, parent, classParent)
663 664 else: 665
666 - class ToolTipWindow(ToolTipWindowBase, wx.PopupWindow):
667 """ 668 A simple wx.PopupWindow that holds fancy tooltips. 669 Not available on Mac as wx.PopupWindow is not implemented. 670 """ 671
672 - def __init__(self, parent, classParent):
673 """ 674 Default class constructor. 675 676 @param parent: the L{SuperToolTip} parent; 677 @param classParent: the L{SuperToolTip} object parent. 678 """ 679 680 wx.PopupWindow.__init__(self, parent) 681 # Call the base class 682 ToolTipWindowBase.__init__(self, parent, classParent)
683 684
685 -class SuperToolTip(object):
686 """ 687 The main class for L{SuperToolTip}, which holds all the methods 688 and setters/getters available to the user. 689 """ 690
691 - def __init__(self, message, bodyImage=wx.NullBitmap, header="", headerBmp=wx.NullBitmap, 692 footer="", footerBmp=wx.NullBitmap):
693 """ 694 Default class constructor. 695 696 @param message: the main message in L{SuperToolTip} body; 697 @param bodyImage: the image in the L{SuperToolTip} body; 698 @param header: the header text; 699 @param headerBmp: the header bitmap; 700 @param footer: the footer text; 701 @param footerBmp: the footer bitmap. 702 """ 703 704 self._superToolTip = None 705 706 # Set all the initial options 707 self.SetMessage(message) 708 self.SetBodyImage(bodyImage) 709 self.SetHeader(header) 710 self.SetHeaderBitmap(headerBmp) 711 self.SetFooter(footer) 712 self.SetFooterBitmap(footerBmp) 713 self._dropShadow = False 714 self._useFade = False 715 716 # Get the running applications 717 self._runningApp = wx.GetApp() 718 self._runningApp.__superToolTip = True 719 720 # Build a couple of timers... 721 self._startTimer = wx.PyTimer(self.OnStartTimer) 722 self._endTimer = wx.PyTimer(self.OnEndTimer) 723 724 self.SetStartDelay() 725 self.SetEndDelay()
726 727
728 - def SetTarget(self, widget):
729 """ 730 Sets the target window for L{SuperToolTip}. 731 732 @param widget: the widget to which L{SuperToolTip} is associated. 733 """ 734 735 self._widget = widget 736 737 self._widget.Bind(wx.EVT_ENTER_WINDOW, self.OnWidgetEnter) 738 self._widget.Bind(wx.EVT_LEAVE_WINDOW, self.OnWidgetLeave)
739 740
741 - def GetTarget(self):
742 """ Returns the target window for L{SuperToolTip}. """ 743 744 if not hasattr(self, "_widget"): 745 raise Exception("\nError: the widget target for L{SuperToolTip} has not been set.") 746 747 return self._widget
748 749
750 - def SetStartDelay(self, delay=1):
751 """ 752 Sets the time delay (in seconds) after which the L{SuperToolTip} is created. 753 754 @param delay: the delay in seconds. 755 """ 756 757 self._startDelayTime = float(delay)
758 759
760 - def GetStartDelay(self):
761 """ Returns the tim delay (in seconds) after which the L{SuperToolTip} is created.""" 762 763 return self._startDelayTime
764 765
766 - def SetEndDelay(self, delay=1e6):
767 """ 768 Sets the delay time (in seconds) after which the L{SuperToolTip} is destroyed. 769 770 @param delay: the delay in seconds. 771 """ 772 773 self._endDelayTime = float(delay)
774 775
776 - def GetEndDelay(self):
777 """ Returns the delay time (in seconds) after which the L{SuperToolTip} is destroyed.""" 778 779 return self._endDelayTime
780 781
782 - def OnWidgetEnter(self, event):
783 """Starts the L{SuperToolTip} timer for creation, handles the wx.EVT_ENTER_WINDOW event.""" 784 785 if self._superToolTip: 786 # Not yet created 787 return 788 789 if not self._runningApp.__superToolTip: 790 # The running app doesn't want tooltips... 791 return 792 793 if self._startTimer.IsRunning(): 794 # We are already running 795 event.Skip() 796 return 797 798 self._startTimer.Start(self._startDelayTime*1000) 799 event.Skip()
800 801
802 - def OnWidgetLeave(self, event):
803 """ Handles the wx.EVT_LEAVE_WINDOW for the target widgets. """ 804 805 pos = wx.GetMousePosition() 806 realPos = self._widget.ScreenToClient(pos) 807 rect = self._widget.GetClientRect() 808 809 if rect.Contains(realPos): 810 # We get fake leave events... 811 event.Skip() 812 return 813 814 if self._superToolTip: 815 if self.GetUseFade(): 816 # Fade out... 817 self._superToolTip.StartAlpha(False) 818 else: 819 self._superToolTip.Destroy() 820 821 self._startTimer.Stop() 822 self._endTimer.Stop() 823 824 event.Skip()
825 826
827 - def OnStartTimer(self):
828 """ The creation time has expired, create the L{SuperToolTip}. """ 829 830 tip = ToolTipWindow(self._widget, self) 831 self._superToolTip = tip 832 self._superToolTip.CalculateBestSize() 833 self._superToolTip.SetPosition(wx.GetMousePosition()) 834 self._superToolTip.DropShadow(self.GetDropShadow()) 835 836 if self.GetUseFade(): 837 self._superToolTip.StartAlpha(True) 838 else: 839 self._superToolTip.Show() 840 841 self._startTimer.Stop() 842 self._endTimer.Start(self._endDelayTime*1000)
843 844
845 - def OnEndTimer(self):
846 """ The show time for L{SuperToolTip} has expired, destroy the L{SuperToolTip}. """ 847 848 if self._superToolTip: 849 if self.GetUseFade(): 850 self._superToolTip.StartAlpha(False) 851 else: 852 self._superToolTip.Destroy() 853 854 self._endTimer.Stop()
855 856
857 - def OnDestroy(self, event):
858 """ Handles the L{SuperToolTip} target destruction. """ 859 860 if self._superToolTip: 861 # Unbind the events! 862 self._widget.Unbind(wx.EVT_LEAVE_WINDOW) 863 self._widget.Unbind(wx.EVT_ENTER_WINDOW) 864 865 self._superToolTip.Destroy() 866 del self._superToolTip 867 self._superToolTip = None
868 869
870 - def SetHeaderBitmap(self, bmp):
871 """ 872 Sets the header bitmap for L{SuperToolTip}. 873 874 @param bmp: the bitmap to use. 875 """ 876 877 self._headerBmp = bmp 878 if self._superToolTip: 879 self._superToolTip.Invalidate()
880 881
882 - def GetHeaderBitmap(self):
883 """ Returns the header bitmap. """ 884 885 return self._headerBmp
886 887
888 - def SetHeader(self, header):
889 """ 890 Sets the header text. 891 892 @param header: the header text to display. 893 """ 894 895 self._header = header 896 if self._superToolTip: 897 self._superToolTip.Invalidate()
898 899
900 - def GetHeader(self):
901 """ Returns the header text. """ 902 903 return self._header
904 905
906 - def SetDrawHeaderLine(self, draw):
907 """ 908 Whether to draw a separator line after the header or not. 909 910 @param draw: bool value. 911 """ 912 913 self._topLine = draw 914 if self._superToolTip: 915 self._superToolTip.Refresh()
916 917
918 - def GetDrawHeaderLine(self):
919 """ Returns whether the separator line after the header is drawn or not. """ 920 921 return self._topLine
922 923
924 - def SetBodyImage(self, bmp):
925 """ 926 Sets the main body bitmap for L{SuperToolTip}. 927 928 @param bmp: the bitmap to use. 929 """ 930 931 self._embeddedImage = bmp 932 if self._superToolTip: 933 self._superToolTip.Invalidate()
934 935
936 - def GetBodyImage(self):
937 """ Returns the main body bitmap used in L{SuperToolTip}. """ 938 939 return self._embeddedImage
940 941
942 - def SetDrawFooterLine(self, draw):
943 """ 944 Whether to draw a separator line before the footer or not. 945 946 @param draw: bool value. 947 """ 948 949 self._bottomLine = draw 950 if self._superToolTip: 951 self._superToolTip.Refresh()
952 953
954 - def GetDrawFooterLine(self):
955 """ Returns whether the separator line before the footer is drawn or not. """ 956 957 return self._bottomLine
958 959
960 - def SetFooterBitmap(self, bmp):
961 """ 962 Sets the footer bitmap for L{SuperToolTip}. 963 964 @param bmp: the bitmap to use. 965 """ 966 967 self._footerBmp = bmp 968 if self._superToolTip: 969 self._superToolTip.Invalidate()
970 971
972 - def GetFooterBitmap(self):
973 """ Returns the footer bitmap. """ 974 975 return self._footerBmp
976 977
978 - def SetFooter(self, footer):
979 """ 980 Sets the footer text. 981 982 @param footer: the footer text to display. 983 """ 984 985 self._footer = footer 986 if self._superToolTip: 987 self._superToolTip.Invalidate()
988 989
990 - def GetFooter(self):
991 """ Returns the footer text. """ 992 993 return self._footer
994 995
996 - def SetMessage(self, message):
997 """ 998 Sets the main body message for L{SuperToolTip}. 999 1000 @param message: the message to display in the body. 1001 """ 1002 1003 self._message = message 1004 if self._superToolTip: 1005 self._superToolTip.Invalidate()
1006 1007
1008 - def GetMessage(self):
1009 """ Returns the main body message in L{SuperToolTip}. """ 1010 1011 return self._message
1012 1013
1014 - def SetTopGradientColour(self, colour):
1015 """ 1016 Sets the top gradient colour for L{SuperToolTip}. 1017 1018 @param colour: the colour to use as top colour. 1019 """ 1020 1021 self._topColour = colour 1022 if self._superToolTip: 1023 self._superToolTip.Refresh()
1024 1025
1026 - def SetMiddleGradientColour(self, colour):
1027 """ 1028 Sets the middle gradient colour for L{SuperToolTip}. 1029 1030 @param colour: the colour to use as middle colour. 1031 """ 1032 1033 self._middleColour = colour 1034 if self._superToolTip: 1035 self._superToolTip.Refresh()
1036 1037
1038 - def SetBottomGradientColour(self, colour):
1039 """ 1040 Sets the bottom gradient colour for L{SuperToolTip}. 1041 1042 @param colour: the colour to use as bottom colour. 1043 """ 1044 1045 self._bottomColour = colour 1046 if self._superToolTip: 1047 self._superToolTip.Refresh()
1048 1049
1050 - def SetTextColour(self, colour):
1051 """ 1052 Sets the text colour for L{SuperToolTip}. 1053 1054 @param colour: the colour to use as text colour. 1055 """ 1056 1057 self._textColour = colour 1058 if self._superToolTip: 1059 self._superToolTip.Refresh()
1060 1061
1062 - def GetTopGradientColour(self):
1063 """ Returns the top gradient colour. """ 1064 1065 return self._topColour
1066 1067
1068 - def GetMiddleGradientColour(self):
1069 """ Returns the middle gradient colour. """ 1070 1071 return self._middleColour
1072 1073
1074 - def GetBottomGradientColour(self):
1075 """ Returns the bottom gradient colour. """ 1076 1077 return self._bottomColour
1078 1079
1080 - def GetTextColour(self):
1081 """ Returns the text colour. """ 1082 1083 return self._textColour
1084 1085 1086 SetTopGradientColor = SetTopGradientColour 1087 SetMiddleGradientColor = SetMiddleGradientColour 1088 SetBottomGradientColor = SetBottomGradientColour 1089 GetTopGradientColor = GetTopGradientColour 1090 GetMiddleGradientColor = GetMiddleGradientColour 1091 GetBottomGradientColor = GetBottomGradientColour 1092 SetTextColor = SetTextColour 1093 GetTextColor = GetTextColour 1094 1095
1096 - def SetMessageFont(self, font):
1097 """ 1098 Sets the font for the main body message. 1099 1100 @param font: the font to use for the main body message. 1101 """ 1102 1103 self._messageFont = font 1104 if self._superToolTip: 1105 self._superToolTip.Invalidate()
1106 1107
1108 - def SetHeaderFont(self, font):
1109 """ 1110 Sets the font for the header text. 1111 1112 @param font: the font to use for the header text. 1113 """ 1114 1115 self._headerFont = font 1116 if self._superToolTip: 1117 self._superToolTip.Invalidate()
1118 1119
1120 - def SetFooterFont(self, font):
1121 """ 1122 Sets the font for the footer text. 1123 1124 @param font: the font to use for the footer text. 1125 """ 1126 1127 self._footerFont = font 1128 if self._superToolTip: 1129 self._superToolTip.Invalidate()
1130 1131
1132 - def GetMessageFont(self):
1133 """ Returns the font used in the main body message. """ 1134 1135 return self._messageFont
1136 1137
1138 - def GetHeaderFont(self):
1139 """ Returns the font used for the header text. """ 1140 1141 return self._headerFont
1142 1143
1144 - def GetFooterFont(self):
1145 """ Returns the font used for the footer text. """ 1146 1147 return self._footerFont
1148 1149
1150 - def SetDropShadow(self, drop):
1151 """ 1152 Whether to draw a shadow below L{SuperToolTip} or not. 1153 1154 @param drop: boolean value. 1155 @note: this functionality is available only in Windows XP 1156 with Mark Hammond win32all extensions installed. 1157 """ 1158 1159 self._dropShadow = drop 1160 if self._superToolTip: 1161 self._superToolTip.Invalidate()
1162 1163
1164 - def GetDropShadow(self):
1165 """ 1166 Returns whether a shadow below L{SuperToolTip} is drawn or not. 1167 1168 @note: this functionality is available only in Windows XP 1169 with Mark Hammond win32all extensions installed. 1170 """ 1171 1172 return self._dropShadow
1173 1174
1175 - def SetUseFade(self, fade):
1176 """ 1177 Whether to use a fade in/fade out effect or not. 1178 1179 @param fade: boolean value. 1180 @note: this functionality is available only in Windows XP 1181 with Mark Hammond win32all extensions installed. 1182 """ 1183 1184 self._useFade = fade
1185 1186
1187 - def GetUseFade(self):
1188 """ 1189 Returns whether a fade in/fade out effect is used or not. 1190 1191 @note: this functionality is available only in Windows XP 1192 with Mark Hammond win32all extensions installed. 1193 """ 1194 1195 return self._useFade
1196 1197
1198 - def ApplyStyle(self, style):
1199 """ 1200 Applies none of the predefined styles. 1201 1202 @param style: one of the predefined styles available at the 1203 beginning of the module. 1204 """ 1205 1206 if style not in _colorSchemes: 1207 raise Exception("Invalid style '%s' selected"%style) 1208 1209 top, middle, bottom, text = _colorSchemes[style] 1210 self._topColour = top 1211 self._middleColour = middle 1212 self._bottomColour = bottom 1213 self._textColour = text 1214 1215 if self._superToolTip: 1216 self._superToolTip.Refresh()
1217 1218
1219 - def EnableTip(self, enable=True):
1220 """ 1221 Globally (application-wide) enables/disables L{SuperToolTip}. 1222 1223 @param enable: whether to enable or disable L{SuperToolTip}. 1224 """ 1225 1226 wx.GetApp().__superToolTip = enable 1227 if not enable and self._superToolTip: 1228 self._superToolTip.Destroy() 1229 self._superToolTip = None 1230 del self._superToolTip
1231