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
|
@@ -0,0 +1,117 @@
|
|
|
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 math import isnan, isfinite
|
|
18
|
+
|
|
19
|
+
from PyQt5.QtCore import pyqtSignal, Qt
|
|
20
|
+
from PyQt5.QtGui import QDoubleValidator
|
|
21
|
+
|
|
22
|
+
from pyqttoolkit.properties import AutoProperty
|
|
23
|
+
from pyqttoolkit.views import LineEdit
|
|
24
|
+
|
|
25
|
+
def _type_editor(dtype, default_value):
|
|
26
|
+
class _TypeEdit(LineEdit):
|
|
27
|
+
def __init__(self, parent):
|
|
28
|
+
LineEdit.__init__(self, parent)
|
|
29
|
+
self.setValidator(QDoubleValidator(self))
|
|
30
|
+
self.editComplete.connect(self._set_value)
|
|
31
|
+
self.valueChanged.connect(self._set_text)
|
|
32
|
+
|
|
33
|
+
valueChanged = pyqtSignal(dtype)
|
|
34
|
+
|
|
35
|
+
value = AutoProperty(dtype)
|
|
36
|
+
|
|
37
|
+
def _set_value(self, text):
|
|
38
|
+
try:
|
|
39
|
+
self.value = dtype(text)
|
|
40
|
+
except (TypeError, ValueError):
|
|
41
|
+
self.value = default_value
|
|
42
|
+
self._set_text(self.value)
|
|
43
|
+
|
|
44
|
+
def _set_text(self, value):
|
|
45
|
+
self.setText(str(value))
|
|
46
|
+
|
|
47
|
+
return _TypeEdit
|
|
48
|
+
|
|
49
|
+
FloatEdit = _type_editor(float, 0.0)
|
|
50
|
+
IntEdit = _type_editor(int, 0)
|
|
51
|
+
|
|
52
|
+
class InfFloatLineEdit(LineEdit):
|
|
53
|
+
def __init__(self, parent, formatter=None, update_on_enter_pressed=True):
|
|
54
|
+
LineEdit.__init__(self, parent)
|
|
55
|
+
self.editComplete.connect(self._handle_edit_complete)
|
|
56
|
+
self.valueChanged.connect(self._handle_value_changed)
|
|
57
|
+
self._formatter = formatter
|
|
58
|
+
self._update_on_enter_pressed = update_on_enter_pressed
|
|
59
|
+
|
|
60
|
+
valueChanged = pyqtSignal(float)
|
|
61
|
+
value = AutoProperty(float)
|
|
62
|
+
|
|
63
|
+
def _handle_edit_complete(self):
|
|
64
|
+
try:
|
|
65
|
+
self.value = float(self.text())
|
|
66
|
+
except ValueError:
|
|
67
|
+
self.setText(self._get_text(self.value))
|
|
68
|
+
|
|
69
|
+
def _handle_value_changed(self, value):
|
|
70
|
+
self.setText(self._get_text(value))
|
|
71
|
+
|
|
72
|
+
def _get_text(self, value):
|
|
73
|
+
if self._formatter is not None:
|
|
74
|
+
return self._formatter(value)
|
|
75
|
+
else:
|
|
76
|
+
return str(value)
|
|
77
|
+
|
|
78
|
+
def keyPressEvent(self, event):
|
|
79
|
+
super().keyPressEvent(event)
|
|
80
|
+
if event.key() in [Qt.Key_Enter, Qt.Key_Return] and self._update_on_enter_pressed:
|
|
81
|
+
self.valueChanged.emit(self.value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AutoFloatLineEdit(InfFloatLineEdit):
|
|
85
|
+
def __init__(self, parent, allow_inf=True, default_text=None, formatter=None, update_on_enter_pressed=True):
|
|
86
|
+
InfFloatLineEdit.__init__(self, parent, formatter, update_on_enter_pressed)
|
|
87
|
+
self._allow_inf = allow_inf
|
|
88
|
+
self._default_text_ = default_text or self.tr('Auto')
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def _default_text(self):
|
|
92
|
+
return self._default_text_
|
|
93
|
+
|
|
94
|
+
def _handle_edit_complete(self):
|
|
95
|
+
if self.text().lower() == self._default_text.lower() or not self.text():
|
|
96
|
+
self._reset()
|
|
97
|
+
else:
|
|
98
|
+
if not self._allow_inf:
|
|
99
|
+
try:
|
|
100
|
+
if not isfinite(float(self.text())):
|
|
101
|
+
self._reset()
|
|
102
|
+
except ValueError:
|
|
103
|
+
pass
|
|
104
|
+
InfFloatLineEdit._handle_edit_complete(self)
|
|
105
|
+
|
|
106
|
+
def _reset(self):
|
|
107
|
+
self.value = float('nan')
|
|
108
|
+
self.setText(self._default_text)
|
|
109
|
+
|
|
110
|
+
def _handle_value_changed(self, value):
|
|
111
|
+
if isnan(value):
|
|
112
|
+
self.setText(self._default_text)
|
|
113
|
+
else:
|
|
114
|
+
InfFloatLineEdit._handle_value_changed(self, value)
|
|
115
|
+
|
|
116
|
+
def setText(self, value):
|
|
117
|
+
InfFloatLineEdit.setText(self, self._default_text if value == 'nan' else value)
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
""":mod:`editable_combo_box`
|
|
18
|
+
Defines the EditableComboBox class
|
|
19
|
+
"""
|
|
20
|
+
#pylint: disable=no-name-in-module
|
|
21
|
+
from PyQt5.QtCore import QEvent
|
|
22
|
+
from PyQt5.QtWidgets import QComboBox
|
|
23
|
+
#pylint: enable=no-name-in-module
|
|
24
|
+
|
|
25
|
+
class EditableComboBox(QComboBox):
|
|
26
|
+
"""class::EditableComboBox
|
|
27
|
+
A combo box that is editable by default, and adds the current item if focus is lost
|
|
28
|
+
"""
|
|
29
|
+
def __init__(self, parent):
|
|
30
|
+
QComboBox.__init__(self, parent)
|
|
31
|
+
self.setEditable(True)
|
|
32
|
+
self.installEventFilter(self)
|
|
33
|
+
|
|
34
|
+
def eventFilter(self, _obj, event):
|
|
35
|
+
"""function::eventFilter(self, _obj, evet)
|
|
36
|
+
Override the QComboBox.eventFilter method to provide lost focus functionality
|
|
37
|
+
"""
|
|
38
|
+
if event.type() == QEvent.FocusOut and self.currentText():
|
|
39
|
+
index = self.findText(self.currentText())
|
|
40
|
+
if index == -1:
|
|
41
|
+
self.addItem(self.currentText())
|
|
42
|
+
index = self.findText(self.currentText())
|
|
43
|
+
self.setCurrentIndex(index)
|
|
44
|
+
self.activated.emit(index)
|
|
45
|
+
return QComboBox.eventFilter(self, _obj, event)
|
|
@@ -0,0 +1,51 @@
|
|
|
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.QtWidgets import QWidget, QLineEdit, QHBoxLayout
|
|
18
|
+
from PyQt5.QtCore import pyqtSignal
|
|
19
|
+
|
|
20
|
+
from pyqttoolkit.properties import auto_property
|
|
21
|
+
|
|
22
|
+
from . import ShrunkPushButton
|
|
23
|
+
|
|
24
|
+
class FileSelector(QWidget):
|
|
25
|
+
def __init__(self, parent, filter_, file_dialog_service):
|
|
26
|
+
QWidget.__init__(self, parent)
|
|
27
|
+
self._file_dialog_service = file_dialog_service
|
|
28
|
+
self._filter = filter_
|
|
29
|
+
self._line_edit = QLineEdit(self)
|
|
30
|
+
self._open_dialog = ShrunkPushButton('...', self)
|
|
31
|
+
self._layout = QHBoxLayout(self)
|
|
32
|
+
self._layout.setContentsMargins(0, 0, 0, 0)
|
|
33
|
+
self._layout.addWidget(self._line_edit)
|
|
34
|
+
self._layout.addWidget(self._open_dialog)
|
|
35
|
+
self._line_edit.textChanged.connect(self.textChanged)
|
|
36
|
+
self._open_dialog.clicked.connect(self._open_file_dialog)
|
|
37
|
+
|
|
38
|
+
textChanged = pyqtSignal(str)
|
|
39
|
+
|
|
40
|
+
@auto_property(str)
|
|
41
|
+
def text(self):
|
|
42
|
+
return self._line_edit.text()
|
|
43
|
+
|
|
44
|
+
@text.setter
|
|
45
|
+
def text(self, value):
|
|
46
|
+
self._line_edit.setText(value)
|
|
47
|
+
|
|
48
|
+
def _open_file_dialog(self):
|
|
49
|
+
file_name = self._file_dialog_service.get_open_filename(self, self._filter)
|
|
50
|
+
if file_name:
|
|
51
|
+
self.text = file_name
|
|
@@ -0,0 +1,67 @@
|
|
|
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.QtWidgets import QHBoxLayout, QWidget
|
|
18
|
+
from PyQt5.QtCore import QFile, QSize, Qt
|
|
19
|
+
from PyQt5.QtSvg import QSvgWidget
|
|
20
|
+
from PyQt5.QtXml import QDomDocument
|
|
21
|
+
|
|
22
|
+
from pyqttoolkit.colors import format_color, ColorFormat
|
|
23
|
+
from pyqttoolkit.services.theme_manager import ThemeManager
|
|
24
|
+
from .styleable import make_styleable
|
|
25
|
+
|
|
26
|
+
class Icon(QWidget):
|
|
27
|
+
def __init__(self, parent, icon, size=None, padding=None, color=None):
|
|
28
|
+
QWidget.__init__(self, parent)
|
|
29
|
+
padding = padding or 0
|
|
30
|
+
self._theme_manager = ThemeManager.get(self)
|
|
31
|
+
|
|
32
|
+
self._color = format_color(color or self._theme_manager.get_color('button_foreground'), ColorFormat.rgb_string_256)
|
|
33
|
+
|
|
34
|
+
self._svgdoc = QDomDocument()
|
|
35
|
+
self._icon_widget = QSvgWidget(self)
|
|
36
|
+
self.loadIcon(icon)
|
|
37
|
+
|
|
38
|
+
if size:
|
|
39
|
+
self._icon_widget.setFixedSize(QSize(size.width() - 2 * padding, size.height() - 2 * padding))
|
|
40
|
+
|
|
41
|
+
self._layout = QHBoxLayout(self)
|
|
42
|
+
self._layout.setContentsMargins(padding, padding, padding, padding)
|
|
43
|
+
self._layout.addWidget(self._icon_widget, Qt.AlignCenter)
|
|
44
|
+
|
|
45
|
+
def loadIcon(self, icon):
|
|
46
|
+
file = QFile(f'icons:{icon}', self)
|
|
47
|
+
try:
|
|
48
|
+
file.open(QFile.ReadOnly)
|
|
49
|
+
self._svgdoc.setContent(file.readAll())
|
|
50
|
+
self._svgdoc.documentElement().setAttribute('fill', self._color)
|
|
51
|
+
self._icon_widget.load(self._svgdoc.toByteArray())
|
|
52
|
+
finally:
|
|
53
|
+
file.close()
|
|
54
|
+
|
|
55
|
+
def setEnabled(self, enabled):
|
|
56
|
+
QWidget.setEnabled(self, enabled)
|
|
57
|
+
self._svgdoc.documentElement().setAttribute('fill', self._color)
|
|
58
|
+
self._svgdoc.documentElement().setAttribute('fill-opacity', '1' if self.isEnabled() else '0.4')
|
|
59
|
+
self._icon_widget.load(self._svgdoc.toByteArray())
|
|
60
|
+
|
|
61
|
+
def setColor(self, color=None):
|
|
62
|
+
self._color = format_color(color or self._theme_manager.get_color('button_foreground'), ColorFormat.rgb_string_256) or self._color
|
|
63
|
+
self._svgdoc.documentElement().setAttribute('fill', self._color)
|
|
64
|
+
self._svgdoc.documentElement().setAttribute('fill-opacity', '1' if self.isEnabled() else '0.4')
|
|
65
|
+
self._icon_widget.load(self._svgdoc.toByteArray())
|
|
66
|
+
|
|
67
|
+
Icon = make_styleable(Icon)
|
|
@@ -0,0 +1,129 @@
|
|
|
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 QPropertyAnimation, QEasingCurve, pyqtSignal, pyqtProperty
|
|
18
|
+
from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QSizePolicy
|
|
19
|
+
|
|
20
|
+
from pyqttoolkit.properties import AutoProperty
|
|
21
|
+
from pyqttoolkit.colors import format_color, ColorFormat
|
|
22
|
+
from pyqttoolkit.services.theme_manager import ThemeManager
|
|
23
|
+
from .icon import Icon
|
|
24
|
+
|
|
25
|
+
class IconButton(QPushButton):
|
|
26
|
+
def __init__(self, icon, title, parent, size=None, padding=None, color=None, hover_color=None):
|
|
27
|
+
QPushButton.__init__(self, parent)
|
|
28
|
+
self._theme_manager = ThemeManager.get(self)
|
|
29
|
+
self._background_color = hover_color or self._theme_manager.get_color('button_background_hover')
|
|
30
|
+
self.setToolTip(title)
|
|
31
|
+
self._icon = Icon(self, icon, size, padding, color)
|
|
32
|
+
self._iconname = icon
|
|
33
|
+
|
|
34
|
+
self._layout = QHBoxLayout(self)
|
|
35
|
+
self._layout.addWidget(self._icon)
|
|
36
|
+
self._layout.setContentsMargins(0, 0, 0, 0)
|
|
37
|
+
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
|
38
|
+
self._size = size
|
|
39
|
+
self.setFlat(True)
|
|
40
|
+
self._background_opacity = 0.0
|
|
41
|
+
self._background_opacity_stop = 0.0
|
|
42
|
+
self._update_style_sheet()
|
|
43
|
+
self._animation = QPropertyAnimation(self, b'backgroundOpacity', self)
|
|
44
|
+
self.clicked.connect(self._update_background_opacity_stop)
|
|
45
|
+
|
|
46
|
+
def setColor(self, color=None):
|
|
47
|
+
self._icon.setColor(color)
|
|
48
|
+
|
|
49
|
+
def setIcon(self, icon):
|
|
50
|
+
self._icon.loadIcon(icon)
|
|
51
|
+
|
|
52
|
+
@pyqtProperty(float)
|
|
53
|
+
def backgroundOpacity(self):
|
|
54
|
+
return self._background_opacity
|
|
55
|
+
|
|
56
|
+
@backgroundOpacity.setter
|
|
57
|
+
def backgroundOpacity(self, value):
|
|
58
|
+
self._background_opacity = value
|
|
59
|
+
self._update_style_sheet()
|
|
60
|
+
|
|
61
|
+
def sizeHint(self):
|
|
62
|
+
return self._size or self._icon.sizeHint()
|
|
63
|
+
|
|
64
|
+
def enterEvent(self, event):
|
|
65
|
+
if self.isEnabled():
|
|
66
|
+
self._start_opacity_animation(350, 0, 1)
|
|
67
|
+
return QPushButton.enterEvent(self, event)
|
|
68
|
+
|
|
69
|
+
def leaveEvent(self, event):
|
|
70
|
+
if self.isEnabled():
|
|
71
|
+
self._start_opacity_animation(350, 1, 0)
|
|
72
|
+
return QPushButton.leaveEvent(self, event)
|
|
73
|
+
|
|
74
|
+
def mousePressEvent(self, event):
|
|
75
|
+
if self.isEnabled():
|
|
76
|
+
self._start_opacity_animation(50, 1, 0.6)
|
|
77
|
+
return QPushButton.mousePressEvent(self, event)
|
|
78
|
+
|
|
79
|
+
def mouseReleaseEvent(self, event):
|
|
80
|
+
if self.isEnabled():
|
|
81
|
+
self._start_opacity_animation(50, self._background_opacity, 1)
|
|
82
|
+
return QPushButton.mouseReleaseEvent(self, event)
|
|
83
|
+
|
|
84
|
+
def _start_opacity_animation(self, duration, start, end):
|
|
85
|
+
self._animation.stop()
|
|
86
|
+
self._animation.setDuration(duration)
|
|
87
|
+
self._animation.setStartValue(start)
|
|
88
|
+
self._animation.setEndValue(end)
|
|
89
|
+
self._animation.setEasingCurve(QEasingCurve.BezierSpline)
|
|
90
|
+
self._animation.start()
|
|
91
|
+
|
|
92
|
+
def setEnabled(self, value):
|
|
93
|
+
QPushButton.setEnabled(self, value)
|
|
94
|
+
self._icon.setEnabled(value)
|
|
95
|
+
|
|
96
|
+
def _update_background_opacity_stop(self):
|
|
97
|
+
self._background_opacity_stop = 0.0 if not self.isChecked() else 0.8
|
|
98
|
+
self._update_style_sheet()
|
|
99
|
+
|
|
100
|
+
def setChecked(self, value):
|
|
101
|
+
QPushButton.setChecked(self, value)
|
|
102
|
+
self._update_background_opacity_stop()
|
|
103
|
+
|
|
104
|
+
def _update_style_sheet(self):
|
|
105
|
+
stylesheet = """
|
|
106
|
+
Icon {{
|
|
107
|
+
background-color: {color};
|
|
108
|
+
border-radius: 2px;
|
|
109
|
+
}}
|
|
110
|
+
QPushButton {{
|
|
111
|
+
background-color: transparent;
|
|
112
|
+
border: none;
|
|
113
|
+
}}
|
|
114
|
+
""".format(color=format_color(self._background_color, ColorFormat.rgba_string_256, max(self._background_opacity, self._background_opacity_stop)))
|
|
115
|
+
self.setStyleSheet(stylesheet)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class BindableIconButton(IconButton):
|
|
119
|
+
def __init__(self, icon, title, parent, size=None, padding=None, color=None, hover_color=None):
|
|
120
|
+
IconButton.__init__(self, icon, title, parent, size, padding, color, hover_color)
|
|
121
|
+
self.setCheckable(True)
|
|
122
|
+
self.clicked.connect(self._handle_clicked)
|
|
123
|
+
self.checkedChanged.connect(self.setChecked)
|
|
124
|
+
|
|
125
|
+
checkedChanged = pyqtSignal(bool)
|
|
126
|
+
checked = AutoProperty(bool)
|
|
127
|
+
|
|
128
|
+
def _handle_clicked(self):
|
|
129
|
+
self.checked = self.isChecked()
|
|
@@ -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 .code import CodeEdit
|
|
18
|
+
|
|
19
|
+
class JsonEdit(CodeEdit):
|
|
20
|
+
def __init__(self, parent):
|
|
21
|
+
CodeEdit.__init__(self, parent)
|
|
@@ -0,0 +1,29 @@
|
|
|
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.QtWidgets import QWidget, QHBoxLayout, QLabel
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def make_labelled(parent, label: str, widget: QWidget):
|
|
21
|
+
"""Put a widget into a container with a label to the left of it. Like a single row of a `QFormLayout`"""
|
|
22
|
+
container = QWidget(parent)
|
|
23
|
+
label_widget = QLabel(label)
|
|
24
|
+
layout = QHBoxLayout(container)
|
|
25
|
+
layout.setSpacing(0)
|
|
26
|
+
layout.addWidget(label_widget)
|
|
27
|
+
layout.addWidget(widget)
|
|
28
|
+
layout.setContentsMargins(0, 0, 0, 0)
|
|
29
|
+
return container
|
|
@@ -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 .lines import HLine, VLine
|
|
18
|
+
from .group_panel import GroupPanelLayout, VGroupPanelLayout, HGroupPanelLayout, GroupPanelWidget, HGroupPanelWidget, VGroupPanelWidget
|
|
19
|
+
from .group_box import wrap_in_group_box, wrap_many_in_group_box
|
|
20
|
+
from .collabsible import HCollapsibleLayout, VCollapsibleLayout
|
|
21
|
+
from .link import LinkWidget
|
|
@@ -0,0 +1,59 @@
|
|
|
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.QtWidgets import QBoxLayout, QToolButton
|
|
18
|
+
from PyQt5.QtCore import Qt
|
|
19
|
+
|
|
20
|
+
class CollapsibleLayout(QBoxLayout):
|
|
21
|
+
def __init__(self, parent, direction):
|
|
22
|
+
QBoxLayout.__init__(self, direction, parent)
|
|
23
|
+
self._index = 0
|
|
24
|
+
self._widgets = []
|
|
25
|
+
|
|
26
|
+
def addWidget(self, widget, group_name, expanded=True):
|
|
27
|
+
QBoxLayout.addWidget(self, self._create_header(group_name, expanded))
|
|
28
|
+
QBoxLayout.addWidget(self, widget)
|
|
29
|
+
self._widgets.append(widget)
|
|
30
|
+
widget.setVisible(expanded)
|
|
31
|
+
self._index += 1
|
|
32
|
+
|
|
33
|
+
def _create_header(self, name, expanded):
|
|
34
|
+
button = QToolButton(self.parent())
|
|
35
|
+
button.setStyleSheet('QToolButton { border: none; }')
|
|
36
|
+
button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
|
|
37
|
+
button.setArrowType(self._get_arrow(expanded))
|
|
38
|
+
button.setText(name)
|
|
39
|
+
button.setCheckable(True)
|
|
40
|
+
button.setChecked(True)
|
|
41
|
+
button.clicked.connect(self._handle_toggle(button, self._index))
|
|
42
|
+
return button
|
|
43
|
+
|
|
44
|
+
def _handle_toggle(self, button, index):
|
|
45
|
+
def _handler(checked):
|
|
46
|
+
button.setArrowType(self._get_arrow(checked))
|
|
47
|
+
self._widgets[index].setVisible(checked)
|
|
48
|
+
return _handler
|
|
49
|
+
|
|
50
|
+
def _get_arrow(self, expanded):
|
|
51
|
+
return Qt.DownArrow if expanded else Qt.RightArrow
|
|
52
|
+
|
|
53
|
+
class VCollapsibleLayout(CollapsibleLayout):
|
|
54
|
+
def __init__(self, parent):
|
|
55
|
+
CollapsibleLayout.__init__(self, parent, QBoxLayout.TopToBottom)
|
|
56
|
+
|
|
57
|
+
class HCollapsibleLayout(CollapsibleLayout):
|
|
58
|
+
def __init__(self, parent):
|
|
59
|
+
CollapsibleLayout.__init__(self, parent, QBoxLayout.LeftToRight)
|
|
@@ -0,0 +1,48 @@
|
|
|
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.QtWidgets import QGroupBox, QGridLayout, QVBoxLayout
|
|
18
|
+
from PyQt5.QtCore import Qt
|
|
19
|
+
|
|
20
|
+
class LinkGroupBox(QGroupBox):
|
|
21
|
+
def __init__(self, name, parent, linkable_widget):
|
|
22
|
+
QGroupBox.__init__(self, name, parent)
|
|
23
|
+
self._linkable_widget = linkable_widget
|
|
24
|
+
if hasattr(self._linkable_widget, 'setLinkDisplay'):
|
|
25
|
+
self._linkable_widget.setLinkDisplay(self)
|
|
26
|
+
|
|
27
|
+
def mouseDoubleClickEvent(self, _event):
|
|
28
|
+
if hasattr(self._linkable_widget, 'linkRequested'):
|
|
29
|
+
self._linkable_widget.linkRequested()
|
|
30
|
+
|
|
31
|
+
def wrap_in_group_box(widget, parent, name, alignment=Qt.AlignTop):
|
|
32
|
+
group_box = LinkGroupBox(name, parent, widget)
|
|
33
|
+
group_box_layout = QGridLayout(group_box)
|
|
34
|
+
if alignment is not None:
|
|
35
|
+
group_box_layout.addWidget(widget, 0, 0, alignment)
|
|
36
|
+
else:
|
|
37
|
+
group_box_layout.addWidget(widget, 0, 0)
|
|
38
|
+
return group_box
|
|
39
|
+
|
|
40
|
+
def wrap_many_in_group_box(widgets, parent, name, alignment=Qt.AlignTop):
|
|
41
|
+
group_box = QGroupBox(name, parent)
|
|
42
|
+
group_box_layout = QVBoxLayout(group_box)
|
|
43
|
+
for widget in widgets:
|
|
44
|
+
if alignment is not None:
|
|
45
|
+
group_box_layout.addWidget(widget, alignment)
|
|
46
|
+
else:
|
|
47
|
+
group_box_layout.addWidget(widget)
|
|
48
|
+
return group_box
|