Re: Re: Looking for an "on/off" toggle control

Rolf <[email protected]> Mon, 25 Jan 2021 17:22:48 +0100
Newsgroups gmane.comp.python.wxpython
Message-ID <[email protected]>
Hopefully, this is the final version of OnOffbutton, short of bug fixes.

The discovery of dc.SetPen() cleaned up the image.

For Font size changes, I believe I've resolved the Label and Control
sizing for both manual and sizer layouts, including multiline labels
(within reason).

Plus partial sizes e.g. (40, -1) should be catered for.

Submitted in the hope that someone finds it useful.

Rolf

demo image
On 22/01/2021 18:44, john fabiani wrote:
> As any true programmer would do - it works don't mess with it! LOL!
>
> Johnf
>
> On 1/21/21 10:36 PM, Rolf wrote:
>> Yes!
>>
>> They are hangovers from trying to size the image without resorting to
>> image scaling.
>>
>> It seems that imgsize is superfluous as well as "imgh" and "imgw".
>>
>> As you can see, if someone knows their way around DC's (which I don't)
>> there's plenty of room for improvement.
>>
>> In the end, I went for "that will do".
>>
>> Rolf
>>
>> On 22/01/2021 00:21, john fabiani wrote:
>>> There are two vars that appear not to be used - "imgh" and "imgw"  can
>>> they be removed?
>>>
>>> johnf
>>>
>>>   def SetImageSize(self,img):
>>>          imgsize = img.GetSize()
>>>          imgh = imgsize[1]
>>>          imgw = imgsize[0]
>>>          w = self._size[0]
>>>          h = self._size[1]
>>>
>>> On 1/21/21 11:41 AM, Rolf wrote:
>>>> I found a use case ( str(xxx.GetValue()) where it fails logically
>>>> where
>>>> I expected "True" or "False", that may just be my code.
>>>>
>>>> So change the GetValue function in onoffbutton.py to:
>>>>
>>>>       def GetValue(self):
>>>>           if self._Value:
>>>>               return True
>>>>           else:
>>>>               return False
>>>>
>>>>
>>>> As always, Beware there be dragons!
>>>>
>>>> Regards,
>>>>
>>>> Rolf
>>>>
>>>>
>>>> On 21/01/2021 20:18, john fabiani wrote:
>>>>> Thank you so much.  I have downloaded it and will use it in the
>>>>> future
>>>>> for sure!
>>>>>
>>>>> Johnf
>>>>>
>>>>> On 1/21/21 8:13 AM, Rolf wrote:
>>>>>> Whilst I agreed with Johnf, I decided that I liked the idea of an
>>>>>> on/off
>>>>>> button, so while this is not of industrial strength, it serves the
>>>>>> purpose for my requirements.
>>>>>>
>>>>>> You may want to alter the code to fit yours.
>>>>>>
>>>>>> I enclose the code, the demo image and an image of the control in
>>>>>> one of
>>>>>> my projects.
>>>>>>
>>>>>> Use at your own discretion.
>>>>>>
>>>>>> Usage:
>>>>>>
>>>>>> 'python onoffbutton.py'
>>>>>>
>>>>>> and
>>>>>>
>>>>>> 'import onoffbutton as oob' then 'self.fc_logging =
>>>>>> oob.OnOffButton(self.tab7, wx.ID_ANY)' as a drop-in replacement for
>>>>>> CheckBox
>>>>>>
>>>>>> Regards
>>>>>>
>>>>>> Rolf
>>>>>>
>>>>>> demo project
>>>>>> On 14/01/2021 07:39, Matthias Brennwald wrote:
>>>>>>> Wouldn't bitmapped buttons ask for trouble with todays variety of
>>>>>>> screen resolutions?
>>>>>>>
>>>>>>> On Do, Jan 14, 2021 at 04:43, Steve Barnes <[email protected]>
>>>>>>> wrote:
>>>>>>>> Of course the agw SBitmapToggleButton could be used to provide a
>>>>>>>> non-native switch effect. It would still be a button rather than a
>>>>>>>> switch but with the appropriate bitmaps could give the
>>>>>>>> appearance of
>>>>>>>> just about any switch.
>>>>>>>>
>>>>>>>>    *From:*[email protected]
>>>>>>>> <[email protected]> *On Behalf Of *john fabiani
>>>>>>>> *Sent:* 13 January 2021 22:18
>>>>>>>> *To:* [email protected]
>>>>>>>> *Subject:* Re: [wxPython-users] Re: Looking for an "on/off" toggle
>>>>>>>> control
>>>>>>>>
>>>>>>>>    I agree with your links.  That said - all we had was a
>>>>>>>> checkbox or
>>>>>>>> the toggle switch and some how we made due.
>>>>>>>>
>>>>>>>> Johnf
>>>>>>>>
>>>>>>>> On 1/13/21 12:45 PM, Matt Newville wrote:
>>>>>>>>
>>>>>>>>                    On Wed, Jan 13, 2021 at 2:01 PM john fabiani
>>>>>>>>        <[email protected] <mailto:[email protected]>>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>            I normally don't use anything from windows 10 uwp - but
>>>>>>>> I am
>>>>>>>>            fairly sure
>>>>>>>>            the Toggle switch is available.
>>>>>>>>
>>>>>>>>            Also "re-inventing"  - if you are not willing to
>>>>>>>> write a
>>>>>>>>            custom widget
>>>>>>>>            then you will either have to wait until some future
>>>>>>>> version
>>>>>>>>            that has the
>>>>>>>>            control built-in or make do with the checkbox.  You
>>>>>>>> must be
>>>>>>>>            aware that
>>>>>>>>            for many many years the checkbox was all that was
>>>>>>>> available
>>>>>>>>            on all
>>>>>>>>            platforms.  It seem to work just fine.  The switch is a
>>>>>>>> new UI
>>>>>>>>            development as are many others.  I happen to like it
>>>>>>>> - but
>>>>>>>>            many others I
>>>>>>>>            don't believe add anything other than change for
>>>>>>>> changes
>>>>>>>>            sake.  That
>>>>>>>>            said, I believe the current toggle switch conveys the
>>>>>>>> action
>>>>>>>>            of being
>>>>>>>>            "on or off" very well.
>>>>>>>>
>>>>>>>>              References:
>>>>>>>>
>>>>>>>>           
>>>>>>>>    https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/toggles
>>>>>>>>     
>>>>>>>> <https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fwindows%2Fuwp%2Fdesign%2Fcontrols-and-patterns%2Ftoggles&data=04%7C01%7C%7Ca2a95f59c0cb4a03ee8608d8b81119cb%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637461730867743993%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=tpJyuSTDHH9cXv61MujvETweguvJba740XGFmw%2BhWpE%3D&reserved=0>
>>>>>>>>
>>>>>>>>           
>>>>>>>>    https://developer.apple.com/design/human-interface-guidelines/macos/buttons/switches/
>>>>>>>>     
>>>>>>>> <https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdeveloper.apple.com%2Fdesign%2Fhuman-interface-guidelines%2Fmacos%2Fbuttons%2Fswitches%2F&data=04%7C01%7C%7Ca2a95f59c0cb4a03ee8608d8b81119cb%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637461730867773977%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=tu8c3inyJ9zsPEfg%2BQdZII0Wjjj7Fp1YYTBNEeAr2f0%3D&reserved=0>
>>>>>>>>
>>>>>>>>              Both companies suggest that toggle switches have a
>>>>>>>> different
>>>>>>>>        purpose from a checkbox, implying both immediate action (as
>>>>>>>>        opposed to the selection of a preference) and
>>>>>>>> propagation to
>>>>>>>>        other GUI elements, turning on/off a whole category of
>>>>>>>> options
>>>>>>>>        and displays for example.
>>>>>>>>
>>>>>>>>              --Matt
>>>>>>>>
>>>>>>>>              --
>>>>>>>>        You received this message because you are subscribed to the
>>>>>>>>        Google Groups "wxPython-users" group.
>>>>>>>>        To unsubscribe from this group and stop receiving emails
>>>>>>>> from it,
>>>>>>>>        send an email to
>>>>>>>> [email protected]
>>>>>>>>        <mailto:[email protected]>.
>>>>>>>>        To view this discussion on the web visit
>>>>>>>>     
>>>>>>>> https://groups.google.com/d/msgid/wxpython-users/CA%2B7ESbqwVdq%3DyXbNypgGNHCWnuM4Qe_0T1i7Tuk3yNjnQspzbg%40mail.gmail.com
>>>>>>>>     
>>>>>>>> <https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Fwxpython-users%2FCA%252B7ESbqwVdq%253DyXbNypgGNHCWnuM4Qe_0T1i7Tuk3yNjnQspzbg%2540mail.gmail.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7C%7Ca2a95f59c0cb4a03ee8608d8b81119cb%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637461730867783972%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=QkXnY58ZGooOPnVaWxbKZgLbpJLAOsxBVEMxEyaT6J0%3D&reserved=0>.
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>> Groups "wxPython-users" group.
>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>> send an email to [email protected]
>>>>>>>> <mailto:[email protected]>.
>>>>>>>> To view this discussion on the web visit
>>>>>>>> https://groups.google.com/d/msgid/wxpython-users/48ec4c47-d518-9975-3391-5f6e5f2d29cc%40gmail.com
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> <https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Fwxpython-users%2F48ec4c47-d518-9975-3391-5f6e5f2d29cc%2540gmail.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7C%7Ca2a95f59c0cb4a03ee8608d8b81119cb%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637461730867793966%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=vCIvhd%2B24K2P4h4R3Dd%2Fp9NhPp7Mf1NXrFkzEZISRoo%3D&reserved=0>.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>> Groups "wxPython-users" group.
>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>> send an email to [email protected]
>>>>>>>> <mailto:[email protected]>.
>>>>>>>> To view this discussion on the web visit
>>>>>>>> https://groups.google.com/d/msgid/wxpython-users/VI1PR03MB44790BABF461FD2FEF12BE8D9BA80%40VI1PR03MB4479.eurprd03.prod.outlook.com
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> <https://groups.google.com/d/msgid/wxpython-users/VI1PR03MB44790BABF461FD2FEF12BE8D9BA80%40VI1PR03MB4479.eurprd03.prod.outlook.com?utm_medium=email&utm_source=footer>.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>> -- 
>>>>>>> You received this message because you are subscribed to the Google
>>>>>>> Groups "wxPython-users" group.
>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>> send
>>>>>>> an email to [email protected]
>>>>>>> <mailto:[email protected]>.
>>>>>>> To view this discussion on the web visit
>>>>>>> https://groups.google.com/d/msgid/wxpython-users/0UVWMQ.7I5FXIYNQBZJ1%40gmail.com
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> <https://groups.google.com/d/msgid/wxpython-users/0UVWMQ.7I5FXIYNQBZJ1%40gmail.com?utm_medium=email&utm_source=footer>.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>> -- 
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "wxPython-users" group.
>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>> send
>>>>> an email to [email protected].
>>>>> To view this discussion on the web visit
>>>>> https://groups.google.com/d/msgid/wxpython-users/ac2e7eb8-12a5-3acb-e973-6f21d7660a0d%40gmail.com.
>>>>>
>>>>>
>>> -- 
>>> You received this message because you are subscribed to the Google
>>> Groups "wxPython-users" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/wxpython-users/1d653b69-06f1-221f-7158-e8696c2eafa4%40gmail.com.
>>>
>
> -- 
> You received this message because you are subscribed to the Google
> Groups "wxPython-users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/wxpython-users/fceb3b36-7243-10f8-6f43-8fa16f80c64e%40gmail.com.

-- 
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/f2d0e78b-a80d-552a-bd0f-f2f5e48f24dc%40gmail.com.
onoffbutton.py (text/x-python, 14.4 KB)
'''
    onoffbutton.py

    A custom class On/Off clickable - meant as a drop-in replacement for CheckBox.

    Events: EVT_ON_OFF          Generic event on binary change

            EVT_ON              The control changed to On

            EVT_OFF             The control changed to Off

    Event Notes:
        All events return GetValue() i.e.

            def OnOff(self,event):
                current_value = event.GetValue()

    Functions:
        GetValue()              Returns numeric value in the control On (True) = 1 Off (False) = 0
        SetValue(int)           Set numeric value in the control
        GetLabel()              Get current label for the control
        SetLabel(str)           Set label for the control
        SetToolTip(str)         Set tooltip for the control
        SetForegroundColour     Set text colour
        SetBackgroundColour     Set background colour
        SetFont                 Set label font
        GetFont                 Get label font
        Enable(Bool)            Enable/Disable the control
                                On Disable the control value is frozen
        IsEnabled()             Is the control Enabled - Returns True/False

    Default Values:
        initial - 0 (False) Off
        label   - blank
        style   - wx.BORDER_NONE
        mono    - False
        border  - True
        size    - 24x16 (Almost as small as a CheckBox)
                
Author:     J Healey
Created:    14/01/2021
Copyright:  J Healey - 2021
License:    GPL 3 or any later version
Version:    1.7.4
Email:      [email protected]
'''

import wx

#Events OnOff, On and Off
oobEVT_ON_OFF = wx.NewEventType()
EVT_ON_OFF = wx.PyEventBinder(oobEVT_ON_OFF, 1)
oobEVT_ON = wx.NewEventType()
EVT_ON = wx.PyEventBinder(oobEVT_ON, 1)
oobEVT_OFF = wx.NewEventType()
EVT_OFF = wx.PyEventBinder(oobEVT_OFF, 1)

class OnOffEvent(wx.PyCommandEvent):
    """ Events sent from the :class:`OnOffButton` when the control changes.
        EVT_ON_OFF  The Control value has changed
        EVT_ON      The Control turned On    
        EVT_OFF     The Control turned Off    
    """

    def __init__(self, eventType, eventId=1, value=0):
        """
        Default class constructor.

        :param `eventType`: the event type;
        :param `eventId`: the event identifier.
        """

        wx.PyCommandEvent.__init__(self, eventType, eventId)
        self._eventType = eventType
        self.value = value

    def GetValue(self):
        """
        Retrieve the value of the control at the time
        this event was generated."""
        return self.value

class OnOffButton(wx.Control):

    def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, initial=0, style=wx.BORDER_NONE, mono=False, border=True, name="OnOff_Button"):
        """
        Default class constructor.

        @param parent:  Parent window. Must not be None.
        @param id:      identifier. A value of -1 indicates a default value.
        @param label:   control label.
        @param pos:     Position. If the position (-1, -1) is specified
                        then a default position is chosen.
        @param size:    If the default size (-1, -1) is specified then the minimum size is chosen.
        @param initial: Initial value 0 False or 1 True - default False.
        @param style:   control border style.
        @param mono:    True or False makes the image monochrome - default False
        @param border:  True or False adds a border to the image - default True
        @param name:    Widget name.
        """

        wx.Control.__init__(self, parent, id, pos=pos, size=size, style=style, name=name)
        self._initial = initial
        self._frozen_value = initial
        self._label = label
        self._pos = pos
        self._size = size
        self._name = name
        self._id = id
        self._mono = mono
        self._border = border
        self._style = style
        self.OnClr = 'green' 
        self.OffClr = 'red' 
        self._font = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
        if self._mono:
            self.OnClr = self.OffClr = 'white' 
        if self._initial > 1:
            self._initial = 1
        if self._initial < 0:
            self._initial = 0

        self._Value = initial

        # Initialize images
        self.InitialiseBitmaps()
        self.onoff = wx.StaticBitmap(self, -1, bitmap=wx.Bitmap(self._img))
        self.label = wx.StaticText(self, -1, self._label)

        # Bind the event
        self.onoff.Bind(wx.EVT_LEFT_DOWN, self.OnOff)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.onoff, 0, 0, 0)
        sizer.Add(self.label, 0, wx.ALIGN_CENTRE, 0)
        self.SetImage(self._initial)
        self.SetSizerAndFit(sizer)
        
    def InitialiseBitmaps(self):
        self._bitmaps = {
            "On": self._CreateBitmap("On"),
            "Off": self._CreateBitmap("Off"),
            "Disable": self._CreateBitmap("Disable"),
            }
        if self._initial <= 0:
            self._img = self._bitmaps['Off']
        elif self._initial >= 1:
            self._img = self._bitmaps['On']

    def _CreateBitmap(self, type):
        if type == "On":
            self.SetImageSize()
        bmp = wx.Bitmap(self.bmpw, self.bmph)
        dc = wx.MemoryDC(bmp)
        bg = self.GetBackgroundColour()
        fg = self.GetForegroundColour()
        brush = wx.Brush(bg)  
        dc.SetBackground(brush)
        dc.SetPen(wx.Pen(fg)) 
        dc.Clear() 
        
        dc.SetBrush(brush) 
        if self._border:
            dc.DrawRoundedRectangle(
                self.rrouterposx,
                self.rrouterposy,
                self.rrouterw,
                self.rrouterh,
                self.rrouterradius
                )
        if type == "On":
            dc.SetBrush(wx.Brush(self.OnClr)) 
        elif type == "Off":
            dc.SetBrush(wx.Brush(self.OffClr)) 
        else:
            dc.SetBrush(wx.Brush('grey')) 
        dc.SetPen(wx.Pen(bg)) 
        dc.DrawRoundedRectangle(
            self.rrinnerposx,
            self.rrinnerposy,
            self.rrinnerw,
            self.rrinnerh,
            self.rrinnerradius
            )
        dc.SetBrush(wx.Brush(fg))
        if type == "On":
            dc.DrawCircle(self.circonpos[0],self.circonpos[1],self.circradius)
        else:
            dc.DrawCircle(self.circoffpos[0],self.circoffpos[1],self.circradius)

        bmp = dc.GetAsBitmap((0, 0, self.bmpw, self.bmph))
        return bmp

    def SetImageSize(self):
        w, h = self._size
        #Cater for only Width or only Height parameter given
        if h < 0 and w >= 0:
            h = int(w * 0.6667)
        if w < 0 and h >= 0:
            w = int(h / 0.6667)    
        #Default minimum size
        w = max(w,24)
        h = max(h,16)
        #Size bitmap
        self.bmpw = w
        self.bmph = h
        #Outer rounded rectangle
        self.rrouterw = w - 2
        self.rrouterh = h - 2
        self.rrouterposx = 2
        self.rrouterposy = 2
        self.rrouterradius = (self.rrouterh/2)
        #Inner rounded rectangle 
        self.rrinnerw = self.rrouterw - 4
        self.rrinnerh = self.rrouterh - 4
        self.rrinnerposx = self.rrouterposx + 2 
        self.rrinnerposy = self.rrouterposy + 2 
        self.rrinnerradius = self.rrinnerh/2
        #Circle position and size
        self.circradius = self.rrinnerradius - 1
        self.circoffpos = (int(self.rrinnerradius+self.rrinnerposx),self.rrinnerposy + self.rrinnerradius)
        self.circonpos = (int(self.rrinnerw - (self.rrinnerradius-3) ),self.rrinnerposy + self.rrinnerradius)

    def SetValue(self,value):
        if value > 1:
            value = 1
        if value < 0:
            value = 0
        self._Value = value
        self.SetImage(value)
        self.Update()

    def GetValue(self):
        if self._Value:
            return True
        else:
            return False

    def SetLabel(self,label):
        self.label.SetLabel(label)
        w,h = self._font.GetPixelSize()
        #Allow for multi-line labels
        nls = len(label.split('\n')) #Count newlines in label text
        lw = max([len(line) for line in label.split('\n')]) #Get the longest line in the label text
        w = w*lw #Width = char pixel width x the longest line width
        h = max(nls*h, h) #Height maximum of char pixel height x no of lines or char pixel height
        w += 2
        h += 2
        self.label.SetMinSize(wx.Size(w,h))        
        self.label.SetSize(wx.Size(w,h))        
        iw,ih = self._img.GetSize()
        #Control size image width+text width + Greater of image or text height
        self.SetMinSize(wx.Size(w+iw,max(ih,h)))
        self.Fit()
        self.Update()
        
    def GetLabel(self):
        return self.label.GetLabel()

    def IsEnabled(self):
        return wx.Control.IsEnabled(self)

    def Enable(self, value):
        if value and self.IsEnabled(): # If value = current state do nothing
            return
        if not value and not self.IsEnabled():
            return
        wx.Control.Enable(self, value)
        self.Update()
        if value:
            #Enable via callafter in case someone has been stabbing away on the disabled control
            wx.CallAfter(self.OnReset)
        else:
            #Disable - Freeze the controls Value and change bitmap
            self._frozen_value = self.GetValue()
            self._img = self._bitmaps['Disable']
            self.onoff.SetBitmap(wx.Bitmap(self._img))
            self.onoff.SetToolTip(self._name+" - [Disabled]")

    def SetToolTip(self, tip):
        wx.Control.SetToolTip(self,tip)
        self.Refresh()

    def SetForegroundColour(self, colour):
        wx.Control.SetForegroundColour(self, colour)
        self.InitialiseBitmaps()
        self.SetImage(self._Value)        
        self.Refresh()

    def SetBackgroundColour(self, colour):
        wx.Control.SetBackgroundColour(self, colour)
        self.InitialiseBitmaps()
        self.SetImage(self._Value)        
        self.Refresh()

    def SetFont(self,font):
        wx.Control.SetFont(self, font)
        self._font = font
        #Font changed reset label
        self.SetLabel(self._label)

    def GetFont(self):
        return self._font
        
    def OnReset(self):
        #Reset the control to the state it was in when it was Disabled
        self.SetValue(self._frozen_value)
        self.SetImage(self._frozen_value)

    def OnOff(self, event):
        state = self._Value
        if state == 0:
            state = 1
        else:
            state = 0

        self.SetImage(state)

        if state:
            self.SetValue(state)
            #event change
            event = OnOffEvent(oobEVT_ON_OFF, self.GetId(), state)
            event.SetEventObject(self)
            self.GetEventHandler().ProcessEvent(event)
            #event On
            event = OnOffEvent(oobEVT_ON, self.GetId(), state)
            event.SetEventObject(self)
            self.GetEventHandler().ProcessEvent(event)
        else:
            self.SetValue(state)
            #event change
            event = OnOffEvent(oobEVT_ON_OFF, self.GetId(), state)
            event.SetEventObject(self)
            self.GetEventHandler().ProcessEvent(event)
            #event Off
            event = OnOffEvent(oobEVT_OFF, self.GetId(), state)
            event.SetEventObject(self)
            self.GetEventHandler().ProcessEvent(event)

    def SetImage(self, value):
        #Set appropriate image and tooltip
        tp = self.GetToolTip()
        if not self.IsEnabled():
            self._img = self._bitmaps['Disable']
            if tp != None:
                tt = tp.GetTip()+" - [Currently Disabled]"
            else:
                tt = "[Disabled]"
        else:
            if value <= 0:
                self._img = self._bitmaps['Off']
                if tp != None:
                    tt = tp.GetTip()+" - [Currently Off]"
                else:
                    tt = "[ Off ]"
            else:
                self._img = self._bitmaps['On']
                if tp != None:
                    tt = tp.GetTip()+" - [Currently On]"
                else:
                    tt = "[ On ]"

        self.onoff.SetBitmap(wx.Bitmap(self._img))
        self.onoff.SetToolTip(tt)

if __name__ == '__main__':

    class MyFrame(wx.Frame):

        def __init__(self, parent):

            wx.Frame.__init__(self, parent, -1, "On/Off Demo")

            panel = wx.Panel(self)
            panel.SetBackgroundColour("lightgreen")
            
            self.onoff = OnOffButton(panel, -1, label=" 80 X 48", pos=(10, 50), size=(160,96), initial=1)
            self.onoff1 = OnOffButton(panel, -1, " 60 X 36 line 1\n line2\n line3", pos=(10, 200), size=(60,36), initial=0, border=False)
            self.onoff2 = OnOffButton(panel, -1, " 40 x 24", pos=(10, 250), size =(40,24), initial=1)
            self.onoff3 = OnOffButton(panel, -1, " 30 x 20 Mono", pos=(10, 300), size =(30,20), initial=0, mono=True)
            self.onoff4 = OnOffButton(panel, -1, " Minimum Mono No border", pos=(10, 350), initial=0, mono=True, border=False)
            self.onoff5 = OnOffButton(panel, -1, " Minimum 20 X 16", pos=(10, 400), initial=0)
            self.txt = wx.TextCtrl(panel, -1, "On", size=(50,30), pos=(300,50))
            self.txt1 = wx.TextCtrl(panel, -1, "Off", size=(50,30), pos=(300,200))
            self.onoff.Bind(EVT_ON_OFF, self.OnOff)
            self.Bind(EVT_ON, self.SwOn)
            self.Bind(EVT_OFF, self.SwOff)
            self.onoff1.Bind(EVT_ON_OFF, self.OnOff1)
            font = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
            font.SetPointSize(16)
            font.SetFaceName('FreeMono')
            self.onoff.SetFont(font) 
            self.Show()
            
        def OnOff(self, event):
            if event.GetValue():
                self.txt.SetValue("On")
                self.onoff.SetLabel(" Label On")
            else:
                self.txt.SetValue("Off")
                self.onoff.SetLabel(" Label OffffffX")
            event.Skip()

        def OnOff1(self, event):
            if event.GetValue():
                self.txt1.SetValue("On")
            else:
                self.txt1.SetValue("Off")
            event.Skip()

        def SwOn(self, event):
            obj = event.GetEventObject()
            print(obj.Name + " On")

        def SwOff(self, event):
            obj = event.GetEventObject()
            print(obj.Name + " Off")

    app = wx.App()
    frame = MyFrame(None)
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()