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/apps/port-config.yaml | 5 +- tagit/apps/port-schema.nt | 5 -- 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 ++++++++++++++++++++++++++++++++++++ tagit/widgets/browser.py | 2 +- 8 files changed, 321 insertions(+), 12 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 diff --git a/tagit/apps/port-config.yaml b/tagit/apps/port-config.yaml index 35f524a..7335c16 100644 --- a/tagit/apps/port-config.yaml +++ b/tagit/apps/port-config.yaml @@ -129,8 +129,9 @@ ui: #Searchtree: {} #TagHistogram: {} #Tagcloud: {} - sidebar_right: {} + sidebar_right: + Info: {} #CursorTags: {} - #Info: {} + BrowserTags: {} #Venn: {} window_size: 1024x768 diff --git a/tagit/apps/port-schema.nt b/tagit/apps/port-schema.nt index b18a6ad..7317496 100644 --- a/tagit/apps/port-schema.nt +++ b/tagit/apps/port-schema.nt @@ -55,11 +55,6 @@ bst:label rdfs:subClassOf bsfs:Predicate ; rdfs:range xsd:string ; bsfs:unique "true"^^xsd:boolean . -bse:author rdfs:subClassOf bsfs:Predicate ; - rdfs:domain bsfs:Entity ; - rdfs:range xsd:string ; - bsfs:unique "true"^^xsd:boolean . - bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range xsd:string . 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 ## diff --git a/tagit/widgets/browser.py b/tagit/widgets/browser.py index 1dfc528..f778181 100644 --- a/tagit/widgets/browser.py +++ b/tagit/widgets/browser.py @@ -217,7 +217,7 @@ class Browser(GridLayout, StorageAwareMixin, ConfigAwareMixin): unfolded |= self.folds[itm].shadow else: unfolded |= {itm} - return reduce(operator.add, unfolded) + return reduce(operator.add, unfolded) # FIXME: What if items is empty? def neighboring_unselected(self): """Return the item closest to the cursor and not being selected. May return None.""" -- cgit v1.2.3