Re: Re: Looking for an "on/off" toggle control
Rolf <[email protected]> Thu, 21 Jan 2021 17:13:31 +0100
| Newsgroups | gmane.comp.python.wxpython |
|---|---|
| Message-ID | <[email protected]> |
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/b094c960-1dc9-18cd-abeb-367a598a48e8%40gmail.com.
onoffbutton.py
(text/x-python, 11.6 KB)
'''
onoffbutton.py
A custom class On/Off clickable.
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
SetLabel(str) Set label for the control
SetToolTip(str) Set tooltip for the control
SetForegroundColour Set text colour
SetBackgroundColour Set background colour
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
Issues:
I traded the ability to scale the image for any sensible ability to
change the label font size, as I rarely use the label of a checkbox,
instead opting to use a statictext to the left of it.
Author: J Healey
Created: 14/01/2021
Copyright: J Healey - 2021
License: GPL 3 or any later version
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):
""" Event sent from the :class:`OnOff` when the control changes. """
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 or colour - 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'
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)
self.Show()
def InitialiseBitmaps(self):
self._bitmaps = {
"On": self.SetImageSize(self._CreateBitmap("On")),
"Off": self.SetImageSize(self._CreateBitmap("Off")),
"Disable": self.SetImageSize(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):
bmp = wx.Bitmap(80, 36)
dc = wx.MemoryDC(bmp)
dc.SetBackgroundMode(wx.TRANSPARENT)
bg = self.GetBackgroundColour()
fg = self.GetForegroundColour()
brush = wx.Brush(bg)
dc.SetBackground(brush)
dc.Clear()
dc.SetBrush(brush)
if self._border:
dc.DrawRoundedRectangle(2,3,70,33,16.0)
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.DrawRoundedRectangle(12,9,48,21,10.0)
dc.SetBrush(wx.Brush(fg))
if type == "On":
dc.DrawCircle(52,19,12)
else:
dc.DrawCircle(22,19,12)
bmp = dc.GetAsBitmap((0, 0, 80, 36))
return bmp
def SetImageSize(self,img):
imgsize = img.GetSize()
imgh = imgsize[1]
imgw = imgsize[0]
w = self._size[0]
h = self._size[1]
if w <= 0:
w = 20
if h <= 0:
h = 12
img = img.ConvertToImage()
img = img.Scale(w,h,quality=wx.IMAGE_QUALITY_HIGH)
return img
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):
return self._Value
def SetLabel(self,label):
self.label.SetLabel(label)
self.Update()
def GetLabel(self,label):
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 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
if state:
self.SetValue(1)
value = 1
#event change
event = OnOffEvent(oobEVT_ON_OFF, self.GetId(), value)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
#event On
event = OnOffEvent(oobEVT_ON, self.GetId(), value)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
else:
self.SetValue(0)
value = 0
#event change
event = OnOffEvent(oobEVT_ON_OFF, self.GetId(), value)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
#event Off
event = OnOffEvent(oobEVT_OFF, self.GetId(), value)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
self.SetImage(value)
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)
self.Layout()
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=(50, 50), size=(80,48), initial=1)
self.onoff1 = OnOffButton(panel, -1, label="60 X 36 No border", pos=(50, 100), size=(60,36), initial=1, border=False)
self.onoff2 = OnOffButton(panel, -1, "40 x 24", pos=(50, 150), size =(40,24), initial=0)
self.onoff3 = OnOffButton(panel, -1, "30 x 18 Mono", pos=(50, 200), size =(30,18), initial=0, mono=True)
self.onoff4 = OnOffButton(panel, -1, " Minimum Mono No border", pos=(50, 250), initial=0, mono=True, border=False)
self.onoff5 = OnOffButton(panel, -1, " Minimum 20 X 16", pos=(50, 300), initial=0)
self.txt = wx.TextCtrl(panel, -1, "On", size=(50,30), pos=(220,50))
self.txt1 = wx.TextCtrl(panel, -1, "Off", size=(50,30), pos=(220,100))
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)
def OnOff(self, event):
if event.GetValue():
self.txt.SetValue("On")
else:
self.txt.SetValue("Off")
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()