#---------------------------------------------------------------------- # Name: wx.lib.checkbox # Purpose: Various kinds of generic checkbox stuff, (not native controls # but self-drawn.) # # Author: wxPython Team and wxPyWiki Contributors # # Created: 22-June-2020 # Copyright: (c) 2020 by Total Control Software # Licence: wxWindows license # Tags: phoenix-port, py3-port, documented #---------------------------------------------------------------------- """ This module implements various forms of generic checkboxes, meaning that they are not built on native controls but are self-drawn. Description =========== This module implements various forms of generic checkboxes, meaning that they are not built on native controls but are self-drawn. They should act like normal checkboxes but you are able to better control how they look, etc... Usage ===== Sample usage:: app = wx.App(redirect=False) class MyFrame(wx.Frame, DefineNativeCheckBoxBitmapsMixin): def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name='frame'): wx.Frame.__init__(self, parent, id, title, pos, size, style, name) ## self.DefineNativeCheckBoxBitmaps() ## self.checkbox_bitmaps = self.GetNativeCheckBoxBitmaps() cb1 = GenCheckBox(self, label="PurePython Checkbox1", pos=(10, 10)) cb2 = GenCheckBox(self, label="PurePython Checkbox2", pos=(10, 50)) cb1.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) cb2.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) cb2.SetForegroundColour(wx.GREEN) cb2.SetBackgroundColour(wx.BLACK) sizer = wx.BoxSizer() sizer.Add(cb1, 0, wx.ALL, 5) sizer.Add(cb2, 0, wx.ALL, 5) self.SetSizer(sizer) def OnCheckBox(self, event): evtObj = event.GetEventObject() print(evtObj.GetLabel(), evtObj.IsChecked()) frame = MyFrame(None, wx.ID_ANY, "Test Pure-Py Checkbox") frame.Show() app.MainLoop() """ # Imports.--------------------------------------------------------------------- # -wxPython Imports. import wx class GenCheckBox(wx.Control): """ A generic class that replicates some of the functionalities of :class:`wx.Checkbox`, while being completely owner-drawn with a nice check bitmaps. """ def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.NO_BORDER, validator=wx.DefaultValidator, name="GenCheckBox"): """ Default class constructor. :param `parent`: Pointer to a parent window. Must not be ``None``. :type `parent`: `wx.Window` :param `id`: Window identifier. ``wx.ID_ANY`` indicates a default value. :type `id`: int :param `label`: Text to be displayed next to the checkbox. :type `label`: str :param `pos`: Window position. The value ``wx.DefaultPosition`` indicates a default position, chosen by either the windowing system or wxWidgets, depending on platform. :type `pos`: `wx.Point` :param `size`: Window size. The value ``wx.DefaultSize`` indicates a default size, chosen by either the windowing system or wxWidgets, depending on platform. :type `size`: `wx.Size` :param `style`: Window style. Not used in this widget, GenCheckBox has only 2 state. :type `style`: long :param `validator`: Window validator. :type `validator`: `wx.Validator` :param `name`: Window name. :type `name`: str """ wx.Control.__init__(self, parent, id, pos, size, style, validator, name) self.SYS_DEFAULT_GUI_FONT = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) # Initialize our cool bitmaps. self.InitializeBitmaps() # Initialize the focus pen colour/dashes, for faster drawing later. self.InitializeColours() # By default, we start unchecked. self._checked = False # Set the spacing between the check bitmap and the label to 3 by default. # This can be changed using SetSpacing later. self._spacing = 3 self._hasFocus = False # Ok, set the wx.PyControl label, its initial size (formerly known an # SetBestFittingSize), and inherit the attributes from the standard # wx.CheckBox . self.SetLabel(label) self.SetInitialSize(size) self.InheritAttributes() # Bind the events related to our control: first of all, we use a # combination of wx.BufferedPaintDC and an empty handler for # wx.EVT_ERASE_BACKGROUND (see later) to reduce flicker. self.Bind(wx.EVT_PAINT, self.OnPaint) # Since the paint event draws the whole widget, we will use # SetBackgroundStyle(wx.BG_STYLE_PAINT) and then # implementing an erase-background handler is not necessary. self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Add a size handler to refresh so the paint won't smear when resizing. self.Bind(wx.EVT_SIZE, self.OnSize) # Then we want to monitor user clicks, so that we can switch our # state between checked and unchecked. self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseClick) if wx.Platform == '__WXMSW__': # MSW Sometimes does strange things... self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseClick) # We want also to react to keyboard keys, namely the space bar that can # toggle our checked state. Whether key-up or key-down is used is based # on platform. if 'wxMSW' in wx.PlatformInfo: self.Bind(wx.EVT_KEY_UP, self.OnKeyEvent) else: self.Bind(wx.EVT_KEY_DOWN, self.OnKeyEvent) # Then, we react to focus event, because we want to draw a small # dotted rectangle around the text if we have focus. # This might be improved!!! self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) def InitializeBitmaps(self): """ Initializes the check bitmaps. """ # We keep 4 bitmaps for GenCheckBox, depending on the # checking state (Checked/UnChecked) and the control # state (Enabled/Disabled). self._bitmaps = { "CheckedEnable": _GetCheckedBitmap(self), "UnCheckedEnable": _GetNotCheckedBitmap(self), "CheckedDisable": _GetCheckedImage(self).ConvertToDisabled().ConvertToBitmap(), "UnCheckedDisable": _GetNotCheckedImage(self).ConvertToDisabled().ConvertToBitmap()} def InitializeColours(self): """ Initializes the focus indicator pen. """ textClr = self.GetForegroundColour() self._focusIndPen = wx.Pen(textClr, 1, wx.USER_DASH) self._focusIndPen.SetDashes([1, 1]) self._focusIndPen.SetCap(wx.CAP_BUTT) self.SYS_COLOUR_GRAYTEXT = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT) def GetBitmap(self): """ Returns the appropriated bitmap depending on the checking state (Checked/UnChecked) and the control state (Enabled/Disabled). """ if self.IsEnabled(): # So we are Enabled. if self.IsChecked(): # We are Checked. return self._bitmaps["CheckedEnable"] else: # We are UnChecked. return self._bitmaps["UnCheckedEnable"] else: # Poor GenCheckBox, Disabled and ignored! if self.IsChecked(): return self._bitmaps["CheckedDisable"] else: return self._bitmaps["UnCheckedDisable"] def SetLabel(self, label): """ Sets the :class:`GenCheckBox` text label and updates the control's size to exactly fit the label plus the bitmap. :param `label`: Text to be displayed next to the checkbox. :type `label`: str """ wx.Control.SetLabel(self, label) # The text label has changed, so we must recalculate our best size # and refresh ourselves. self.InvalidateBestSize() self.Refresh() def SetFont(self, font): """ Sets the :class:`GenCheckBox` text font and updates the control's size to exactly fit the label plus the bitmap. :param `font`: Font to be used to render the checkboxs label. :type `font`: `wx.Font` """ wx.Control.SetFont(self, font) # The font for text label has changed, so we must recalculate our best # size and refresh ourselves. self.InvalidateBestSize() self.Refresh() def DoGetBestSize(self): """ Overridden base class virtual. Determines the best size of the control based on the label size, the bitmap size and the current font. """ # Retrieve our properties: the text label, the font and the check # bitmap. label = self.GetLabel() font = self.GetFont() bitmap = self.GetBitmap() if not font: # No font defined? So use the default GUI font provided by the system. font = self.SYS_DEFAULT_GUI_FONT # Set up a wx.ClientDC. When you don't have a dc available (almost # always you don't have it if you are not inside a wx.EVT_PAINT event), # use a wx.ClientDC (or a wx.MemoryDC) to measure text extents. dc = wx.ClientDC(self) dc.SetFont(font) # Measure our label. textWidth, textHeight = dc.GetTextExtent(label) # Retrieve the check bitmap dimensions. bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight() # Get the spacing between the check bitmap and the text. spacing = self.GetSpacing() # Ok, we're almost done: the total width of the control is simply # the sum of the bitmap width, the spacing and the text width, # while the height is the maximum value between the text width and # the bitmap width. totalWidth = bitmapWidth + spacing + textWidth totalHeight = max(textHeight, bitmapHeight) best = wx.Size(totalWidth, totalHeight) # Cache the best size so it doesn't need to be calculated again, # at least until some properties of the window change. self.CacheBestSize(best) return best def AcceptsFocusFromKeyboard(self): """ Overridden base class virtual. """ # We can accept focus from keyboard, obviously. return True def AcceptsFocus(self): """ Overridden base class virtual. """ # If it seems that wx.CheckBox does not accept focus with mouse, It does. # You just can't see the focus rectangle until there's # another keypress or navigation event (at least on some platforms.) return True # This will draw focus rectangle always on mouse click. def HasFocus(self): """ Returns whether or not we have the focus. """ # We just returns the _hasFocus property that has been set in the # wx.EVT_SET_FOCUS and wx.EVT_KILL_FOCUS event handlers. return self._hasFocus def SetForegroundColour(self, colour): """ Overridden base class virtual. :param `colour`: Set the foreground colour of the checkboxs label. :type `colour`: `wx.Colour` """ wx.Control.SetForegroundColour(self, colour) # We have to re-initialize the focus indicator per colour as it should # always be the same as the foreground colour. self.InitializeColours() self.Refresh() def SetBackgroundColour(self, colour): """ Overridden base class virtual. :param `colour`: Set the background colour of the checkbox. :type `colour`: `wx.Colour` """ wx.Control.SetBackgroundColour(self, colour) # We have to refresh ourselves. self.Refresh() def Enable(self, enable=True): """ Enables/Disables :class:`GenCheckBox`. :param `enable`: Set the enabled state of the checkbox. :type `enable`: bool """ wx.Control.Enable(self, enable) # We have to refresh ourselves, as our state changed. self.Refresh() def GetDefaultAttributes(self): """ Overridden base class virtual. By default we should use the same font/colour attributes as the native wx.CheckBox. """ return wx.CheckBox.GetClassDefaultAttributes() def ShouldInheritColours(self): """ Overridden base class virtual. If the parent has non-default colours then we want this control to inherit them. """ return True def SetSpacing(self, spacing): """ Sets a new spacing between the check bitmap and the text. :param `spacing`: Set the amount of space between the checkboxs bitmap and text. :type `spacing`: int """ self._spacing = spacing # The spacing between the check bitmap and the text has changed, # so we must recalculate our best size and refresh ourselves. self.InvalidateBestSize() self.Refresh() def GetSpacing(self): """ Returns the spacing between the check bitmap and the text. """ return self._spacing def GetValue(self): """ Returns the state of :class:`GenCheckBox`, True if checked, False otherwise. """ return self._checked def IsChecked(self): """ This is just a maybe more readable synonym for GetValue: just as the latter, it returns True if the :class:`GenCheckBox` is checked and False otherwise. """ return self._checked def SetValue(self, state): """ Sets the :class:`GenCheckBox` to the given state. This does not cause a ``wx.wxEVT_COMMAND_CHECKBOX_CLICKED`` event to get emitted. :param `state`: Set the value of the checkbox. True or False. :type `state`: bool """ self._checked = state # Refresh ourselves: the bitmap has changed. self.Refresh() def OnKeyEvent(self, event): """ Handles the ``wx.EVT_KEY_UP`` or ``wx.EVT_KEY_UP`` event (depending on platform) for :class:`GenCheckBox`. :param `event`: A `wx.KeyEvent` to be processed. :type `event`: `wx.KeyEvent` """ if event.GetKeyCode() == wx.WXK_SPACE: # The spacebar has been pressed: toggle our state. self.SendCheckBoxEvent() else: event.Skip() def OnSetFocus(self, event): """ Handles the ``wx.EVT_SET_FOCUS`` event for :class:`GenCheckBox`. :param `event`: A `wx.FocusEvent` to be processed. :type `event`: `wx.FocusEvent` """ self._hasFocus = True # We got focus, and we want a dotted rectangle to be painted # around the checkbox label, so we refresh ourselves. self.Refresh() def OnKillFocus(self, event): """ Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`GenCheckBox`. :param `event`: A `wx.FocusEvent` to be processed. :type `event`: `wx.FocusEvent` """ self._hasFocus = False # We lost focus, and we want a dotted rectangle to be cleared # around the checkbox label, so we refresh ourselves. self.Refresh() def OnSize(self, event): """ Handles the ``wx.EVT_SIZE`` event for :class:`GenCheckBox`. :param `event`: A `wx.SizeEvent` to be processed. :type `event`: `wx.SizeEvent` """ self.Refresh() def OnPaint(self, event): """ Handles the ``wx.EVT_PAINT`` event for :class:`GenCheckBox`. :param `event`: A `wx.PaintEvent` to be processed. :type `event`: `wx.PaintEvent` """ # If you want to reduce flicker, a good starting point is to # use wx.BufferedPaintDC . # wx.AutoBufferedPaintDC would be marginally better. dc = wx.AutoBufferedPaintDC(self) # Is is advisable that you don't overcrowd the OnPaint event # (or any other event) with a lot of code, so let's do the # actual drawing in the Draw() method, passing the newly # initialized wx.AutoBufferedPaintDC . self.Draw(dc) def Draw(self, dc): """ Actually performs the drawing operations, for the bitmap and for the text, positioning them centered vertically. :param `dc`: device context to use. :type `dc`: `wx.DC` """ # Get the actual client size of ourselves. width, height = self.GetClientSize() if not width or not height: # Nothing to do, we still don't have dimensions! return # Initialize the wx.BufferedPaintDC, assigning a background # colour and a foreground colour (to draw the text). backColour = self.GetBackgroundColour() backBrush = wx.Brush(backColour, wx.SOLID) dc.SetBackground(backBrush) dc.Clear() if self.IsEnabled(): dc.SetTextForeground(self.GetForegroundColour()) else: dc.SetTextForeground(self.SYS_COLOUR_GRAYTEXT) dc.SetFont(self.GetFont()) # Get the text label for the checkbox, the associated check bitmap # and the spacing between the check bitmap and the text. label = self.GetLabel() bitmap = self.GetBitmap() spacing = self.GetSpacing() # Measure the text extent and get the check bitmap dimensions. textWidth, textHeight = dc.GetTextExtent(label) bitmapWidth, bitmapHeight = bitmap.GetWidth(), bitmap.GetHeight() # Position the bitmap centered vertically. bitmapXpos = 0 bitmapYpos = (height - bitmapHeight) // 2 # Position the text centered vertically. textXpos = bitmapWidth + spacing textYpos = (height - textHeight) // 2 # Draw the bitmap on the DC. try: dc.DrawBitmap(bitmap, bitmapXpos, bitmapYpos, True) except Exception as exc: # bitmap might be image and need converted. Ex: if disabled. dc.DrawBitmap(bitmap.ConvertToBitmap(), bitmapXpos, bitmapYpos, True) # Draw the text dc.DrawText(label, textXpos, textYpos) # Let's see if we have keyboard focus and, if this is the case, # we draw a dotted rectangle around the text (Windows behavior, # I don't know on other platforms...). if self.HasFocus(): # Yes, we are focused! So, now, use a transparent brush with # a dotted black pen to draw a rectangle around the text. dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetPen(self._focusIndPen) dc.DrawRectangle(textXpos, textYpos, textWidth, textHeight) def OnMouseClick(self, event): """ Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`GenCheckBox`. :param `event`: A `wx.MouseEvent` to be processed. :type `event`: `wx.MouseEvent` """ if not self.IsEnabled(): # Nothing to do, we are disabled. return self.SendCheckBoxEvent() event.Skip() def SendCheckBoxEvent(self): """ Actually sends the wx.wxEVT_COMMAND_CHECKBOX_CLICKED event. """ # This part of the code may be reduced to a 3-liner code # but it is kept for better understanding the event handling. # If you can, however, avoid code duplication; in this case, # I could have done: # # self._checked = not self.IsChecked() # checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED, # self.GetId()) # checkEvent.SetInt(int(self._checked)) if self.IsChecked(): # We were checked, so we should become unchecked. self._checked = False # Fire a wx.CommandEvent: this generates a # wx.wxEVT_COMMAND_CHECKBOX_CLICKED event that can be caught by the # developer by doing something like: # MyCheckBox.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED, self.GetId()) # Set the integer event value to 0 (we are switching to unchecked state). checkEvent.SetInt(0) else: # We were unchecked, so we should become checked. self._checked = True checkEvent = wx.CommandEvent(wx.wxEVT_COMMAND_CHECKBOX_CLICKED, self.GetId()) # Set the integer event value to 1 (we are switching to checked state). checkEvent.SetInt(1) # Set the originating object for the event (ourselves). checkEvent.SetEventObject(self) # Watch for a possible listener of this event that will catch it and # eventually process it. self.GetEventHandler().ProcessEvent(checkEvent) # Refresh ourselves: the bitmap has changed. self.Refresh() # ----------------------------------------------------------------------------- class DefineNativeCheckBoxBitmapsMixin(): """ Inherit this mixin in your :class:`wx.Window` based subclass to easily define the native CheckBox Bitmaps as attributes which can then be used to customize a widgets appearance/functionality with. Sample example usage:: class MyCheckListBoxSTC(wx.stc.StyledTextCtrl, DefineNativeCheckBoxBitmapsMixin): '''Customized StyledTextCtrl Setup like a CheckListBox.''' def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, name='styledtextctrl'): wx.stc.StyledTextCtrl.__init__(self, parent, id, pos, size, style, name) # Define the checkbox bitmaps as attributes. self.DefineNativeCheckBoxBitmaps() # After the bitmaps have become attributes you can easily snag # them all later on from inside a method with this inherited method. ## self.checkbox_bitmaps = self.GetNativeCheckBoxBitmaps() # Setup a margin to hold bookmarks. self.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL) self.SetMarginSensitive(1, True) self.SetMarginWidth(1, 16) # Define the bookmark images. self.MarkerDefineBitmap(0, self.native_checkbox_unchecked_bmp) self.MarkerDefineBitmap(1, self.native_checkbox_checked_bmp) # ... do something with the bitmaps when you click the margin event. """ def DefineNativeCheckBoxBitmaps(self): """ Define native checkbox bitmaps as attributes. Returns True if all bitmaps was defined Ok. bitmaps defined:: self.native_checkbox_unchecked_bmp self.native_checkbox_unchecked_disabled_bmp self.native_checkbox_checked_bmp self.native_checkbox_checked_disabled_bmp self.native_checkbox_3state_bmp self.native_checkbox_3state_disabled_bmp self.native_checkbox_current_bmp self.native_checkbox_pressed_bmp :rtype: bool """ render = wx.RendererNative.Get() cbX, cbY = render.GetCheckBoxSize(self) bmp = wx.Bitmap(cbX, cbY) dc = wx.MemoryDC(bmp) DrawCheckBox = render.DrawCheckBox DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_ISDEFAULT) self.native_checkbox_unchecked_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_ISDEFAULT | wx.CONTROL_DISABLED) self.native_checkbox_unchecked_disabled_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CHECKED) self.native_checkbox_checked_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CHECKED | wx.CONTROL_DISABLED) self.native_checkbox_checked_disabled_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CHECKABLE) self.native_checkbox_3state_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CHECKABLE | wx.CONTROL_DISABLED) self.native_checkbox_3state_disabled_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CURRENT) self.native_checkbox_current_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_PRESSED) self.native_checkbox_pressed_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) if (self.native_checkbox_unchecked_bmp.IsOk() and self.native_checkbox_unchecked_disabled_bmp.IsOk() and self.native_checkbox_checked_bmp.IsOk() and self.native_checkbox_checked_disabled_bmp.IsOk() and self.native_checkbox_3state_bmp.IsOk() and self.native_checkbox_3state_disabled_bmp.IsOk() and self.native_checkbox_current_bmp.IsOk() and self.native_checkbox_pressed_bmp.IsOk() ): return True return False def GetNativeCheckBoxBitmaps(self): """ Get a tuple of the defined checkbox bitmaps. :rtype: tuple """ return (self.native_checkbox_unchecked_bmp, self.native_checkbox_unchecked_disabled_bmp, self.native_checkbox_checked_bmp, self.native_checkbox_checked_disabled_bmp, self.native_checkbox_3state_bmp, self.native_checkbox_3state_disabled_bmp, self.native_checkbox_current_bmp, self.native_checkbox_pressed_bmp, ) # ----------------------------------------------------------------------------- def _GetCheckedBitmap(self): """ Get a native checkbox(Checked) bitmap. :rtype: `wx.Bitmap` """ render = wx.RendererNative.Get() cbX, cbY = render.GetCheckBoxSize(self) bmp = wx.Bitmap(cbX, cbY) dc = wx.MemoryDC(bmp) render.DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_CHECKED) native_checkbox_checked_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) return native_checkbox_checked_bmp def _GetCheckedImage(self): """ Get a native checkbox(Checked) image. :rtype: `wx.Image` """ return _GetCheckedBitmap(self).ConvertToImage() def _GetNotCheckedBitmap(self): """ Get a native checkbox(Unchecked) bitmap. :rtype: `wx.Bitmap` """ render = wx.RendererNative.Get() cbX, cbY = render.GetCheckBoxSize(self) bmp = wx.Bitmap(cbX, cbY) dc = wx.MemoryDC(bmp) render.DrawCheckBox(self, dc, (0, 0, cbX, cbY), wx.CONTROL_ISDEFAULT) native_checkbox_unchecked_bmp = dc.GetAsBitmap((0, 0, cbX, cbY)) return native_checkbox_unchecked_bmp def _GetNotCheckedImage(self): """ Get a native checkbox(Unchecked) image. :rtype: `wx.Image` """ return _GetNotCheckedBitmap(self).ConvertToImage() # ----------------------------------------------------------------------------- def _GrayOut(anImage): """ Convert the given image (in place) to a grayed-out version, appropriate for a 'disabled' appearance. :param `anImage`: A `wx.Image` to gray out. :type `anImage`: `wx.Image` :rtype: `wx.Bitmap` """ factor = 0.7 # 0 < f < 1. Higher Is Grayer if anImage.HasMask(): maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue()) else: maskColor = None data = map(ord, list(anImage.GetData())) for i in range(0, len(data), 3): pixel = (data[i], data[i + 1], data[i + 2]) pixel = _MakeGray(pixel, factor, maskColor) for x in range(3): data[i + x] = pixel[x] anImage.SetData(''.join(map(chr, data))) return anImage.ConvertToBitmap() def _MakeGray(rgbTuple, factor, maskColor): """ Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be changed. :type `rgbTuple`: red, green, blue 3-tuple :type `factor`: float :type `maskColor`: red, green, blue 3-tuple """ r, g, b = rgbTuple if (r, g, b) != maskColor: return map(lambda x: int((230 - x) * factor) + x, (r, g, b)) else: return (r, g, b) if __name__ == '__main__': # Small sample program to test. app = wx.App(redirect=False) class MyFrame(wx.Frame, DefineNativeCheckBoxBitmapsMixin): def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name='frame'): wx.Frame.__init__(self, parent, id, title, pos, size, style, name) ## self.DefineNativeCheckBoxBitmaps() ## self.checkbox_bitmaps = self.GetNativeCheckBoxBitmaps() cb1 = GenCheckBox(self, label="PurePython Checkbox1", pos=(10, 10)) cb2 = GenCheckBox(self, label="PurePython Checkbox2", pos=(10, 50)) cb1.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) cb2.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) cb2.SetForegroundColour(wx.GREEN) cb2.SetBackgroundColour(wx.BLACK) sizer = wx.BoxSizer() sizer.Add(cb1, 0, wx.ALL, 5) sizer.Add(cb2, 0, wx.ALL, 5) self.SetSizer(sizer) def OnCheckBox(self, event): evtObj = event.GetEventObject() print(evtObj.GetLabel(), evtObj.IsChecked()) frame = MyFrame(None, wx.ID_ANY, "Test Pure-Py Checkbox") frame.Show() app.MainLoop()