From ad49aedaad3acece200ea92fd5d5a5b3e19c143b Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Fri, 6 Jan 2023 14:07:15 +0100 Subject: desktop dependent widgets early port --- tagit/tiles/__init__.py | 61 ++++++++++++++++++++++++++++++++++++++++++ tagit/tiles/decoration.kv | 58 ++++++++++++++++++++++++++++++++++++++++ tagit/tiles/decoration.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 tagit/tiles/__init__.py create mode 100644 tagit/tiles/decoration.kv create mode 100644 tagit/tiles/decoration.py (limited to 'tagit/tiles') diff --git a/tagit/tiles/__init__.py b/tagit/tiles/__init__.py new file mode 100644 index 0000000..3ed53b9 --- /dev/null +++ b/tagit/tiles/__init__.py @@ -0,0 +1,61 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import typing + +# tagit imports +from tagit.utils.builder import BuilderBase + +# inner-module imports +##from .anomalies import Anomalies # FIXME: skeleton only +#from .browser_tags import BrowserTags +#from .buttons import Buttons +#from .cursor_tags import CursorTags +#from .entity_histogram import EntityHistogram +#from .geo import Map +#from .hints import Hints +#from .info import Info +#from .libsummary import LibSummary +#from .searchtree import Searchtree +#from .selection_tags import SelectionTags +#from .suggested_tags import SuggestedTags +#from .tag_distribution import TagDistribution +#from .tag_histogram import TagHistogram +#from .tag_tree import TagTree +#from .tagcloud import Tagcloud +#from .venn import Venn + +# exports +__all__: typing.Sequence[str] = ( + 'TileBuilder', + ) + + +## code ## + +class TileBuilder(BuilderBase): + _factories = { +# #'Anomalies': Anomalies, +# 'BrowserTags': BrowserTags, +# 'Buttons': Buttons, +# 'CursorTags': CursorTags, +# 'EntityHistogram': EntityHistogram, +# 'Geo': Map, +# 'Hints': Hints, +# 'Info': Info, +# 'LibSummary': LibSummary, +# 'Searchtree': Searchtree, +# 'SelectionTags': SelectionTags, +# 'SuggestedTags': SuggestedTags, +# 'TagDistribution': TagDistribution, +# 'TagHistogram': TagHistogram, +# 'TagTree': TagTree, +# 'Tagcloud': Tagcloud, +# 'Venn': Venn, + } + +## EOF ## diff --git a/tagit/tiles/decoration.kv b/tagit/tiles/decoration.kv new file mode 100644 index 0000000..a53d013 --- /dev/null +++ b/tagit/tiles/decoration.kv @@ -0,0 +1,58 @@ + +# NOTE: +# TileDecoration assumes as *cbox* property that identifies the widget +# to which the main content will be added. + +: + cbox: cbox + + RelativeLayout: + id: cbox + +: + cbox: cbox + + canvas.after: + # tile shadow + Color: + rgb: 0.2,0.2,0.2 + Line: + rectangle: self.x+5,self.y+5,self.width-10,self.height-10 + width: 2 + + Color: + rgb: 0.6,0.6,0.6 + Line: + rectangle: self.x+7,self.y+7,self.width-14,self.height-14 + width: 1 + + RelativeLayout: + id: cbox + pos: 15, 15 + size: root.width-30, root.height-30 + size_hint: None, None + +: + cbox: cbox + + Label: + text: root.client.title + size: root.width, 20 + size_hint: None, None + pos: 0, root.height - self.height - 5 + + RelativeLayout: + id: cbox + pos: 5, 5 + size: root.width-10, root.height-30 + size_hint: None, None + + canvas.before: + Color: + rgba: 1,0,0,0.5 + Rectangle: + pos: 0, 0 + size: self.size + + +## EOF ## diff --git a/tagit/tiles/decoration.py b/tagit/tiles/decoration.py new file mode 100644 index 0000000..471058d --- /dev/null +++ b/tagit/tiles/decoration.py @@ -0,0 +1,68 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import os +import typing + +# kivy imports +from kivy.lang import Builder +from kivy.uix.relativelayout import RelativeLayout +import kivy.properties as kp + +# exports +__all__: typing.Sequence[str] = ( + 'TileDecorationBorder', + 'TileDecorationFilledRectangle', + 'TileDecorationVanilla', + ) + + +## code ## + +# load kv +Builder.load_file(os.path.join(os.path.dirname(__file__), 'decoration.kv')) + +# classes +class TileDecoration(RelativeLayout): + + cbox = kp.ObjectProperty(None) + client = kp.ObjectProperty(None) + + def __repr__(self): + return f'{self.__class__.__name__}({self.client})' + + def on_cbox(self, wx, cbox): + if cbox is not None and len(cbox.children) == 0: + cbox.add_widget(self.client) + + @property + def default_size(self): + return self.client.default_size + + +class TileDecorationVanilla(TileDecoration): + pass + + +class TileDecorationFilledRectangle(TileDecoration): + @property + def default_size(self): + width, height = self.client.default_size + width = None if width is None else width + 10 + height = None if height is None else height + 30 + return width, height + + +class TileDecorationBorder(TileDecoration): + @property + def default_size(self): + width, height = self.client.default_size + width = None if width is None else width + 30 + height = None if height is None else height + 30 + return width, height + +## EOF ## -- cgit v1.2.3 From 56865c524bddaee9ec86d57e62af9524be80d1b3 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Wed, 25 Jan 2023 10:55:49 +0100 Subject: first tiles --- tagit/tiles/__init__.py | 8 ++-- tagit/tiles/browser_tags.py | 108 ++++++++++++++++++++++++++++++++++++++++++++ tagit/tiles/info.py | 84 ++++++++++++++++++++++++++++++++++ tagit/tiles/tile.kv | 33 ++++++++++++++ tagit/tiles/tile.py | 88 ++++++++++++++++++++++++++++++++++++ 5 files changed, 317 insertions(+), 4 deletions(-) create mode 100644 tagit/tiles/browser_tags.py create mode 100644 tagit/tiles/info.py create mode 100644 tagit/tiles/tile.kv create mode 100644 tagit/tiles/tile.py (limited to 'tagit/tiles') diff --git a/tagit/tiles/__init__.py b/tagit/tiles/__init__.py index 3ed53b9..11f4a82 100644 --- a/tagit/tiles/__init__.py +++ b/tagit/tiles/__init__.py @@ -12,13 +12,13 @@ from tagit.utils.builder import BuilderBase # inner-module imports ##from .anomalies import Anomalies # FIXME: skeleton only -#from .browser_tags import BrowserTags +from .browser_tags import BrowserTags #from .buttons import Buttons #from .cursor_tags import CursorTags #from .entity_histogram import EntityHistogram #from .geo import Map #from .hints import Hints -#from .info import Info +from .info import Info #from .libsummary import LibSummary #from .searchtree import Searchtree #from .selection_tags import SelectionTags @@ -40,13 +40,13 @@ __all__: typing.Sequence[str] = ( class TileBuilder(BuilderBase): _factories = { # #'Anomalies': Anomalies, -# 'BrowserTags': BrowserTags, + 'BrowserTags': BrowserTags, # 'Buttons': Buttons, # 'CursorTags': CursorTags, # 'EntityHistogram': EntityHistogram, # 'Geo': Map, # 'Hints': Hints, -# 'Info': Info, + 'Info': Info, # 'LibSummary': LibSummary, # 'Searchtree': Searchtree, # 'SelectionTags': SelectionTags, diff --git a/tagit/tiles/browser_tags.py b/tagit/tiles/browser_tags.py new file mode 100644 index 0000000..3a9c25f --- /dev/null +++ b/tagit/tiles/browser_tags.py @@ -0,0 +1,108 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +from functools import reduce +import operator + +# kivy imports +from kivy.lang import Builder + +# tagit imports +from tagit.widgets.browser import BrowserAwareMixin + +# inner-module imports +from .tile import TileWithLabel + +# exports +__all__ = ('BrowserTags', ) + + +## code ## + +# load kv +Builder.load_string(''' +: + title: 'Tags' + tooltip: 'Tags of displayed items.' +''') + +# classes +class BrowserTags(TileWithLabel, BrowserAwareMixin): + """Show tags of displayed items. Tags of selected items are highlighted.""" + + displayed = None + + def on_browser(self, sender, browser): + # remove old binding + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser.unbind(selection=self.update) + self.browser.unbind(items=self.on_items) + # add new binding + self.browser = browser + if self.browser is not None: + self.browser.bind(cursor=self.update) + self.browser.bind(selection=self.update) + self.browser.bind(items=self.on_items) + # populate displayed first, then update + self.on_items(browser, browser.items) + self.update() + + def __del__(self): + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser.unbind(selection=self.update) + self.browser.unbind(items=self.on_items) + self.browser = None + + def on_items(self, browser, items): + # unfold + items = browser.unfold(items) + # get tags + self.displayed = items.tag.label(node=False) + # update view + self.update() + + def update(self, *args): + if not self.visible: + self.text = '' + + elif self.displayed is None: + self.on_items(self.root.browser, self.root.browser.items) + # calls update again with not-None self.displayed + + else: + browser = self.root.browser + + # handle cursor + if browser.cursor is None: + cursor = set() + else: + cursor = browser.cursor.tag.label() + + # handle selection + if len(browser.selection) == 0: + selected = set() + else: + selection = reduce(operator.add, browser.selection) + selected = selection.tag.label(node=False) + + # assemble tag list + tags = [] + for tag in sorted(self.displayed | selected | cursor): + pretty = tag + if tag in cursor: + pretty = f'[b]{pretty}[/b]' # bold + if tag in selected: + pretty = f'[color=#415bCD]{pretty}[/color]' # blue color + + tags.append(pretty) + + # Apply prefix and display + self.text = ', '.join(tags) + +## EOF ## diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py new file mode 100644 index 0000000..2221b60 --- /dev/null +++ b/tagit/tiles/info.py @@ -0,0 +1,84 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +from collections import OrderedDict + +# kivy imports +from kivy.lang import Builder + +# tagit imports +from tagit.utils import ttime, ns +from tagit.widgets.browser import BrowserAwareMixin + +# inner-module imports +from .tile import TileTabular + +# exports +__all__ = ('Info', ) + + +## code ## + +# load kv +Builder.load_string(''' +: + title: 'Item info' + tooltip: 'Key properties of the cursor item' + # assuming 7 info items + default_size: None, 7*self.font_size + 6*5 + keywidth: min(65, self.width * 0.4) +''') + + +# classes +class Info(TileTabular, BrowserAwareMixin): + """Show essential attributes about the cursor.""" + + def on_browser(self, sender, browser): + # remove old binding + if self.browser is not None: + self.browser.unbind(cursor=self.update) + # add new binding + self.browser = browser + if self.browser is not None: + self.browser.bind(cursor=self.update) + self.update() + + def __del__(self): + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser = None + + def update(self, *args): + cursor = self.root.browser.cursor + if not self.visible or cursor is None: + # invisible or no cursor, nothing to show + self.tabledata = OrderedDict({}) + + else: + preds = cursor.get( + ns.bsm.t_created, + ns.bse.filesize, + ns.bse.filename, + ns.bse.comment, + ns.bse.author, + ) + self.tabledata = OrderedDict({ + 'Date' : ttime.from_timestamp_utc( + preds.get(ns.bsm.t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), + #'Size' : f'{preds.get(ns.bse.width, "?")} x {preds.get(ns.bse.height, "?")}', + #'ISO' : preds.get(ns.bse.iso, '?'), + 'Filesize' : preds.get(ns.bse.filesize, 'n/a'), + 'Filename' : preds.get(ns.bse.filename, 'n/a'), + 'Comment' : '; '.join(preds.get(ns.bse.comment, [])), + #'Exposure' : f'1 / {preds.get(ns.bse.exposure, 0):0.2f}', + #'Aperture' : preds.get(ns.bse.aperture, '?'), + #'Fx' : preds.get(ns.bse.focal_length_35, '?'), + #'Flash' : 'Yes' if preds.get(ns.bse.flash, False) else 'No', + }) + +## EOF ## diff --git a/tagit/tiles/tile.kv b/tagit/tiles/tile.kv new file mode 100644 index 0000000..277b23d --- /dev/null +++ b/tagit/tiles/tile.kv @@ -0,0 +1,33 @@ + +: + title: '' + tooltip: '' + default_size: None, None + +: + # config + font_size: sp(15) + spacer: 10 + keywidth: 0.5 + + # table + grid: grid + GridLayout: + id: grid + cols: 2 + size: root.size + size_hint: None, None + +: + text: '' + text_align: 'left', 'top' + font_size: sp(15) + Label: + text: root.text + markup: True + text_size: root.size + font_size: root.font_size + halign: root.text_align[0] + valign: root.text_align[1] + +## EOF ## diff --git a/tagit/tiles/tile.py b/tagit/tiles/tile.py new file mode 100644 index 0000000..7ea690a --- /dev/null +++ b/tagit/tiles/tile.py @@ -0,0 +1,88 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import os + +# kivy imports +from kivy.lang import Builder +from kivy.uix.label import Label +from kivy.uix.relativelayout import RelativeLayout +import kivy.properties as kp + +# exports +__all__ = ('Tile', 'TileWithLabel', 'TileTabular') + + +## code ## + +# load kv +Builder.load_file(os.path.join(os.path.dirname(__file__), 'tile.kv')) + +# classes +class Tile(RelativeLayout): + visible = kp.BooleanProperty(False) + root = kp.ObjectProperty(None) + + def on_visible(self, wx, visible): + if visible: + self.update() + + def update(self, *args, **kwargs): + abstract() + + +class TileWithLabel(Tile): + pass + + +class TileTabular(Tile): + + tabledata = kp.ObjectProperty() + spacer = kp.NumericProperty(10) + keywidth = kp.NumericProperty(0.5) + + def on_size(self, wx, size): + if not self.visible or len(self.grid.children) == 0: + return + + height = self.height / (len(self.grid.children) / 2) + kwidth = self.width * self.keywidth if self.keywidth < 1 else self.keywidth + vwidth = self.width - kwidth - self.spacer + + # adjust size + for idx, child in enumerate(self.grid.children): + if idx % 2 == 1: # key + child.text_size = kwidth, height + child.width = kwidth + else: # label + child.text_size = vwidth, height + + def on_tabledata(self, wx, data): + # set items + self.grid.clear_widgets() + for key, value in data.items(): + # left column (keys) + self.grid.add_widget(Label( + text=key, + halign='right', + valign='top', + font_size = self.font_size, + size_hint = (None, 1), + )) + + # right column (values) + self.grid.add_widget(Label( + text=str(value), + halign='left', + valign='top', + font_size = self.font_size, + )) + + # set sizes + self.on_size(self, self.size) + +## EOF ## -- cgit v1.2.3 From b243b02de05fc247e554723137911f7d05b00b82 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Wed, 25 Jan 2023 11:10:52 +0100 Subject: more tiles --- tagit/tiles/__init__.py | 12 ++++----- tagit/tiles/buttons.py | 50 ++++++++++++++++++++++++++++++++++++ tagit/tiles/cursor_tags.py | 60 +++++++++++++++++++++++++++++++++++++++++++ tagit/tiles/info.py | 1 - tagit/tiles/selection_tags.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 tagit/tiles/buttons.py create mode 100644 tagit/tiles/cursor_tags.py create mode 100644 tagit/tiles/selection_tags.py (limited to 'tagit/tiles') diff --git a/tagit/tiles/__init__.py b/tagit/tiles/__init__.py index 11f4a82..7817339 100644 --- a/tagit/tiles/__init__.py +++ b/tagit/tiles/__init__.py @@ -13,15 +13,15 @@ from tagit.utils.builder import BuilderBase # inner-module imports ##from .anomalies import Anomalies # FIXME: skeleton only from .browser_tags import BrowserTags -#from .buttons import Buttons -#from .cursor_tags import CursorTags +from .buttons import Buttons +from .cursor_tags import CursorTags #from .entity_histogram import EntityHistogram #from .geo import Map #from .hints import Hints from .info import Info #from .libsummary import LibSummary #from .searchtree import Searchtree -#from .selection_tags import SelectionTags +from .selection_tags import SelectionTags #from .suggested_tags import SuggestedTags #from .tag_distribution import TagDistribution #from .tag_histogram import TagHistogram @@ -41,15 +41,15 @@ class TileBuilder(BuilderBase): _factories = { # #'Anomalies': Anomalies, 'BrowserTags': BrowserTags, -# 'Buttons': Buttons, -# 'CursorTags': CursorTags, + 'Buttons': Buttons, + 'CursorTags': CursorTags, # 'EntityHistogram': EntityHistogram, # 'Geo': Map, # 'Hints': Hints, 'Info': Info, # 'LibSummary': LibSummary, # 'Searchtree': Searchtree, -# 'SelectionTags': SelectionTags, + 'SelectionTags': SelectionTags, # 'SuggestedTags': SuggestedTags, # 'TagDistribution': TagDistribution, # 'TagHistogram': TagHistogram, diff --git a/tagit/tiles/buttons.py b/tagit/tiles/buttons.py new file mode 100644 index 0000000..2a13911 --- /dev/null +++ b/tagit/tiles/buttons.py @@ -0,0 +1,50 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 + +""" +# kivy imports +from kivy.lang import Builder +import kivy.properties as kp + +# inner-module imports +from .tile import Tile + +# exports +__all__ = ('Buttons', ) + +## code ## + +# load kv +# NOTE: ButtonDock doesn't need to be imported... why?! +Builder.load_string(''' +: + title: 'Actions' + tooltip: 'Some buttons' + + # content + slim: False + btns: btns + ButtonDock: + root: root.root + orientation: 'lr-tb' + id: btns + # space between childs + spacing: (5, 0) if root.slim else (30, 5) + # space between ButtonDock and its children + padding: (0, 0) if root.slim else (10, 5) +''') + +# classes +class Buttons(Tile): + """A container for buttons to trigger some action.""" + buttons = kp.ListProperty() + + def update(self): + if self.visible and len(self.btns.children) == 0: + self.btns.clear_widgets() + self.btns.populate(self.buttons) + +## EOF ## diff --git a/tagit/tiles/cursor_tags.py b/tagit/tiles/cursor_tags.py new file mode 100644 index 0000000..2ab2f30 --- /dev/null +++ b/tagit/tiles/cursor_tags.py @@ -0,0 +1,60 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# kivy imports +from kivy.lang import Builder + +# tagit imports +from tagit.widgets.browser import BrowserAwareMixin + +# inner-module imports +from .tile import TileWithLabel + +# exports +__all__ = ('CursorTags', ) + + +## code ## + +# load kv +Builder.load_string(''' +: + title: "Cursor's tags" + tooltip: 'Tags at the cursor' +''') + + +# classes +class CursorTags(TileWithLabel, BrowserAwareMixin): + """Show tags of cursor item.""" + + def on_browser(self, sender, browser): + # remove old binding + if self.browser is not None: + self.browser.unbind(cursor=self.update) + # add new binding + self.browser = browser + if self.browser is not None: + self.browser.bind(cursor=self.update) + self.update() + + def __del__(self): + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser = None + + def update(self, *args): + cursor = self.root.browser.cursor + if not self.visible or cursor is None: + # no cursor, nothing to do + self.text = '' + else: + tags = cursor.tag.label() + tags = {tag.title() for tag in tags} # nice display + tags = sorted(tags) + self.text = ', '.join(tags) + +## EOF ## diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py index 2221b60..725e098 100644 --- a/tagit/tiles/info.py +++ b/tagit/tiles/info.py @@ -65,7 +65,6 @@ class Info(TileTabular, BrowserAwareMixin): ns.bse.filesize, ns.bse.filename, ns.bse.comment, - ns.bse.author, ) self.tabledata = OrderedDict({ 'Date' : ttime.from_timestamp_utc( diff --git a/tagit/tiles/selection_tags.py b/tagit/tiles/selection_tags.py new file mode 100644 index 0000000..7951cfe --- /dev/null +++ b/tagit/tiles/selection_tags.py @@ -0,0 +1,60 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# kivy imports +from kivy.lang import Builder + +# tagit imports +from tagit.widgets.browser import BrowserAwareMixin + +# inner-module imports +from .tile import TileWithLabel + +# exports +__all__ = ('SelectionTags', ) + + +## code ## + +# load kv +Builder.load_string(''' +: + title: "Selection's tags" + default_size: None, 50 +''') + +# classes +class SelectionTags(TileWithLabel, BrowserAwareMixin): + """Show tags of selected items.""" + + def on_browser(self, sender, browser): + # remove old binding + if self.browser is not None: + self.browser.unbind(selection=self.update) + # add new binding + self.browser = browser + if self.browser is not None: + self.browser.bind(selection=self.update) + self.update() + + def __del__(self): + if self.browser is not None: + self.browser.unbind(selection=self.update) + self.browser = None + + def update(self, *args): + browser = self.root.browser + selection = browser.unfold(browser.selection) + if not self.visible or len(selection) == 0: + # nothing selected, nothing to do + self.text = '' + else: + tags = selection.tag.label(node=False) + tags = {tag.title() for tag in tags} # nice display + tags = sorted(tags) + self.text = ', '.join(tags) + +## EOF ## -- cgit v1.2.3 From bfb86bdd23c2fb7211636841545b4e003f07b643 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 29 Jan 2023 12:01:11 +0100 Subject: geo tile --- tagit/tiles/__init__.py | 4 +- tagit/tiles/geo.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 tagit/tiles/geo.py (limited to 'tagit/tiles') diff --git a/tagit/tiles/__init__.py b/tagit/tiles/__init__.py index 7817339..40345b6 100644 --- a/tagit/tiles/__init__.py +++ b/tagit/tiles/__init__.py @@ -16,7 +16,7 @@ from .browser_tags import BrowserTags from .buttons import Buttons from .cursor_tags import CursorTags #from .entity_histogram import EntityHistogram -#from .geo import Map +from .geo import Map #from .hints import Hints from .info import Info #from .libsummary import LibSummary @@ -44,7 +44,7 @@ class TileBuilder(BuilderBase): 'Buttons': Buttons, 'CursorTags': CursorTags, # 'EntityHistogram': EntityHistogram, -# 'Geo': Map, + 'Geo': Map, # 'Hints': Hints, 'Info': Info, # 'LibSummary': LibSummary, diff --git a/tagit/tiles/geo.py b/tagit/tiles/geo.py new file mode 100644 index 0000000..796a1c2 --- /dev/null +++ b/tagit/tiles/geo.py @@ -0,0 +1,140 @@ +""" + +Part of the tagit module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import os + +# kivy ipmorts +from kivy.lang import Builder +from kivy.uix.label import Label +import kivy.properties as kp + +# tagit imports +# NOTE: the following line segfaults: +# mapview.source.py:128:self.dp_tile_size = min(dp(self.tile_size), self.tile_size * 2) +# setting it to a static value (e.g. 256) works. +from tagit.external.kivy_garden.mapview import MapView, MapMarkerPopup +from tagit.utils import ns +from tagit.widgets.browser import BrowserAwareMixin + +# inner-module imports +from .tile import Tile + +# exports +__all__ = ('Map', ) + + +## code ## + +Builder.load_string(''' +: + # meta + title: "Map" + tooltip: 'Location of an item' + # content + map_: map_ + MapView: + id: map_ + zoom: 9 + +: + size: self.texture_size + size_hint: None, None + padding: 5, 5 + font_size: '15sp' + + canvas.before: + # background + Color: + rgba: 0,0,0,0.6 + RoundedRectangle: + size: self.size + pos: self.pos + radius: [10] # keep in sync with the line's radius + # border + Color: + rgba: 0,0,0,1 + Line: + rounded_rectangle: self.x, self.y, self.width, self.height, 10 +''') + +class MapLabel(Label): + pass + +class Map(Tile, BrowserAwareMixin): + """Draw a map which indicates visible items' locations.""" + + # list of map markers + markers = kp.ListProperty() + + def on_browser(self, sender, browser): + """Bind to browser properties.""" + # remove old binding + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser.unbind(items=self.update_markers) + # add new binding + self.browser = browser + if self.browser is not None: + self.browser.bind(cursor=self.update) + self.browser.bind(items=self.update_markers) + # initial calls + self.update_markers() + self.update() + + def __del__(self): + if self.browser is not None: + self.browser.unbind(cursor=self.update) + self.browser.unbind(items=self.update_markers) + self.browser = None + + def update_markers(self, *args): + """Draw markers for all browser items.""" + # remove old markers + for mark in self.markers: + self.map_.remove_marker(mark) + self.markers.clear() + + # get view data + data = self.root.browser.unfold(self.root.browser.items).get( + ns.bse.filename, + ns.bse.latitude, + ns.bse.longitude, + node=True, + ) + + # draw new markers + for ent, vdict in data.items(): + if ns.bse.latitude not in vdict: + continue + if ns.bse.longitude not in vdict: + continue + # TODO: cluster points, one marker for multiple items + lat = vdict[ns.bse.latitude] + lon = vdict[ns.bse.longitude] + # create popup marker + mark = MapMarkerPopup(lat=lat, lon=lon) + text = vdict.get(ns.bse.filename, + ', '.join(os.path.basename(guid) for guid in ent.guids)) + mark.add_widget(MapLabel(text=text)) + # add marker + self.markers.append(mark) + self.map_.add_marker(mark) + + def update(self, *args): + """Focus the map on the cursor.""" + if not self.visible: + return + + cursor = self.root.browser.cursor + if cursor is not None: + coords = cursor.get(ns.bse.latitude, ns.bse.longitude) + if set(coords.keys()) == {ns.bse.latitude, ns.bse.longitude}: + lat = coords[ns.bse.latitude] + lon = coords[ns.bse.longitude] + self.map_.center_on(lat, lon) + +## EOF ## -- cgit v1.2.3 From 57327d3df562736cad9e278e13beeb55bf3b52ed Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 29 Jan 2023 12:23:15 +0100 Subject: cleanup --- tagit/tiles/__init__.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'tagit/tiles') diff --git a/tagit/tiles/__init__.py b/tagit/tiles/__init__.py index 40345b6..f51ee2a 100644 --- a/tagit/tiles/__init__.py +++ b/tagit/tiles/__init__.py @@ -11,13 +11,11 @@ import typing from tagit.utils.builder import BuilderBase # inner-module imports -##from .anomalies import Anomalies # FIXME: skeleton only from .browser_tags import BrowserTags from .buttons import Buttons from .cursor_tags import CursorTags #from .entity_histogram import EntityHistogram from .geo import Map -#from .hints import Hints from .info import Info #from .libsummary import LibSummary #from .searchtree import Searchtree @@ -39,13 +37,11 @@ __all__: typing.Sequence[str] = ( class TileBuilder(BuilderBase): _factories = { -# #'Anomalies': Anomalies, 'BrowserTags': BrowserTags, 'Buttons': Buttons, 'CursorTags': CursorTags, # 'EntityHistogram': EntityHistogram, 'Geo': Map, -# 'Hints': Hints, 'Info': Info, # 'LibSummary': LibSummary, # 'Searchtree': Searchtree, -- cgit v1.2.3 From 580caf6f5c9b795f9c38b9c970efce12d006ce1d Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Fri, 17 Feb 2023 08:25:44 +0100 Subject: New UI design * Moved style definitions to its own file (themes/default) * Updated the desktop.kv to the new UI design * Removed planes * Adjusted port config --- tagit/tiles/decoration.kv | 29 +++++++++++++++++++++++++++++ tagit/tiles/decoration.py | 9 +++++++++ 2 files changed, 38 insertions(+) (limited to 'tagit/tiles') diff --git a/tagit/tiles/decoration.kv b/tagit/tiles/decoration.kv index a53d013..d4b37a2 100644 --- a/tagit/tiles/decoration.kv +++ b/tagit/tiles/decoration.kv @@ -55,4 +55,33 @@ size: self.size +: + cbox: cbox + + Label: + text: root.client.title + size: root.width, 20 + size_hint: None, None + pos: 0, root.height - self.height - 5 + + RelativeLayout: + id: cbox + pos: 5, 5 + size: root.width-10, root.height-30 + size_hint: None, None + + canvas.before: + Color: + rgb: 0xc5/256, 0xc9/256, 0xc7/256 + RoundedRectangle: + pos: self.pos + #radius: [10, 10, 10, 10] + size: self.size + Color: + rgb: 0x1c/256, 0x1b/256, 0x22/256 # FIXME: re-use from MainWindow? + RoundedRectangle: + pos: self.pos[0] + 2, self.pos[1] + 2 + radius: [5, 5, 5, 5] + size: self.size[0] - 4, self.size[1] - 4 + ## EOF ## diff --git a/tagit/tiles/decoration.py b/tagit/tiles/decoration.py index 471058d..c772f64 100644 --- a/tagit/tiles/decoration.py +++ b/tagit/tiles/decoration.py @@ -16,6 +16,7 @@ import kivy.properties as kp # exports __all__: typing.Sequence[str] = ( 'TileDecorationBorder', + 'TileDecorationRoundedBorder', 'TileDecorationFilledRectangle', 'TileDecorationVanilla', ) @@ -65,4 +66,12 @@ class TileDecorationBorder(TileDecoration): height = None if height is None else height + 30 return width, height +class TileDecorationRoundedBorder(TileDecoration): + @property + def default_size(self): + width, height = self.client.default_size + width = None if width is None else width + 30 + height = None if height is None else height + 30 + return width, height + ## EOF ## -- cgit v1.2.3 From 6889cff096d30e76cc5379ee68da42b555895eed Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Fri, 17 Feb 2023 08:32:27 +0100 Subject: info tile update and port data loading --- tagit/tiles/info.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tagit/tiles') diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py index 725e098..a5eacdf 100644 --- a/tagit/tiles/info.py +++ b/tagit/tiles/info.py @@ -11,7 +11,7 @@ from collections import OrderedDict from kivy.lang import Builder # tagit imports -from tagit.utils import ttime, ns +from tagit.utils import ttime, ns, magnitude_fmt from tagit.widgets.browser import BrowserAwareMixin # inner-module imports @@ -30,7 +30,7 @@ Builder.load_string(''' tooltip: 'Key properties of the cursor item' # assuming 7 info items default_size: None, 7*self.font_size + 6*5 - keywidth: min(65, self.width * 0.4) + keywidth: min(75, self.width * 0.4) ''') @@ -64,16 +64,16 @@ class Info(TileTabular, BrowserAwareMixin): ns.bsm.t_created, ns.bse.filesize, ns.bse.filename, - ns.bse.comment, + (ns.bse.tag, ns.bst.label), ) self.tabledata = OrderedDict({ 'Date' : ttime.from_timestamp_utc( preds.get(ns.bsm.t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), #'Size' : f'{preds.get(ns.bse.width, "?")} x {preds.get(ns.bse.height, "?")}', #'ISO' : preds.get(ns.bse.iso, '?'), - 'Filesize' : preds.get(ns.bse.filesize, 'n/a'), + 'Filesize' : magnitude_fmt(preds.get(ns.bse.filesize, 0)), 'Filename' : preds.get(ns.bse.filename, 'n/a'), - 'Comment' : '; '.join(preds.get(ns.bse.comment, [])), + 'Tags' : ', '.join(sorted(preds.get((ns.bse.tag, ns.bst.label), []))), #'Exposure' : f'1 / {preds.get(ns.bse.exposure, 0):0.2f}', #'Aperture' : preds.get(ns.bse.aperture, '?'), #'Fx' : preds.get(ns.bse.focal_length_35, '?'), -- cgit v1.2.3 From 141cfeade2750e0255ca010079421efce4abeca2 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 4 Mar 2023 14:16:00 +0100 Subject: namespace updates --- tagit/tiles/info.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tagit/tiles') diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py index a5eacdf..8edc9bb 100644 --- a/tagit/tiles/info.py +++ b/tagit/tiles/info.py @@ -61,14 +61,14 @@ class Info(TileTabular, BrowserAwareMixin): else: preds = cursor.get( - ns.bsm.t_created, + ns.bsfs.Node().t_created, ns.bse.filesize, ns.bse.filename, (ns.bse.tag, ns.bst.label), ) self.tabledata = OrderedDict({ 'Date' : ttime.from_timestamp_utc( - preds.get(ns.bsm.t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), + preds.get(ns.bsfs.Node().t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), #'Size' : f'{preds.get(ns.bse.width, "?")} x {preds.get(ns.bse.height, "?")}', #'ISO' : preds.get(ns.bse.iso, '?'), 'Filesize' : magnitude_fmt(preds.get(ns.bse.filesize, 0)), -- cgit v1.2.3 From ceb95600c5d3cff789d466706bcdec7143c11bb3 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sat, 4 Mar 2023 14:19:06 +0100 Subject: info tile refactoring --- tagit/tiles/decoration.kv | 4 ++-- tagit/tiles/info.py | 20 +++++++++------- tagit/tiles/tile.kv | 26 ++++++++++++++------ tagit/tiles/tile.py | 61 ++++++++++++++++++++++------------------------- 4 files changed, 61 insertions(+), 50 deletions(-) (limited to 'tagit/tiles') diff --git a/tagit/tiles/decoration.kv b/tagit/tiles/decoration.kv index d4b37a2..ae7e49d 100644 --- a/tagit/tiles/decoration.kv +++ b/tagit/tiles/decoration.kv @@ -62,11 +62,11 @@ text: root.client.title size: root.width, 20 size_hint: None, None - pos: 0, root.height - self.height - 5 + pos: 0, root.height - self.height - 3 RelativeLayout: id: cbox - pos: 5, 5 + pos: 5, 3 size: root.width-10, root.height-30 size_hint: None, None diff --git a/tagit/tiles/info.py b/tagit/tiles/info.py index 8edc9bb..9555b35 100644 --- a/tagit/tiles/info.py +++ b/tagit/tiles/info.py @@ -13,6 +13,7 @@ from kivy.lang import Builder # tagit imports from tagit.utils import ttime, ns, magnitude_fmt from tagit.widgets.browser import BrowserAwareMixin +from tagit.widgets.session import StorageAwareMixin # inner-module imports from .tile import TileTabular @@ -29,15 +30,19 @@ Builder.load_string(''' title: 'Item info' tooltip: 'Key properties of the cursor item' # assuming 7 info items - default_size: None, 7*self.font_size + 6*5 + #default_size: None, 7*self.font_size + 6*5 keywidth: min(75, self.width * 0.4) ''') # classes -class Info(TileTabular, BrowserAwareMixin): +class Info(TileTabular, BrowserAwareMixin, StorageAwareMixin): """Show essential attributes about the cursor.""" + def on_root(self, wx, root): + BrowserAwareMixin.on_root(self, wx, root) + StorageAwareMixin.on_root(self, wx, root) + def on_browser(self, sender, browser): # remove old binding if self.browser is not None: @@ -53,6 +58,9 @@ class Info(TileTabular, BrowserAwareMixin): self.browser.unbind(cursor=self.update) self.browser = None + def on_predicate_modified(self, *args): + self.update() + def update(self, *args): cursor = self.root.browser.cursor if not self.visible or cursor is None: @@ -69,15 +77,9 @@ class Info(TileTabular, BrowserAwareMixin): self.tabledata = OrderedDict({ 'Date' : ttime.from_timestamp_utc( preds.get(ns.bsfs.Node().t_created, ttime.timestamp_min)).strftime('%d.%m.%y %H:%M'), - #'Size' : f'{preds.get(ns.bse.width, "?")} x {preds.get(ns.bse.height, "?")}', - #'ISO' : preds.get(ns.bse.iso, '?'), 'Filesize' : magnitude_fmt(preds.get(ns.bse.filesize, 0)), 'Filename' : preds.get(ns.bse.filename, 'n/a'), - 'Tags' : ', '.join(sorted(preds.get((ns.bse.tag, ns.bst.label), []))), - #'Exposure' : f'1 / {preds.get(ns.bse.exposure, 0):0.2f}', - #'Aperture' : preds.get(ns.bse.aperture, '?'), - #'Fx' : preds.get(ns.bse.focal_length_35, '?'), - #'Flash' : 'Yes' if preds.get(ns.bse.flash, False) else 'No', + 'Tags' : ', '.join(sorted(preds.get((ns.bse.tag, ns.bst.label), [' ']))), }) ## EOF ## diff --git a/tagit/tiles/tile.kv b/tagit/tiles/tile.kv index 277b23d..fcd9821 100644 --- a/tagit/tiles/tile.kv +++ b/tagit/tiles/tile.kv @@ -7,16 +7,28 @@ : # config font_size: sp(15) - spacer: 10 keywidth: 0.5 # table - grid: grid - GridLayout: - id: grid - cols: 2 - size: root.size - size_hint: None, None + rows: rows + TileTabularLine: + id: rows + orientation: 'tb-lr' + size_hint: 1, 1 + +: + spacing: 10 + +: + orientation: 'horizontal' + size_hint: 1, None + height: self.minimum_height + spacing: 10 + +: + valign: 'top' + height: self.texture_size[1] + text_size: self.width, None : text: '' diff --git a/tagit/tiles/tile.py b/tagit/tiles/tile.py index 7ea690a..981d45b 100644 --- a/tagit/tiles/tile.py +++ b/tagit/tiles/tile.py @@ -9,6 +9,7 @@ import os # kivy imports from kivy.lang import Builder +from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.relativelayout import RelativeLayout import kivy.properties as kp @@ -38,51 +39,47 @@ class Tile(RelativeLayout): class TileWithLabel(Tile): pass +class TileTabularRow(BoxLayout): + pass + +class TileTabularCell(Label): + pass class TileTabular(Tile): tabledata = kp.ObjectProperty() - spacer = kp.NumericProperty(10) keywidth = kp.NumericProperty(0.5) - def on_size(self, wx, size): - if not self.visible or len(self.grid.children) == 0: - return - - height = self.height / (len(self.grid.children) / 2) - kwidth = self.width * self.keywidth if self.keywidth < 1 else self.keywidth - vwidth = self.width - kwidth - self.spacer - - # adjust size - for idx, child in enumerate(self.grid.children): - if idx % 2 == 1: # key - child.text_size = kwidth, height - child.width = kwidth - else: # label - child.text_size = vwidth, height - def on_tabledata(self, wx, data): # set items - self.grid.clear_widgets() - for key, value in data.items(): + self.rows.clear_widgets() + for t_key, t_value in data.items(): + # row + row = TileTabularRow() # left column (keys) - self.grid.add_widget(Label( - text=key, + key = TileTabularCell( + text=t_key, halign='right', - valign='top', font_size = self.font_size, - size_hint = (None, 1), - )) - + size_hint=(None, 1), + width=self.width * self.keywidth if self.keywidth < 1 else self.keywidth, + ) # right column (values) - self.grid.add_widget(Label( - text=str(value), + value = TileTabularCell( + text=str(t_value), halign='left', - valign='top', font_size = self.font_size, - )) - - # set sizes - self.on_size(self, self.size) + size_hint=(1, None), + ) + # adjust key's width and height dynamically. + # value's width and height are adjusted automatically + self.bind(width=lambda wx, width, key=key: setattr(key, 'width', + width * self.keywidth if self.keywidth < 1 else self.keywidth)) + key.bind(height=lambda wx, height, key=key: setattr(key, 'text_size', + (key.width, height))) + # add widgets + row.add_widget(key) + row.add_widget(value) + self.rows.add_widget(row) ## EOF ## -- cgit v1.2.3