In any case I finally got tired of the annoyance and wrote some utilities to help me out. Everyone here on the forums has tried to be really helpful to me, so I suppose it's time I humbly give back something.
Code: Select all
# Standard
import re
import logging
LOG = logging.getLogger(__name__)
class ColorConvert(object):
"""
Conversion utilities for OpenOffice/LibreOffice color values
"""
@staticmethod
def int_to_rgb(oo_int):
"""
Convert OpenOffice/LibreOffice color integer representation back to
rgb format.
:type oo_int: int
:param oo_int: openoffice integer representation of a color value
:retuns: red, green, and blue values
:rtype: tuple
"""
red = oo_int >> 16
green = (oo_int >> 8) - (red << 8)
blue = oo_int - (green << 8) - (red << 16)
return (red, green, blue)
@staticmethod
def rgb_to_int(red, green, blue):
"""
Return an integer which repsents an OpenOffice/LibreOffice color from a
set or rgb values.
:type red: int
:param red: amount of included red
:type green: int
:param green: amount of included green
:type blue: int
:param blue: amount of included blue
:returns: special int representation of color value
:rtype: int
"""
assert isinstance(red, int), '%s is not an integer'
assert isinstance(green, int), '%s is not an integer'
assert isinstance(blue, int), '%s is not an integer'
red_int = (red & 255) << 16
green_int = (green & 255) << 8
blue_int = (blue & 255)
return (red_int | green_int | blue_int)
@staticmethod
def rgb_to_html(red, green, blue):
"""
Given a set of integers representing red, green, and blue values,
return an html type format code.
:type red: int
:param red: amount of included red
:type green: int
:param green: amount of included green
:type blue: int
:param blue: amount of included blue
:returns: html code representation of color value
:rtype: str
"""
assert isinstance(red, int), '%s is not an integer'
assert isinstance(green, int), '%s is not an integer'
assert isinstance(blue, int), '%s is not an integer'
red_int = (red & 255)
green_int = (green & 255)
blue_int = (blue & 255)
red_str = hex(red_int)[2:].upper()
red_str = '00' if red_str == '0' else red_str
green_str = hex(green_int)[2:].upper()
green_str = '00' if green_str == '0' else green_str
blue_str = hex(blue_int)[2:].upper()
blue_str = '00' if blue_str == '0' else blue_str
return "#%s%s%s" % (red_str, green_str, blue_str)
@staticmethod
def html_to_rgb(html_str):
"""
Convert an html string into rgb integer values
:type html_str: str
:param html_str: html color notation value
:returns: rgb integer values
:rtype: tuple
"""
html_str = html_str.lstrip('#')
if len(html_str) == 3:
red = int("%s%s" % (html_str[0], html_str[0]), 16)
green = int("%s%s" % (html_str[1], html_str[1]), 16)
blue = int("%s%s" % (html_str[2], html_str[2]), 16)
elif len(html_str) == 6:
red = int(html_str[:2], 16)
green = int(html_str[2:4], 16)
blue = int(html_str[4:6], 16)
else:
raise ValueError('"%s" is not valid html notation.' % html_str)
return (red, green, blue)
@staticmethod
def html_to_int(html_str):
"""
Convert a string of html color notation into an openoffice color
integer.
:type html_str: str
:param html_str: html color notation value
:returns: OpenOffice/LibreOffice color notation integer value
:rtype: int
"""
rgb = ColorConvert.html_to_rgb(html_str)
return ColorConvert.rgb_to_int(*rgb)
@staticmethod
def int_to_html(oo_int):
"""
Convert an OpenOffice/LibreOffice color integer into html notation.
:type oo_int: int
:param oo_int: openoffice integer representation of a color value
:returns: html code representation of color value
:rtype: str
"""
rgb = ColorConvert.int_to_rgb(oo_int)
return ColorConvert.rgb_to_html(*rgb)
class StrConst(str):
"""Subclasses string and allows other arguments to pass through to init"""
def __init__(self, *args, **kwargs):
super(StrConst, self).__init__()
def __new__(cls, value, *args, **kwargs):
return str.__new__(cls, value)
class PropertyName(StrConst):
"""Constants value for names of openoffice property values"""
def __init__(self, *args, **kwargs):
super(PropertyName, self).__init__(*args, **kwargs)
def __call__(self, x_model):
"""
Extract the value from the x_model by calling through to the
extract_value method.
"""
return self.get_value(x_model)
def apply_value(self, x_model, value, overwrite=True):
"""
Apply the given value to the specified x_model using this properties
name.
:type x_model: object
:param x_model: object to apply this property and value to
:type value: str | int | object
:param value: new value to set for the property value on the x_model
:type overwrite: bool
:param overwrite: Switches overwriting of existing values on and off
:raises: ValueError
"""
if not overwrite:
if x_model.getPropertySetInfo().hasPropertyByName(self.__str__()):
raise ValueError('%s already exists on xmodel and'
' overwrite set to "False"')
LOG.debug("Setting Property %s to %s" % (self.__str__(), value))
try:
x_model.setPropertyValue(self.__str__(), value)
except Exception as ex:
LOG.error('Set property "%s" failed. %s' % (self, ex))
raise ValueError('Invalid property name %s' % str(self))
def get_value(self, x_model, default=None):
"""
Extract this property from the given x_model. If the property does not
exist then return the default value instead.
"""
if x_model.getPropertySetInfo().hasPropertyByName(self.__str__()):
return x_model.getPropertyValue(self.__str__())
else:
LOG.debug("No property named %s. Using default" % self.__str__())
return default
class ColorMixins(object):
"""Mixin class for Color values """
@property
def rgb_exp(self):
return (
r'^(?:[R,r][G,g][B,b])' # Match rgb case insensitive
r'[(]' # Match start of rgb parenthesis
r'(?:\d{1,3}, ?){2}' # Match up to 3 digits with trailing comma
r'(?:\d{1,3} ?){1}' # Match 3 digits with no trailing comma
r'[)]$' # End rgb parenthesis
)
@property
def html_exp(self):
"""Regex for matching string if html color notation"""
return (
r'^#?(?:' # Start regex, optional # at html code start
r'[A-F,a-f,0-9]{6}' # Allow 6 numbers or characters a-f
r'|' # OR
r'[A-F,a-f,0-9]{3}' # Allow 3 numbers or characters af
r')$' # End regex
)
def _parse_rgb(self, rgb_val):
"""
Parse a string into a tuple of three integers. The string should
resemble::
rgb(255, 201, 90)
:type rgb_val: str
:param rgb_val: value to parse into tuple of three integers
:returns: integers contained in rgb string
:rtype: tuple
"""
rgb_val = rgb_val.lower.replace('rgb', '').replace(' ', '')
rgb_val = rgb_val.replace('(', '')
rgb_val = rgb_val.replace(')', '')
val_list = rgb_val.split(',')
return (int(val_list[0]), int(val_list[1]), int(val_list[1]))
def _value_to_int(self, value):
"""
Convert whatever value is provided to the correct int notation. If the
value is already in integer notation, just return it.
Example allowed values::
FOO.apply_value(16777215) # API Integer Notation
FOO.apply_value('#FEFD4A') # HTML Notation
FOO.apply_value('rgb(255, 255, 255)') # RGB String
FOO.apply_value((255, 255, 255)) # RGB Tuple
:type value: str | int | object
:param value: new value to set for the property value on the x_model
:returns: value converted to openoffice color integer value
:rtype: int
"""
# If we're given a html code or rgb value, convert it to an integer
if isinstance(value, basestring):
is_html = re.match(self.html_exp, value)
if is_html:
value = ColorConvert.html_to_int(value)
else:
is_rgb = re.match(self.rgb_exp, value)
if is_rgb:
rgb = self._parse_rgb(value)
value = ColorConvert.rgb_to_int(*rgb)
elif isinstance(value, tuple):
assert len(value) == 3, 'RGB requires 3 values in Tuple'
else:
assert isinstance(value, int), 'Invalid color value'
if value > 16777215:
raise ValueError('Value larger than 16777215 - invalid color.')
return value
class ColorPropertyName(PropertyName, ColorMixins):
"""
Constants value for names of OpenOffice/LibreOffice property values which
reference properties using the special color integer type. To make use of
color properties easier, values are returned by default as an HTML color
notation string (i.e. #FE25A1). Alternatively they can be returned as a
simple tuple of rgb integers (i.e. (223, 10, 149)). Values may also be set
using color notation, a tuple of rgb values, or a string indicating rgb
values. The default OpenOffice/LibreOffice integer notation may also be
used to set the value.
"""
def apply_value(self, x_model, value, overwrite=True):
"""
Apply the given color value to the specified x_model using this
properties name. If the value passed is an rgb value or html color
notation, convert the value to a color integer first.
Example allowed values::
FOO.apply_value(16777215) # API Integer Notation
FOO.apply_value('#FEFD4A') # HTML Notation
FOO.apply_value('rgb(255, 255, 255)') # RGB String
FOO.apply_value((255, 255, 255)) # RGB Tuple
:type x_model: object
:param x_model: object to apply this property and value to
:type value: str | int | object
:param value: new value to set for the property value on the x_model
:type overwrite: bool
:param overwrite: Switches overwriting of existing values on and off
:raises: ValueError
"""
if not overwrite:
if x_model.getPropertySetInfo().hasPropertyByName(self.__str__()):
raise ValueError('%s already exists on xmodel and'
' overwrite set to "False"')
# If we're given a html code or rgb value, convert it to an integer
value = self._value_to_int(value)
LOG.debug("Setting Color Property %s to %s" % (self.__str__(), value))
x_model.setPropertyValue(self.__str__(), value)
def get_value(self, x_model, default=None, rgb=False):
"""
Extract this property from the given x_model. If the property does not
exist then return the default value instead. By default this method
returns html notation for the color value. It can alternatively return
a tuple of red, green, and blue values.
:type default: Anything
:param default: value to return if the property not set
:type rgb: bool
:param rgb: if True, return an rgb tuple instead of html notation
:returns: html notation string or tuple
:rtype: str | tuple
"""
if x_model.getPropertySetInfo().hasPropertyByName(self.__str__()):
value = x_model.getPropertyValue(self.__str__())
if rgb:
return ColorConvert.int_to_rgb(value)
return ColorConvert.int_to_html(value)
else:
LOG.debug("No property named %s. Using default" % self.__str__())
return default
Code: Select all
TEXT_COLOR = ColorPropertyName('TextColor')
BORDER_COLOR = ColorPropertyName('BorderColor')
TEXT_LINE_COLOR = ColorPropertyName('TextLineColor')
BACKGROUND_COLOR = ColorPropertyName('BackgroundColor')
Code: Select all
TEXT_COLOR.apply_value(char_span_model, '#FE569F')
Code: Select all
span_color = TEXT_COLOR.get_value(char_span_model)
Code: Select all
class Label(object):
def __init__(self, x_model, control):
self._control = control
self._x_model = x_model
@property
def control(self):
return self._control
@property
def x_model(self):
return self._x_model
def _get_text_color(self):
return TEXT_COLOR.get_value(self.x_model)
def _set_text_color(self, value):
TEXT_COLOR.set_value(self.x_model, value)
text_color = property(_get_text_color, _set_text_color)
# Then on any instance of a Label I can just set its color like this:
somelabel.text_color = '#55667F'
Anyway, I hope someone finds this useful and saves them from ripping out some of their hair!
Cheers all!