aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--tagit/actions/__init__.py8
-rw-r--r--tagit/actions/session.kv7
-rw-r--r--tagit/actions/session.py56
-rw-r--r--tagit/apps/port-config.yaml4
-rw-r--r--tagit/assets/icons/scalable/session/open.svg180
-rw-r--r--tagit/dialogues/__init__.py8
-rw-r--r--tagit/dialogues/file_picker.py39
-rw-r--r--tagit/dialogues/path_picker.kv27
-rw-r--r--tagit/dialogues/path_picker.py41
-rw-r--r--tagit/widgets/session.py21
11 files changed, 374 insertions, 18 deletions
diff --git a/.gitignore b/.gitignore
index 084e82d..ba73cdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@ tagit/assets/icons/kivy/grouping*
tagit/assets/icons/kivy/misc*
tagit/assets/icons/kivy/planes*
tagit/assets/icons/kivy/search*
+tagit/assets/icons/kivy/session*
tagit/assets/icons/kivy/tagging*
## EOF ##
diff --git a/tagit/actions/__init__.py b/tagit/actions/__init__.py
index 7144e44..fa2bed0 100644
--- a/tagit/actions/__init__.py
+++ b/tagit/actions/__init__.py
@@ -18,7 +18,7 @@ from . import misc
#from . import objects
from . import planes
from . import search
-#from . import session
+from . import session
from . import tagging
# exports
@@ -104,11 +104,7 @@ class ActionBuilder(BuilderBase):
#'SortKey': search.SortKey,
'SortOrder': search.SortOrder,
## session
- #'LoadSession': session.LoadSession,
- #'CreateSession': session.CreateSession,
- #'CreateTempSession': session.CreateTempSession,
- #'ReloadSession': session.ReloadSession,
- #'CloseSessionAndExit': session.CloseSessionAndExit,
+ 'LoadSession': session.LoadSession,
}
## EOF ##
diff --git a/tagit/actions/session.kv b/tagit/actions/session.kv
new file mode 100644
index 0000000..21807b2
--- /dev/null
+++ b/tagit/actions/session.kv
@@ -0,0 +1,7 @@
+#:import resource_find kivy.resources.resource_find
+
+<LoadSession>:
+ source: resource_find('atlas://session/open')
+ tooltip: 'Load a session'
+
+## EOF ##
diff --git a/tagit/actions/session.py b/tagit/actions/session.py
new file mode 100644
index 0000000..3c5ad39
--- /dev/null
+++ b/tagit/actions/session.py
@@ -0,0 +1,56 @@
+"""
+
+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
+import kivy.properties as kp
+
+# tagit imports
+from tagit import config, dialogues
+from tagit.config.loader import load_settings
+
+# inner-module imports
+from .action import Action
+
+# exports
+__all__ = []
+
+
+## code ##
+
+# load kv
+Builder.load_file(os.path.join(os.path.dirname(__file__), 'session.kv'))
+
+# classes
+class LoadSession(Action):
+ """Load a session from a project file."""
+ text = kp.StringProperty('Load')
+
+ def apply(self):
+ """Open a file load dialogue to select a session file."""
+ dlg = dialogues.FilePicker(title='Select a session file to load')
+ dlg.bind(on_ok=self.load_from_path)
+ dlg.open()
+
+ def load_from_path(self, wx):
+ """Load a session from *path*."""
+ with self.root.session as session:
+ try:
+ if not os.path.exists(wx.path) or not os.path.isfile(wx.path):
+ raise FileNotFoundError(wx.path)
+
+ # load config from path
+ cfg = load_settings(wx.path, verbose=self.cfg('session', 'verbose'))
+ session.load(cfg)
+
+ except Exception as e:
+ dialogues.Error(text=f'The file cannot be loaded ({e})').open()
+
+
+## EOF ##
diff --git a/tagit/apps/port-config.yaml b/tagit/apps/port-config.yaml
index 6b0b06d..c4d47dd 100644
--- a/tagit/apps/port-config.yaml
+++ b/tagit/apps/port-config.yaml
@@ -87,8 +87,8 @@ ui:
- AddToGroup
# - RepresentGroup
# - RemoveFromGroup
- # root:
- # - CloseSessionAndExit
+ root:
+ - LoadSession
# search:
# - ShowSelected
# - RemoveSelected
diff --git a/tagit/assets/icons/scalable/session/open.svg b/tagit/assets/icons/scalable/session/open.svg
new file mode 100644
index 0000000..0b2d29c
--- /dev/null
+++ b/tagit/assets/icons/scalable/session/open.svg
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="open.svg"
+ inkscape:export-filename="../../kivy/session/open.png"
+ inkscape:export-xdpi="7.6199999"
+ inkscape:export-ydpi="7.6199999">
+ <defs
+ id="defs4">
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath868">
+ <rect
+ style="opacity:1;fill:#c8c8c8;fill-opacity:1;stroke:none;stroke-width:40;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ id="rect870"
+ width="422.74884"
+ height="230.81985"
+ x="144.88318"
+ y="556.44757" />
+ </clipPath>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="126.58161"
+ inkscape:cy="190.628"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-global="true"
+ inkscape:snap-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1031"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ units="mm"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:snap-page="true"
+ inkscape:lockguides="false">
+ <sodipodi:guide
+ orientation="0,1"
+ position="13.637059,643.40404"
+ id="guide3788"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="0,1"
+ id="guide1099"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="188.97638,188.97638"
+ orientation="1,0"
+ id="guide1101"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="233.588,370"
+ orientation="1,0"
+ id="guide1107"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="144.36496,311.42857"
+ orientation="1,0"
+ id="guide1109"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="-77.142857,144.36496"
+ orientation="0,1"
+ id="guide1111"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ <sodipodi:guide
+ position="5.000315,233.58779"
+ orientation="0,1"
+ id="guide1113"
+ inkscape:locked="false"
+ inkscape:label=""
+ inkscape:color="rgb(0,0,255)" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-167.28122,-322.85977)">
+ <rect
+ style="fill:none;stroke:#c8c8c8;stroke-width:72.52050018;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect2985"
+ width="299.20886"
+ height="197.09306"
+ x="206.65317"
+ y="470.40216"
+ clip-path="url(#clipPath868)"
+ transform="matrix(1.0167417,0,0,0.98002182,-5.9643702,11.116803)" />
+ <g
+ id="g840"
+ transform="matrix(0.51843117,0,0,0.4508665,223.54494,300.20217)">
+ <rect
+ y="292.88489"
+ x="155.32295"
+ height="368.57141"
+ width="202.85715"
+ id="rect4389"
+ style="fill:#c8c8c8;fill-opacity:1;stroke:none" />
+ <path
+ transform="matrix(0.57204501,-0.03460321,0.03460321,0.57204501,160.61503,278.02467)"
+ inkscape:transform-center-y="-50.478468"
+ inkscape:transform-center-x="-0.051265028"
+ d="M 460.00001,209.50504 155.15754,191.22021 -149.68493,172.93537 18.571437,-81.923531 186.8278,-336.78244 323.4139,-63.638699 Z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="false"
+ sodipodi:arg2="1.6307058"
+ sodipodi:arg1="0.58350825"
+ sodipodi:r2="176.3172"
+ sodipodi:r1="352.6344"
+ sodipodi:cy="15.219325"
+ sodipodi:cx="165.71429"
+ sodipodi:sides="3"
+ id="path4391"
+ style="fill:#c8c8c8;fill-opacity:1;stroke:#c8c8c8;stroke-width:100;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="star" />
+ </g>
+ </g>
+</svg>
diff --git a/tagit/dialogues/__init__.py b/tagit/dialogues/__init__.py
index a467699..3647bf0 100644
--- a/tagit/dialogues/__init__.py
+++ b/tagit/dialogues/__init__.py
@@ -23,11 +23,11 @@ from .console import Console
#from .dir_picker import DirPicker
from .error import Error
#from .file_creator import FileCreator
-#from .file_picker import FilePicker
+from .file_picker import FilePicker
from .message import Message
from .numeric_input import NumericInput
#from .path_creator import PathCreator
-#from .path_picker import PathPicker
+from .path_picker import PathPicker
#from .project import Project
from .simple_input import SimpleInput
from .stoken import TokenEdit
@@ -40,11 +40,11 @@ __all__: typing.Sequence[str] = (
#'DirPicker',
'Error',
#'FileCreator',
- #'FilePicker',
+ 'FilePicker',
'Message',
'NumericInput',
#'PathCreator',
- #'PathPicker',
+ 'PathPicker',
#'Project',
'SimpleInput',
'TokenEdit',
diff --git a/tagit/dialogues/file_picker.py b/tagit/dialogues/file_picker.py
new file mode 100644
index 0000000..283adb6
--- /dev/null
+++ b/tagit/dialogues/file_picker.py
@@ -0,0 +1,39 @@
+"""Dialogue to pick a file.
+
+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
+
+# inner-module imports
+from .path_picker import PathPicker
+from .error import Error
+
+# exports
+__all__ = ('FilePicker', )
+
+
+## code ##
+
+# load kv
+Builder.load_string('''
+<FilePicker>:
+ dirselect: False
+ title: 'Please select a file'
+''')
+
+# classes
+class FilePicker(PathPicker):
+ """Dialogue with a file browser to select a file."""
+ def ok(self):
+ if not os.path.exists(self.path) or not os.path.isfile(self.path):
+ Error(text='Please select a file').open()
+ else:
+ super(FilePicker, self).ok()
+
+## EOF ##
diff --git a/tagit/dialogues/path_picker.kv b/tagit/dialogues/path_picker.kv
new file mode 100644
index 0000000..1837b80
--- /dev/null
+++ b/tagit/dialogues/path_picker.kv
@@ -0,0 +1,27 @@
+#:import join os.path.join
+#:import pwd os.path.curdir
+
+<PathPicker>:
+ path: ''
+ title: 'Please select a file or directory'
+ filters: []
+ dirselect: True
+
+ DialogueContentTitle:
+ title: root.title
+ size_hint_y: 0.8
+
+ FileChooserListView:
+ text_size: self.width - dp(16), None
+ halign: 'center'
+ dirselect: root.dirselect
+ path: pwd
+ filters: root.filters
+
+ on_selection: root.path = join(self.path, self.selection[0]); buttons.ok_enabled = True
+
+ DialogueButtons_Two:
+ id: buttons
+ ok_enabled: False
+
+## EOF ##
diff --git a/tagit/dialogues/path_picker.py b/tagit/dialogues/path_picker.py
new file mode 100644
index 0000000..25bbf32
--- /dev/null
+++ b/tagit/dialogues/path_picker.py
@@ -0,0 +1,41 @@
+"""Dialogue to pick a file or directory.
+
+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
+import kivy.properties as kp
+
+# inner-module imports
+from .dialogue import Dialogue
+from .error import Error
+
+# exports
+__all__ = ('PathPicker', )
+
+
+## code ##
+
+# load kv
+Builder.load_file(os.path.join(os.path.dirname(__file__), 'path_picker.kv'))
+
+# classes
+class PathPicker(Dialogue):
+ """Dialogue with a file browser to select a file or directory."""
+
+ title = kp.StringProperty('')
+ path = kp.StringProperty('')
+ filters = kp.ListProperty()
+
+ def ok(self):
+ if not os.path.exists(self.path):
+ Error(text='Please select a file or directory').open()
+ else:
+ super(PathPicker, self).ok()
+
+## EOF ##
diff --git a/tagit/widgets/session.py b/tagit/widgets/session.py
index f45ab35..e97a688 100644
--- a/tagit/widgets/session.py
+++ b/tagit/widgets/session.py
@@ -5,7 +5,7 @@ A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# standard imports
-from threading import current_thread
+import os
import typing
# kivy imports
@@ -16,8 +16,7 @@ import kivy.properties as kp
# tagit imports
from tagit import parsing
from tagit.config.loader import load_settings
-#from tagit.storage.broker import Broker # FIXME: mb/port
-#from tagit.storage.loader import load_broker, load_log # FIXME: mb/port
+from tagit.utils import bsfs
# exports
__all__: typing.Sequence[str] = (
@@ -63,10 +62,20 @@ class Session(Widget):
def load(self, cfg):
"""Load the session from configuration *cfg*."""
- self.cfg = cfg
+ #self.log = load_log(cfg) # FIXME: mb/port
# initialize storages from config
- self.log = load_log(cfg)
- self.storage = load_broker(cfg)
+ # open BSFS storage
+ store = bsfs.Open(cfg('session', 'bsfs'))
+ # check storage schema
+ # FIXME: how to properly load the required schema?
+ with open(os.path.join(os.path.dirname(__file__), '..', 'apps', 'port-schema.nt'), 'rt') as ifile:
+ required_schema = bsfs.schema.from_string(ifile.read())
+ # FIXME: Since the store isn't persistent, we migrate to the required one here.
+ #if not required_schema <= store.schema:
+ # raise Exception('')
+ store.migrate(required_schema)
+ # replace current with new storage
+ self.storage = store
def update_settings_key(self, key, value):
# change setting