pyqttoolkit 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyqttoolkit/__init__.py +20 -0
- pyqttoolkit/_version.py +21 -0
- pyqttoolkit/application/__init__.py +18 -0
- pyqttoolkit/application/application.py +86 -0
- pyqttoolkit/application/garbage_collector.py +65 -0
- pyqttoolkit/colors/__init__.py +84 -0
- pyqttoolkit/config/__init__.py +20 -0
- pyqttoolkit/config/base.py +22 -0
- pyqttoolkit/config/composite.py +31 -0
- pyqttoolkit/config/default.py +27 -0
- pyqttoolkit/config/environment.py +35 -0
- pyqttoolkit/config/multi_json_file.py +93 -0
- pyqttoolkit/config/with_signal.py +30 -0
- pyqttoolkit/config/with_value_changed.py +31 -0
- pyqttoolkit/data/__init__.py +21 -0
- pyqttoolkit/data/bits.py +23 -0
- pyqttoolkit/data/cache.py +64 -0
- pyqttoolkit/data/interval.py +26 -0
- pyqttoolkit/data/message.py +29 -0
- pyqttoolkit/data/names.py +24 -0
- pyqttoolkit/datetime/__init__.py +16 -0
- pyqttoolkit/datetime/datetime.py +51 -0
- pyqttoolkit/datetime/qdatetime.py +53 -0
- pyqttoolkit/dependencies/__init__.py +56 -0
- pyqttoolkit/errors/__init__.py +19 -0
- pyqttoolkit/exceptions/__init__.py +18 -0
- pyqttoolkit/filesystem/__init__.py +24 -0
- pyqttoolkit/logs/__init__.py +140 -0
- pyqttoolkit/metadata/__init__.py +23 -0
- pyqttoolkit/metadata/dataframe_metadata_manager.py +105 -0
- pyqttoolkit/metadata/dict_metadata_manager.py +63 -0
- pyqttoolkit/metadata/id_generator.py +20 -0
- pyqttoolkit/metadata/metadata_manager_base.py +54 -0
- pyqttoolkit/models/__init__.py +26 -0
- pyqttoolkit/models/code_text.py +43 -0
- pyqttoolkit/models/dataframetable.py +135 -0
- pyqttoolkit/models/dictpropertytree.py +155 -0
- pyqttoolkit/models/dicttable.py +140 -0
- pyqttoolkit/models/json_text.py +52 -0
- pyqttoolkit/models/list_model.py +52 -0
- pyqttoolkit/models/module.py +26 -0
- pyqttoolkit/models/roles.py +23 -0
- pyqttoolkit/models/series_data.py +68 -0
- pyqttoolkit/models/span.py +36 -0
- pyqttoolkit/models/validation.py +27 -0
- pyqttoolkit/modules/__init__.py +18 -0
- pyqttoolkit/modules/events/__init__.py +19 -0
- pyqttoolkit/modules/events/add_signal.py +31 -0
- pyqttoolkit/modules/events/module_opening.py +21 -0
- pyqttoolkit/modules/events/rejectable.py +29 -0
- pyqttoolkit/modules/launcher_config.py +55 -0
- pyqttoolkit/modules/module.py +111 -0
- pyqttoolkit/properties/__init__.py +18 -0
- pyqttoolkit/properties/auto.py +221 -0
- pyqttoolkit/properties/throttled.py +77 -0
- pyqttoolkit/scripting/__init__.py +19 -0
- pyqttoolkit/scripting/script_context.py +36 -0
- pyqttoolkit/scripting/script_runner.py +132 -0
- pyqttoolkit/scripting/script_template.py +42 -0
- pyqttoolkit/services/__init__.py +32 -0
- pyqttoolkit/services/autosave.py +41 -0
- pyqttoolkit/services/email.py +25 -0
- pyqttoolkit/services/event_registry.py +52 -0
- pyqttoolkit/services/file_dialog.py +71 -0
- pyqttoolkit/services/hotkey_manager.py +137 -0
- pyqttoolkit/services/link_manager.py +135 -0
- pyqttoolkit/services/message_board.py +90 -0
- pyqttoolkit/services/module_service.py +154 -0
- pyqttoolkit/services/object_converter.py +45 -0
- pyqttoolkit/services/project_updater.py +149 -0
- pyqttoolkit/services/task_runner.py +281 -0
- pyqttoolkit/services/theme_manager.py +46 -0
- pyqttoolkit/services/tool_window_service.py +46 -0
- pyqttoolkit/viewmanagers/__init__.py +17 -0
- pyqttoolkit/viewmanagers/tool.py +23 -0
- pyqttoolkit/views/__init__.py +50 -0
- pyqttoolkit/views/bulk_value_selector.py +112 -0
- pyqttoolkit/views/check_box.py +36 -0
- pyqttoolkit/views/code/__init__.py +18 -0
- pyqttoolkit/views/code/code_edit.py +268 -0
- pyqttoolkit/views/code/python_syntax.py +226 -0
- pyqttoolkit/views/combo_box.py +101 -0
- pyqttoolkit/views/count_selector.py +59 -0
- pyqttoolkit/views/datetime.py +111 -0
- pyqttoolkit/views/datetime_range_selector.py +231 -0
- pyqttoolkit/views/default_text_line_edit.py +50 -0
- pyqttoolkit/views/delegates.py +117 -0
- pyqttoolkit/views/dtype_edit.py +117 -0
- pyqttoolkit/views/editable_combo_box.py +45 -0
- pyqttoolkit/views/file_selector.py +51 -0
- pyqttoolkit/views/icon.py +67 -0
- pyqttoolkit/views/icon_button.py +129 -0
- pyqttoolkit/views/json_edit.py +21 -0
- pyqttoolkit/views/labelled.py +29 -0
- pyqttoolkit/views/layout/__init__.py +21 -0
- pyqttoolkit/views/layout/collabsible.py +59 -0
- pyqttoolkit/views/layout/group_box.py +48 -0
- pyqttoolkit/views/layout/group_panel.py +188 -0
- pyqttoolkit/views/layout/lines.py +30 -0
- pyqttoolkit/views/layout/link.py +38 -0
- pyqttoolkit/views/line_edit.py +61 -0
- pyqttoolkit/views/linkable.py +49 -0
- pyqttoolkit/views/list_view.py +43 -0
- pyqttoolkit/views/main_window.py +91 -0
- pyqttoolkit/views/metadata_selector.py +84 -0
- pyqttoolkit/views/modulewindow.py +81 -0
- pyqttoolkit/views/plot/__init__.py +26 -0
- pyqttoolkit/views/plot/legend_control.py +121 -0
- pyqttoolkit/views/plot/matplotlib/__init__.py +23 -0
- pyqttoolkit/views/plot/matplotlib/base.py +1056 -0
- pyqttoolkit/views/plot/matplotlib/colorbar.py +75 -0
- pyqttoolkit/views/plot/matplotlib/colormap.py +48 -0
- pyqttoolkit/views/plot/matplotlib/font.py +55 -0
- pyqttoolkit/views/plot/plot_options.py +211 -0
- pyqttoolkit/views/plot/plot_toolbar_options.py +133 -0
- pyqttoolkit/views/plot/size.py +41 -0
- pyqttoolkit/views/plot/tool_type.py +26 -0
- pyqttoolkit/views/plot/toolbar.py +100 -0
- pyqttoolkit/views/popout.py +41 -0
- pyqttoolkit/views/popup.py +41 -0
- pyqttoolkit/views/property_tree.py +26 -0
- pyqttoolkit/views/shrunk_push_button.py +34 -0
- pyqttoolkit/views/steppable_combo_box.py +67 -0
- pyqttoolkit/views/styleable.py +37 -0
- pyqttoolkit/views/table.py +146 -0
- pyqttoolkit/views/text_edit.py +31 -0
- pyqttoolkit/views/tool_window.py +35 -0
- pyqttoolkit-0.9.1.dist-info/METADATA +69 -0
- pyqttoolkit-0.9.1.dist-info/RECORD +133 -0
- pyqttoolkit-0.9.1.dist-info/WHEEL +5 -0
- pyqttoolkit-0.9.1.dist-info/licenses/LICENSE.txt +674 -0
- pyqttoolkit-0.9.1.dist-info/licenses/LICENSE_SHORT +16 -0
- pyqttoolkit-0.9.1.dist-info/top_level.txt +1 -0
pyqttoolkit/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
|
|
18
|
+
from ._version import get_versions
|
|
19
|
+
__version__ = get_versions()['version']
|
|
20
|
+
del get_versions
|
pyqttoolkit/_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# This file was generated by 'versioneer.py' (0.18) from
|
|
3
|
+
# revision-control system data, or from the parent directory name of an
|
|
4
|
+
# unpacked source archive. Distribution tarballs contain a pre-generated copy
|
|
5
|
+
# of this file.
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
version_json = '''
|
|
10
|
+
{
|
|
11
|
+
"date": "2026-10-20T09:25:30+0100",
|
|
12
|
+
"dirty": false,
|
|
13
|
+
"error": null,
|
|
14
|
+
"full-revisionid": "06c8bebf565d6ca91beb9a0fb7d8b171ebfd80fc",
|
|
15
|
+
"version": "0.9.1"
|
|
16
|
+
}
|
|
17
|
+
''' # END VERSION_JSON
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_versions():
|
|
21
|
+
return json.loads(version_json)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from .application import Application
|
|
18
|
+
from .garbage_collector import GarbageCollector
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
import sys
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
from PyQt5.QtCore import pyqtSignal
|
|
21
|
+
from PyQt5.QtWidgets import QApplication, QWidget
|
|
22
|
+
|
|
23
|
+
from pyqttoolkit.services import MessageBoard, TaskRunner, ToolWindowService, EventRegistry, ObjectConverter
|
|
24
|
+
from pyqttoolkit.dependencies import DependencyContainer
|
|
25
|
+
from .garbage_collector import GarbageCollector
|
|
26
|
+
|
|
27
|
+
LOGGER = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
class Application(QApplication):
|
|
30
|
+
def __init__(self, argv):
|
|
31
|
+
QApplication.__init__(self, argv)
|
|
32
|
+
self._dependency_container = DependencyContainer()
|
|
33
|
+
self._garbage_collector = GarbageCollector(self)
|
|
34
|
+
|
|
35
|
+
self._module_service = None
|
|
36
|
+
self._message_board = None
|
|
37
|
+
self._project_manager = None
|
|
38
|
+
|
|
39
|
+
self._handling_exception = False
|
|
40
|
+
|
|
41
|
+
sys.excepthook = self._handle_exception
|
|
42
|
+
|
|
43
|
+
mainWindowLoaded = pyqtSignal(QWidget)
|
|
44
|
+
|
|
45
|
+
def run(self, filename=None):
|
|
46
|
+
LOGGER.info('Running Application')
|
|
47
|
+
self._register_default_services()
|
|
48
|
+
self._register_modules()
|
|
49
|
+
|
|
50
|
+
if filename:
|
|
51
|
+
self._project_manager.load_project(filename)
|
|
52
|
+
|
|
53
|
+
self.mainWindowLoaded.emit(self._module_service.openModule('Base'))
|
|
54
|
+
return self.exec_()
|
|
55
|
+
|
|
56
|
+
def _handle_exception(self, ex_type, ex_value, traceback_obj):
|
|
57
|
+
LOGGER.error('Application Exception: %s, %s', ex_type, ex_value)
|
|
58
|
+
if self._handling_exception:
|
|
59
|
+
return
|
|
60
|
+
self._handling_exception = True
|
|
61
|
+
self._perform_exception_handling(ex_type, ex_value, traceback_obj)
|
|
62
|
+
self._handling_exception = False
|
|
63
|
+
|
|
64
|
+
def _perform_exception_handling(self, ex_type, ex_value, traceback_obj):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
def _register_default_services(self):
|
|
68
|
+
self._dependency_container.register_instance('application', self)
|
|
69
|
+
self._dependency_container.register_instance('dependency_container', self._dependency_container)
|
|
70
|
+
self._dependency_container.register_instance('task_runner', TaskRunner(self))
|
|
71
|
+
self._dependency_container.register_instance('message_board', MessageBoard(self))
|
|
72
|
+
self._dependency_container.register_instance('tool_window_service', ToolWindowService(self._dependency_container))
|
|
73
|
+
self._dependency_container.register_instance('event_registry', EventRegistry())
|
|
74
|
+
self._dependency_container.register_instance('object_converter', ObjectConverter())
|
|
75
|
+
self._dependency_container.register_instance('garbage_collector', self._garbage_collector)
|
|
76
|
+
|
|
77
|
+
def _register_modules(self):
|
|
78
|
+
modules = self._get_modules()
|
|
79
|
+
for module_type in [v for k, v in modules.__dict__.items() if k.endswith('Module')]:
|
|
80
|
+
module = self._dependency_container.resolve(module_type)
|
|
81
|
+
module.register(dependency_container=self._dependency_container)
|
|
82
|
+
|
|
83
|
+
self._module_service = self._dependency_container.get_instance('module_service')
|
|
84
|
+
|
|
85
|
+
self._message_board = self._dependency_container.get_instance('message_board')
|
|
86
|
+
self._project_manager = self._dependency_container.get_instance('project_manager')
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
import gc
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
from PyQt5.QtCore import QObject, QTimer
|
|
21
|
+
|
|
22
|
+
from pyqttoolkit.logs import TRACE
|
|
23
|
+
|
|
24
|
+
LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
class GarbageCollector(QObject):
|
|
27
|
+
'''
|
|
28
|
+
Disable automatic garbage collection and instead collect manually
|
|
29
|
+
every INTERVAL milliseconds.
|
|
30
|
+
|
|
31
|
+
This is done to ensure that garbage collection only happens in the GUI
|
|
32
|
+
thread, as otherwise Qt can crash.
|
|
33
|
+
'''
|
|
34
|
+
|
|
35
|
+
def __init__(self, parent, interval=60000):
|
|
36
|
+
super().__init__(parent)
|
|
37
|
+
self._interval = interval
|
|
38
|
+
self._threshold = gc.get_threshold()
|
|
39
|
+
gc.disable()
|
|
40
|
+
|
|
41
|
+
if self._interval:
|
|
42
|
+
self._timer = QTimer(self)
|
|
43
|
+
self._timer.timeout.connect(self.check)
|
|
44
|
+
self._timer.start(self._interval)
|
|
45
|
+
else:
|
|
46
|
+
self._timer = None
|
|
47
|
+
|
|
48
|
+
def check(self, full=False):
|
|
49
|
+
l0, l1, l2 = gc.get_count()
|
|
50
|
+
LOGGER.log(TRACE, 'gc_check called: l0=%d, l1=%d, l2=%d', l0, l1, l2)
|
|
51
|
+
if full or l0 > self._threshold[0]:
|
|
52
|
+
num = gc.collect(0)
|
|
53
|
+
LOGGER.log(TRACE, 'Collecting gen 0, found: %d unreachable', num)
|
|
54
|
+
if full or l1 > self._threshold[1]:
|
|
55
|
+
num = gc.collect(1)
|
|
56
|
+
LOGGER.log(TRACE, 'Collecting gen 1, found: %d unreachable', num)
|
|
57
|
+
if full or l2 > self._threshold[2]:
|
|
58
|
+
num = gc.collect(2)
|
|
59
|
+
LOGGER.log(TRACE, 'Collecting gen 2, found: %d unreachable', num)
|
|
60
|
+
|
|
61
|
+
def debug_cycles(self):
|
|
62
|
+
gc.set_debug(gc.DEBUG_SAVEALL)
|
|
63
|
+
gc.collect()
|
|
64
|
+
for obj in gc.garbage:
|
|
65
|
+
LOGGER.log(TRACE, "gc: obj=%s, (%s), type=%s", obj, repr(obj), type(obj))
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from enum import Enum
|
|
18
|
+
|
|
19
|
+
from itertools import chain
|
|
20
|
+
from math import ceil
|
|
21
|
+
|
|
22
|
+
def hex_to_rgb(hex_value):
|
|
23
|
+
hex_value = hex_value.lstrip('#')
|
|
24
|
+
return tuple(int(hex_value[i:i+2], 16) / 255 for i in [0, 2, 4])
|
|
25
|
+
|
|
26
|
+
def rgb_to_hex(rgb_value):
|
|
27
|
+
return '#' + ''.join('{:02x}'.format(int(v * 255)) for v in rgb_value)
|
|
28
|
+
|
|
29
|
+
def interpolate_rgb(a, b, n):
|
|
30
|
+
if n <= 0:
|
|
31
|
+
return [a, b]
|
|
32
|
+
step = tuple((b_val - a_val) / (n + 1) for a_val, b_val in zip(a, b))
|
|
33
|
+
return [tuple(a_val + step_val * i for a_val, step_val in zip(a, step)) for i in range(1, n+1)]
|
|
34
|
+
|
|
35
|
+
def expand_rgb(colors, n):
|
|
36
|
+
extras_per_gap = int(ceil((n - len(colors)) / (len(colors) - 1)))
|
|
37
|
+
if extras_per_gap <= 0:
|
|
38
|
+
return list(colors)
|
|
39
|
+
result = [*chain(
|
|
40
|
+
*([a, *interpolate_rgb(a, b, extras_per_gap)] for a, b in zip(colors, colors[1:]))
|
|
41
|
+
), colors[-1]]
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
class ColorFormat(Enum):
|
|
45
|
+
rgba_string_256 = 0
|
|
46
|
+
rgb_string_256 = 1
|
|
47
|
+
hex_string = 2
|
|
48
|
+
hexa_string = 3
|
|
49
|
+
|
|
50
|
+
def format_color(color, color_format=ColorFormat.rgba_string_256, opacity=None, lighten=None):
|
|
51
|
+
if len(color) == 4:
|
|
52
|
+
r, g, b, a = color
|
|
53
|
+
elif len(color) == 3:
|
|
54
|
+
r, g, b = color
|
|
55
|
+
a = 255
|
|
56
|
+
else:
|
|
57
|
+
raise ValueError(f'Invalid value for color: {color}')
|
|
58
|
+
a = a if opacity is None else opacity
|
|
59
|
+
|
|
60
|
+
if lighten is not None:
|
|
61
|
+
r, g, b = redistribute_rgb(lighten * (r or 100), lighten * (g or 100), lighten * (b or 100))
|
|
62
|
+
|
|
63
|
+
if color_format == ColorFormat.rgba_string_256:
|
|
64
|
+
return f'rgba({r},{g},{b},{a})'
|
|
65
|
+
if color_format == ColorFormat.rgb_string_256:
|
|
66
|
+
return f'rgb({r},{g},{b})'
|
|
67
|
+
if color_format == ColorFormat.hex_string:
|
|
68
|
+
return '#{:02x}{:02x}{:02x}'.format(*(int(c * 255) for c in (r, g, b)))
|
|
69
|
+
if color_format == ColorFormat.hexa_string:
|
|
70
|
+
return '#{:02x}{:02x}{:02x}{:02x}'.format(*(int(c * 255) for c in (a, r, g, b)))
|
|
71
|
+
|
|
72
|
+
raise ValueError(f'Invalid format: {color_format}')
|
|
73
|
+
|
|
74
|
+
def redistribute_rgb(r, g, b):
|
|
75
|
+
threshold = 255.999
|
|
76
|
+
m = max(r, g, b)
|
|
77
|
+
if m <= threshold:
|
|
78
|
+
return int(r), int(g), int(b)
|
|
79
|
+
total = r + g + b
|
|
80
|
+
if total >= 3 * threshold:
|
|
81
|
+
return int(threshold), int(threshold), int(threshold)
|
|
82
|
+
x = (3 * threshold - total) / (3 * m - total)
|
|
83
|
+
gray = threshold - x * m
|
|
84
|
+
return int(gray + x * r), int(gray + x * g), int(gray + x * b)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from .default import DefaultApplicationConfiguration
|
|
18
|
+
from .multi_json_file import MultiJsonFileApplicationConfiguration
|
|
19
|
+
from .composite import CompositeApplicationConfiguration
|
|
20
|
+
from .environment import EnvironmentApplicationConfiguration
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
class BaseApplicationConfiguration:
|
|
18
|
+
def get_value(self, key):
|
|
19
|
+
raise NotImplementedError()
|
|
20
|
+
|
|
21
|
+
def set_value(self, key, value):
|
|
22
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from .base import BaseApplicationConfiguration
|
|
18
|
+
|
|
19
|
+
class CompositeApplicationConfiguration(BaseApplicationConfiguration):
|
|
20
|
+
def __init__(self, *inners):
|
|
21
|
+
self._inners = inners
|
|
22
|
+
|
|
23
|
+
def get_value(self, key):
|
|
24
|
+
for inner in self._inners:
|
|
25
|
+
value = inner.get_value(key)
|
|
26
|
+
if value is not None:
|
|
27
|
+
return value
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def set_value(self, key, value):
|
|
31
|
+
self._inners[0].set_value(key, value)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from .base import BaseApplicationConfiguration
|
|
18
|
+
|
|
19
|
+
class DefaultApplicationConfiguration(BaseApplicationConfiguration):
|
|
20
|
+
def __init__(self, config):
|
|
21
|
+
self._configuration = config
|
|
22
|
+
|
|
23
|
+
def get_value(self, key):
|
|
24
|
+
return self._configuration.get(key)
|
|
25
|
+
|
|
26
|
+
def set_value(self, key, value):
|
|
27
|
+
self._configuration[key] = value
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
import os
|
|
18
|
+
import json
|
|
19
|
+
from .base import BaseApplicationConfiguration
|
|
20
|
+
|
|
21
|
+
class EnvironmentApplicationConfiguration(BaseApplicationConfiguration):
|
|
22
|
+
def get_value(self, key):
|
|
23
|
+
value = os.getenv(self._format_key(key))
|
|
24
|
+
if value is None:
|
|
25
|
+
return value
|
|
26
|
+
try:
|
|
27
|
+
return json.loads(value)
|
|
28
|
+
except json.decoder.JSONDecodeError:
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
def set_value(self, key, value):
|
|
32
|
+
os.environ[self._format_key(key)] = str(value)
|
|
33
|
+
|
|
34
|
+
def _format_key(self, key):
|
|
35
|
+
return key.replace('.', '_').upper()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from os import path, makedirs
|
|
18
|
+
from json import load, dump, JSONDecodeError
|
|
19
|
+
import logging
|
|
20
|
+
import shutil
|
|
21
|
+
|
|
22
|
+
from .base import BaseApplicationConfiguration
|
|
23
|
+
|
|
24
|
+
LOGGER = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
class MultiJsonFileApplicationConfiguration(BaseApplicationConfiguration):
|
|
27
|
+
def __init__(self, directory):
|
|
28
|
+
self._directory = directory
|
|
29
|
+
|
|
30
|
+
def get_value(self, key):
|
|
31
|
+
parts = key.split('.')
|
|
32
|
+
if len(parts) < 2:
|
|
33
|
+
raise ValueError('key')
|
|
34
|
+
|
|
35
|
+
filename = path.join(self._directory, f'{parts[0]}.json')
|
|
36
|
+
value_path = parts[1:]
|
|
37
|
+
|
|
38
|
+
if not path.exists(filename):
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with open(filename) as fp:
|
|
43
|
+
values = load(fp)
|
|
44
|
+
except JSONDecodeError as err:
|
|
45
|
+
LOGGER.warning('Invalid json file, exception: %s', err)
|
|
46
|
+
self._backup_file(filename)
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
result = values
|
|
50
|
+
for p in value_path:
|
|
51
|
+
result = result.get(p, {})
|
|
52
|
+
|
|
53
|
+
if result == {}:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
def _backup_file(self, filename):
|
|
59
|
+
backup_path_root = path.join(path.dirname(filename), f'{path.basename(filename)}.json.bak')
|
|
60
|
+
attempt = 0
|
|
61
|
+
backup_path = backup_path_root
|
|
62
|
+
while path.isfile(backup_path):
|
|
63
|
+
attempt += 1
|
|
64
|
+
backup_path = backup_path_root + f'.{attempt}'
|
|
65
|
+
LOGGER.warning('Backing up config file %s to %s', filename, backup_path)
|
|
66
|
+
shutil.move(filename, backup_path)
|
|
67
|
+
|
|
68
|
+
def set_value(self, key, value):
|
|
69
|
+
parts = key.split('.')
|
|
70
|
+
if len(parts) < 2:
|
|
71
|
+
raise ValueError('key')
|
|
72
|
+
|
|
73
|
+
filename = path.join(self._directory, f'{parts[0]}.json')
|
|
74
|
+
if path.exists(filename):
|
|
75
|
+
try:
|
|
76
|
+
with open(filename) as fp:
|
|
77
|
+
current_config = load(fp)
|
|
78
|
+
except JSONDecodeError as err:
|
|
79
|
+
LOGGER.warning('Invalid json file, exception: %s', err)
|
|
80
|
+
current_config = {}
|
|
81
|
+
else:
|
|
82
|
+
current_config = {}
|
|
83
|
+
|
|
84
|
+
parent = current_config
|
|
85
|
+
for p in parts[1:-1]:
|
|
86
|
+
parent = current_config.setdefault(p, {})
|
|
87
|
+
parent[parts[-1]] = value
|
|
88
|
+
|
|
89
|
+
if not path.isdir(path.dirname(filename)):
|
|
90
|
+
makedirs(path.dirname(filename))
|
|
91
|
+
|
|
92
|
+
with open(filename, 'w') as fp:
|
|
93
|
+
dump(current_config, fp, indent=4, sort_keys=True)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from PyQt5.QtCore import pyqtSignal, QObject
|
|
18
|
+
|
|
19
|
+
class WithValueChangedConfiguration(QObject):
|
|
20
|
+
def __init__(self, inner):
|
|
21
|
+
self._inner = inner
|
|
22
|
+
|
|
23
|
+
onValueChanged = pyqtSignal(str, object)
|
|
24
|
+
|
|
25
|
+
def get_value(self, key):
|
|
26
|
+
return self._inner.get_value(key)
|
|
27
|
+
|
|
28
|
+
def set_value(self, key, value):
|
|
29
|
+
self._inner.set_value(key, value)
|
|
30
|
+
self.onValueChanged(key, value)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from PyQt5.QtCore import pyqtSignal, QObject
|
|
18
|
+
|
|
19
|
+
class WithValueChangedConfiguration(QObject):
|
|
20
|
+
def __init__(self, parent, inner):
|
|
21
|
+
super().__init__(parent)
|
|
22
|
+
self._inner = inner
|
|
23
|
+
|
|
24
|
+
onValueChanged = pyqtSignal(str, object)
|
|
25
|
+
|
|
26
|
+
def get_value(self, key):
|
|
27
|
+
return self._inner.get_value(key)
|
|
28
|
+
|
|
29
|
+
def set_value(self, key, value):
|
|
30
|
+
self._inner.set_value(key, value)
|
|
31
|
+
self.onValueChanged.emit(key, value)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
from .cache import Cache
|
|
18
|
+
from .interval import calculate_interval
|
|
19
|
+
from .bits import get_next_available_bit
|
|
20
|
+
from .names import get_next_available_name
|
|
21
|
+
from .message import MessageType, MessageResponse
|
pyqttoolkit/data/bits.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# pyqttoolkit
|
|
2
|
+
# Copyright (C) 2018-2019, Simmovation Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, write to the Free Software Foundation,
|
|
16
|
+
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17
|
+
def get_next_available_bit(bits):
|
|
18
|
+
bit = 1
|
|
19
|
+
for _ in range(63):
|
|
20
|
+
if not bit in bits:
|
|
21
|
+
return bit
|
|
22
|
+
bit <<= 1
|
|
23
|
+
return 0
|