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.
Files changed (133) hide show
  1. pyqttoolkit/__init__.py +20 -0
  2. pyqttoolkit/_version.py +21 -0
  3. pyqttoolkit/application/__init__.py +18 -0
  4. pyqttoolkit/application/application.py +86 -0
  5. pyqttoolkit/application/garbage_collector.py +65 -0
  6. pyqttoolkit/colors/__init__.py +84 -0
  7. pyqttoolkit/config/__init__.py +20 -0
  8. pyqttoolkit/config/base.py +22 -0
  9. pyqttoolkit/config/composite.py +31 -0
  10. pyqttoolkit/config/default.py +27 -0
  11. pyqttoolkit/config/environment.py +35 -0
  12. pyqttoolkit/config/multi_json_file.py +93 -0
  13. pyqttoolkit/config/with_signal.py +30 -0
  14. pyqttoolkit/config/with_value_changed.py +31 -0
  15. pyqttoolkit/data/__init__.py +21 -0
  16. pyqttoolkit/data/bits.py +23 -0
  17. pyqttoolkit/data/cache.py +64 -0
  18. pyqttoolkit/data/interval.py +26 -0
  19. pyqttoolkit/data/message.py +29 -0
  20. pyqttoolkit/data/names.py +24 -0
  21. pyqttoolkit/datetime/__init__.py +16 -0
  22. pyqttoolkit/datetime/datetime.py +51 -0
  23. pyqttoolkit/datetime/qdatetime.py +53 -0
  24. pyqttoolkit/dependencies/__init__.py +56 -0
  25. pyqttoolkit/errors/__init__.py +19 -0
  26. pyqttoolkit/exceptions/__init__.py +18 -0
  27. pyqttoolkit/filesystem/__init__.py +24 -0
  28. pyqttoolkit/logs/__init__.py +140 -0
  29. pyqttoolkit/metadata/__init__.py +23 -0
  30. pyqttoolkit/metadata/dataframe_metadata_manager.py +105 -0
  31. pyqttoolkit/metadata/dict_metadata_manager.py +63 -0
  32. pyqttoolkit/metadata/id_generator.py +20 -0
  33. pyqttoolkit/metadata/metadata_manager_base.py +54 -0
  34. pyqttoolkit/models/__init__.py +26 -0
  35. pyqttoolkit/models/code_text.py +43 -0
  36. pyqttoolkit/models/dataframetable.py +135 -0
  37. pyqttoolkit/models/dictpropertytree.py +155 -0
  38. pyqttoolkit/models/dicttable.py +140 -0
  39. pyqttoolkit/models/json_text.py +52 -0
  40. pyqttoolkit/models/list_model.py +52 -0
  41. pyqttoolkit/models/module.py +26 -0
  42. pyqttoolkit/models/roles.py +23 -0
  43. pyqttoolkit/models/series_data.py +68 -0
  44. pyqttoolkit/models/span.py +36 -0
  45. pyqttoolkit/models/validation.py +27 -0
  46. pyqttoolkit/modules/__init__.py +18 -0
  47. pyqttoolkit/modules/events/__init__.py +19 -0
  48. pyqttoolkit/modules/events/add_signal.py +31 -0
  49. pyqttoolkit/modules/events/module_opening.py +21 -0
  50. pyqttoolkit/modules/events/rejectable.py +29 -0
  51. pyqttoolkit/modules/launcher_config.py +55 -0
  52. pyqttoolkit/modules/module.py +111 -0
  53. pyqttoolkit/properties/__init__.py +18 -0
  54. pyqttoolkit/properties/auto.py +221 -0
  55. pyqttoolkit/properties/throttled.py +77 -0
  56. pyqttoolkit/scripting/__init__.py +19 -0
  57. pyqttoolkit/scripting/script_context.py +36 -0
  58. pyqttoolkit/scripting/script_runner.py +132 -0
  59. pyqttoolkit/scripting/script_template.py +42 -0
  60. pyqttoolkit/services/__init__.py +32 -0
  61. pyqttoolkit/services/autosave.py +41 -0
  62. pyqttoolkit/services/email.py +25 -0
  63. pyqttoolkit/services/event_registry.py +52 -0
  64. pyqttoolkit/services/file_dialog.py +71 -0
  65. pyqttoolkit/services/hotkey_manager.py +137 -0
  66. pyqttoolkit/services/link_manager.py +135 -0
  67. pyqttoolkit/services/message_board.py +90 -0
  68. pyqttoolkit/services/module_service.py +154 -0
  69. pyqttoolkit/services/object_converter.py +45 -0
  70. pyqttoolkit/services/project_updater.py +149 -0
  71. pyqttoolkit/services/task_runner.py +281 -0
  72. pyqttoolkit/services/theme_manager.py +46 -0
  73. pyqttoolkit/services/tool_window_service.py +46 -0
  74. pyqttoolkit/viewmanagers/__init__.py +17 -0
  75. pyqttoolkit/viewmanagers/tool.py +23 -0
  76. pyqttoolkit/views/__init__.py +50 -0
  77. pyqttoolkit/views/bulk_value_selector.py +112 -0
  78. pyqttoolkit/views/check_box.py +36 -0
  79. pyqttoolkit/views/code/__init__.py +18 -0
  80. pyqttoolkit/views/code/code_edit.py +268 -0
  81. pyqttoolkit/views/code/python_syntax.py +226 -0
  82. pyqttoolkit/views/combo_box.py +101 -0
  83. pyqttoolkit/views/count_selector.py +59 -0
  84. pyqttoolkit/views/datetime.py +111 -0
  85. pyqttoolkit/views/datetime_range_selector.py +231 -0
  86. pyqttoolkit/views/default_text_line_edit.py +50 -0
  87. pyqttoolkit/views/delegates.py +117 -0
  88. pyqttoolkit/views/dtype_edit.py +117 -0
  89. pyqttoolkit/views/editable_combo_box.py +45 -0
  90. pyqttoolkit/views/file_selector.py +51 -0
  91. pyqttoolkit/views/icon.py +67 -0
  92. pyqttoolkit/views/icon_button.py +129 -0
  93. pyqttoolkit/views/json_edit.py +21 -0
  94. pyqttoolkit/views/labelled.py +29 -0
  95. pyqttoolkit/views/layout/__init__.py +21 -0
  96. pyqttoolkit/views/layout/collabsible.py +59 -0
  97. pyqttoolkit/views/layout/group_box.py +48 -0
  98. pyqttoolkit/views/layout/group_panel.py +188 -0
  99. pyqttoolkit/views/layout/lines.py +30 -0
  100. pyqttoolkit/views/layout/link.py +38 -0
  101. pyqttoolkit/views/line_edit.py +61 -0
  102. pyqttoolkit/views/linkable.py +49 -0
  103. pyqttoolkit/views/list_view.py +43 -0
  104. pyqttoolkit/views/main_window.py +91 -0
  105. pyqttoolkit/views/metadata_selector.py +84 -0
  106. pyqttoolkit/views/modulewindow.py +81 -0
  107. pyqttoolkit/views/plot/__init__.py +26 -0
  108. pyqttoolkit/views/plot/legend_control.py +121 -0
  109. pyqttoolkit/views/plot/matplotlib/__init__.py +23 -0
  110. pyqttoolkit/views/plot/matplotlib/base.py +1056 -0
  111. pyqttoolkit/views/plot/matplotlib/colorbar.py +75 -0
  112. pyqttoolkit/views/plot/matplotlib/colormap.py +48 -0
  113. pyqttoolkit/views/plot/matplotlib/font.py +55 -0
  114. pyqttoolkit/views/plot/plot_options.py +211 -0
  115. pyqttoolkit/views/plot/plot_toolbar_options.py +133 -0
  116. pyqttoolkit/views/plot/size.py +41 -0
  117. pyqttoolkit/views/plot/tool_type.py +26 -0
  118. pyqttoolkit/views/plot/toolbar.py +100 -0
  119. pyqttoolkit/views/popout.py +41 -0
  120. pyqttoolkit/views/popup.py +41 -0
  121. pyqttoolkit/views/property_tree.py +26 -0
  122. pyqttoolkit/views/shrunk_push_button.py +34 -0
  123. pyqttoolkit/views/steppable_combo_box.py +67 -0
  124. pyqttoolkit/views/styleable.py +37 -0
  125. pyqttoolkit/views/table.py +146 -0
  126. pyqttoolkit/views/text_edit.py +31 -0
  127. pyqttoolkit/views/tool_window.py +35 -0
  128. pyqttoolkit-0.9.1.dist-info/METADATA +69 -0
  129. pyqttoolkit-0.9.1.dist-info/RECORD +133 -0
  130. pyqttoolkit-0.9.1.dist-info/WHEEL +5 -0
  131. pyqttoolkit-0.9.1.dist-info/licenses/LICENSE.txt +674 -0
  132. pyqttoolkit-0.9.1.dist-info/licenses/LICENSE_SHORT +16 -0
  133. pyqttoolkit-0.9.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,75 @@
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 pyqttoolkit.views.plot.matplotlib import MatPlotLibBase
18
+ from mpl_toolkits.axes_grid1 import Size
19
+ from mpl_toolkits.axes_grid1.mpl_axes import Axes
20
+
21
+ def _is_horizontal(position):
22
+ return position == 'top' or position == 'bottom'
23
+
24
+
25
+ def _define_axes(position, cbar_size, _gap):
26
+ if position == 'bottom':
27
+ return [Size.Fixed(cbar_size), Size.Fixed(_gap), Size.Scaled(1.0)]
28
+ elif position == 'top':
29
+ return [Size.Scaled(1.0), Size.Fixed(_gap), Size.Fixed(cbar_size)]
30
+ elif position == 'left':
31
+ return [Size.Fixed(_gap/4), Size.Fixed(cbar_size), Size.Fixed(_gap*3), Size.Scaled(1.0)]
32
+ elif position == 'right':
33
+ return [Size.Scaled(1.0), Size.Fixed(_gap), Size.Fixed(cbar_size), Size.Fixed(_gap)]
34
+ else:
35
+ return None
36
+
37
+
38
+ class MatPlotColorbar(MatPlotLibBase):
39
+ def __init__(self, parent, file_dialog_service, cbar_height=0.2, v_gap=0.6, cbar_width=0.2, h_gap=0.6,
40
+ position='bottom', **kwargs):
41
+
42
+ # First element of the tuple correspond to the nx_default or ny_default.
43
+ # The second element is the nx and ny position for _colorbar_axes.
44
+ self._nx = {'bottom': (1, 1), 'top': (1, 1), 'right': (1, 3), 'left': (4, 1)}
45
+ self._ny = {'bottom': (3, 1), 'top': (1, 3), 'right': (1, 1), 'left': (1, 1)}
46
+
47
+ super().__init__(parent,
48
+ file_dialog_service,
49
+ v_axes=[Size.Scaled(1.0)] if not _is_horizontal(position)
50
+ else _define_axes(position, cbar_height, v_gap),
51
+ h_axes=[Size.Scaled(1.0)] if _is_horizontal(position)
52
+ else _define_axes(position, cbar_width, h_gap),
53
+ nx_default=self._nx[position][0],
54
+ ny_default=self._ny[position][0], **kwargs)
55
+ self._colorbar_axes = Axes(self._figure, self._divider.get_position())
56
+ self._colorbar_axes.set_axes_locator(self._divider.new_locator(nx=self._nx[position][1], ny=self._ny[position][1]))
57
+ self._figure.add_axes(self._colorbar_axes)
58
+ self._cbar = None
59
+ self._points = None
60
+ self._position = position
61
+
62
+ def _plot_colorbar(self, label):
63
+ if self._cbar is not None:
64
+ self._colorbar_axes.clear()
65
+ self._cbar = self._figure.colorbar(self._points, self._colorbar_axes, orientation='horizontal' if
66
+ _is_horizontal(self._position) else 'vertical')
67
+ self._cbar.set_label(label)
68
+
69
+ @property
70
+ def colorbar(self):
71
+ return self._cbar
72
+
73
+ @colorbar.setter
74
+ def colorbar(self, cbar):
75
+ self._cbar = cbar
@@ -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 matplotlib.colors import LinearSegmentedColormap
18
+
19
+ class MatPlotLibColormap:
20
+ def __init__(self, colormap, limits=None, invert=False):
21
+ self._colormap = colormap.reversed() if colormap and invert else colormap
22
+ self._limits = limits
23
+
24
+ @property
25
+ def colormap(self):
26
+ return self._colormap
27
+
28
+ @property
29
+ def has_limits(self):
30
+ return self._limits is not None
31
+
32
+ @property
33
+ def limits(self):
34
+ return self._limits
35
+
36
+ @staticmethod
37
+ def from_list(values, invert=False):
38
+ if all(len(v) == 3 for v in values):
39
+ return MatPlotLibColormap(LinearSegmentedColormap.from_list('cmap', values), invert=invert)
40
+ if not all(len(v) == 2 for v in values):
41
+ return MatPlotLibColormap(None)
42
+ values = sorted(values, key=lambda v: v[0])
43
+ range_lower, range_upper = values[0][0], values[-1][0]
44
+ if range_lower == 0.0 and range_upper == 1.0:
45
+ return MatPlotLibColormap(LinearSegmentedColormap.from_list('cmap', values), invert=invert)
46
+ total_range = range_upper - range_lower
47
+ values = [((p - range_lower) / total_range, c) for p, c in values]
48
+ return MatPlotLibColormap(LinearSegmentedColormap.from_list('cmap', values), limits=(range_lower, range_upper), invert=invert)
@@ -0,0 +1,55 @@
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 matplotlib import rcParams
18
+ from matplotlib.font_manager import findfont, FontProperties
19
+ from fontTools.ttLib import TTFont
20
+ from fontTools.ttLib.tables._c_m_a_p import CmapSubtable
21
+
22
+ class MatPlotLibFont:
23
+ def __init__(self, font):
24
+ font_file = findfont(FontProperties(family=font))
25
+ self._is_ttf = font_file.endswith('.ttf')
26
+ if not self._is_ttf:
27
+ raise ValueError('Unknown font type')
28
+ self._font = TTFont(font_file)
29
+
30
+ def get_size(self, text, pointsize, dpi):
31
+ return self._get_ttf_size(text, pointsize, dpi)
32
+
33
+ def _get_ttf_size(self, text, pointsize, dpi):
34
+ cmap = self._font['cmap']
35
+ t = cmap.getcmap(3,1).cmap
36
+ s = self._font.getGlyphSet()
37
+ width = 0
38
+ for c in text:
39
+ if ord(c) in t and t[ord(c)] in s:
40
+ char = s[t[ord(c)]]
41
+ else:
42
+ char = s['.notdef']
43
+ width += char.width
44
+ units_per_em = self._font['head'].unitsPerEm
45
+ return self._convert_to_pixels(width, pointsize, dpi, units_per_em), self._convert_to_pixels(units_per_em, pointsize, dpi, units_per_em)
46
+
47
+ def _convert_to_pixels(self, value, pointsize, dpi, units_per_em):
48
+ value_pts = value * float(pointsize) / units_per_em
49
+ value_in = value_pts / 72
50
+ value_pixels = value_in * dpi
51
+ return value_pixels
52
+
53
+ @staticmethod
54
+ def default():
55
+ return MatPlotLibFont(rcParams['font.family'])
@@ -0,0 +1,211 @@
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 math
18
+
19
+ import numpy as np
20
+
21
+ from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
22
+ from PyQt5.QtCore import pyqtSignal
23
+
24
+ from pyqttoolkit.properties import AutoProperty, bind
25
+ from pyqttoolkit.views import BindableCheckBox, AutoFloatLineEdit
26
+
27
+ class PlotOptionsView(QWidget):
28
+ def __init__(self, parent, grid_lines=True, x_limits=True, y_limits=True, secondary_x_limits=False, secondary_y_limits=False):
29
+ QWidget.__init__(self, parent)
30
+
31
+ edit_width = 60
32
+ self._grid_lines = grid_lines
33
+ self._x_limits = x_limits
34
+ self._y_limits = y_limits
35
+ self._secondary_y_limits = secondary_y_limits
36
+ self._secondary_x_limits = secondary_x_limits
37
+
38
+ self.showGridLines = False
39
+ self.xAxisLowerLimit = self.xAxisUpperLimit = self.yAxisLowerLimit = self.yAxisUpperLimit = float('nan')
40
+ self.secondaryXAxisLowerLimit = self.secondaryXAxisUpperLimit = self.secondaryYAxisLowerLimit = self.secondaryYAxisUpperLimit = float('nan')
41
+
42
+ self._layout = QGridLayout(self)
43
+ if grid_lines:
44
+ self._show_gridlines = BindableCheckBox(None, self)
45
+ self._layout.addWidget(QLabel(self.tr('Show Grid Lines'), self), 0, 0)
46
+ self._layout.addWidget(self._show_gridlines, 0, 1)
47
+ bind(self, self._show_gridlines, 'showGridLines', 'checked')
48
+ else:
49
+ self._show_gridlines = None
50
+
51
+ if x_limits:
52
+ self._x_axis_lower_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
53
+ self._x_axis_upper_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
54
+ self._x_axis_lower_limit.setFixedWidth(edit_width)
55
+ self._x_axis_upper_limit.setFixedWidth(edit_width)
56
+ self._layout.addWidget(QLabel(self.tr('X Axis Limits'), self), 1, 0)
57
+ self._layout.addWidget(self._x_axis_lower_limit, 1, 1)
58
+ self._layout.addWidget(self._x_axis_upper_limit, 1, 2)
59
+ bind(self, self._x_axis_lower_limit, 'xAxisLowerLimit', 'value')
60
+ bind(self, self._x_axis_upper_limit, 'xAxisUpperLimit', 'value')
61
+ else:
62
+ self._x_axis_lower_limit = self._x_axis_upper_limit = None
63
+
64
+ if y_limits:
65
+ self._y_axis_lower_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
66
+ self._y_axis_upper_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
67
+ self._y_axis_lower_limit.setFixedWidth(edit_width)
68
+ self._y_axis_upper_limit.setFixedWidth(edit_width)
69
+ self._layout.addWidget(QLabel(self.tr('Y Axis Limits'), self), 2, 0)
70
+ self._layout.addWidget(self._y_axis_lower_limit, 2, 1)
71
+ self._layout.addWidget(self._y_axis_upper_limit, 2, 2)
72
+ bind(self, self._y_axis_lower_limit, 'yAxisLowerLimit', 'value')
73
+ bind(self, self._y_axis_upper_limit, 'yAxisUpperLimit', 'value')
74
+ else:
75
+ self._y_axis_lower_limit = self._y_axis_upper_limit = None
76
+
77
+ if secondary_y_limits:
78
+ self._secondary_y_axis_lower_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
79
+ self._secondary_y_axis_upper_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
80
+ self._secondary_y_axis_lower_limit.setFixedWidth(edit_width)
81
+ self._secondary_y_axis_upper_limit.setFixedWidth(edit_width)
82
+ self._secondary_y_limits_label = QLabel(self.tr('2nd Y Axis Limits'), self)
83
+ self._layout.addWidget(self._secondary_y_limits_label, 3, 0)
84
+ self._layout.addWidget(self._secondary_y_axis_lower_limit, 3, 1)
85
+ self._layout.addWidget(self._secondary_y_axis_upper_limit, 3, 2)
86
+ bind(self, self._secondary_y_axis_lower_limit, 'secondaryYAxisLowerLimit', 'value')
87
+ bind(self, self._secondary_y_axis_upper_limit, 'secondaryYAxisUpperLimit', 'value')
88
+ else:
89
+ self._secondary_y_axis_lower_limit = self._secondary_y_axis_upper_limit = self._secondary_y_limits_label = None
90
+
91
+ if secondary_x_limits:
92
+ self._secondary_x_axis_lower_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
93
+ self._secondary_x_axis_upper_limit = AutoFloatLineEdit(self, allow_inf=False, formatter=self._value_formatter)
94
+ self._secondary_x_axis_lower_limit.setFixedWidth(edit_width)
95
+ self._secondary_x_axis_upper_limit.setFixedWidth(edit_width)
96
+ self._secondary_x_limits_label = QLabel(self.tr('2nd X Axis Limits'), self)
97
+ self._layout.addWidget(self._secondary_x_limits_label, 4, 0)
98
+ self._layout.addWidget(self._secondary_x_axis_lower_limit, 4, 1)
99
+ self._layout.addWidget(self._secondary_x_axis_upper_limit, 4, 2)
100
+ bind(self, self._secondary_x_axis_lower_limit, 'secondaryXAxisLowerLimit', 'value')
101
+ bind(self, self._secondary_x_axis_upper_limit, 'secondaryXAxisUpperLimit', 'value')
102
+ else:
103
+ self._secondary_x_axis_lower_limit = self._secondary_x_axis_upper_limit = self._secondary_x_limits_label = None
104
+
105
+
106
+ showGridLinesChanged = pyqtSignal(bool)
107
+ xAxisLowerLimitChanged = pyqtSignal(float)
108
+ xAxisUpperLimitChanged = pyqtSignal(float)
109
+ xAxisLimitsChanged = pyqtSignal(float, float)
110
+ yAxisLowerLimitChanged = pyqtSignal(float)
111
+ yAxisUpperLimitChanged = pyqtSignal(float)
112
+ yAxisLimitsChanged = pyqtSignal(float, float)
113
+ secondaryXAxisLowerLimitChanged = pyqtSignal(float)
114
+ secondaryXAxisUpperLimitChanged = pyqtSignal(float)
115
+ secondaryXAxisLimitsChanged = pyqtSignal(float, float)
116
+ secondaryYAxisLowerLimitChanged = pyqtSignal(float)
117
+ secondaryYAxisUpperLimitChanged = pyqtSignal(float)
118
+ secondaryYAxisLimitsChanged = pyqtSignal(float, float)
119
+
120
+ showGridLines = AutoProperty(bool)
121
+ xAxisLowerLimit = AutoProperty(float)
122
+ xAxisUpperLimit = AutoProperty(float)
123
+ yAxisLowerLimit = AutoProperty(float)
124
+ yAxisUpperLimit = AutoProperty(float)
125
+ secondaryYAxisLowerLimit = AutoProperty(float)
126
+ secondaryYAxisUpperLimit = AutoProperty(float)
127
+ secondaryXAxisLowerLimit = AutoProperty(float)
128
+ secondaryXAxisUpperLimit = AutoProperty(float)
129
+
130
+ def _value_formatter(self, value):
131
+ if isinstance(value, str):
132
+ return value
133
+ if value == np.inf:
134
+ return 'inf'
135
+ if value == -np.inf:
136
+ return '-inf'
137
+ if np.isnan(value):
138
+ return 'Auto'
139
+ value = int(value * 100) / 100
140
+ if value == 0:
141
+ return '0.0'
142
+ precision = min(int(math.log10(abs(value))), 2) + 2
143
+ return f'{value:.{precision}g}'
144
+
145
+ @property
146
+ def grid_lines(self):
147
+ return self._grid_lines
148
+
149
+ def setXLimits(self, x_lower, x_upper):
150
+ blocked = self.blockSignals(True)
151
+ self.xAxisLowerLimit = x_lower
152
+ self._x_axis_lower_limit.value = x_lower
153
+ self.xAxisUpperLimit = x_upper
154
+ self._x_axis_upper_limit.value = x_upper
155
+ self.blockSignals(blocked)
156
+ self.xAxisLimitsChanged.emit(x_lower, x_upper)
157
+
158
+ def setYLimits(self, y_lower, y_upper):
159
+ blocked = self.blockSignals(True)
160
+ self.yAxisLowerLimit = y_lower
161
+ self._y_axis_lower_limit.value = y_lower
162
+ self.yAxisUpperLimit = y_upper
163
+ self._y_axis_upper_limit.value = y_upper
164
+ self.blockSignals(blocked)
165
+ self.yAxisLimitsChanged.emit(y_lower, y_upper)
166
+
167
+ def setSecondaryXLimits(self, x_lower, x_upper):
168
+ blocked = self.blockSignals(True)
169
+ self.secondaryXAxisLowerLimit = x_lower
170
+ self._secondary_x_axis_lower_limit.value = x_lower
171
+ self.secondaryXAxisUpperLimit = x_upper
172
+ self._secondary_x_axis_upper_limit.value = x_upper
173
+ self.blockSignals(blocked)
174
+ self.secondaryXAxisLimitsChanged.emit(x_lower, x_upper)
175
+
176
+ def setSecondaryYLimits(self, y_lower, y_upper):
177
+ blocked = self.blockSignals(True)
178
+ self.secondaryYAxisLowerLimit = y_lower
179
+ self._secondary_y_axis_lower_limit.value = y_lower
180
+ self.secondaryYAxisUpperLimit = y_upper
181
+ self._secondary_y_axis_upper_limit.value = y_upper
182
+ self.blockSignals(blocked)
183
+ self.secondaryYAxisLimitsChanged.emit(y_lower, y_upper)
184
+
185
+ @property
186
+ def x_limits(self):
187
+ return self._x_limits
188
+
189
+ @property
190
+ def y_limits(self):
191
+ return self._y_limits
192
+
193
+ @property
194
+ def secondary_y_limits(self):
195
+ return self._secondary_y_limits
196
+
197
+ @property
198
+ def secondary_x_limits(self):
199
+ return self._secondary_x_limits
200
+
201
+ def setSecondaryYLimitsEnabled(self, enabled):
202
+ if self._secondary_y_limits:
203
+ self._secondary_y_axis_lower_limit.setEnabled(enabled)
204
+ self._secondary_y_axis_upper_limit.setEnabled(enabled)
205
+ self._secondary_y_limits_label.setEnabled(enabled)
206
+
207
+ def setSecondaryXLimitsEnabled(self, enabled):
208
+ if self._secondary_x_limits:
209
+ self._secondary_x_axis_lower_limit.setEnabled(enabled)
210
+ self._secondary_x_axis_upper_limit.setEnabled(enabled)
211
+ self._secondary_x_limits_label.setEnabled(enabled)
@@ -0,0 +1,133 @@
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, QGridLayout, QSizePolicy, QVBoxLayout
18
+ from PyQt5.QtCore import Qt
19
+
20
+ from pyqttoolkit.views import PopoutWidget, make_styleable
21
+ from pyqttoolkit.colors import interpolate_rgb, format_color, ColorFormat
22
+ from pyqttoolkit.views.layout import HLine
23
+
24
+ from .tool_type import ToolType
25
+ from .toolbar import PlotToolbarWidget
26
+ from .plot_options import PlotOptionsView
27
+ from .legend_control import LegendControlView
28
+
29
+ def _to_qt_color(color):
30
+ return tuple(c * 255 for c in color)
31
+
32
+ class ToolbarContainer(QWidget):
33
+ pass
34
+
35
+ ToolbarContainer = make_styleable(ToolbarContainer)
36
+
37
+ class PlotToolbarOptions(QWidget):
38
+ def __init__(self, parent, series_style, theme_manager, plot, options=None, legend_control=None, right_padding=0, has_extra_tools=False):
39
+ QWidget.__init__(self, parent)
40
+ self._theme_manager = theme_manager
41
+ self._plot = plot
42
+ self._toolbar_container = ToolbarContainer(plot)
43
+
44
+ self._background_color_qt = _to_qt_color(series_style.get_color_from_key('axes_background'))
45
+ self._foreground_color_qt = _to_qt_color(series_style.get_color_from_key('axes_foreground'))
46
+ interpolation = interpolate_rgb(self._background_color_qt, self._foreground_color_qt, 3)
47
+ self._icon_hover_color = interpolation[1]
48
+
49
+ self._toolbar = PlotToolbarWidget(self._toolbar_container, plot, self._foreground_color_qt, self._icon_hover_color)
50
+ self._toolbar.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
51
+ self._layout = QGridLayout(self)
52
+ self._layout.setContentsMargins(0, 0, 0, 0)
53
+ self._layout.setSpacing(0)
54
+ self._layout.addWidget(plot, 0, 0, 1, 3)
55
+ self._background_opacity = 0.8
56
+ plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
57
+ if options is not None:
58
+ if isinstance(options, PlotOptionsView):
59
+ plot.setOptionsView(options)
60
+ self._options = self._add_popout(ToolType.options, options)
61
+ else:
62
+ self._options = None
63
+
64
+ if legend_control is not None:
65
+ if isinstance(legend_control, LegendControlView):
66
+ plot.setLegendControl(legend_control)
67
+ self._legend_control = self._add_popout(ToolType.legend, legend_control)
68
+ legend_control.hasHiddenSeriesChanged.connect(self._handle_has_hidden_series_changed)
69
+ else:
70
+ self._legend_control = None
71
+
72
+ self._toolbar_layout = QVBoxLayout(self._toolbar_container)
73
+ self._toolbar_layout.addWidget(self._toolbar, Qt.AlignTop)
74
+
75
+ self._layout.addWidget(self._toolbar_container, 0, 1, Qt.AlignRight | Qt.AlignTop)
76
+ self._layout.setColumnStretch(0, 1)
77
+ self._layout.setColumnStretch(1, 0)
78
+ if right_padding > 0:
79
+ self._padding_widget = QWidget(self)
80
+ self._padding_widget.setVisible(False)
81
+ self._layout.addWidget(self._padding_widget, 0, 2)
82
+ self._layout.setColumnMinimumWidth(2, int(right_padding))
83
+ else:
84
+ self._padding_widget = None
85
+
86
+ if not has_extra_tools:
87
+ self._toolbar_layout.addStretch()
88
+
89
+ def _handle_has_hidden_series_changed(self, has_hidden):
90
+ color = None if not has_hidden else self._theme_manager.get_color('highlight')
91
+ self._toolbar.setColor(ToolType.legend, color)
92
+
93
+ def _handle_tool_activated(self, tool_type, view):
94
+ def _(tool, active):
95
+ if tool == tool_type:
96
+ view.setVisible(active)
97
+ if self._padding_widget:
98
+ self._padding_widget.setVisible(active)
99
+ if active:
100
+ self._toolbar_container.setStyleSheet("ToolbarContainer {{ background-color: {} }}".format(format_color(self._background_color_qt, ColorFormat.rgba_string_256, self._background_opacity)))
101
+ self._layout.setAlignment(self._toolbar_container, Qt.AlignRight)
102
+ if self._padding_widget:
103
+ self._padding_widget.setStyleSheet("QWidget {{ background-color: {} }}".format(format_color(self._background_color_qt, ColorFormat.rgba_string_256, self._background_opacity)))
104
+ else:
105
+ self._layout.setAlignment(self._toolbar_container, Qt.AlignRight | Qt.AlignTop)
106
+ self._toolbar_container.setStyleSheet("")
107
+ if self._padding_widget:
108
+ self._padding_widget.setStyleSheet("")
109
+ return _
110
+
111
+ def addTool(self, tool_widget):
112
+ self._toolbar_layout.addWidget(HLine(self._plot), Qt.AlignTop)
113
+ self._toolbar_layout.addWidget(tool_widget, Qt.AlignTop | Qt.AlignCenter)
114
+ self._toolbar_layout.addStretch()
115
+
116
+ @property
117
+ def icon_color(self):
118
+ return self._foreground_color_qt
119
+
120
+ @property
121
+ def icon_hover_color(self):
122
+ return self._icon_hover_color
123
+
124
+ @property
125
+ def toolbar(self):
126
+ return self._toolbar
127
+
128
+ def _add_popout(self, tool_type, view):
129
+ popout = PopoutWidget(self, view, self._background_color_qt, self._foreground_color_qt, self._background_opacity)
130
+ popout.setVisible(False)
131
+ self._layout.addWidget(popout, 0, 0, Qt.AlignRight)
132
+ self._toolbar.toolActivated.connect(self._handle_tool_activated(tool_type, popout))
133
+ return popout
@@ -0,0 +1,41 @@
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 mpl_toolkits.axes_grid1 import Size
18
+
19
+ class ToggleableFixed(Size._Base):
20
+ def __init__(self, size):
21
+ self._size = size
22
+ self._active = False
23
+
24
+ def activate(self):
25
+ self._active = True
26
+
27
+ def deactivate(self):
28
+ self._active = False
29
+
30
+ def get_size(self, _renderer):
31
+ return 0.0, self._size if self._active else 0.0
32
+
33
+ class MaxWidth(Size.MaxWidth):
34
+ def remove_artist(self, artist):
35
+ if artist in self._artist_list:
36
+ self._artist_list.remove(artist)
37
+
38
+ def get_size(self, renderer):
39
+ if self._artist_list:
40
+ return Size.MaxWidth.get_size(self, renderer)
41
+ return 0, 0
@@ -0,0 +1,26 @@
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
+ class ToolType(Enum):
20
+ zoom = 0
21
+ polygon = 1
22
+ span = 2
23
+ pan = 3
24
+ reset = 4
25
+ options = 5
26
+ legend = 6