1# --------------------------------------------------------------------------- # 2# SHAPEDBUTTON Control wxPython IMPLEMENTATION 3# Python Code By: 4# 5# Andrea Gavana, @ 18 Oct 2005 6# Latest Revision: 16 Jul 2012, 15.00 GMT 7# 8# 9# TODO List/Caveats 10# 11# 1. Elliptic Buttons May Be Handled Better, In My Opinion. They Look Nice 12# But They Are Somewhat More Difficult To Handle When Using Sizers. 13# This Is Probably Due To Some Lack In My Implementation; 14# 15# 2. I Am Unable To Translate The 2 Files "UpButton.png" And "DownButton.png" 16# Using "img2py" (Under wx.tools) Or With PIL In Order To Embed Them In 17# A Python File. Every Translation I Made, Did Not Preserve The Alpha 18# Channel So I Ended Up With Round Buttons Inside Black Squares. Does 19# Anyone Have A Suggestion Here? 20# 21# 3. Creating *A Lot* Of ShapedButtons May Require Some Time. In The Demo, 22# I Create 23 Buttons In About 0.4 Seconds On Windows XP, 3 GHz 1 GB RAM. 23# 24# 4. Creating Buttons With Size Greater Than wx.Size(200, 200) May Display 25# Buttons With Clearly Evident Pixel Edges. This Is Due To The Size Of The 26# Image Files I Load During Initialization. If This Is Not Satisfactory, 27# Please Let Me Know And I Will Upload Bigger Files. 28# 29# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please 30# Write To Me At: 31# 32# andrea.gavana@gmail.com 33# andrea.gavana@maerskoil.com 34# 35# Or, Obviously, To The wxPython Mailing List!!! 36# 37# Tags: phoenix-port, unittest, documented, py3-port 38# 39# End Of Comments 40# --------------------------------------------------------------------------- # 41 42 43""" 44:class:`~wx.lib.agw.shapedbutton.SButton` tries to fill the lack of "custom shaped" controls in wxPython 45and it can be used to build round or elliptic-shaped buttons. 46 47 48Description 49=========== 50 51`ShapedButton` tries to fill the lack of "custom shaped" controls in wxPython 52(that depends on the same lack in wxWidgets). It can be used to build round 53buttons or elliptic buttons. 54 55I have stolen some code from :mod:`lib.buttons` in order to recreate the same 56classes (`GenButton`, `GenBitmapButton`, `GenBitmapTextButton`, `GenToggleButton`, 57`GenBitmapToggleButton`, `GenBitmapTextToggleButton`). Here you have the same 58classes (with "Gen" replaced by "S"), with the same event handling, but they 59are rounded/elliptical buttons. 60 61`ShapedButton` is based on a :class:`wx.Window`, in which 2 images are drawn depending 62on the button state (pressed or not pressed). The 2 images have been stolen 63from Audacity (written with wxWidgets) and rearranged/reshaped/restyled 64using adobe PhotoShop. 65Changing the button colour in runtime was more difficult, but using some 66intelligent instruction from the PIL library it can be done. 67 68`ShapedButton` reacts on mouse events *only* if the mouse event occurred inside 69the circle/ellipse, even if `ShapedButton` is built on a rectangular window. 70This behavior is a lot different with respect to Audacity round buttons. 71 72 73Usage 74===== 75 76Usage example:: 77 78 import wx 79 import wx.lib.agw.shapedbutton as SB 80 81 class MyFrame(wx.Frame): 82 83 def __init__(self, parent): 84 85 wx.Frame.__init__(self, parent, -1, "ShapedButton Demo") 86 87 panel = wx.Panel(self) 88 89 # Create 2 bitmaps for the button 90 upbmp = wx.Bitmap("play.png", wx.BITMAP_TYPE_PNG) 91 disbmp = wx.Bitmap("playdisabled.png", wx.BITMAP_TYPE_PNG) 92 93 play = SB.SBitmapToggleButton(panel, -1, upbmp, (100, 50)) 94 play.SetUseFocusIndicator(False) 95 play.SetBitmapDisabled(disbmp) 96 97 98 # our normal wxApp-derived class, as usual 99 100 app = wx.App(0) 101 102 frame = MyFrame(None) 103 app.SetTopWindow(frame) 104 frame.Show() 105 106 app.MainLoop() 107 108 109The `ShapedButton` construction and usage is quite similar to the :mod:`lib.buttons` 110implementation. 111 112 113Methods and Settings 114==================== 115 116With `ShapedButton` you can: 117 118- Create rounded/elliptical buttons/togglebuttons; 119- Set images for the enabled/disabled/focused/selected state of the button; 120- Draw the focus indicator (or disable it); 121- Set label colour and font; 122- Apply a rotation to the `ShapedButton` label; 123- Change `ShapedButton` shape and text orientation in runtime. 124 125 126:note: `ShapedButton` **requires** PIL (Python Imaging Library) library to be installed, 127 which can be downloaded from http://www.pythonware.com/products/pil/ . 128 129 130Window Styles 131============= 132 133`No particular window styles are available for this class.` 134 135 136Events Processing 137================= 138 139This class processes the following events: 140 141================= ================================================== 142Event Name Description 143================= ================================================== 144``wx.EVT_BUTTON`` Process a `wxEVT_COMMAND_BUTTON_CLICKED` event, when the button is clicked. 145================= ================================================== 146 147 148License And Version 149=================== 150 151`ShapedButton` is distributed under the wxPython license. 152 153Latest revision: Andrea Gavana @ 16 Jul 2012, 15.00 GMT 154 155Version 0.5 156 157""" 158 159 160#---------------------------------------------------------------------- 161# Beginning Of SHAPEDBUTTON wxPython Code 162#---------------------------------------------------------------------- 163 164import wx 165 166# First Check If PIL Is Installed Properly 167try: 168 169 import PIL.Image as Image 170 171except ImportError: 172 173 errstr = ("\nShapedButton *Requires* PIL (Python Imaging Library).\n" 174 "You Can Get It At:\n\n" 175 "http://www.pythonware.com/products/pil/\n\n" 176 "ShapedButton Can Not Continue. Exiting...\n") 177 178 raise Exception(errstr) 179 180import os 181 182folder = os.path.split(__file__)[0] 183 184# Import Some Stuff For The Annoying Ellipsis... ;-) 185from math import sin, cos, pi 186 187#----------------------------------------------------------------------------- 188# PATH & FILE FILLING FUNCTION (OS INDEPENDENT) 189# This Is Required To Load The Pressed And Non-Pressed Images From The 190# "images" Directory. 191#----------------------------------------------------------------------------- 192 193def opj(path): 194 """ 195 Convert paths to the platform-specific separator. 196 197 :param `path`: the path to convert. 198 """ 199 200 strs = os.path.join(*tuple(path.split('/'))) 201 # HACK: on Linux, a leading / gets lost... 202 if path.startswith('/'): 203 strs = '/' + strs 204 205 return strs 206 207#----------------------------------------------------------------------------- 208 209 210#---------------------------------------------------------------------- 211# Class SButtonEvent 212# Code Stolen From wx.lib.buttons. This Class Handles All The Button 213# And ToggleButton Events. 214#---------------------------------------------------------------------- 215 216class SButtonEvent(wx.CommandEvent): 217 """ Event sent from the generic buttons when the button is activated. """ 218 219 def __init__(self, eventType, eventId): 220 """ 221 Default class constructor. 222 223 :param `eventType`: the event type; 224 :param `eventId`: the event identifier. 225 """ 226 227 wx.CommandEvent.__init__(self, eventType, eventId) 228 self.isDown = False 229 self.theButton = None 230 231 232 def SetIsDown(self, isDown): 233 """ 234 Sets the button event as pressed. 235 236 :param `isDown`: ``True`` to set the event as "pressed", ``False`` otherwise. 237 """ 238 239 self.isDown = isDown 240 241 242 def GetIsDown(self): 243 """ Returns ``True`` if the button event is "pressed". """ 244 245 return self.isDown 246 247 248 def SetButtonObj(self, btn): 249 """ 250 Sets the event object for the event. 251 252 :param `btn`: the button object. 253 """ 254 255 self.theButton = btn 256 257 258 def GetButtonObj(self): 259 """ Returns the object associated with this event. """ 260 261 return self.theButton 262 263 264#---------------------------------------------------------------------- 265# SBUTTON Class 266# This Is The Main Class Implementation. See __init__() Method For 267# Details. All The Other Button Types Depend On This Class For Event 268# Handling And Property Settings. 269#---------------------------------------------------------------------- 270 271class SButton(wx.Window): 272 """ This is the main implementation of `ShapedButton`. """ 273 274 _labeldelta = 1 275 276 def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, 277 size=wx.DefaultSize): 278 """ 279 Default class constructor. 280 281 :param `parent`: the :class:`SButton` parent. Must not be ``None``; 282 :param `id`: window identifier. A value of -1 indicates a default value; 283 :param `label`: the button text label; 284 :param `pos`: the control position. A value of (-1, -1) indicates a default position, 285 chosen by either the windowing system or wxPython, depending on platform; 286 :param `size`: the control size. A value of (-1, -1) indicates a default size, 287 chosen by either the windowing system or wxPython, depending on platform. 288 """ 289 290 wx.Window.__init__(self, parent, id, pos, size) 291 292 if label is None: 293 label = "" 294 295 self._enabled = True 296 self._isup = True 297 self._hasfocus = False 298 self._usefocusind = True 299 300 # Initialize Button Properties 301 self.SetButtonColour() 302 self.SetLabel(label) 303 self.SetLabelColour() 304 self.InitColours() 305 self.SetAngleOfRotation() 306 self.SetEllipseAxis() 307 308 if size == wx.DefaultSize: 309 self.SetInitialSize(self.DoGetBestSize()) 310 311 # Event Binding 312 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 313 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) 314 315 if wx.Platform == '__WXMSW__': 316 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) 317 318 self.Bind(wx.EVT_MOTION, self.OnMotion) 319 self.Bind(wx.EVT_SET_FOCUS, self.OnGainFocus) 320 self.Bind(wx.EVT_KILL_FOCUS, self.OnLoseFocus) 321 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) 322 self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) 323 324 self.Bind(wx.EVT_SIZE, self.OnSize) 325 self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) 326 self.Bind(wx.EVT_PAINT, self.OnPaint) 327 328 329 def SetButtonColour(self, colour=None): 330 """ 331 Sets the button colour, for all button states. 332 333 :param `colour`: an instance of :class:`wx.Colour`. 334 335 :note: The original button images are greyscale with a lot of pixels with 336 different colours. Changing smoothly the button colour in order to 337 give the same 3D effect can be efficiently done only with PIL. 338 """ 339 340 if colour is None: 341 colour = wx.WHITE 342 343 palette = colour.Red(), colour.Green(), colour.Blue() 344 345 self._buttoncolour = colour 346 347 self._mainbuttondown = DownButton.GetImage() 348 self._mainbuttonup = UpButton.GetImage() 349 350 351 352 def GetButtonColour(self): 353 """ Returns the button colour. """ 354 355 return self._buttoncolour 356 357 358 def SetLabelColour(self, colour=None): 359 """ 360 Sets the button label colour. 361 362 :param `colour`: an instance of :class:`wx.Colour`. 363 """ 364 365 if colour is None: 366 colour = wx.BLACK 367 368 self._labelcolour = colour 369 370 371 def GetLabelColour(self): 372 """ Returns the button label colour. """ 373 374 return self._labelcolour 375 376 377 def SetLabel(self, label=None): 378 """ 379 Sets the button label. 380 381 :param `label`: the new button label. 382 """ 383 384 if label is None: 385 label = "" 386 387 self._buttonlabel = label 388 389 390 def GetLabel(self): 391 """ Returns the button label. """ 392 393 return self._buttonlabel 394 395 396 def SetBestSize(self, size=None): 397 """ 398 Given the current font settings, calculate and set a good size. 399 400 :param `size`: if not ``None``, an instance of :class:`wx.Size` to pass to 401 `SetInitialSize`. 402 """ 403 404 if size is None: 405 size = wx.DefaultSize 406 407 self.SetInitialSize(size) 408 409 410 def DoGetBestSize(self): 411 """ 412 Overridden base class virtual. Determines the best size of the button 413 based on the label size. 414 415 :note: Overridden from :class:`wx.Window`. 416 """ 417 418 w, h, usemin = self._GetLabelSize() 419 defsize = wx.Button.GetDefaultSize() 420 width = 12 + w 421 422 if usemin and width < defsize.width: 423 width = defsize.width 424 425 height = 11 + h 426 427 if usemin and height < defsize.height: 428 height = defsize.height 429 430 return (width, height) 431 432 433 def AcceptsFocus(self): 434 """ 435 Can this window be given focus by mouse click? 436 437 :note: Overridden from :class:`wx.Window`. 438 """ 439 440 return self.IsShown() and self.IsEnabled() 441 442 443 def ShouldInheritColours(self): 444 """ 445 Overridden base class virtual. Buttons usually do not inherit 446 parent's colours. 447 """ 448 449 return False 450 451 452 def Enable(self, enable=True): 453 """ 454 Enables/disables the button. 455 456 :param `enable`: ``True`` to enable the button, ``False`` to disable it. 457 458 :note: Overridden from :class:`wx.Window`. 459 """ 460 461 self._enabled = enable 462 self.Refresh() 463 464 465 def IsEnabled(self): 466 """ Returns wheter the button is enabled or not. """ 467 468 return self._enabled 469 470 471 def SetUseFocusIndicator(self, flag): 472 """ 473 Specifies if a focus indicator (dotted line) should be used. 474 475 :param `flag`: ``True`` to use the focus indicator, ``False`` otherwise. 476 """ 477 478 self._usefocusind = flag 479 480 481 def GetUseFocusIndicator(self): 482 """ Returns focus indicator flag. """ 483 484 return self._usefocusind 485 486 487 def InitColours(self): 488 """ 489 Calculates a new set of focus indicator colour and indicator pen 490 based on button colour and label colour. 491 """ 492 493 textclr = self.GetLabelColour() 494 faceclr = self.GetButtonColour() 495 496 r, g, b, a = faceclr.Get() 497 hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64) 498 self._focusclr = wx.Colour(hr, hg, hb) 499 500 if wx.Platform == "__WXMAC__": 501 self._focusindpen = wx.Pen(textclr, 1) 502 else: 503 self._focusindpen = wx.Pen(textclr, 1, wx.USER_DASH) 504 self._focusindpen.SetDashes([1,1]) 505 self._focusindpen.SetCap(wx.CAP_BUTT) 506 507 508 def SetDefault(self): 509 """ Sets the button as default item. """ 510 511 self.GetParent().SetDefaultItem(self) 512 513 514 def _GetLabelSize(self): 515 """ Used internally """ 516 517 w, h = self.GetTextExtent(self.GetLabel()) 518 return w, h, True 519 520 521 def Notify(self): 522 """ Notifies an event and let it be processed. """ 523 524 evt = SButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId()) 525 evt.SetIsDown(not self._isup) 526 evt.SetButtonObj(self) 527 evt.SetEventObject(self) 528 self.GetEventHandler().ProcessEvent(evt) 529 530 531 def DrawMainButton(self, dc, width, height): 532 """ 533 Draws the main button, in whichever state it is. 534 535 :param `dc`: an instance of :class:`wx.DC`; 536 :param `width`: the button width; 537 :param `height`: the button height. 538 """ 539 540 w = min(width, height) 541 542 if w <= 2: 543 return 544 545 position = self.GetPosition() 546 547 main, secondary = self.GetEllipseAxis() 548 xc = width/2.0 549 yc = height/2.0 550 551 if abs(main - secondary) < 1e-6: 552 # In This Case The Button Is A Circle 553 if self._isup: 554 img = self._mainbuttonup.Scale(w, w) 555 else: 556 img = self._mainbuttondown.Scale(w, w) 557 else: 558 # In This Case The Button Is An Ellipse... Some Math To Do 559 rect = self.GetRect() 560 561 if main > secondary: 562 # This Is An Ellipse With Main Axis Aligned With X Axis 563 rect2 = w 564 rect3 = w*secondary//main 565 566 else: 567 # This Is An Ellipse With Main Axis Aligned With Y Axis 568 rect3 = w 569 rect2 = w*main//secondary 570 571 if self._isup: 572 img = self._mainbuttonup.Scale(rect2, rect3) 573 else: 574 img = self._mainbuttondown.Scale(rect2, rect3) 575 576 bmp = img.ConvertToBitmap() 577 578 if abs(main - secondary) < 1e-6: 579 if height > width: 580 xpos = 0 581 ypos = (height - width)//2 582 else: 583 xpos = (width - height)//2 584 ypos = 0 585 else: 586 if height > width: 587 if main > secondary: 588 xpos = 0 589 ypos = (height - rect3)//2 590 else: 591 xpos = (width - rect2)//2 592 ypos = (height - rect3)//2 593 else: 594 if main > secondary: 595 xpos = (width - rect2)//2 596 ypos = (height - rect3)//2 597 else: 598 xpos = (width - rect2)//2 599 ypos = 0 600 601 # Draw Finally The Bitmap 602 dc.DrawBitmap(bmp, xpos, ypos, True) 603 604 # Store Bitmap Position And Size To Draw An Elliptical Focus Indicator 605 self._xpos = xpos 606 self._ypos = ypos 607 self._imgx = img.GetWidth() 608 self._imgy = img.GetHeight() 609 610 611 def DrawLabel(self, dc, width, height, dw=0, dh=0): 612 """ 613 Draws the label on the button. 614 615 :param `dc`: an instance of :class:`wx.DC`; 616 :param `width`: the button width; 617 :param `height`: the button height; 618 :param `dw`: width differential, to show a 3D effect; 619 :param `dh`: height differential, to show a 3D effect. 620 """ 621 622 dc.SetFont(self.GetFont()) 623 624 if self.IsEnabled(): 625 dc.SetTextForeground(self.GetLabelColour()) 626 else: 627 dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) 628 629 label = self.GetLabel() 630 tw, th = dc.GetTextExtent(label) 631 632 w = min(width, height) 633 634 # labeldelta Is Used To Give The Impression Of A "3D" Click 635 if not self._isup: 636 dw = dh = self._labeldelta 637 638 angle = self.GetAngleOfRotation()*pi/180.0 639 640 # Check If There Is Any Rotation Chosen By The User 641 if angle == 0: 642 dc.DrawText(label, (width-tw)//2+dw, (height-th)//2+dh) 643 else: 644 xc, yc = (width//2, height//2) 645 646 xp = xc - (tw//2)* cos(angle) - (th//2)*sin(angle) 647 yp = yc + (tw//2)*sin(angle) - (th//2)*cos(angle) 648 649 dc.DrawRotatedText(label, xp + dw, yp + dh , angle*180/pi) 650 651 652 def DrawFocusIndicator(self, dc, width, height): 653 """ 654 Draws the focus indicator. This is a circle/ellipse inside the button 655 drawn with a dotted-style pen, to let the user know which button has 656 the focus. 657 658 :param `dc`: an instance of :class:`wx.DC`; 659 :param `width`: the button width; 660 :param `height`: the button height. 661 """ 662 663 self._focusindpen.SetColour(self._focusclr) 664 dc.SetLogicalFunction(wx.INVERT) 665 dc.SetPen(self._focusindpen) 666 dc.SetBrush(wx.TRANSPARENT_BRUSH) 667 668 main, secondary = self.GetEllipseAxis() 669 670 if abs(main - secondary) < 1e-6: 671 # Ah, That Is A Round Button 672 if height > width: 673 dc.DrawCircle(width//2, height//2, width//2-4) 674 else: 675 dc.DrawCircle(width//2, height//2, height//2-4) 676 else: 677 # This Is An Ellipse 678 if hasattr(self, "_xpos"): 679 dc.DrawEllipse(self._xpos + 2, self._ypos + 2, self._imgx - 4, self._imgy - 4) 680 681 dc.SetLogicalFunction(wx.COPY) 682 683 684 def OnSize(self, event): 685 """ 686 Handles the ``wx.EVT_SIZE`` event for :class:`SButton`. 687 688 :param `event`: a :class:`wx.SizeEvent` event to be processed. 689 """ 690 691 self.Refresh() 692 event.Skip() 693 694 695 def OnPaint(self, event): 696 """ 697 Handles the ``wx.EVT_PAINT`` event for :class:`SButton`. 698 699 :param `event`: a :class:`PaintEvent` event to be processed. 700 """ 701 702 width, height = self.GetClientSize() 703 704 # Use A Double Buffered DC (Good Speed Up) 705 dc = wx.BufferedPaintDC(self) 706 707 # The DC Background *Must* Be The Same As The Parent Background Colour, 708 # In Order To Hide The Fact That Our "Shaped" Button Is Still Constructed 709 # Over A Rectangular Window 710 brush = wx.Brush(self.GetParent().GetBackgroundColour()) 711 712 dc.SetBackground(brush) 713 dc.Clear() 714 715 self.DrawMainButton(dc, width, height) 716 self.DrawLabel(dc, width, height) 717 718 if self._hasfocus and self._usefocusind: 719 self.DrawFocusIndicator(dc, width, height) 720 721 722 def IsOutside(self, x, y): 723 """ 724 Checks if a mouse events occurred inside the circle/ellipse or not. 725 726 :param `x`: the mouse x position; 727 :param `y`: the mouse y position. 728 """ 729 730 width, height = self.GetClientSize() 731 diam = min(width, height) 732 xc, yc = (width//2, height//2) 733 734 main, secondary = self.GetEllipseAxis() 735 736 if abs(main - secondary) < 1e-6: 737 # This Is A Circle 738 if ((x - xc)**2.0 + (y - yc)**2.0) > (diam/2.0)**2.0: 739 return True 740 else: 741 # This Is An Ellipse 742 mn = max(main, secondary) 743 main = self._imgx/2.0 744 secondary = self._imgy/2.0 745 if (((x-xc)/main)**2.0 + ((y-yc)/secondary)**2.0) > 1: 746 return True 747 748 return False 749 750 751 def OnLeftDown(self, event): 752 """ 753 Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`SButton`. 754 755 :param `event`: a :class:`MouseEvent` event to be processed. 756 """ 757 758 if not self.IsEnabled(): 759 return 760 761 x, y = (event.GetX(), event.GetY()) 762 763 if self.IsOutside(x,y): 764 return 765 766 self._isup = False 767 768 if not self.HasCapture(): 769 self.CaptureMouse() 770 771 self.SetFocus() 772 773 self.Refresh() 774 event.Skip() 775 776 777 def OnLeftUp(self, event): 778 """ 779 Handles the ``wx.EVT_LEFT_UP`` event for :class:`SButton`. 780 781 :param `event`: a :class:`MouseEvent` event to be processed. 782 """ 783 784 if not self.IsEnabled() or not self.HasCapture(): 785 return 786 787 if self.HasCapture(): 788 self.ReleaseMouse() 789 790 if not self._isup: 791 self.Notify() 792 793 self._isup = True 794 self.Refresh() 795 event.Skip() 796 797 798 def OnMotion(self, event): 799 """ 800 Handles the ``wx.EVT_MOTION`` event for :class:`SButton`. 801 802 :param `event`: a :class:`MouseEvent` event to be processed. 803 """ 804 805 if not self.IsEnabled() or not self.HasCapture(): 806 return 807 808 if event.LeftIsDown() and self.HasCapture(): 809 x, y = event.GetPosition() 810 811 if self._isup and not self.IsOutside(x, y): 812 self._isup = False 813 self.Refresh() 814 return 815 816 if not self._isup and self.IsOutside(x,y): 817 self._isup = True 818 self.Refresh() 819 return 820 821 event.Skip() 822 823 824 def OnGainFocus(self, event): 825 """ 826 Handles the ``wx.EVT_SET_FOCUS`` event for :class:`SButton`. 827 828 :param `event`: a :class:`FocusEvent` event to be processed. 829 """ 830 831 self._hasfocus = True 832 dc = wx.ClientDC(self) 833 w, h = self.GetClientSize() 834 835 if self._usefocusind: 836 self.DrawFocusIndicator(dc, w, h) 837 838 839 def OnLoseFocus(self, event): 840 """ 841 Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`SButton`. 842 843 :param `event`: a :class:`FocusEvent` event to be processed. 844 """ 845 846 self._hasfocus = False 847 dc = wx.ClientDC(self) 848 w, h = self.GetClientSize() 849 850 if self._usefocusind: 851 self.DrawFocusIndicator(dc, w, h) 852 853 self.Refresh() 854 855 856 def OnKeyDown(self, event): 857 """ 858 Handles the ``wx.EVT_KEY_DOWN`` event for :class:`SButton`. 859 860 :param `event`: a :class:`KeyEvent` event to be processed. 861 """ 862 863 if self._hasfocus and event.GetKeyCode() == ord(" "): 864 865 self._isup = False 866 self.Refresh() 867 868 event.Skip() 869 870 871 def OnKeyUp(self, event): 872 """ 873 Handles the ``wx.EVT_KEY_UP`` event for :class:`SButton`. 874 875 :param `event`: a :class:`KeyEvent` event to be processed. 876 """ 877 878 if self._hasfocus and event.GetKeyCode() == ord(" "): 879 880 self._isup = True 881 self.Notify() 882 self.Refresh() 883 884 event.Skip() 885 886 887 def MakePalette(self, tr, tg, tb): 888 """ 889 Creates a palette to be applied on an image based on input colour. 890 891 :param `tr`: the red intensity of the input colour; 892 :param `tg`: the green intensity of the input colour; 893 :param `tb`: the blue intensity of the input colour. 894 """ 895 896 l = [] 897 for i in range(255): 898 l.extend([tr*i // 255, tg*i // 255, tb*i // 255]) 899 900 return l 901 902 903 def ConvertWXToPIL(self, bmp): 904 """ 905 Converts a :class:`wx.Image` into a PIL image. 906 907 :param `bmp`: an instance of :class:`wx.Image`. 908 """ 909 910 width = bmp.GetWidth() 911 height = bmp.GetHeight() 912 img = Image.frombytes("RGBA", (width, height), bmp.GetData()) 913 914 return img 915 916 917 def ConvertPILToWX(self, pil, alpha=True): 918 """ 919 Converts a PIL image into a :class:`wx.Image`. 920 921 :param `pil`: a PIL image; 922 :param `alpha`: ``True`` if the image contains alpha transparency, ``False`` 923 otherwise. 924 """ 925 926 if alpha: 927 image = wx.Image(*pil.size) 928 image.SetData(pil.convert("RGB").tobytes()) 929 image.SetAlpha(pil.convert("RGBA").tobytes()[3::4]) 930 else: 931 image = wx.Image(pil.size[0], pil.size[1]) 932 new_image = pil.convert('RGB') 933 data = new_image.tobytes() 934 image.SetData(data) 935 936 return image 937 938 939 def SetAngleOfRotation(self, angle=None): 940 """ 941 Sets angle of button label rotation. 942 943 :param `angle`: the label rotation, in degrees. 944 """ 945 946 if angle is None: 947 angle = 0 948 949 self._rotation = angle*pi/180 950 951 952 def GetAngleOfRotation(self): 953 """ Returns angle of button label rotation, in degrees. """ 954 955 return self._rotation*180.0/pi 956 957 958 def SetEllipseAxis(self, main=None, secondary=None): 959 """ 960 Sets the ellipse axis. What it is important is not their absolute values 961 but their ratio. 962 963 :param `main`: a floating point number representing the absolute dimension 964 of the main ellipse axis; 965 :param `secondary`: a floating point number representing the absolute dimension 966 of the secondary ellipse axis. 967 """ 968 969 if main is None: 970 main = 1.0 971 secondary = 1.0 972 973 self._ellipseaxis = (main, secondary) 974 975 976 def GetEllipseAxis(self): 977 """ Returns the ellipse axes. """ 978 979 return self._ellipseaxis 980 981 982#---------------------------------------------------------------------- 983# SBITMAPBUTTON Class 984# It Is Derived From SButton, And It Is A Class Useful To Draw A 985# ShapedButton With An Image In The Middle. The Button Can Have 4 986# Different Bitmaps Assigned To Its Different States (Pressed, Non 987# Pressed, With Focus, Disabled). 988#---------------------------------------------------------------------- 989 990 991class SBitmapButton(SButton): 992 """ 993 Subclass of :class:`SButton` which displays a bitmap, acting like a 994 :class:`wx.BitmapButton`. 995 """ 996 997 def __init__(self, parent, id, bitmap, pos=wx.DefaultPosition, size=wx.DefaultSize): 998 """ 999 Default class constructor. 1000 1001 :param `parent`: the :class:`SBitmapButton` parent. Must not be ``None``; 1002 :param `id`: window identifier. A value of -1 indicates a default value; 1003 :param `bitmap`: the button bitmap (if any); 1004 :param `pos`: the control position. A value of (-1, -1) indicates a default position, 1005 chosen by either the windowing system or wxPython, depending on platform; 1006 :param `size`: the control size. A value of (-1, -1) indicates a default size, 1007 chosen by either the windowing system or wxPython, depending on platform. 1008 """ 1009 1010 self._bmpdisabled = None 1011 self._bmpfocus = None 1012 self._bmpselected = None 1013 1014 self.SetBitmapLabel(bitmap) 1015 1016 SButton.__init__(self, parent, id, "", pos, size) 1017 1018 1019 def GetBitmapLabel(self): 1020 """ Returns the bitmap associated with the button in the normal state. """ 1021 1022 return self._bmplabel 1023 1024 1025 def GetBitmapDisabled(self): 1026 """ Returns the bitmap displayed when the button is disabled. """ 1027 1028 return self._bmpdisabled 1029 1030 1031 def GetBitmapFocus(self): 1032 """ Returns the bitmap displayed when the button has the focus. """ 1033 1034 return self._bmpfocus 1035 1036 1037 def GetBitmapSelected(self): 1038 """ Returns the bitmap displayed when the button is selected (pressed). """ 1039 1040 return self._bmpselected 1041 1042 1043 def SetBitmapDisabled(self, bitmap): 1044 """ 1045 Sets the bitmap to display when the button is disabled. 1046 1047 :param `bitmap`: a valid :class:`wx.Bitmap` object. 1048 """ 1049 1050 self._bmpdisabled = bitmap 1051 1052 1053 def SetBitmapFocus(self, bitmap): 1054 """ 1055 Sets the bitmap to display when the button has the focus. 1056 1057 :param `bitmap`: a valid :class:`wx.Bitmap` object. 1058 """ 1059 1060 self._bmpfocus = bitmap 1061 self.SetUseFocusIndicator(False) 1062 1063 1064 def SetBitmapSelected(self, bitmap): 1065 """ 1066 Sets the bitmap to display when the button is selected (pressed). 1067 1068 :param `bitmap`: a valid :class:`wx.Bitmap` object. 1069 """ 1070 1071 self._bmpselected = bitmap 1072 1073 1074 def SetBitmapLabel(self, bitmap, createothers=True): 1075 """ 1076 Sets the bitmap to display normally. This is the only one that is 1077 required. 1078 1079 :param `bitmap`: a valid :class:`wx.Bitmap` object; 1080 :param `createothers`: if set to ``True``, then the other bitmaps will be 1081 generated on the fly. Currently, only the disabled bitmap is generated. 1082 """ 1083 1084 self._bmplabel = bitmap 1085 1086 if bitmap is not None and createothers: 1087 dis_bitmap = wx.Bitmap(bitmap.ConvertToImage().ConvertToGreyscale()) 1088 self.SetBitmapDisabled(dis_bitmap) 1089 1090 1091 def _GetLabelSize(self): 1092 """ Used internally. """ 1093 1094 if not self._bmplabel: 1095 return -1, -1, False 1096 1097 return self._bmplabel.GetWidth() + 2, self._bmplabel.GetHeight() + 2, False 1098 1099 1100 def DrawLabel(self, dc, width, height, dw=0, dh=0): 1101 """ 1102 Draws the bitmap in the middle of the button. 1103 1104 :param `dc`: an instance of :class:`wx.DC`; 1105 :param `width`: the button width; 1106 :param `height`: the button height; 1107 :param `dw`: width differential, to show a 3D effect; 1108 :param `dh`: height differential, to show a 3D effect. 1109 """ 1110 1111 bmp = self._bmplabel 1112 1113 if self._bmpdisabled and not self.IsEnabled(): 1114 bmp = self._bmpdisabled 1115 1116 if self._bmpfocus and self._hasfocus: 1117 bmp = self._bmpfocus 1118 1119 if self._bmpselected and not self._isup: 1120 bmp = self._bmpselected 1121 1122 bw, bh = bmp.GetWidth(), bmp.GetHeight() 1123 1124 if not self._isup: 1125 dw = dh = self._labeldelta 1126 1127 hasMask = bmp.GetMask() != None 1128 dc.DrawBitmap(bmp, (width - bw)//2 + dw, (height - bh)//2 + dh, hasMask) 1129 1130 1131#---------------------------------------------------------------------- 1132# SBITMAPTEXTBUTTON Class 1133# It Is Derived From SButton, And It Is A Class Useful To Draw A 1134# ShapedButton With An Image And Some Text Ceneterd In The Middle. 1135# The Button Can Have 4 Different Bitmaps Assigned To Its Different 1136# States (Pressed, Non Pressed, With Focus, Disabled). 1137#---------------------------------------------------------------------- 1138 1139class SBitmapTextButton(SBitmapButton): 1140 """ 1141 Subclass of :class:`SButton` which displays a bitmap and a label. 1142 """ 1143 1144 def __init__(self, parent, id, bitmap, label, 1145 pos=wx.DefaultPosition, size=wx.DefaultSize): 1146 """ 1147 Default class constructor. 1148 1149 :param `parent`: the :class:`SBitmapTextButton` parent. Must not be ``None``; 1150 :param `id`: window identifier. A value of -1 indicates a default value; 1151 :param `bitmap`: the button bitmap (if any); 1152 :param `label`: the button text label; 1153 :param `pos`: the control position. A value of (-1, -1) indicates a default position, 1154 chosen by either the windowing system or wxPython, depending on platform; 1155 :param `size`: the control size. A value of (-1, -1) indicates a default size, 1156 chosen by either the windowing system or wxPython, depending on platform. 1157 """ 1158 1159 SBitmapButton.__init__(self, parent, id, bitmap, pos, size) 1160 self.SetLabel(label) 1161 1162 1163 def _GetLabelSize(self): 1164 """ Used internally. """ 1165 1166 w, h = self.GetTextExtent(self.GetLabel()) 1167 1168 if not self._bmplabel: 1169 return w, h, True # if there isn't a bitmap use the size of the text 1170 1171 w_bmp = self._bmplabel.GetWidth() + 2 1172 h_bmp = self._bmplabel.GetHeight() + 2 1173 width = w + w_bmp 1174 1175 if h_bmp > h: 1176 height = h_bmp 1177 else: 1178 height = h 1179 1180 return width, height, True 1181 1182 1183 def DrawLabel(self, dc, width, height, dw=0, dh=0): 1184 """ 1185 Draws the bitmap and the text label. 1186 1187 :param `dc`: an instance of :class:`wx.DC`; 1188 :param `width`: the button width; 1189 :param `height`: the button height; 1190 :param `dw`: width differential, to show a 3D effect; 1191 :param `dh`: height differential, to show a 3D effect. 1192 """ 1193 1194 bmp = self._bmplabel 1195 1196 if bmp != None: # if the bitmap is used 1197 1198 if self._bmpdisabled and not self.IsEnabled(): 1199 bmp = self._bmpdisabled 1200 1201 if self._bmpfocus and self._hasfocus: 1202 bmp = self._bmpfocus 1203 1204 if self._bmpselected and not self._isup: 1205 bmp = self._bmpselected 1206 1207 bw, bh = bmp.GetWidth(), bmp.GetHeight() 1208 1209 if not self._isup: 1210 dw = dh = self._labeldelta 1211 1212 hasMask = bmp.GetMask() != None 1213 1214 else: 1215 1216 bw = bh = 0 # no bitmap -> size is zero 1217 1218 dc.SetFont(self.GetFont()) 1219 1220 if self.IsEnabled(): 1221 dc.SetTextForeground(self.GetLabelColour()) 1222 else: 1223 dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) 1224 1225 label = self.GetLabel() 1226 tw, th = dc.GetTextExtent(label) # size of text 1227 1228 if not self._isup: 1229 dw = dh = self._labeldelta 1230 1231 w = min(width, height) 1232 1233 pos_x = (width - bw - tw)//2 + dw # adjust for bitmap and text to centre 1234 1235 rotangle = self.GetAngleOfRotation()*pi/180.0 1236 1237 if bmp != None: 1238 if rotangle < 1.0/180.0: 1239 dc.DrawBitmap(bmp, pos_x, (height - bh)//2 + dh, hasMask) # draw bitmap if available 1240 pos_x = pos_x + 4 # extra spacing from bitmap 1241 else: 1242 pass 1243 1244 if rotangle < 1.0/180.0: 1245 dc.DrawText(label, pos_x + dw + bw, (height-th)//2+dh) # draw the text 1246 else: 1247 xc, yc = (width//2, height//2) 1248 xp = xc - (tw//2)* cos(rotangle) - (th//2)*sin(rotangle) 1249 yp = yc + (tw//2)*sin(rotangle) - (th//2)*cos(rotangle) 1250 dc.DrawRotatedText(label, xp, yp , rotangle*180.0/pi) 1251 1252 1253#---------------------------------------------------------------------- 1254# __STOGGLEMIXIN Class 1255# A Mixin That Allows To Transform Any Of SButton, SBitmapButton And 1256# SBitmapTextButton In The Corresponding ToggleButtons. 1257#---------------------------------------------------------------------- 1258 1259class __SToggleMixin(object): 1260 """ 1261 A mixin that allows to transform any of :class:`SButton`, :class:`SBitmapButton` and 1262 :class:`SBitmapTextButton` in the corresponding toggle buttons. 1263 """ 1264 1265 def SetToggle(self, flag): 1266 """ 1267 Sets the button as toggled/not toggled. 1268 1269 :param `flag`: ``True`` to set the button as toggled, ``False`` otherwise. 1270 """ 1271 1272 self._isup = not flag 1273 self.Refresh() 1274 1275 SetValue = SetToggle 1276 1277 1278 def GetToggle(self): 1279 """ Returns the toggled state of a button. """ 1280 1281 return not self._isup 1282 1283 GetValue = GetToggle 1284 1285 1286 def OnLeftDown(self, event): 1287 """ 1288 Handles the ``wx.EVT_LEFT_DOWN`` event for the button. 1289 1290 :param `event`: a :class:`MouseEvent` event to be processed. 1291 """ 1292 1293 if not self.IsEnabled(): 1294 return 1295 1296 x, y = (event.GetX(), event.GetY()) 1297 1298 if self.IsOutside(x,y): 1299 return 1300 1301 self._saveup = self._isup 1302 self._isup = not self._isup 1303 1304 if not self.HasCapture(): 1305 self.CaptureMouse() 1306 1307 self.SetFocus() 1308 self.Refresh() 1309 1310 1311 def OnLeftUp(self, event): 1312 """ 1313 Handles the ``wx.EVT_LEFT_UP`` event for the button. 1314 1315 :param `event`: a :class:`MouseEvent` event to be processed. 1316 """ 1317 1318 1319 if not self.IsEnabled() or not self.HasCapture(): 1320 return 1321 1322 if self.HasCapture(): 1323 if self._isup != self._saveup: 1324 self.Notify() 1325 1326 self.ReleaseMouse() 1327 self.Refresh() 1328 1329 1330 def OnKeyDown(self, event): 1331 """ 1332 Handles the ``wx.EVT_KEY_DOWN`` event for the button. 1333 1334 :param `event`: a :class:`KeyEvent` event to be processed. 1335 """ 1336 1337 event.Skip() 1338 1339 1340 def OnMotion(self, event): 1341 """ 1342 Handles the ``wx.EVT_MOTION`` event for the button. 1343 1344 :param `event`: a :class:`MouseEvent` event to be processed. 1345 """ 1346 1347 if not self.IsEnabled(): 1348 return 1349 1350 if event.LeftIsDown() and self.HasCapture(): 1351 1352 x, y = event.GetPosition() 1353 w, h = self.GetClientSize() 1354 1355 if not self.IsOutside(x, y): 1356 self._isup = not self._saveup 1357 self.Refresh() 1358 return 1359 1360 if self.IsOutside(x,y): 1361 self._isup = self._saveup 1362 self.Refresh() 1363 return 1364 1365 event.Skip() 1366 1367 1368 def OnKeyUp(self, event): 1369 """ 1370 Handles the ``wx.EVT_KEY_UP`` event for the button. 1371 1372 :param `event`: a :class:`KeyEvent` event to be processed. 1373 """ 1374 1375 if self._hasfocus and event.GetKeyCode() == ord(" "): 1376 1377 self._isup = not self._isup 1378 self.Notify() 1379 self.Refresh() 1380 1381 event.Skip() 1382 1383 1384#---------------------------------------------------------------------- 1385# STOGGLEBUTTON Class 1386#---------------------------------------------------------------------- 1387 1388class SToggleButton(__SToggleMixin, SButton): 1389 """ A `ShapedButton` toggle button. """ 1390 pass 1391 1392 1393#---------------------------------------------------------------------- 1394# SBITMAPTOGGLEBUTTON Class 1395#---------------------------------------------------------------------- 1396 1397class SBitmapToggleButton(__SToggleMixin, SBitmapButton): 1398 """ A `ShapedButton` toggle bitmap button. """ 1399 pass 1400 1401 1402#---------------------------------------------------------------------- 1403# SBITMAPTEXTTOGGLEBUTTON Class 1404#---------------------------------------------------------------------- 1405 1406class SBitmapTextToggleButton(__SToggleMixin, SBitmapTextButton): 1407 """ A `ShapedButton` toggle bitmap button with a text label. """ 1408 pass 1409 1410 1411 1412#---------------------------------------------------------------------- 1413from wx.lib.embeddedimage import PyEmbeddedImage 1414 1415UpButton = PyEmbeddedImage( 1416 "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAACXBIWXMAAA7DAAAOwwHHb6hk" 1417 "AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAA" 1418 "F2+SX8VGAAADAFBMVEX//////8z//5n//2b//zP//wD/zP//zMz/zJn/zGb/zDP/zAD/mf//" 1419 "mcz/mZn/mWb/mTP/mQD/Zv//Zsz/Zpn/Zmb/ZjP/ZgD/M///M8z/M5n/M2b/MzP/MwD/AP//" 1420 "AMz/AJn/AGb/ADP/AADM///M/8zM/5nM/2bM/zPM/wDMzP/MzMzMzJnMzGbMzDPMzADMmf/M" 1421 "mczMmZnMmWbMmTPMmQDMZv/MZszMZpnMZmbMZjPMZgDMM//MM8zMM5nMM2bMMzPMMwDMAP/M" 1422 "AMzMAJnMAGbMADPMAACZ//+Z/8yZ/5mZ/2aZ/zOZ/wCZzP+ZzMyZzJmZzGaZzDOZzACZmf+Z" 1423 "mcyZmZmZmWaZmTOZmQCZZv+ZZsyZZpmZZmaZZjOZZgCZM/+ZM8yZM5mZM2aZMzOZMwCZAP+Z" 1424 "AMyZAJmZAGaZADOZAABm//9m/8xm/5lm/2Zm/zNm/wBmzP9mzMxmzJlmzGZmzDNmzABmmf9m" 1425 "mcxmmZlmmWZmmTNmmQBmZv9mZsxmZplmZmZmZjNmZgBmM/9mM8xmM5lmM2ZmMzNmMwBmAP9m" 1426 "AMxmAJlmAGZmADNmAAAz//8z/8wz/5kz/2Yz/zMz/wAzzP8zzMwzzJkzzGYzzDMzzAAzmf8z" 1427 "mcwzmZkzmWYzmTMzmQAzZv8zZswzZpkzZmYzZjMzZgAzM/8zM8wzM5kzM2YzMzMzMwAzAP8z" 1428 "AMwzAJkzAGYzADMzAAAA//8A/8wA/5kA/2YA/zMA/wAAzP8AzMwAzJkAzGYAzDMAzAAAmf8A" 1429 "mcwAmZkAmWYAmTMAmQAAZv8AZswAZpkAZmYAZjMAZgAAM/8AM8wAM5kAM2YAMzMAMwAAAP8A" 1430 "AMwAAJkAAGYAADMAAADU1NTT09PS0tLR0dHQ0NDNzc3Ly8vKysrJycnIyMjHx8fGxsbFxcXC" 1431 "wsLBwcH39/f09PTw8PDs7Ozo6Ojj4+Pe3t7Z2dnX19fV1dXOzs7ExMTDw8O+vr66urq1tbWw" 1432 "sLCsrKyoqKikpKSgoKCcnJyUlJSPj4////+2FWUJAAABAHRSTlP/////////////////////" 1433 "////////////////////////////////////////////////////////////////////////" 1434 "////////////////////////////////////////////////////////////////////////" 1435 "////////////////////////////////////////////////////////////////////////" 1436 "////////////////////////////////////////////////////////////////////////" 1437 "//////////////////////////////8AU/cHJQAAIcNJREFUeNpi+D9MAEAAMQwXjwAE0LDx" 1438 "CEAA0cIjL9+8wwreA8GHGzdvvn1FA0sBAoj6HsHhCySP3Lx54y3VrQUIIGp6BOHkt2/fAgkQ" 1439 "BWKBIIpvQB76cPPmrVtUtBwggBio6o232AHYT9g8cvs21awHCCCqeOQV2LFg8AYI4Yw3EIDq" 1440 "KUTUfPhw48ONGzdvvaaGGwACiAoeeQF14hv8AN03sJgB+uTWS8pdARBAlHoE2QuvXwMROgAK" 1441 "Y/UNIqWBY4XiRAYQQJR55DVKVLzGBbDEDCL/gzwCipVbbyhyCkAAUeARhnfIkQEFr5AAVr9g" 1442 "ZBZY8gLGyh0KXAMQQAwUJyp4enqFCRD+wOsTSO0CKsTukO0cgAAi0yMvoXEBcj/UBy/BAKdP" 1443 "wKohHkHzDKSeBCYvkFfu3CGz2gcIIAayYwPuESRvQHyC8A5q0nqNvTBG88gd8mIFIIAYyPIG" 1444 "PF9APIHwBlqkvMYaI2jZHV4Mg3L87Tu373z8SIajAAKIgazoQIqNl+jg1UusKes1IotAPPMO" 1445 "pUKBe+Q2KFLI8AlAAJHqkVeQDA72BLLzX2CLEWw5nVCMgD1y5+NdUgtjgAAi0SMMkNhAigug" 1446 "D168ePHyBdwr8Bh5/QqSpJA88vYNlsYKvO2F4pG7JLoMIIAYSM8d0KwB9sQLKECKEeQkhdZI" 1447 "eYPWknyH3FRB9gfII9raJDkNIIAYSKvHkWID5PQXCIDFH8hewNLcIugR7XckOA4ggBhIiw54" 1448 "3niBCtB8Ac/bb1Gd/w49f+DwyEeIR0iJFIAAIt4jqJkDiz+QMjiW5jtKF+sthjdu3ECOD6A/" 1449 "IB4h3icAAcRAWu5A8sbz58+RvPEKFhuoTUMC4D0MQFvASP6AeoRorwAEEJEeeQHxCKysAnsD" 1450 "2SOocYElAeH1CKTshXrjzkeEJ0CAyCYLQAAxEJ2skHPH8+cIj4C9AfUDSsEKdiEo0dy4AaE/" 1451 "gGkogPoAzPhwA8Uj2nfvaWvfA3niHhDcv0+UEwECiCiPvHqDiI4XSB55DvHIKzSPvEM0ocBO" 1452 "vHnzBrgpdQNMQwGSnyCKbsELLIgHYOD+faKGXAACiIHIShASHy+wRser17BGFCLBQPpKt3EA" 1453 "oBTMUx8gnrx5E9o4+Qj1yH0YeAAERLgSIIAYiKs9MP2BiA00f0Dj4RY0oeAAt2/dhIAbN2AN" 1454 "X6hH7mqDfAHxyEMgAHmEiEgBCCAGwtkclj+QPAH2BixzQJMUpNcKchKiJrh3TxsFQAqju3c/" 1455 "fgR6BR45EIDqkfsPHiKB+/cJDk8ABBADUf6A5o7nyB4BF7pgf8DiAlip3UJUaCDnAMMSCdy7" 1456 "D0ky9+4BvQKPGpBnQMQduEdA+iAeeAQEEI/cJ1R4AQQQAxHFLjA+sPkDmqjgjXFg3YyICUji" 1457 "Rg5VkKdgHtHW/ggFEM8gkhxQ9z1wdDyCAVjyIuATgABiIMYfryGlFcwTkOwB8gckb0C8cQOY" 1458 "Xe/AvQHyxSMUAHTOQ0iyv/8AnMjA4ONHlKzzEeaRR48eP0b1yAP8qQsggPB65DW82EWKjhdI" 1459 "qQocG5BRkNug0LwHThQgRzx58gSEkQDIYZDUAk4qkMR2T/se2DcwcBfZIzB9MC/hbUMCBBAD" 1460 "3nIX5BFocYWZzaGxASpqQd74CMwVD+4/hHrk0yc8HnkEKVaJ8cinT48hAOQTfI4FCCAG/PU5" 1461 "tNh9jpw9oKUVJHe8f38T4gvte5DU9BjkgE9A8OQJukfArnkM9RA010ByDCyh3dWG1iHg4AAa" 1462 "8xkIIGaBteFxLEAAMeDr1cKrDzSPIOXy9+9h/rhPkkdAAQz3yD3cHnn69CnQLzCP4KlOAAKI" 1463 "AV97F8MbKMkKlMeBjW+IJx4+gqakT6BgfAq2/xM6ePzkMTJ49BCUo6ClGbyuQfbI56fPnj2D" 1464 "+AUUMkAtOJ0LEEA4PfICVn+gegQRHeDYAFZ+4LgA+wPsWFBiwOWRJ+geQSqWtWGeQfHIM4hH" 1465 "QMZBPIKzEAYIIAa83UGUYhepEgTX5MCyClRSgS0FpieoH6DegHgFDaB45BEsqwC9Am0fwuME" 1466 "JUYg4CkkTh7hihOAAMLjEfSM/gI1PkDV+B1gSQUupUBpCskL2AEuj8AiBb9HID7B6RGAAGLA" 1467 "X2Chxwc8e7z/cOs2MG8CMzg4c4M98QyREEj1CNAruD3yBQQgfgEZgd3FAAHEgCdhoWQQFH+A" 1468 "Cl1gBwiYOSB5A+gNmEfw+AWnRxAlMdg3cI88+fQU5g+YV0D1E1YnAwQQVo+ABz9eofkD5BFY" 1469 "sgL2HoCp6j44b4DLKaAznxHwCCjzI3kCpS0FihOYR2ANHFD5Ac7tX74geQVoypMb2NwMEEAM" 1470 "OHu2mAkLXpuD6g5QfEDyBtgjz5AAdm8g53UMj8D9Au9MgUsQsMFfkH0CLr6wuRkggBjwllio" 1471 "6QoaH8BkBWqkP3gEyRuQaoOwR5BLX2I9Ak5bED98/QpPX5+x+QQggBiwNrHQa3R4eQXyx4fb" 1472 "d7TB0YGoNJ49I+wRUCsSzSOPH6FkFEgjF+SfB+BWAqiR8Bnska8g8AXmlc+fsLgaIIAYsI63" 1473 "o3sEls9B+eMGMJvfv/8I4g+QC9H9gdUj4PgAtyMf48rwcI88gETKw8fApAtMW19APoB4BZq8" 1474 "Pj/BbKsABBADrhLrFaJ/jpQ/gOXuzdsf74IKXWgex4gPXB55/BjSniXSI+AW8COIR2BxAvUL" 1475 "0COYJRdAAGF6BNE0gXgEPNQOi4/3wNocWFxBK/KnNPfI40+QtAX3CMgrwMSFmUsAAogBd9v9" 1476 "BaLgBfcGgc0ScDUIbK+DyiqwD2jqEUil+BTJI9++QT2CWXIBBBADrqrwJUpGh+Tzd8Dq4yMo" 1477 "m3/+jCVrPMPlIVjRCy6qnyCXWQ9RAepYBdgnT4CJC+yRb0BPgDAkToCFMJrDAQIIq0dQcjrI" 1478 "I+BuLTB/3LoDbJU8fIzcJCHKI5CiF8MjDwl65CGoiAf7AxQdYACLEzSHAwQQA9a2InLKghVY" 1479 "4PrjHtAbj2G5g9YeAVUmoNz47BnQFzB/fIPk+GdP0XwCEEAMGOsA4IOKSCUWOKND8gcwPsCx" 1480 "QSePgGpFUHZHipFvXyG1CerqKIAAYsCaQxAl1gt4RfgB2IkCtdmffCIlNhAeQfR4cXrkPjp4" 1481 "+ADURQB5BOKP70DwDVJ2AbMJitMBAogBayPrJWwiB17yAitCYEa/B27JkeMReC8euWZHi44H" 1482 "GB55gOSRrzCPfIPm96coTgcIIAa8/ngBa2EBMwgwn2s/fAwdECDTI2hNFCI8Ago5kG3QKPmG" 1483 "5JNnKD4BCCAGtHEs2PDoC/hIA7jAen/j9kdg8xpcYIGrD9I8AvEKcR5BnlIAV4vAwh7ska/Q" 1484 "GPkOK7ueIjseIIAYcDROYJNqkBwC7NcCK/QH9x+R5RF4dgd55AmWihApg8B6JDAfgTzy6RPM" 1485 "I9AYgRXCyFECEEAMOAYcENM4kKYJsIEFSrFP0LoeRHsGtWbHEhsP4IOo2tr3kLwE9AnYI1/g" 1486 "eeQ7rBB+hpzdAQKIAXVGB9q/RfgD2nS/dUcb2B8EV0/098gTYC5BLre+QwphoE/eI1wPEEAM" 1487 "2AZO4D6BNLFAJdZHkEceQ3vR1PbIA3SPwABkzOwxMJdAWinf4QCcuJ49Q7geIIAYUFYvoXgE" 1488 "HCHgEusDqIcO6tgS0zDBUiE+RXRzQQM6qBkd4Yn7yHOg9xADKjCPfEXxyLdvX5B9AhBADBhZ" 1489 "Hckj4KIXNDAKGmqHeuQZGR7B1l/HVuyiegLqEWAj9RHMI0Cf/EB4BcUjAAHEgJmyXiKVWJAq" 1490 "BNg0gRS9pMQHZhOFfI8Aszty2vrxA5K4gGJw5wMEEAPy5BTKghlowoLmdKBHnjz5/OwZeR5B" 1491 "GpAnyyNPYM0UkCfAAJpLvsDHggECCO6Rd2gegUcIsAr5CGpSPyalwEKtEKEtLewNRiRfaGMB" 1492 "QK8A7X4CLYC/w3wCihhwEQxzP0AAMSAv8UPyCLwqBHoE2EdHH4klwyPYW74o0YHTI/AYQY6S" 1493 "7+CCC7ZkECCAGOCbJhCrTV7CIwTSK7yrDR0sGzCPPHr8FN0jsMQFixKAAIJ75C3SshnI8itI" 1494 "oxeUQ+6BapDPT2nikfvEeOThI8iwENQjP2EeAeV3qAcAAogBo68OA6+hHrkN9gh4sIEEr6A2" 1495 "tRDTgDBfPHoAy+dITSttrOAeqIMFHk2BeuQnEIB8guIRgABC8cgr5DWvSOOK90Ctd3CnkKzi" 1496 "F1wAo3kEPqqIUQdi9wh4NAUeIzCPgHqLUA8ABBADvMxCXY8M8gh4eg1YGd67D6oKn1HoEbTR" 1497 "E1iFTtgj4CUdj8GjKV+/wmLkJyyXwDIJQADBPQJelIy8mhpSh4Da7/cHgUfAQ3XIHvmJ5hGA" 1498 "AGJAVCJIWQS0eAnSyrp5CzR/AOuFkFuzo/XVHyGVVjg9gJiyBmUihEe+wzzyE8UnAAEE9Qgk" 1499 "RuAp6xWsdQIbWSSpDiHJI/dJ9Mg3mEd+gj0CL4ABAgjhkTcoMQLMIW9AleGdO/fAWY0Sj6As" 1500 "wyDTI6DRG0jHHeyRX79+QRMXzCMAAYSatF6hZXXwFMLDweGRxxCPQDMJ0CO/UDwCEEDIHkEq" 1501 "s2BzhaApBNCg+OfPz8gF0LkRij3y6MnTZ7AY+QUGP8FFMLQmAQggBpR6/RU8h0CbJ3dAzRNg" 1502 "M5qcCEFMkH9GHplDH/rB5pG7yACyjA3YcAR7BJLboR75jvAIQAAxQPYJw2Lk1UuklXGw5snj" 1503 "x5R6BBonZHoE1gT+DKsSYTECye5fwHuZAAKIATmvvwIvDH85hDzyA55JAAII5JFX72DdXPhW" 1504 "HMiygA/gWh3UYIQvziA1o8PXK0AXnj0CDU2DpjvvIVWG2shr4e8irURD+AU0CPEJWLeDPAJK" 1505 "Wb9//4Z75AuoKQ8QQAyQHehYPAJapgGdZhscHnkE8wgsRsBpC+QRUNoCCCAGSMpC8chr2Lzn" 1506 "jY/alHoEufhFNBbRa3TklPQRC7gLXmYB8shXaIz8+g3LJF8hLWCAAMLpEXB1ODg9Ao6R3zCf" 1507 "QD0CEEDIHoF5Bb7C4TYoSkFOgM9EP0VxJEkZHeERuCfgKQfqAbhHIIugYUtpgWIghzx8As7t" 1508 "sBgB+QSWtoCeAAggLB55jeQRUAOaSh5BrgjRPIIRE+geAY+iQosteIz8hlQlUI8ABBCKR14h" 1509 "e+Q9uPAFjfNR5BGUWh2xxow0j9wFj0eBGykoMYLkEYAAwowR2HJ3SN9wMHrkB6T4BZfAcI8A" 1510 "BBBmjMBS1gesHsEF0DM6PGHBhq0fIryhrY3sEVgxi3uLBkgePUZ+//7zBxQn8NYWQABBPPIO" 1511 "ESOv4WXWDaBHwEPIyKUWsR6B1SDwvjrS4kWwR1BigpBHwFsYwJn9GzxGEB75+hXoCYAAgnkE" 1512 "2oyH7zECNuEp9Ah07hDJIzBvoJa4KPsWcHrlHtQj0KQF9MYfsE9+/oB6BCCAUDzyGr51DZi0" 1513 "BrVHwD4BF8AwjwAEEGrSeo3Y9UWER55B1g3g8Aik4IUva0dZE4te76FsWoICBBvJI8+QY+Q3" 1514 "cowABBAss79FOoACusAM7BHQxNeTJ9gzO3oNjuyRzzg8AtmWh15zE/YIsCKBeOQrOLNDkxZS" 1515 "jAAEECJpwc7SeANdv/+BZI9gX+2A4g1oqsK7jQyPR56ieAQlRgACCOqRd0hngkD3hQxij/zC" 1516 "EiMAAYQcI8h7VHF6BGnxO3jFJ6618NAlG+j5A9GWIsUjoH0dD+B55Ce2UgsggJBiBHpiDkke" 1517 "eQZy8NPPWJb0w/IHcm1+D7Z1j5A/kD0E2UkGrEgQHgHXIpAogXsEIIBIjBHIWgykWAA5Fus2" 1518 "C1iyQktUcG+gOBgtMnB75AvCI2FhoCiBewQggFBjBHHGxCD2yA+sHgEIIJRSiwiPQOZsoB6B" 1519 "bBpC8QjKTiRQ/niA2EX1Eb4jlAoeCYN55DvEIwABhFxqgRMX3CM3buL2CHTaB80j2LZUITwC" 1520 "y+TYNiET45F7D558/gLzyB+ER6AxAhBAqDECLXwhHXaQR+6hegQ8OwDdaPEZXsIic5BXwiI3" 1521 "S+5+RHcs0QDFI9+gHgkDeQS51AIIIAb46XFIHnk7uD3yC5tHAAII5JH379EyCcIj0LYWbP8U" 1522 "pJcE3RaGyBJI6+MeIQ3xwuYI4ZkcvsGYLI/AxrWwewQggLB6BLp4HNyxgux1efIZaYXlpyeI" 1523 "XA1eKPMJfTMq8oAi1CPwndJI275vI9PIAHN/OCizY/cINLMDBBA2jyDnkfvQzaawBjksMp5A" 1524 "Vy09hq1eQp3qROlGwQtdbG7GCjA9AgrSz8/QPIJcagEEEMgjHz6AG/JvkU/GweqRxzg8gjb9" 1525 "QSuPPH2GUmihegQggEAeuXHjPSJGIGcsImLkATaPwNIUcnJCzhtIc+hoGf0WmR4BOQSvRwAC" 1526 "COYRpBh5gxwj4GHwx5B2E8wjSKXTE/SNRrBMjt0joEMrbkKObYKd+QDn4/YISCvII5AZK5hH" 1527 "/qJ6BCCAEB55+xblFEWwR+5CVoI8eoxR12FLUihrAWDL+rTvIkUIzPmEPILsIUgzHlR8QmZ1" 1528 "f/78/RvkjbC/KB4BCCAkj6AeVzR4PfIL6pEwlNYvQACBPPL2xof372Dn42GNkUeYrQ80jzxE" 1529 "XwsA750jGia3iPYITAzmkbughVZPEFkE6BEggHsEdL4QQACBJ3pgHsEWIw9I98hDengEkrQg" 1530 "HgFtHQMIIPDU201QAYx2GBnYIx+hS1oePcaanLAUubB0BWu4IycrVIdiA+gegXgGWPiCVoyB" 1531 "Gyg/gLUIMKuDIgSYtKADdCA/AAQQ2CNv0TwC2XM/+DzyFegRYIcdVPZCkhbEI/dAfgAIIMj0" 1532 "9I0b75DOHX4HPXXmNvhAovv3ifUIYq8XbNgH4pHb6B65CT0uiDiPgEa1QEvfnsJSFihhgWME" 1533 "OmQK9gJAAEE8chNaACMfKwXxCOTcGFiVh1ZCAfmwA2VQd6whJqM+whu8EAfCPQIHN5EOEUKu" 1534 "Z9A98ukpqBaBlllggOIRgACCeOTWzffwo6wRx9feAh+4AszvuDyCc9UrYkrtI1LLHZGkcHkE" 1535 "S+YHZ3XQoprPsIEHSA75C2o0QtqMYC8ABBCyR6Bn3Q1Kj4DXPcA8AsnrYdDReIhHAAII4pHb" 1536 "N99DowQ5Rm7eAp8+Q5RHkFa430ee+CfPI0ieAWf1Bw+gUwo/4P6AeAQRIwABhOoRlPPhPtyE" 1537 "nT/z4BHK2UoYu3EePMBYIwOZHQd105EPB8PmETSAnv3BHrkPWpwJKXxhKQvskZ8/v8M8AhBA" 1538 "0KWAtz68Rz/mDnQiECi7Yx4Shc0j97F4BOUcrdtopS5ejyB8BDpY6R6sLwIss+ARAvLIL6BH" 1539 "oP74DxBACI+8H7QeAc26QZrwP9E9Ak9Z/wECCLZc9gb4mD5Uj3wArTu7izgoi2KP3CLdI6Dj" 1540 "f0FbosANRmAl8geRRUDVIdAjUA8ABBDCIx/QPPIe6hHIli3YeVk4PQJfhww9mRCllQXxyG28" 1541 "MQIVRDqXDnIMHGj3PKydBa7V//799w9cH0Jmp6EeAAggmEduQqLkPeoxkLfAKfQ+7OwCLB55" 1542 "gLwQGb3o/Yg+AnQLe8GFnMuRPAECYI88fPIJNDQHap6A4+MftIGClLL+AwQQzCPA2hYeJ6ge" 1543 "gewPGhiPQMZPgA1fWDsLWqv/gzQZkT0CEEAwj7wGtRs+YPHIHegio4HxCHiKB7ya8Ru0efIX" 1544 "krJA9Tp4AR3s9AeAAIJvhIF6BHFMKugkSJhH7mH1COIwAGj/4x6isYh8vhzpHrmF5BHQgnb4" 1545 "dMIfcA4B+gTuEZj7AQII7pGXt24Am46oHgHXq5BFRg8wt2yjbq1FWdGHPOmMfh4j3uoQ6ido" 1546 "pQ46ZQk2MPcdXBn++QuPEXB//Tt8LzhAACE2i4Fs+PAByRvQenUAPQLOIeDKEN41BOcPcBaB" 1547 "eATufIAAQvPIDZRjUgfaI5BxObS++r9/GE14EAAIICSPgK1A8QjFSQsph5DgkZtIHrn/4PEn" 1548 "xKwIPGHBPPIV7nyAAELa4opixU1Uj4D3Bz1APgsTzSOoa14hKxswJmpvY+vlYsn04FEHcB1y" 1549 "7yGkQwXpq8NyOqzBiBQh/wECCMkjb2C2wAIFfkzqXWhNQiePwIZPgBHyALRJ4StyDoF4BNqn" 1550 "QrqtCCCAkLeBI/erERNiA+URyJzIk2dIs1Rgb0DKXtDCeOQI+Q8QQNg8ApuuRHgEujMbejTG" 1551 "A4wDM5A6U9DGIopH0FrAmL7B9AhkcufBY8hUAqwf8g85Qn4g5ZD//wECCOW0AcgwMyI2oCUP" 1552 "4nRqSKMLwyMYK1/Rl8qgD8Pj9QikAgGdjX3vwWPQpj3QeiDwkAMiQiApC9nxAAGEcnjFR5TJ" 1553 "YvhCaGSP3Id2T/B5BHPNDwkegd4MA56jevDo0xfYwDU8Qv7B+4bIEfIfIIBQjxOB+OQOymH6" 1554 "8PMssXoEY58XhR4BV16QLvZ9YFsRntHh3vgHrUNQc8j//wABRIZH7mN4BDOfgzyBaGlh8Qjm" 1555 "wANafNwFpqtPT8HbE8ATCQiPhGFUhiAAEECoHnmFurQTiyfg+QTLhjXkpa8YGR2nR9CrEHj+" 1556 "gC3qB/cLkfwB6at//4Z6QwlAAKEdgvQRzSNIp7fTxSO34Kua7kHbWN9gjUUMj6C6HCCA0I+l" 1557 "+oh0OLU2Yhct7T0CHf25A11mBl/SD+mnI+cQzDoEBAACCJdHPiJ5BOMgHMiR4/dx78Mh2SOw" 1558 "MTnQXUOgMIQMkaI1TVAqdXSPAAQQxtFt0PXcd7WxnJeOs5GI6hFEoYVtwQxamYXaRQerBk2K" 1559 "f0LsTPgdhih54c13dH/8BwggzMP0IO5AHDaONMhOO48gJqfAM7jgcSzINiR4mxeWsMAR8h3D" 1560 "2QABhMUjd1E8gjwqSmCPF2UeuQM9Ol8bMtiAWHONHB+w8V6MCPkPEEBYjjeEegR6EDLaIAPS" 1561 "FAjGpmdUj9wh0iOwDsNteBnzCFaB/ICOLP5D8gikxMI8lxUggLAdponYxgiadUOMMKKMV9+7" 1562 "By/N7uGs1WHewLLkAbtHwIetQ3flwjuF/9Bz+ncsrgYIIGyHsqJ6BPVMffTBd/weuXOHoEdg" 1563 "s7fQchc00fcZsSnsdxhKhIBnDX9gSVj//wMEENZjclE88vgxxuo+KnsEceUTaFUVYkvYb8gU" 1564 "wj+UEgtplBQFAAQQVo+8gx8Sg1gb9Amydw1+SwJyvifHI7cQMyCwy8RA8yCgmSl4/kDK6OA2" 1565 "FmS09/sDbG4GCCDsR0kjTj0HH+j8CXbI+JMnj6B7KJDzPMGWL0aFCOKjRgh0hOMxJFl9A+/a" 1566 "AY9iIWUQcImFI0L+AwQQjsO9YUc6o3jkE3wP9EPI0fWUeOQ2fAIXkjkgu6DAhx1Bu7ZIFSEi" 1567 "YX3H4Y//AAGE67h1VI9Ajrx+irjoALbukiKP3ILHBviAioefwIv4vyIlK4wCC1d8/P8PEEC4" 1568 "PHIfzSOwjSGwFZqw9IXNI1hHHrCs+kFawa8N3ooLPcPlB9JgHIo/fuIosUAAIIBwXknwCna6" 1569 "IJJHYFvZIIeDQCd4EPsQULYbYQ6hIHkIedkypBEBqs1hh+r8xOYPaA2C85pEgADCfUkE7OhK" 1570 "8ImG4PPb0XYXwjd6IjwCm829i3YJDzaPwLs94PmXx+D14pBzT37BVjcg1ejQhPUdp3MBAgjP" 1571 "tR1vUT3yBePYc6hvwK0w1ObKXcLbRO6ANuDCLk158gmSqr5BvIESHdBRE/B84U3crgUIIHwX" 1572 "qUCP2CbkkYcPHiDvECHdI4/hhe53aLGL7A/48A/u/AECAAGE97YYuE+QPfIV5pmnzz6DffMI" 1573 "tLQG+QQd5Fndj2h77D9CW7iQQT/QQWog0+HHsmHxxl9YfKCOY6EDgADCe9nQW1jaeop8Dv1X" 1574 "+P0AkLs0oDeJoHpEm4BHYMd7Qq4dgJ7c8gOy6egvarkLrwjx3rMNEED4r396CfYI9LQz1MPC" 1575 "vyLd3PD08xPIlRqImNG+h3r1HOo1dNCblT5BDlP/ijiS4jdqdKDEB/5r3AECiMCFXK/AJ+5i" 1576 "9QjMM89AaQy6vhmxAO0+zi4y9BRf6PGuiAPZfkL3tyBnc0i5CzmkgsB19AABROiKtFcPERdo" 1577 "QDzyDQy/Ih+t/wyxnwferHyA1E9G6SxDDiP+hBIb3yHe+I3uDXC7hJj4+P8fIIAIXlr3EhTG" 1578 "jyFH1UKjBH7K9jdErCBOE0C6swoLgLYVYLdYQI+M/QFfJ46azcMgWw0Jx8f//wABRPgawbcP" 1579 "IZ0SqN1IB59Dj3FHzf6Yp2CjAHhRDj+/F5yowN5AzuSwZAU9juY2QWcCBBAxVyaCeyVPYCka" 1580 "2R9wv6B4Bt6SwQKewiMW2RvQ1e5///1F8wc0fxDhSoAAIuqqzbfQVI3shm+gA4S/w49A/4Za" 1581 "mH3BdnDCly/IiRNy4iLk+Abomh+UVAXb54K3PkcAgAAi7vLTh5BbOuDZE3rONtwj0JOEkX2C" 1582 "4RlobQoPBuj5fjCPoFYefyEr339hHYvDCgACiMjraF9BbqqCHBeOODAcfmQt7KhqlOSGARDa" 1583 "kP0AXYEF9wWkTQI79eQbkdeCAwQQsRcEI100g8UjsKOEkbzz9RumHyB6fkBOWoQe3PAHtq4a" 1584 "qcyFeQR8fhaRDgQIIOKvbIYcAwqtwr4i++QHGKJEz3fUAuE7TC3cD6BaA7Kd5S960yoMsi36" 1585 "J/jcVaKdBxBAJFyi/Qjhka/QvA51G8gjP9Di5zs6gJ6lCvEGzB+QxeF/kapAcNvqN+yoPOJd" 1586 "BxBApFxrfgN8zwVG6vpBGPyEHUkI8QJ0jxRkkeU/aKH79y+syP0Fi44HJDgOIIBIu2getAkU" 1587 "uTZB9wfkHEgQjeR4mA9+wX0B8QfUIyj+QNQdxOcOCAAIINI88p/hyZPPn5EbK3Cf/EQFv0AQ" 1588 "Dn5Dj8j5DfMDNElB4uMv3BPgEhfStvpGossAAohE5f/fPv70Ca12/vEd7hOQ85HCH+x8mB+A" 1589 "vvgNjwpYbCAAuKQCZw5IYfWBRIcBBBCpHoGkL/AwF6LtivAJcixAUxH0VAOUmEDyB3jbF7T6" 1590 "gzQQYXt0SAMAAUSGR/6De4xPUS4FgfoEkZx+wz3yB8Uj4M1Rf5E8A/MGOIuD8zipuQMCAAKI" 1591 "HI9AMv0XRHPlO1I9hxwhGB6BhD/CA2GQjAE7vQxyqvI3spwEEEDkeeT/68+fUdriKBU2wiPI" 1592 "XglD+CQsDO6PMMg5Lb+gp8US3yRBBwABRKZHgAB25yJynYIovpD8ghIvcACVg5xSCEtS5GQO" 1593 "KAAIIPI98p8BmrzgNT2KV5ALXah3fv9G8gHEDxBfQK8YILnIRQYAAUSBVtCMEOKWP6S2OXKs" 1594 "IMcMqg+gyQnhi1sUOQUggCjzyP//z56h9ZZQEhhycYwKYIco/4Alqm8UOgQggCj1CKivgiWz" 1595 "fEdqtGABiGsFIM3k15S7AiCAqOCR///fQHqAX7H0ubA1IVH6MEAtb6nhBoAAoopHoKXYl2fw" 1596 "zI8xRIHomKD2jqlmPUAAUc8joPzy7BnqfZLfcACYPBUtBwgganoEAu48RR0xwQq0qW4tQABR" 1597 "3yPALHMbv0fuvqWBpQABRAuPDAgACKBh4xGAABo2HgEIMABb8EQbIiGeqQAAAABJRU5ErkJg" 1598 "gg==") 1599 1600#---------------------------------------------------------------------- 1601DownButton = PyEmbeddedImage( 1602 "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAACXBIWXMAAA7DAAAOwwHHb6hk" 1603 "AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAA" 1604 "F2+SX8VGAAADAFBMVEX//////8z//5n//2b//zP//wD/zP//zMz/zJn/zGb/zDP/zAD/mf//" 1605 "mcz/mZn/mWb/mTP/mQD/Zv//Zsz/Zpn/Zmb/ZjP/ZgD/M///M8z/M5n/M2b/MzP/MwD/AP//" 1606 "AMz/AJn/AGb/ADP/AADM///M/8zM/5nM/2bM/zPM/wDMzP/MzMzMzJnMzGbMzDPMzADMmf/M" 1607 "mczMmZnMmWbMmTPMmQDMZv/MZszMZpnMZmbMZjPMZgDMM//MM8zMM5nMM2bMMzPMMwDMAP/M" 1608 "AMzMAJnMAGbMADPMAACZ//+Z/8yZ/5mZ/2aZ/zOZ/wCZzP+ZzMyZzJmZzGaZzDOZzACZmf+Z" 1609 "mcyZmZmZmWaZmTOZmQCZZv+ZZsyZZpmZZmaZZjOZZgCZM/+ZM8yZM5mZM2aZMzOZMwCZAP+Z" 1610 "AMyZAJmZAGaZADOZAABm//9m/8xm/5lm/2Zm/zNm/wBmzP9mzMxmzJlmzGZmzDNmzABmmf9m" 1611 "mcxmmZlmmWZmmTNmmQBmZv9mZsxmZplmZmZmZjNmZgBmM/9mM8xmM5lmM2ZmMzNmMwBmAP9m" 1612 "AMxmAJlmAGZmADNmAAAz//8z/8wz/5kz/2Yz/zMz/wAzzP8zzMwzzJkzzGYzzDMzzAAzmf8z" 1613 "mcwzmZkzmWYzmTMzmQAzZv8zZswzZpkzZmYzZjMzZgAzM/8zM8wzM5kzM2YzMzMzMwAzAP8z" 1614 "AMwzAJkzAGYzADMzAAAA//8A/8wA/5kA/2YA/zMA/wAAzP8AzMwAzJkAzGYAzDMAzAAAmf8A" 1615 "mcwAmZkAmWYAmTMAmQAAZv8AZswAZpkAZmYAZjMAZgAAM/8AM8wAM5kAM2YAMzMAMwAAAP8A" 1616 "AMwAAJkAAGYAADMAAADV1dXU1NTT09PS0tLR0dHQ0NDOzs7Nzc3Ly8vKysrJycnIyMjGxsbD" 1617 "w8PCwsL39/f09PTx8fHu7u7p6enk5OTf39/Z2dnW1tbPz8/Hx8fExMS/v7+7u7u3t7e0tLSw" 1618 "sLCtra2pqamlpaWhoaGdnZ2Tk5OPj4////+ZlMjzAAABAHRSTlP/////////////////////" 1619 "////////////////////////////////////////////////////////////////////////" 1620 "////////////////////////////////////////////////////////////////////////" 1621 "////////////////////////////////////////////////////////////////////////" 1622 "////////////////////////////////////////////////////////////////////////" 1623 "//////////////////////////////8AU/cHJQAAIMRJREFUeNpi+D9MAEAAMQwXjwAE0LDx" 1624 "CEAA0cIjb+9/wQE+g8GdNzSwFCCAqO8R7S+4AcQjz549+0B1awECiJoeAbr0KwR8QwNQ4a9f" 1625 "viK8A/IPFS0HCCAG6nvjGy4A8Qsibp5R0ysAAUQVj7yFeeA7CPzADr5DANRHyL6hSpYBCCAq" 1626 "eOQ1NBJgfviJDSB7B9Mvryl3BUAAUeoRkCcgPgA59xcY/IYBKPcXlPEL4Smwb6ApjTr5BSCA" 1627 "KPPI7a+QqEDyxm/sAMUrkJhBRAzEJ+8ocgpAAFHgEQZIbEAiA+aFP1gAmm+Q/PIdyStAvzyl" 1628 "wDUAAcRAYaKCxQXcD2FQgN0zEJ/8gmYaSLRACmWwTz59Its5AAFEpkfewjwBiQhkH2AArL4B" 1629 "e+Yn3C9wrzz9RGbGBwgg8jwCLmrh/vjzG+6Pv38x/QH3ye8/6EkMFiuIbP/06acnZDkJIIAY" 1630 "yEtU4OgA+QIRFX+hANU3KMkLwyPwAgwSJ1/AcUKeVwACiIF8f/z8hUhSf+EewZmuUPMJwiff" 1631 "kYtisFc+PSHDJwABRKpH3oN9AU5TUE/8hccFqk9gSQqpCEb4AZhHEEUxwi/w4uvTk7ckOgwg" 1632 "gBhILXLBsQFJVMgJCs0bEF/AowAlIqBegBbDP38gl8RfYXn+yRMSXQYQQAykZnJQdPxE9cY/" 1633 "NH8g/IDi8p+/sLZdfqK0XJCS10eSnAYQQKR45AHUH6BkhYiMf3CfhCF7A7VVgtrgggHUNhi4" 1634 "wQZtf4Gi5Mnj9yQ4DiCAGEiKDkgWB3kD4oV/QAAkkOMCWk0g3P0dJ0BvGyO3WUDZ5OPHx8S7" 1635 "DiCAiPcIuAqEFLngDP4PCv6iegPqD2TngZ0IcymssY/mG1gTH1Z4gTP8ExJ8AhBADERHByyT" 1636 "QxPVP2SPQLwB8gRSM+o7om+I6Dti6XvBOipoLWJQgwWYvIjNKQABRKRH3kBzx29o7viH7A+E" 1637 "N34h1QxInQ40gOad78jRhewRcJx8JLLJAhBADMQmqx9IeRzNG6AkBU5OP38gJw945xwToHoK" 1638 "s3ePVJ98fPyIKCcCBBBRHrkJblhBklUYkjdAHgHFBjQygHEBb2wgjZigAQzvYHoFphvqEaLq" 1639 "RoAAIsIjDLBk9Qc1OqCJCpIzIG0mtM44sN0EA6D0DgKfPoF4yB5CpDZknyB55PHDh0S4EiCA" 1640 "CCu5Ayp24f74h5bJQakKxRufEZ749AnmfBj4+BHmmWdP4V4BewSRyBCtLkh18vHjo0dERApA" 1641 "ABH0yGtoskLzBiRRQWMD6At44wKcRUHWP34MdMCjx1Dw6NFDMICIIHkI4h+UYgAx+gWu4Z8A" 1642 "dT98RcidAAFEyCOvv4JrD1CTBCNZwWMD0rSApgWgHz5CHf7gIRw8eKCt/QAI4J75CE9qsGQG" 1643 "jhiwP5A9AiqCifEJQAAxEPIHuDaH+AM5PqB5HJKooNnzKahdAcycIGeDgPb9+/e1oeA+lA3x" 1644 "DCyqQDHzCZ5jMIdXIb2Tj2AzCfgEIIAYCPkDXO6CmogYhS4oWaGkKpAvQIEHCn2Qw+/fQwUw" 1645 "b4Hj5REk4YG9AvcLFq88hcQJMHBe4nUqQADh9cjtb5DaHCV/QH0Bjo1vUF+AG6tAy8Bhf+/D" 1646 "h7tYwYcPSB56AE9o0BwDjhp4/v8ML75B8Qz2iDbeHA8QQAx4Ox+g/AFuW6FkD2juAGcOaPcU" 1647 "6A9QTECiAe6ROwgA8woIgLxyHx43sHhB9clnRCEO8shHcDbD51iAAGLA274CxQd6sgLXHeCy" 1648 "ChQfkAwJSsTALAHyA9gbd7ACRLyAE959aLQ8APkFmA+eIOeXz5+RYwRUBIMyHh7HAgQQA/76" 1649 "HJQ/UMpdSFUOzh3gsgpS1kP9AQlwiEduAwEO34CTGNgrD6A+eQgrxz49hZfI6B4BlSF4EhdA" 1650 "ADHgaV/B4wMlf0Cyx/fvsMwBzhv3kSPiNgzgiBdEXrkPjxpwZnkMTWEIvzxD9cgDbZzOBQgg" 1651 "BtztXUj9gdJmh5ZW4OiAduQ+QiMD5I3bIE+A8K1bt+AeuY3qr9tYvAJOYw8fPn70+COibgH5" 1652 "5RmGR3AWwgABxIAnf0Drc5RS9w80WX0Fd36ePIHljbsQN0OcfQvqETSAGScwz8CzPbAyRYoV" 1653 "aGMNVDvBPKKNK04AAogBd8ICtq8w88fv3z/h/ngKzOQPH0CLqTsIP9zC6RFISYbNIyCvQD0C" 1654 "9gqkcQlvbhL2CEAA4fAIJF39Rs0f8GQFzh6g3IFIVLehLqfEI+DyC1pPPoG1YD6hewSHTwAC" 1655 "iAFnR+rXL5QKBNpIBPsDVOg+BRW52veheQPug5sgQIZHoMnrEaTKh9UsiGYz3CNA1VidDBBA" 1656 "WD3yECmjY40PUO4AZg5tSN4AxwTQ9TeRPIKIHjSPwCpGFI+APXMftVn5Edkv4Kb0Q2jTB+uM" 1657 "EEAAMeAYaQB5BDNhgfIHqFECKq1AbUJ4soL6AArI8sh9FI+gRspHhEdAWrC5GSCAGHDUIKCM" 1658 "jlzugvseP0FNRFChCMod4MwBzhuguLhJBY8g2soPYG0XKEDzCDafAAQQA9auLaRGR4kPcN8D" 1659 "lK6AzfVPjx6BcscdaJUBcvsNIMDvEUQjBbdHkDI+oq2PxSNYXA0QQAzYWibf0Uos5PzxGZKs" 1660 "7t2DJ6pbEG+geQSt9AJ7A5rREQC1kY/iGVgqewjrZz4CFfWwngHm1DxAADHgLrHgHoHWHxB/" 1661 "AIvdx6D4+HAXXlRh88gtDI8gSiyiPAKNFrhHwDGC6OJgOBsggDA98h0to8PKK2j++AT0hjYi" 1662 "WdHQI7BOC9xDeD0CEEAYHvkKGmv4jc0j0Ph49PA+MFkBqw6QD2jrEaTOMaTfD/fIB4zlRQAB" 1663 "xIC15AV10VEqQmAGAeePT6Dy6sMHSNWBWlRhAuSMjuhoIVeH2DI6LgCqDe/d/wDrK6D7BCCA" 1664 "MD2C1saCNhR/gJvtkPwBKnbhtTjVPQJLTsiegMmBm3XYPQIQQAwYEYJaYkEL3h+gZvuzp48f" 1665 "AePjLqxgpY9HYKMYaN3ou2g+AQggBrT1SmCPICIEWCeC+ufQgvcTqHV1F+aRm/TxCCRLwPR8" 1666 "+ADrvn1A7ZoABBADZtH7+88fhDf+ghvuYH8A+wWg+hzctoJn8Zuw2hDKhtCYDWBEpx0tj9/D" 1667 "VVIhxwbcI0j90Lt3UZwOEEAMmN1bpN4UrAcC6p5DK0KER27g8wjWbhVsIIUWHgEIIAbUovc7" 1668 "aFQR4Q/IQAMoo3/+DOrUgrqCsMYHvT0CH9dAJFdktwMEEANKI+v7D2SPwFq830EZ/Rmocw5q" 1669 "mMAqCBp55AEyQPIIbKQMXg0DTUR2PEAAMWCOYyEnLFBGB5dYT8G9c1D/A+ILRDUI9QQyG1Yj" 1670 "3kZt+0LyKJo/wIN1yB6BVH2I0WO4PyBDF7eR6to7SI4HCCBsHoE236FVOmjEBFyjQzpStxGl" 1671 "FYpHYHykyv3mLbRyC5y0sXkEpfkO9QiIBqcuiEfgIzI4PAIQQAyoI71Iray/0D7IN3CJ9fER" 1672 "pCN1h0iPoDTnyfbIA2SPYJT6t24jNYIBAogBtTJEamWBPQKpCr8Am4qgQbh7d+8gN0zo7RFE" 1673 "zwdq561bCNcDBBAD8uollI46KGGBRqpBTRNgifUA2FT8gPAHiuPRPYAIMrS+IcQjiCFT1DHg" 1674 "Bw8Q00JwH92HjMRCcjnMvvdAAPbIbbjzAQKIAaW5CBoihfgDOsb7E1r0gsevQHX6rZvEewSj" 1675 "Zgd5hSiPIE90ARM01CMQ295j9whAADGgDMkhWieIzhS4afIAUhUi0hW5HrlLoUfeIwFQ8Qh3" 1676 "PkAAMSAmPb/B50Kgy0sgHgH20UFDP/dBVeHNQeURkE/gDS6AAGJA6YjAptKho7zg3u3nZ8Cm" 1677 "yX1wZ+r2zZsEPQJlwyotcMUFrgthnSrwJArqMNAD1J4gEgCXwCAdoLyO8MY7EAAnL3iUAAQQ" 1678 "A7zd+/07Yk0AzCPfIW2sx5ASC1uDF8UfSBzkLiLK4PUHLDU6Po88gLR/7+LyCGypCkAAwTzy" 1679 "5Su0ZxgGS1h/oK1eoD8egUqOO7ep4pF7pHoE0sHF4hGwV+C5BCCAGFCmEf4gLRaFdW9BvXTI" 1680 "YNwg8cg7BADGCcwjAAGE5JGfSCv84K1eUB0CbCwCPXIbI6Oje+QGmkduIvVKwM2su9jG5Qh4" 1681 "5OGDh2CPAH1y8waaP5A9AhBAGB6BLYiD1iFfwHUIMECI9Qiemh0xcII8UoLkCUyPgL1yHzTo" 1682 "APII2Cc4PAIQQFCPfIGNv4eh5JAv0FYWpIVAVY9oYxu7wuIRcMF1HzwgCOw5oHgD7JWb0DoR" 1683 "IIAQHvmJWOYHnwkBeeTxQ8g476DzyFsUjwAEEAO0zEKMnsDWZwD7IV8hleF96DjvLSweQfYA" 1684 "No/cQp47BNUhmLNU0LYVzOGP0QB4hh1UlyA8AvbC27dg6ga05QgQQHCP/ED1yK9fkMrw0xPE" 1685 "gDWucRNSPIIx3Qavw4nxCHIOgXrkPdQjAAHEgFSLILIIZKwXPHLyGBStd2jvkYdY/IBY6gVM" 1686 "FUgeeQvyBRiA0hbEIwABxABtwiNG4BGD1sBa/dPHR/dAzfdB5hG4P4A+gXoEIICQPQJb7w4b" 1687 "AgJ2qIC1+r170CE5DMejd64w2l3o8yMf0DtTyHn8MU4AHodHeOQtMgBmkptgLwAEEGaMQBZc" 1688 "QponcI/cun0Tt0ewdhexllqwihBzyuAR0R55i90jAAEE9oj2V+gII9Qjf2DNk6fQ5glmsqLY" 1689 "I+i1+WNyY+T9DfBSG4AAYoCVvlCPQBa5w9tZ8ObJ4PEIeoy8fw/OJAABBPLIW3Dp+xvukT/I" 1690 "XVxt1PbiLbTJT6weuQGfAbqJaGtBV2ppQxYHaD9E8gg+X0BmD0FDH8DgxPQIuNwC9a4AAgjk" 1691 "kfvQagTNI6AuFcwjt28htWixVYqoNT2qR+7APIKeyR/AKsLHBMAj8EIqYPsXu0dAaQsggBjA" 1692 "G7i/ocYIuPAFLZsBDZ7cvYuY9rxFcF4Eq0dgjXik5ZlEFLsoiQu06uXObSweAWX3G0BPAAQQ" 1693 "wiNIMQJtMD4bSh4BCCAsMfIHPnoCbjDexVjChJxXiPUIYi0j1COPEEtmHxPjEaBP7iJ55A0Q" 1694 "oHgEIIAwY+QP2CPg6hDa8kX2yE20vEJ4zgoxwXPvPmKRGaKRi83hH8GruSH0RyweAXnizds3" 1695 "8HIL6AmAAMKMkT+IOcNPj6jmEdS1pYTqQEIeAUcINE6gHgEIICwx8hvWhB+0HgH64A3cK1CP" 1696 "AAQQlhj5DW3Cg/oiMI/cQvYInE3IM7Chh7uwkSz4gnIsHvkIBY+hK2c/IglgeOQNhkcAAgji" 1697 "EaQK8Q9s+QxoTgQywnjnDmKdD7JHcJZk8AVo0KoQuvpaGxYbWKLjI7K7Hz9B9ghY7CHMI8gp" 1698 "C9kjAAGEyyPg6hCLR5BpfB5BDKBAWr3Ia0mxZXLEKrOPH1HZaB6BxchrIIB4BOgVoCcAAgjT" 1699 "I78Ht0dQUxbQJ6AOCtATAAGE6pE/UI9A1gcAPaKN7hEYuIW2cAbDAyD6DswjiGQF8gCqR2CO" 1700 "fYILfAQvYAZ55A5KjAAxNG2BPAIQQMgeAUXJHzSPgGZFsHkE1tWCiWLGBKQ3BfcI0t4E1E1L" 1701 "ODyCIgL0CKhqvnPzBsgjb1BzOyRpAQQQwiO/oMdPwJf8IXnkDpbFr6gewbrWF6VpgrZMEWdM" 1702 "QHeYwdYxf8LiETB4jcjtII8ABBBajAwhj7xB9QhAAFEUI7B6Atd6eFgbCzwHAsnmiCIVPfkg" 1703 "9ilC1l5Dl8hj8chbSKEFiROYRwACCC2zk+IR6KYEnLLQuhA61gDfu4MUER+RowC2ghzJI5+g" 1704 "HvkI9chtpBh5/RpSAMPyCEAAIccIkke+E+URzJ0VWJMVuGeLsgMRJQ6eQrZeQjyBtIsUwsXq" 1705 "kddQAIwUWIwABBBGjPwmIUYGzCNwn7yGxwhAACHFyC9YHvlFcYwgFtDcgxW88GSFlIpg/kD3" 1706 "yFO4BIEYQUpaAAGEkdnBu1bBY1rPnnzE9Ai6u7FHCGLi8z4Oj3zC45GnxHsEESMAAQT1CGSA" 1707 "DuQVcj2CbR8V0sL9RyiJCppqPuHyCBIbxSOwJgoiQuAeAQggbB75ORQ9AhBAII98/gwdjQed" 1708 "ZwTeY4/dI3eQdrUhOxtl/T582SI0k8N3T6F7A5QXYA7G8AjSRni4R+7dufX+/Vu0lAX3CEAA" 1709 "MUDP84MesPEb6pHvSB75cAdthyTWnXl3kddYIwaqYQNwKHkDOeSfIgEUDtoeK6BHbt9CixBk" 1710 "jwAEECxGQEucYEfj/ER45AF4OT98oSvCR1g24mLM2GImq09YnYofQD3y4P6HWzch9fprZI+8" 1711 "gXoEIICQPAKPkcHtEeRaBKnUAgggqEe+wGLkF9wjoGk38OADTo98gK6ZRF+vj7xiFCV/oOwu" 1712 "JMUj4G2CH24hF76vwAgRIwABhMUjP2E1+yfogswPSB5BiwSsGw/g44na0I1fSPGB1yNwSWRF" 1713 "T2AeuYvskVdAgBIjAAGE6pFfkLwO37oDWgtP2CP3sGzAh+8mRs7oT6EnipDiEfDZC2B33L0J" 1714 "7h++gXoD4hNIXxfoCYAAwoiRX4gYGZQegWSRV0gxAvEIQACBPHLn2ecv0JOaoCcYQTdNfgJ1" 1715 "lRGrVUnyyEMkb1DBI8C8Cumxv8ESI6DFpgABBPLIm2dfMD3yBbdHkDP5PSwAl0eekuWRT6ge" 1716 "AfvkFWqMgCZ6AAIIPPX2DFz+Qj3yE3zQCXQbK2xf0weMUgrP5iLYtAHk2JCP8O4T3CPIAOED" 1717 "JB9ieuQ+0CNo/nj1ClIhglPWf4AAAnvk7ufPEI/8hMUIaP/n4PLIvbu336P54xU0RsCToQAB" 1718 "BJmeBnrkK+REGsiZg9+huR101II2ikfQAYZHkOsPJI+A2lXkeATU2AdNvYHmQt9hegQcI2Av" 1719 "AAQQzCNfvsKOwvwJPZfiC3yH9L3797DGBEGPPIZ5BKlNhc8jOKp1kEc+YPfIa7hHAAII2SPg" 1720 "XIIcI89AZoAWrN7D6wH0xQyoAz/QI1xweQSrp1DbvqDp6Q/AXhWiFnkJBLBCC+oRgACCeOQZ" 1721 "uCb5Djs8GeGRj4PCI8D2xd07wHod2R/oHgEIIKhHnkGj5AfsWE7w0S3PoI2D+7CNWjg8grEh" 1722 "B3wQBekeeYbLI9rad27ffA9LWC/hHoEMYoO9ABBAlHgEfjwQ+mIGkEegm54hcx3U8MgNLB5B" 1723 "yiMAAQRdCgjyCDS7w058BZ/rACz7IEvxsJxphH2h6APUjI44SYsYj2CkM2iDEeiRW4jCF+oP" 1724 "lLz+HyCAEB75Aj4X88cPZI88HSQeuX8fmLIgHnmF7BFEFvkPEECw5bIgj0AS1w+oR75CD6IB" 1725 "reqGbIMaKI+AGq6QBiMiYSE8AvUAQADh8QikSwLxiDYuj2hj2awGzSHwgV7MniEpHgFvO/8A" 1726 "iRA8HgEIILhHwD5BeOQ7xCNPwR55AN20hWf3oDZmfFDBI+BDwkDNkztoOeQluD58g+QRgACC" 1727 "eeQZ+Ag46PHpMI98gXjk4UB6BLSN88PtW+8wIwTiEdjZIgABBPPIG/A5YwiPQM7eBXd3YQeS" 1728 "EOURlKnnj4gWI3keAWd1bfC8CKo/ECkLthMGIIDgG2HAUYIUI7DzgcAtHUyPwHZoY/MIyqwU" 1729 "YtqDZI+Ahx2ALYu74MoQ3SOvUVLWf4AAQmxNguQScBEMPdkZfGDTp4/Q48zwnwUAW7eEOu0M" 1730 "bTCS4xFYwnpw/wNG0QuNEZA/XsDcDxBAiM1izz5//gK98gFybO3Xr9DzzMBV0gB4BJKw7n0A" 1731 "LZwD9dVfIXvkNUqZ9f8/QAAheQSR32FnbYPPAhtIjwCLXnCH6h1SifUC7BHYUBDc+QABhOIR" 1732 "SDsFcd8Jske0CXsEc0UDondIokeg0z2gEV+sHnkFHp1D9ghAACFtcX0GyyWwc4TB52E+gzWk" 1733 "0eMEc9Ur+loyyMwtWTU7eKAespcI3g8B++MFEMCyCLI//gMEEJJH3kF8AjkTGXZNAGiNEOQM" 1734 "RnI9QkozHqnAgjZOPsDGF8EeefEC4hFYgxHpdGaAAELeBv4MESdIHoE0E0Art+jnEcisHCiH" 1735 "3EZp9aJ7BMnxAAGE7JGnzyBHvH5F9chTqEe0cZ6ZAc7lmB5BrCkhzSOweV3QmTi3biJ55AWy" 1736 "R96gegQggFBOG3gKOUcU5hPoOaXgsxjBuQTppBL0dfoYay3h055A+hPWkThCCQuUQ+5Cpz/B" 1737 "Aw5wj8CaJ8iOBwgglMMrPqEkrq8wj0BOpkY9coVQskI5eY1Ej4C9Ackh8GFSNH+8Qo+Q/wAB" 1738 "hHqcyNOnz6BxAj/vHXqsJCUe+USGR0BLzZBKrNcvX+JNWP//AwQQqkc+fYJWi9T1yFMSPPIU" 1739 "mJIhxcsDRIkFL3lxewQggFA98voT7PxgxHVmEI8AI/oh5EQl2JFEaBu3sS4axTfdht0Xz6Dr" 1740 "gUAZ/Q64bYIoeZ8/RymyUA8yBgggtEOQnmD1CPToVeRjV2jkEViBBa0KIbO40AzyHMkjqJUh" 1741 "CAAEEPqxVJ+gGR7lQGfIwcTwoyvBB9yS4pFP2DyDuwIBzaqDlvTDRt8hNchzsEdwJKz//wEC" 1742 "CN0jT548hfkEcZg9uNyCHJgOO4gM0UiE7KfFtukAZW6dCI8Aswd0rBcUH6A5XGgbC5RBniN5" 1743 "5A0WjwAEEMbRbcBSHzVOYAfRQ/fRoR1zh2+tPsqSpk9ofsDwzFNY9gANQEG2WEBXB7yEZJDn" 1744 "0LIXa4T8BwggzMP0sHrkGXiW4jGtPIK8Tgs8/APehgRac/0aJWGBPPL6FShCMJwNEECYHgFV" 1745 "xMinuNPRI5CzPsFzIe+Rpqcg3sCbQ/7/BwggTI+8hZ8Wjri04hn8ugaERyC7ox7i2M+CvoL0" 1746 "EwGPgIoEyBQC+HC4m+9QmopQf8ByCEqzFwoAAgjbYZqgxPUMfoEDFo+gHDqIiBvUMQdUb3zC" 1747 "mPBE9wi04Y6oP2AFFpYSC4urAQII26GskKUW8KPosXoEtc2IzSN4khW6R57COyCPtCH1B7QC" 1748 "weURLI4GCCCsx+SC55KRTz3H7hFtIj3yCY9HkNbNgFvYHz6AeiDQtTPQLggig0DXNWFzM0AA" 1749 "YfXIDdgCGMzT22EtLuSDWQh65CkRHvkIObb97t1bN2FbEmBdKUQGgXgE62WpAAGE/ShpUIHz" 1750 "CXFDACTyYefpa6N3rMDnjKLld2SPPMXpEfhlK+CNFfeRNk1C59hgBRahhPX/P0AA4TjcGxyk" 1751 "sDiBpuInsMsacJ1mi73oxbdGCzLGAElWkNNwbkI37UAqdLg/niOVWNhdDBBAuI5bhywWQ8mO" 1752 "NPAI/Jzoh5ATOUH9QXh5RUp8/P8PEEA4PfIIlDygZ+rDp/OAHnmEOpzyAFqnYHoEW+sXNlYH" 1753 "W4wJ8Qd4lBx0NtFNRDWINT7weQQggHBeSfAKtLMAyRmwqftH8GtS7t+7j76gCZtX0G9OAnvg" 1754 "EyJvQEZogLU5YgfVa2hDEV6jv8Ac60UHAAGE+5KIhw/h909Ai3kMj0BH5RG9RGyNlE9IAHkt" 1755 "OSw2wPeWQOID7g/kcpeo+Pj/HyCA8Fzb8fYh0pH6iOPbUT2Cuk0S9xYwjH1HkCuJoLd9IMUG" 1756 "0ggWvKEI9wae69IAAgjfRSpoHvkI8wjsJGT47S4oHoF5Bst5/E8QQ0SwkwPAdxogzg6ArFdE" 1757 "TVa4+yDIACCA8N4Wg7JzBdx+ghy38gB65iDS+iD0zXmwiwU+YomJjx8ht0NpQ7xx+xasSfIG" 1758 "rZkIbyhC/YHPsQABhPeyobfw+yeQXQD2CHjtFspqofso+z7hHsECYFcr3YdGB2JPNLwaRPMI" 1759 "wYT1/z9AAOG//unVI+T9j6geQb1PCLatGFtj8hHm2V/a0BOIb99E2+yJHB+o+QN3gQUGAAFE" 1760 "4EKuVw/R+k7QM8jA7riL9coaZM88xHbMH3jKHnYkBux8ENiy5JeY6Qo6WYjfH/8BAojQFWmv" 1761 "cJ7TdxdlMw/y+mzktdios0FIJ3qCjsO48R4lOiBTa+jx8Qqyue0lAYcCBBDBS+teoaZ7SKMR" 1762 "cUwq5kYl5L3r6AB2hPJdZG+g+OMFotkOndF5/ZqI+Pj/HyCACF8j+Bb5hNGH6B7B3KAP2dKH" 1763 "9WoxxHmkkNOMEDn8NWyiE7nVjpQ/CN+3CRBAxFyZ+AAVIHvkFvaDK7Dd/ATbTHoDdpDc27co" 1764 "mRw2vIvwB3yJ8lsiXAkQQERdtfkWMYANLnHug0reO/AjnmDnvULP5YQf/YK+QRz5KDw0TyCP" 1765 "UsOHqokqdmEAIICIu/wUNb9CVs+CPXIT+eBaZK8gvIM4iAfVG6geeYnqEVh0vCZQnyMAQAAR" 1766 "eR3tK6QuCIZH3qOfA4k42BJVHHEqyxuEJ2Cp6iXCE4g1MxiD7jgBQAARe0Ew0sUA9xC3uSE8" 1767 "8o4IgPAFPDLgyeolcmzAcweukQYsACCAiL+yGeqRe7CLq2Bnx2PGB/RQNchhd2hH5MA21b9G" 1768 "TlOovoAtu3xDdLICAYAAIuES7fvIZ+mjeQRxduJbzNN90FIUYo8UfG0Jki9evkLkjjck3DYP" 1769 "EECkXGv+jpBHwDGB1QPIm+mhnkBe64Pii1ew2MA+7oMDAAQQaRfNwzxyF0eMYIkAlPMAXr9B" 1770 "eALJHyiryeDbC9+S5DSAACLNI/8Z7kHa79ADLWAeeYd2fCIsFtCONUDxBaZHYP4AJ6q3JLoM" 1771 "IIBIVP7/DbRHdQdyjjuqR3AmKKSNg69eY/cI0iYdcHS8JtFhAAFEqkcg6QveCkfxyDvs5ROK" 1772 "N5DiA+yRV8h7KGAl7tt3pLsKIIDI8Mh/XB7BWkIhe+M1si/QAKzEJT13QABAAJHjEaBXwB5B" 1773 "tLKwxsgbjBh5hRohyFHx+vUbSrzx/z9AAJHnkf+vkDxyg0CMoOYQHJHx+g0sWb0kz0UAAUSm" 1774 "R0C7F+F3HbxHbw2iHR31GjV5gTMK6g5oxIlyZDsHIIDI98h/BmgzHrldi80foLB+jQu8eY3s" 1775 "DQpcAxBAFGgFFcaIrhL6SbzYanM0P6BEBekFLioACCDKPPL/P7QFjFkpolbp8DT2BtUTSEfi" 1776 "UegQgACi1CNA8BJWDKMnrzcQ/Aarp5Aj490Lyl0BEEBU8Mj//69vofVk35IAIFttKQYAAUQV" 1777 "j0ASGTzjox/B/fYdZqMYctI4xQkKAQACiHoe+f8fOtSAWiBD+ihvMbuLVPXG//8AAURNj0CH" 1778 "XCCjKzh78hBvUN1agACivkeAWeYtfo+8fUUDSwECiBYeGRAAEEDDxiMAATRsPAIQYAAZi6PF" 1779 "fdLmvAAAAABJRU5ErkJggg==") 1780 1781 1782if __name__ == '__main__': 1783 1784 import wx 1785 1786 class MyFrame(wx.Frame): 1787 1788 def __init__(self, parent): 1789 1790 wx.Frame.__init__(self, parent, -1, "ShapedButton Demo") 1791 1792 panel = wx.Panel(self) 1793 1794 # Create bitmaps for the button 1795 bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_OTHER, (16, 16)) 1796 1797 play = SBitmapToggleButton(panel, -1, bmp, (100, 50)) 1798 play.SetUseFocusIndicator(False) 1799 1800 1801 # our normal wxApp-derived class, as usual 1802 1803 app = wx.App(0) 1804 1805 frame = MyFrame(None) 1806 app.SetTopWindow(frame) 1807 frame.Show() 1808 1809 app.MainLoop() 1810