mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2025-07-21 12:41:10 +02:00
838 lines
30 KiB
Python
838 lines
30 KiB
Python
#----------------------------------------------------------------------
|
|
# 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()
|
|
|