All pastes #47725 Raw Edit

MultiStateButton.py

public python v1 · immutable
#47725 ·published 2006-03-31 20:27 UTC
rendered paste body
"""MultiStateButton class@copyright: Copyright (c) 2006 Open Source Applications Foundation@license: http://osafoundation.org/Chandler_0.1_license_terms.htm"""import typesimport osimport sysimport wxfrom wx.lib.buttons import GenBitmapButtonclass BitmapInfo(object):    __slots__ = ["normal",   "normalBitmap",                 "rollover", "rolloverBitmap",                 "disabled", "disabledBitmap",                 "focus",    "focusBitmap",                 "selected", "selectedBitmap",                 "stateName"]class MultiStateButton(GenBitmapButton):    """    A MultiStateButton can have multiple bitmaps in its default state. These    bitmaps are passed in as a list of names, which are also the names of the    PNG/GIF/JPG/TIFF/PSD bitmaps.        The name of a "state" is the same as the base name of the image. So for    "/var/image/foo.png", the state name would be "foo"        mailButton = MultiStateButton(parent_view, 100,            multibitmaps=["/var/image/NoMail", "/var/image/Mail"])    will create a button with the two bitmaps Mail.png and    NoMail.png. The default initial value will be the first named state;    in this case "NoMail". When desired (presumably when mail comes in),    the state of the button can be changed thusly:        mailButton.SetState("/var/image/Mail")    and the button's bitmap will change to Mail.png. Later, after all the    mail has been read, the button can be changed back:        mailButton.SetState("/var/image/NoMail")    Roll-over bitmaps can be passed in through the use of tuples:        mailButton = MultiStateButton(parent_view, 100,            multibitmaps=[("/var/image/NoMail", "/var/image/NoMailRollover"),                          ("/var/image/Mail",   "/var/image/MailRollover")])    When the mouse rolls over the button in a particular state, it will    display the named rollover bitmap.    Even more control can be exercised through the use of the BitmapInfo    class. You can specify the normally auto-generated bitmaps for disabled,    selected and focus states, as well as a separate state name not derived    from the bitmap name(s).        no_mail_bitmaps = MultiStateButton.BitmapInfo()        no_mail_bitmaps.normal      = "/var/image/NoMail"        no_mail_bitmaps.rollover    = "/var/image/NoMailRollover"        no_mail_bitmaps.disabled    = "/var/image/NoMailDisabled"        no_mail_bitmaps.focus       = "/var/image/NoMailFocus"        no_mail_bitmaps.selected    = "/var/image/NoMailSelected"        no_mail_bitmaps.stateName   = "ThereIsNoMail"        mail_bitmaps = MultiStateButton.BitmapInfo()        mail_bitmaps.normal      = "/var/image/Mail"        mail_bitmaps.rollover    = "/var/image/MailRollover"        mail_bitmaps.disabled    = "/var/image/MailDisabled"        mail_bitmaps.focus       = "/var/image/MailFocus"        mail_bitmaps.selected    = "/var/image/MailSelected"        mail_bitmaps.stateName   = "ThereIsSomeMail"        mailButton = MultiStateButton(parent_view, 100,            multibitmaps=[no_mail_bitmaps, mail_bitmaps])        mailButton.SetState("ThereIsSomeMail")    Obviously this latter method is a lot more verbose    """    def __init__(self, parent, ID=-1, multibitmaps=(), *args, **kwds):        """        Initialize the button to the usual button states, plus a list of        button bitmap filenames and their optional rollover bitmap filenames                @param multibitmaps: a list containing strings/tuples/BitmapInfo's.        Each item in the list represents a button state, the name of which is        the base root name of the "normal" bitmap file.        e.g. /var/share/NoMail.png would become the state "NoMail", and could        thus be passed in to SetState() as SetState("NoMail").        If an item in the list is a tuple of two strings, the first string is        the normal button bitmap filename, while the second is the rollover        button bitmap filename. The rollover bitmap is displayed when the        mouse just moves over the button.        If a BitmapInfo is passed in, it can specify separate bitmaps for each        possible situation (pressed, rollover, selected, etc) as well as the        name of the state itself        """        self.stateBitmaps = {}        self.currentState = None        if kwds.get("multibitmaps"):            multibitmaps = kwds["multibitmaps"]            del kwds["multibitmaps"]        else:            multibitmaps = {}        super(MultiStateButton, self).__init__(parent, ID, None, *args, **kwds)        firstStateName = self.AddStates(multibitmaps)        assert firstStateName is not None        self.SetState(firstStateName)        # calls to Bind must come after call to super's  __init__        self.Bind(wx.EVT_ENTER_WINDOW, self._RolloverStart)        self.Bind(wx.EVT_LEAVE_WINDOW, self._RolloverFinish)    def AddStates(self, multibitmaps):        """        Add more state bitmaps to the button.                @param multibitmaps: a list of strings/tuples/NitmpaInfo's. See        __init__ for a description of this list. More bitmap states can be        added at any time to the button.        """        firstFoundState = None        found = False        for entry in multibitmaps:            stateName = None            paths = {}            bitmaps = {}            # try/except method of type detection suggested by Heikki            # did not work because strings can be accessed via [] just            # as well as tuples            # alecf suggest actually using types            if type(entry) == types.TupleType:                # assume it's a tuple with normal and rollover bitmap names                paths["normal"] = entry[0]                paths["rollover"] = entry[1]            elif type(entry) == types.StringType:                paths["normal"] = entry                paths["rollover"] = None            elif type(entry) == type(BitmapInfo):                stateName = entry.get("stateName")                for variation in ["normal", "rollover", "disabled", "focus", "selected"]:                    bitmaps[variation] = entry.get(variation + "Bitmap")                    paths[variation] = entry.get(variation)            else:                # this will fail, throwing an exception that will help                # to explain what's wrong; needs to be more elegant; how                # do you pass an error string up the chain of exception?                assert type(entry) == type(BitmapInfo)            if stateName is None:                assert paths["normal"] is not None                # The name of the state is the same as the base name of the                # bitmap                stateName = os.path.basename(paths["normal"])                assert len(stateName) > 0            self.stateBitmaps[stateName] = BitmapInfo()            for variation in ["normal", "rollover", "disabled", "focus", "selected"]:                sys.stderr.write("Checking '" + stateName + "'[" + variation + "]")                if bitmaps.get(variation) is not None:                    setattr(self.stateBitmaps[stateName], variation, bitmaps[variation])                    assert getattr(self.stateBitmaps[stateName], variation).GetWidth() > 0#                   self.stateBitmaps[stateName][variation] = bitmaps[variation]                elif paths.get(variation) is not None:                    setattr(self.stateBitmaps[stateName], variation, self._GetBitmapFor(paths[variation]))#                   self.stateBitmaps[stateName][variation] = self._GetBitmapFor(paths[variation])            assert self.stateBitmaps[stateName].normal is not None            if firstFoundState is None:                firstFoundState = stateName        assert firstFoundState is not None        return firstFoundState    def SetState(self, inStateName):        """        Set the current state name of the button. The appropriate bitmap will        be used.                @param multibitmaps: a list of strings or tuples of two strings. See        __init__ for a description of this list. More bitmap states can be        added at any time to the button.        """        if inStateName != self.currentState:            stateBitmaps = self.stateBitmaps.get(inStateName)            assert stateBitmaps is not None, "invalid state name '" + inStateName + "'"            assert getattr(stateBitmaps, "normal", None) is not None, "invalid state '" + inStateName + "' is missing 'normal' bitmap"            import pdb;pdb.set_trace()            self.SetBitmapLabel(stateBitmaps.normal)            #rae the following seems a bit awkward; is there a better way?            if getattr(stateBitmaps, "disabled", None) is not None:                self.SetBitmapDisabled(stateBitmaps.disabled)            if getattr(stateBitmaps, "focus", None) is not None:                self.SetBitmapFocus(stateBitmaps.focus)            if getattr(stateBitmaps, "selected", None) is not None:                self.SetBitmapSelected(stateBitmaps.selected)            self.currentState = inStateName            self.Refresh()            self.Update()    def _RolloverStart(self, event):        """        Change the state of the button to its possible rollover state.        """        stateBitmaps = self.stateBitmaps[self.currentState]        assert stateBitmaps is not None        rolloverBitmap = getattr(stateBitmaps, "rollover", None)        if rolloverBitmap is not None:                self.SetBitmapLabel(rolloverBitmap)                self.Refresh()                # will the app need this call to Update?                self.Update()    def _RolloverFinish(self, event):        """        Return the button to its non-rollover state.        """        stateBitmaps = self.stateBitmaps[self.currentState]        # only do all this if there was actually a rollover        if getattr(stateBitmaps, "rollover", None) is not None:            assert getattr(stateBitmaps, "normal", None) is not None, "invalid state '" + inStateName + "' is missing 'normal' bitmap"            self.SetBitmapLabel(stateBitmaps.normal)            self.Refresh()            # will the app need this call to Update?            self.Update()    def _GetBitmapFor(self, bitmapName):        """        Find the named bitmap, checking various file type extensions (are        these available inside wx.Image somewhere?)                The design decision here is that the speicifc type of the image        is not as important as its name. Indeed, the type of the image can        change over time to accomodate new technologies. By leaving the        type unspecified, code does not have to change whenever the file        format changes.        """        bitmap = None        for ext in ["png", "gif", "jpg", "tiff", "psd"]:            try:                #img = wx.Image("%s.%s" % (bitmapName, ext))                img = wx.GetApp().GetImage("%s.%s" % (bitmapName, ext))                bitmap = img.ConvertToBitmap()                assert bitmap.GetWidth() > 0                break            except IOError:                # file was not found                pass        assert bitmap is not None        return bitmap # execute with execfile("/Users/rae/work/rae-button/MultiStateButton.py", { "__name__" :"__main__" })# or similarif __name__ == "__main__":    #    # Change this to where you keep your bitmaps; remember the    # trailing path separator - it prevents needing os.path code    dir = "/Users/rae/work/rae-button/"	# these lines assume you have bitmaps available; you will need to	# change the absolute paths to bitmaps you have.    theWindow = wx.Frame(parent=None, id=-1, title="MultiButton testing")    b = MultiStateButton(theWindow, style=wx.BORDER_NONE,        multibitmaps = [            (dir + "button1", dir + "button4"),            (dir + "button2", dir + "button3")        ])    b2 = MultiStateButton(theWindow, style=wx.BORDER_NONE,        multibitmaps=[            (dir + "button5", dir + "button4"),            (dir + "button2", dir + "button3"),        ])    b3 = MultiStateButton(theWindow, style=wx.BORDER_NONE,        multibitmaps=[            (dir + "button1", dir + "button4"),            (dir + "button2", dir + "button3"),        ])    box = wx.BoxSizer(wx.HORIZONTAL)    box.Add(b)    box.Add(b2)    box.Add(b3)    sb = MultiStateButton(theWindow, 1010, style=wx.BORDER_NONE,            multibitmaps=[        (dir + "button1-small", dir + "button4-small"),        (dir + "button2-small", dir + "button3-small"),        ])    sb2 = MultiStateButton(theWindow, 1011, style=wx.BORDER_NONE,            multibitmaps=[        (dir + "button5-small", dir + "button4-small"),        (dir + "button2-small", dir + "button3-small"),        ])    sb3 = MultiStateButton(theWindow, 1010, style=wx.BORDER_NONE,            multibitmaps=[        (dir + "button1-small", dir + "button4-small"),        (dir + "button2-small", dir + "button3-small"),        ])    box2 = wx.BoxSizer(wx.HORIZONTAL)    box2.Add(sb)    box2.Add(sb2)    box2.Add(sb3)    box.Add(box2)#    bmi = BitmapInfo()#    bmi.normal = dir + "button1"#    bmi.rollover = dir + "button2"#    bmi.focus =    dir + "button3"#    bmi.disabled = dir + "button4"#    bmi.selected = dir + "button5"#    bmi.stateName = "zimbabwe"#    mmb = MultiStateButton(theWindow, 1010, multibitmaps=(bmi))#    box3 = wx.BoxSizer(wx.HORIZONTAL)#    box3.Add(mmb)#    box.Add(box3)    theWindow.SetSizer(box)    theWindow.Show()