1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
"""Main container of the tagit UI.
Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
# standard imports
import logging
import os
import typing
# kivy imports
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
import kivy.properties as kp
# import Image and Loader to overwrite their caches later on
from kivy.cache import Cache
from kivy.loader import Loader
from kivy.resources import resource_find
# tagit imports
from tagit import actions, config, dialogues
from tagit.widgets.browser import Browser
from tagit.widgets.context import Context
from tagit.widgets.dock import TileDock, ButtonDock, KeybindDock
from tagit.widgets.filter import Filter
from tagit.widgets.keyboard import Keyboard
from tagit.widgets.session import Session
from tagit.widgets.status import Status
# exports
__all__: typing.Sequence[str] = (
'KIVY_IMAGE_CACHE_SIZE',
'KIVY_IMAGE_CACHE_TIMEOUT',
'MainWindow',
)
## code ##
logger = logging.getLogger(__name__)
# load kv
Builder.load_file(os.path.join(os.path.dirname(__file__), 'desktop.kv'))
# load styles
Builder.load_file(resource_find('default/style.kv'))
# classes
class MainWindow(FloatLayout):
"""A self-contained user interface for desktop usage.
See `tagit.apps.gui` for an example of how to invoke it.
"""
keys = kp.ObjectProperty(None)
# unnecessary but nicely explicit
browser = kp.ObjectProperty(None)
filter = kp.ObjectProperty(None)
keytriggers = kp.ObjectProperty(None)
# FIXME: log actions and and replay them
action_log = kp.ListProperty()
def __init__ (self, cfg, stor, log, **kwargs):
# initialize the session
self._session = Session(cfg, stor, log)
# initialize key-only actions
self.keys = Keyboard()
# initialize the cache
cache_size = max(0, cfg('ui', 'standalone', 'browser', 'cache_size'))
cache_size = cache_size if cache_size > 0 else None
cache_timeout = max(0, cfg('ui', 'standalone', 'browser', 'cache_timeout'))
cache_timeout = cache_timeout if cache_timeout > 0 else None
Cache.register('kv.loader', limit=cache_size, timeout=cache_timeout)
# initialize the widget
super(MainWindow, self).__init__(**kwargs)
# bind pre-close checks
from kivy.core.window import Window
Window.bind(on_request_close=self.on_request_close)
Window.size = tuple(cfg('ui', 'standalone', 'window_size'))
if cfg('ui', 'standalone', 'maximize'):
Window.maximize()
## properties
@property
def session(self):
return self._session
def trigger(self, action, *args, **kwargs):
"""Trigger an action once."""
actions.ActionBuilder().get(action).single_shot(self, *args, **kwargs)
## startup and shutdown
def on_startup(self):
# run script
for args in self.session.cfg('session', 'script'):
if isinstance(args, str):
cmd, args = args, []
else:
cmd = args.pop(0)
Clock.schedule_once(
lambda dt, cmd=cmd, args=args: self.trigger(cmd, *args),
self.session.cfg('session', 'script_delay'))
# FIXME: mb/port: debugging only
#return
#Clock.schedule_once(lambda dt: self.trigger('Search'), 0)
#Clock.schedule_once(lambda dt: self.trigger('ZoomOut'), 0)
#Clock.schedule_once(lambda dt: self.trigger('ZoomOut'), 0)
#from kivy.app import App
#App.get_running_app().stop()
def on_request_close(self, *args):
#with open('.action_history', 'a') as ofile:
# for itm in self.action_log:
# ofile.write(f'{itm}\n')
#App.get_running_app().stop() # FIXME: mb/port: from CloseSessionAndExit
return False
## config ##
config.declare(('session', 'script'), config.List(config.Any()), [],
__name__, 'start script', 'Actions to run after startup. Intended for testing.')
config.declare(('session', 'script_delay'), config.Unsigned(), 0,
__name__, 'script delay', 'Start script execution delay in seconds.')
config.declare(('ui', 'standalone', 'maximize'), config.Bool(), False,
__name__, 'Window maximization', 'Maximize the window upon startup.')
config.declare(('ui', 'standalone', 'window_size'), config.List(config.Unsigned()), (1024, 768),
__name__, 'Wndow size', 'Set the window size upon startup.')
config.declare(('ui', 'standalone', 'browser', 'cache_size'), config.Unsigned(), 1000,
__name__, 'Cache size', 'Number of preview images that are held in the cache. Should be high or zero if memory is not an issue. Set to a small value to preserve memory, but should be at least the most common page size. It is advised to set a value in accordance with `ui.standalone.browser.cache_items`. If zero, no limit applies.')
config.declare(('ui', 'standalone', 'browser', 'cache_timeout'), config.Unsigned(), 0,
__name__, 'Cache timeout', 'Number of seconds until cached items are discarded. Should be high or zero if memory is not an issue. Set it to a small value to preserve memory when browsing through many images. If zero, no limit applies. Specify in seconds.')
## EOF ##
|