Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add color-picker setting support #783

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions chirp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,60 @@ def get_step(self):
return self._step


class RadioSettingValueRGB(RadioSettingValue):
"""A color setting, stored as (r, g, b)"""
def __init__(self, current):
super().__init__()
self.set_value(current)

@staticmethod
def from_bits(value, rbits, gbits, bbits):
outvals = []
for bits in [rbits, gbits, bbits]:
outval = value & (2 ** bits - 1)
# Map into 8-bpc space
outvals.insert(0, round(outval / (2 ** bits - 1) * 255))
value >>= bits
return RadioSettingValueRGB(tuple(outvals))

@staticmethod
def from_rgb24(value):
"""Return a RadioSettingValueRGB initialized from an rgb24 value"""
return RadioSettingValueRGB.from_bits(value, 8, 8, 8)

@staticmethod
def from_rgb16(value):
"""Return a RadioSettingValueRGB initialized from an rgb16 value"""
return RadioSettingValueRGB.from_bits(value, 5, 6, 5)

def set_value(self, value):
self._r, self._g, self._b = value

def get_value(self):
"""Returns (r, g, b)"""
return self._r, self._g, self._b

def get_bits(self, rbits, gbits, bbits):
"""Return color as a bitpacked integer.

Pass the number of bits for the r, g, and b channels packed
sequentially into an integer to be returned.
"""
outval = 0
for bits, val in zip([rbits, gbits, bbits],
[self._r, self._g, self._b]):
outval = (outval << bits) | round((2 ** bits - 1) * val / 255)
return outval

def get_rgb24(self):
"""Return an integer value for 24-bit color"""
return self.get_bits(8, 8, 8)

def get_rgb16(self):
"""Return an integer value for 16-bit color"""
return self.get_bits(5, 6, 5)


class RadioSettingValueFloat(RadioSettingValue):

"""A floating-point setting"""
Expand Down
12 changes: 11 additions & 1 deletion chirp/wxui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ def __init__(self, settinggroup, *a, **k):
editor = self._get_editor_bool(element, value)
elif isinstance(value, settings.RadioSettingValueString):
editor = self._get_editor_str(element, value)
elif isinstance(value, settings.RadioSettingValueRGB):
editor = self._get_editor_rgb(element, value)
else:
LOG.warning('Unsupported setting type %r' % value)
editor = None
Expand Down Expand Up @@ -510,14 +512,22 @@ def ValidateValue(self, text, info):
setting.get_name(),
value=str(value))

def _get_editor_rgb(self, setting, value):
return wx.propgrid.ColourProperty(setting.get_shortname(),
setting.get_name(),
wx.Colour(*value.get_value()))

def get_setting_values(self):
"""Return a dict of {name: (RadioSetting, newvalue)}"""
values = {}
for prop in self.pg._Items():
if prop.IsCategory():
continue
basename = prop.GetName().split(INDEX_CHAR)[0]
if isinstance(prop, wx.propgrid.EnumProperty):
if isinstance(prop, wx.propgrid.ColourProperty):
c = prop.GetValue()
value = c.red, c.green, c.blue
elif isinstance(prop, wx.propgrid.EnumProperty):
value = self._choices[basename][prop.GetValue()]
else:
value = prop.GetValue()
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ def test_radio_setting_value_string(self):
self._set_and_test(value, "a", "abc", "abdef")
self._set_and_catch(value, "", "abcdefg")

def test_radio_setting_value_rgb(self):
value = settings.RadioSettingValueRGB((0, 0, 0))
self.assertEqual((0, 0, 0), value.get_value())
self.assertEqual(0, value.get_rgb16())
self.assertEqual(0, value.get_rgb24())

# 0x9c9a2f == 0x9cc6
value.set_value((0x9C, 0x9A, 0x2F))
self.assertEqual(0x9C9A2F, value.get_rgb24())
self.assertEqual(0x9CC6, value.get_rgb16())

value = settings.RadioSettingValueRGB.from_rgb16(0x9CC6)
# Rounding error on the B value
self.assertEqual(0x9C9A31, value.get_rgb24())

def test_validate_callback(self):
class TestException(Exception):
pass
Expand Down
Loading