From ee144e8c706de0ee6c5215a0e3e2282e69ee243c Mon Sep 17 00:00:00 2001 From: Marek Ventur Date: Tue, 31 Jul 2018 21:15:50 +0100 Subject: [PATCH] wip: icons --- lib/icons.py | 109 +++++++++++++++++++++++++++++++++++++++ lib/test_database.py | 2 +- lib/test_icons.py | 39 ++++++++++++++ shared/icons/unknown.gif | Bin 0 -> 49018 bytes 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 lib/icons.py create mode 100644 lib/test_icons.py create mode 100644 shared/icons/unknown.gif diff --git a/lib/icons.py b/lib/icons.py new file mode 100644 index 0000000..59bfe1a --- /dev/null +++ b/lib/icons.py @@ -0,0 +1,109 @@ +""" library to display icons + +http://glyph.smarticons.co/ +""" + +___license___ = "MIT" +___dependencies___ = ["ospath", "shared/icons/unknown.gif", "buttons"] + +import ugfx, ospath + +_icon_size = 50; +_half_icon_size = _icon_size // 2 +_padding = 5 +_padded_size = _icon_size + _padding * 2 +_text_height = 30 + +_icon_container_style = ugfx.Style() +_icon_container_style.set_disabled([ugfx.BLACK, ugfx.WHITE, ugfx.WHITE, ugfx.RED]) +_icon_container_style.set_enabled([ugfx.BLACK, ugfx.RED, ugfx.WHITE, ugfx.RED]) +#_icon_container_style.set_background(ugfx.html_color(ugfx.WHITE)) + +class Icon: + def __init__(self, x, y, title, path_to_icon = None): + if path_to_icon == None or not ospath.isfile(path_to_icon): + path_to_icon = "shared/icons/unknown.gif" + self._selected = False + self._init_container(x, y, title, path_to_icon) + + + def _init_container(self, x, y, title, path_to_icon): + self.container = ugfx.Container( + x - _half_icon_size - _padding, y - _half_icon_size - _padding, + _padded_size, _padded_size + _text_height, + style=_icon_container_style + ) + + #This doesn't work reliably at the moment + #ugfx.Imagebox( + # _padding - 2, _padding - 2, + # _icon_size, _icon_size, + # parent=self.container, text=path_to_icon + #) + + self.label = ugfx.Label( + 0, _padded_size, + _padded_size, _text_height, + title, parent=self.container, justification=ugfx.Label.CENTERTOP + ) + + self.container.enabled(self._selected) + + def show(self): + self.container.show() + self.refresh_image() + + def refresh_image(self): + self.container.area(_padding, _padding, _icon_size, _icon_size, ugfx.BLACK) + + @property + def selected(self): + return self._selected + + @selected.setter + def selected(self, value): + self._selected = value + self.container.enabled(value) + self.refresh_image() + + def __del__(self): + self.label.destroy() + self.container.destroy() + +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i + n] + +class IconGrid: + def __init__(self, x, y, items, controls): + self._x = x + self._y = y + self._pages = list(chunks(items, 9)) + self._current_page_index = 0 + self._current_cursor_x = 0 + self._current_cursor_y = 0 + self._last_state = None + self._refresh_page() + + def _refresh_page(self): + state = (self._current_page_index, self._current_cursor_x, self._current_cursor_y) + if self._last_state == state: + return # nothing to do + self._last_state = state + + self._current_icons = [] + for i, item in enumerate(self._pages[self._current_page_index]): + x = i % 3 + y = i // 3 + icon = Icon( + self._x + x * 60 + 30, self._y + y * 90 + 30, + item['title'], item.get('icon', None) + ) + icon.selected = (x == self._current_cursor_x) and (y == self._current_cursor_y) + icon.show() + self._current_icons.append(icon) + + def __del__(self): + del self._current_icons + diff --git a/lib/test_database.py b/lib/test_database.py index 2b575ff..d8e3b26 100644 --- a/lib/test_database.py +++ b/lib/test_database.py @@ -7,7 +7,7 @@ import database, unittest class TestDatabase(unittest.TestCase): def setUp(self): - self.filename = "test/tmp.testdb.json" + self.filename = "tmp.testdb.json" self.database = database.Database(filename = self.filename) self._remove_test_db() diff --git a/lib/test_icons.py b/lib/test_icons.py new file mode 100644 index 0000000..f7076c2 --- /dev/null +++ b/lib/test_icons.py @@ -0,0 +1,39 @@ +"""Tests for icons lib""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "icons"] + +import unittest, ugfx, time +from icons import * + +class TestIcons(unittest.TestCase): + + # incomplete! + + def setUp(self): + ugfx.init() + ugfx.orientation(90) + ugfx.clear() + + def test_icon(self): + icon = Icon(44, 40, "Badge Store with", "badge_store/icon.gif") + icon.show() + + for s in [True, False, True]: + icon.selected = s + time.sleep(0.1) + + icon.__del__() + + def test_icon_grid(self): + items = [] + for i in range(50): + items.append({ + "title": "App %s" % i + }) + icon_grid = IconGrid(5, 5, items, None) + + + +if __name__ == '__main__': + unittest.main() diff --git a/shared/icons/unknown.gif b/shared/icons/unknown.gif new file mode 100644 index 0000000000000000000000000000000000000000..78aed0d0c1e5177001fbe46ee33e283d86d46241 GIT binary patch literal 49018 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 literal 0 HcmV?d00001