Skip to content

Instantly share code, notes, and snippets.

@kived
Created July 31, 2015 17:37
Show Gist options
  • Save kived/7cdcf5182388207a4974 to your computer and use it in GitHub Desktop.
Save kived/7cdcf5182388207a4974 to your computer and use it in GitHub Desktop.
Kivy: SizedLabel
class SizedLabel(Label):
max_lines = BoundedNumericProperty(3, min=1)
sizetext = StringProperty('')
auto_wrap = BooleanProperty(True)
auto_height = BooleanProperty(False)
outline_color = ListProperty([0.0] * 4)
line_threshold = ListProperty((36, 80, 124,))
resize_text = BooleanProperty(True)
resize_text_down_only = BooleanProperty(False)
wordsep_re = re.compile(
r'([^\s\w]*\w+[^0-9\W] -(?=\W)|' # keep en-dashes
r'\s+|' # any whitespace
r'[^\s\w]*\w+[^0-9\W]-(?=\w+[^0-9\W])|' # hyphenated words
r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
wordsep_re_uni = re.compile(wordsep_re.pattern, re.U)
def __init__(self, **kwargs):
super(SizedLabel, self).__init__(**kwargs)
self.halign = 'center'
#self.bind(on_size=self.text_size)
self.wrapper = textwrap.TextWrapper(break_long_words=False, expand_tabs=False)
self.trigger_sizetext = Clock.create_trigger(self.update_sizetext, -1)
self.bind(sizetext=self.trigger_sizetext, size=self.trigger_sizetext)
self.trigger_sizetext()
def get_max_lines(self, height):
height_lines = 99999
if height < self.line_threshold[0]:
height_lines = 1
elif height < self.line_threshold[1]:
height_lines = 2
elif height < self.line_threshold[2]:
height_lines = 3
return min(height_lines, self.max_lines)
def update_sizetext(self, *_args):
if not self.width or not self.height:
return
cw = self.width - max(8, self.width * 0.08)
ch = self.height - max(8, self.height * 0.08)
text = ' '.join(self.sizetext.strip().split())
max_lines = self.get_max_lines(ch)
num_lines = min(max_lines, text.count(' ') + 1)
self.text = text
if self.resize_text:
self.font_size = 16
self.texture_update()
if not text or not ch:
return
ts = self.texture_size
sr = [[float(ts[0]) / ts[1], text, 1, float(ts[0]), float(ts[1])]]
if self.auto_wrap and num_lines > 1:
label = self._label
wrapper = self.wrapper
wrap = wrapper._wrap_chunks
join = '\n'.join
text = text.translate(wrapper.whitespace_trans if isinstance(text, str) else wrapper.unicode_whitespace_trans)
stext = filter(None, (self.wordsep_re if isinstance(text, str) else self.wordsep_re_uni).split(text))
for wrapper.width in reversed(range(int(len(text) / (num_lines + 1)), int(len(text) * 0.75 + 0.5))[1:]):
wrapped = wrap(stext[:])
lines = len(wrapped)
if lines > num_lines:
break
ltext = join(wrapped)
self.text = ltext
label.refresh()
size = label.texture.size
sr.append([float(size[0]) / size[1], ltext, lines, size[0], size[1]])
cr = float(cw) / ch
och = ch
sr = sorted(sr, key=lambda a: abs(cr - a[0]))
for _i, v in enumerate(sr):
_ratio, text, lines, width, height = v
self.text = text
if not self.auto_height:
ch = och * (float(lines) / max_lines)
else:
ch = och
upsize = ch / height
if width * upsize > cw:
# skip any options which require resizing
continue
self.resize_text_method(upsize)
self.texture_update()
return
# well, there weren't any options without resizing
_ratio, text, lines, width, height = max(sr, key=lambda a: a[2] * 2 - abs(cr - a[0]))
self.text = text
upsize = cw / width
self.resize_text_method(upsize)
self.texture_update()
def resize_text_method(self, upsize):
if upsize > 0 and self.resize_text:
if self.resize_text_down_only:
if upsize < 1:
self.font_size *= upsize
else:
self.font_size *= upsize
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment