batoolset-gui 0.2.0__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 (74) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/customdialog.py +136 -0
  3. batoolset/GUI/exception_handler.py +74 -0
  4. batoolset/GUI/list_gui.py +514 -0
  5. batoolset/GUI/multi_image_display.py +1038 -0
  6. batoolset/GUI/open.py +325 -0
  7. batoolset/GUI/overlay_hints.py +45 -0
  8. batoolset/GUI/paint2.py +1369 -0
  9. batoolset/GUI/scrollablepaint.py +1165 -0
  10. batoolset/dialogs/__init__.py +0 -0
  11. batoolset/dialogs/opensave.py +228 -0
  12. batoolset/drawings/__init__.py +0 -0
  13. batoolset/drawings/math_text.py +294 -0
  14. batoolset/drawings/pdf_svg_renderer.py +376 -0
  15. batoolset/drawings/shapes/Position.py +183 -0
  16. batoolset/drawings/shapes/__init__.py +0 -0
  17. batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
  18. batoolset/drawings/shapes/circle2d.py +99 -0
  19. batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
  20. batoolset/drawings/shapes/ellipse2d.py +336 -0
  21. batoolset/drawings/shapes/freehand2d.py +235 -0
  22. batoolset/drawings/shapes/group.py +1985 -0
  23. batoolset/drawings/shapes/image2d.py +3157 -0
  24. batoolset/drawings/shapes/line2d.py +579 -0
  25. batoolset/drawings/shapes/point2d.py +257 -0
  26. batoolset/drawings/shapes/polygon2d.py +357 -0
  27. batoolset/drawings/shapes/polyline2d.py +235 -0
  28. batoolset/drawings/shapes/rect2d.py +388 -0
  29. batoolset/drawings/shapes/rectangle2d.py +601 -0
  30. batoolset/drawings/shapes/scalebar.py +310 -0
  31. batoolset/drawings/shapes/serializablefont.py +133 -0
  32. batoolset/drawings/shapes/serializableguiparams.py +14 -0
  33. batoolset/drawings/shapes/square2d.py +102 -0
  34. batoolset/drawings/shapes/test_publication_style.py +220 -0
  35. batoolset/drawings/shapes/textorientation.py +45 -0
  36. batoolset/drawings/shapes/tools.py +467 -0
  37. batoolset/drawings/shapes/transform2D.py +350 -0
  38. batoolset/drawings/shapes/txt2d.py +615 -0
  39. batoolset/drawings/svg_clip_renderer.py +247 -0
  40. batoolset/drawings/svg_embed.py +165 -0
  41. batoolset/drawings/svg_text_renderer.py +183 -0
  42. batoolset/drawings/widgets/__init__.py +0 -0
  43. batoolset/drawings/widgets/col.py +343 -0
  44. batoolset/drawings/widgets/image_group.py +248 -0
  45. batoolset/drawings/widgets/image_organizer.py +96 -0
  46. batoolset/drawings/widgets/paint.py +184 -0
  47. batoolset/drawings/widgets/vectorial.py +256 -0
  48. batoolset/figure/__init__.py +0 -0
  49. batoolset/figure/alignment.py +822 -0
  50. batoolset/figure/column.py +1117 -0
  51. batoolset/figure/fig_tools.py +285 -0
  52. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  53. batoolset/figure/panel.py +697 -0
  54. batoolset/figure/row.py +852 -0
  55. batoolset/figure/user_selection.py +287 -0
  56. batoolset/icons/__init__.py +0 -0
  57. batoolset/icons/tools.py +248 -0
  58. batoolset/luts/__init__.py +0 -0
  59. batoolset/luts/lut_minimal_test.py +1308 -0
  60. batoolset/luts/lutcreatorfromformula.py +209 -0
  61. batoolset/luts/luteditor.py +243 -0
  62. batoolset/pyqts/__init__.py +0 -0
  63. batoolset/pyqts/pleasewaitdialog.py +14 -0
  64. batoolset/pyqts/tools.py +760 -0
  65. batoolset/serializations/__init__.py +0 -0
  66. batoolset/serializations/tools.py +1338 -0
  67. batoolset/theme.py +647 -0
  68. batoolset/uis/__init__.py +0 -0
  69. batoolset/uis/blinker.py +56 -0
  70. batoolset_gui-0.2.0.dist-info/METADATA +54 -0
  71. batoolset_gui-0.2.0.dist-info/RECORD +74 -0
  72. batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
  73. batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
  74. batoolset_gui-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,220 @@
1
+ """
2
+ Test for publication_style feature on Image2D matplotlib figures.
3
+
4
+ publication_style only takes effect at export (save()), not during display.
5
+
6
+ Run with:
7
+ python BAtools_pkg/batoolset/drawings/shapes/test_publication_style.py
8
+ """
9
+ import sys
10
+ import re
11
+ import numpy as np
12
+ import matplotlib
13
+ matplotlib.use('Agg')
14
+ import matplotlib.pyplot as plt
15
+
16
+ from batoolset.settings.global_settings import set_UI
17
+ set_UI()
18
+ from qtpy.QtWidgets import QApplication
19
+ from batoolset.drawings.shapes.image2d import Image2D
20
+
21
+
22
+ def make_figure(font_size=12, line_width=1.5):
23
+ fig, ax = plt.subplots()
24
+ x = np.linspace(0, 2 * np.pi, 100)
25
+ ax.plot(x, np.sin(x), linewidth=line_width, label='sin')
26
+ ax.plot(x, np.cos(x), linewidth=line_width, label='cos')
27
+ ax.set_xlabel('X axis', fontsize=font_size)
28
+ ax.set_ylabel('Y axis', fontsize=font_size)
29
+ ax.set_title('Test figure', fontsize=font_size)
30
+ ax.tick_params(labelsize=font_size)
31
+ ax.legend(fontsize=font_size)
32
+ return fig
33
+
34
+
35
+ def line_widths(fig):
36
+ return [line.get_linewidth() for ax in fig.get_axes() for line in ax.get_lines()]
37
+
38
+
39
+ def extract_stroke_widths(svg_bytes):
40
+ return [float(v) for v in re.findall(r'stroke-width:\s*([\d.]+)', svg_bytes.decode('utf-8'))]
41
+
42
+
43
+ def all_text_artists(fig):
44
+ import matplotlib.text as mtext
45
+ return fig.findobj(mtext.Text)
46
+
47
+
48
+ def font_families(fig):
49
+ return [t.get_fontfamily() for t in all_text_artists(fig)]
50
+
51
+
52
+ def test_defaults():
53
+ fig = make_figure()
54
+ img = Image2D(fig)
55
+ assert img.publication_style is True
56
+ assert img.publication_font_size == 12
57
+ assert img.publication_line_width == 0.65
58
+ from batoolset.drawings.shapes.image2d import _best_helvetica_clone
59
+ assert img.publication_font == _best_helvetica_clone()
60
+ print("PASS default parameter values are correct")
61
+ plt.close(fig)
62
+
63
+
64
+ def test_display_buffer_unaffected():
65
+ """_toBuffer (display path) must NOT apply publication style."""
66
+ fig = make_figure(line_width=2.0)
67
+ img = Image2D(fig, publication_style=True, publication_font_size=6, publication_line_width=0.3)
68
+
69
+ before = line_widths(fig)
70
+ buf = img._toBuffer(bufferType='svg')
71
+ after = line_widths(fig)
72
+
73
+ assert buf is not None
74
+ for b, a in zip(before, after):
75
+ assert abs(b - a) < 1e-6, f"Display buffer must not modify line widths: {b} -> {a}"
76
+
77
+ # The display SVG should NOT have publication-size strokes
78
+ sw = extract_stroke_widths(buf.read())
79
+ if sw:
80
+ assert max(sw) > 0.3, "Display SVG should retain original stroke widths"
81
+ print("PASS display buffer is unaffected by publication_style")
82
+ plt.close(fig)
83
+
84
+
85
+ def test_export_buffer_styled():
86
+ """_toBuffer(..., apply_publication=True) must apply and restore style."""
87
+ fig = make_figure(line_width=2.0)
88
+ img = Image2D(fig, publication_style=True, publication_font_size=6, publication_line_width=0.3)
89
+
90
+ before = line_widths(fig)
91
+ buf = img._toBuffer(bufferType='svg', apply_publication=True)
92
+ after = line_widths(fig)
93
+
94
+ assert buf is not None
95
+ # Figure must be restored
96
+ for b, a in zip(before, after):
97
+ assert abs(b - a) < 1e-6, f"Line width not restored after export render: {b} -> {a}"
98
+
99
+ # Export SVG must have smaller strokes
100
+ sw = extract_stroke_widths(buf.read())
101
+ if sw:
102
+ assert max(sw) < 2.0, f"Export SVG stroke-widths not reduced: max={max(sw):.2f}"
103
+ print(f"PASS export buffer styled: max stroke-width {2.0:.2f} -> {max(sw):.2f}")
104
+ else:
105
+ print("SKIP no stroke-width in SVG (backend-dependent)")
106
+ plt.close(fig)
107
+
108
+
109
+ def test_publication_style_off():
110
+ """publication_style=False: both display and export buffers are unstyled."""
111
+ fig = make_figure(line_width=1.5)
112
+ img = Image2D(fig, publication_style=False)
113
+
114
+ before = line_widths(fig)
115
+ img._toBuffer(bufferType='svg')
116
+ img._toBuffer(bufferType='svg', apply_publication=False)
117
+ after = line_widths(fig)
118
+
119
+ for b, a in zip(before, after):
120
+ assert abs(b - a) < 1e-6, f"Line width changed unexpectedly: {b} -> {a}"
121
+ print("PASS publication_style=False leaves figure unchanged")
122
+ plt.close(fig)
123
+
124
+
125
+ def test_font_none_leaves_family_unchanged():
126
+ """publication_font=None must not alter font families."""
127
+ fig = make_figure()
128
+ img = Image2D(fig, publication_style=True, publication_font=None)
129
+
130
+ before = font_families(fig)
131
+ img._toBuffer(bufferType='svg', apply_publication=True)
132
+ after = font_families(fig)
133
+
134
+ assert before == after, "Font families changed despite publication_font=None"
135
+ print("PASS publication_font=None leaves font families unchanged")
136
+ plt.close(fig)
137
+
138
+
139
+ def test_font_applied_and_restored():
140
+ """publication_font changes font family during export and restores afterwards."""
141
+ fig = make_figure()
142
+ img = Image2D(fig, publication_style=True, publication_font='DejaVu Serif')
143
+
144
+ before = font_families(fig)
145
+ img._toBuffer(bufferType='svg', apply_publication=True)
146
+ after = font_families(fig)
147
+
148
+ assert before == after, f"Font families not restored after export: {before} -> {after}"
149
+ print("PASS publication_font applied during export and original font restored")
150
+ plt.close(fig)
151
+
152
+
153
+ def test_font_not_applied_on_display():
154
+ """publication_font must not affect the display buffer."""
155
+ fig = make_figure()
156
+ img = Image2D(fig, publication_style=True, publication_font='DejaVu Serif')
157
+
158
+ before = font_families(fig)
159
+ img._toBuffer(bufferType='svg') # display path — no publication
160
+ after = font_families(fig)
161
+
162
+ assert before == after, "Font families changed by display buffer"
163
+ print("PASS publication_font not applied on display buffer")
164
+ plt.close(fig)
165
+
166
+
167
+ def test_toggle_after_construction():
168
+ """Enabling all publication options after construction takes effect on next export."""
169
+ fig = make_figure(line_width=2.0)
170
+ img = Image2D(fig)
171
+ img.publication_style = True
172
+ img.publication_font_size = 5
173
+ img.publication_line_width = 0.25
174
+ img.publication_font = 'DejaVu Serif'
175
+
176
+ before_lw = line_widths(fig)
177
+ before_ff = font_families(fig)
178
+ buf = img._toBuffer(bufferType='svg', apply_publication=True)
179
+ after_lw = line_widths(fig)
180
+ after_ff = font_families(fig)
181
+
182
+ for b, a in zip(before_lw, after_lw):
183
+ assert abs(b - a) < 1e-6, f"Line width not restored: {b} -> {a}"
184
+ assert before_ff == after_ff, f"Font families not restored: {before_ff} -> {after_ff}"
185
+
186
+ sw = extract_stroke_widths(buf.read())
187
+ if sw:
188
+ assert max(sw) < 2.0, f"Export stroke-widths not reduced: max={max(sw):.2f}"
189
+ print("PASS toggle after construction applies on export (line width, font size, font family)")
190
+ plt.close(fig)
191
+
192
+
193
+ if __name__ == '__main__':
194
+ app = QApplication.instance() or QApplication(sys.argv)
195
+
196
+ tests = [
197
+ test_defaults,
198
+ test_display_buffer_unaffected,
199
+ test_export_buffer_styled,
200
+ test_publication_style_off,
201
+ test_font_none_leaves_family_unchanged,
202
+ test_font_applied_and_restored,
203
+ test_font_not_applied_on_display,
204
+ test_toggle_after_construction,
205
+ ]
206
+
207
+ failed = []
208
+ for t in tests:
209
+ try:
210
+ t()
211
+ except Exception as e:
212
+ print(f"FAIL {t.__name__}: {e}")
213
+ failed.append(t.__name__)
214
+
215
+ print()
216
+ if failed:
217
+ print(f"{len(failed)} test(s) FAILED: {', '.join(failed)}")
218
+ sys.exit(1)
219
+ else:
220
+ print(f"All {len(tests)} tests passed.")
@@ -0,0 +1,45 @@
1
+ def get_angle(text, verbose=False):
2
+ if not text:
3
+ # raise ValueError("Invalid orientation text: {}".format(self.text))
4
+ if verbose:
5
+ print(f'unknown text orientation {text}, assuming horizontal')
6
+ return 0
7
+ # angle =0
8
+ if 'h' in text.lower() or 'x' in text.lower():
9
+ angle = 0
10
+ elif 'v' in text.lower() or 'y' in text.lower():
11
+ angle = 90
12
+ else:
13
+ # raise ValueError("Invalid orientation text: {}".format(self.text))
14
+ if verbose:
15
+ print(f'unknown text orientation {text}, assuming horizontal')
16
+ angle =0
17
+ # if '-' in text:
18
+ # angle=-angle
19
+ return angle
20
+
21
+
22
+ class TextOrientation:
23
+ def __init__(self, text):
24
+ self.text = text
25
+
26
+ def get_angle(self, verbose=False):
27
+ return get_angle(self.text, verbose=verbose)
28
+
29
+
30
+
31
+ if __name__ == '__main__':
32
+ orientation = TextOrientation('horizontal')
33
+ angle = orientation.get_angle()
34
+ print(angle) # Output: 0
35
+
36
+ orientation = TextOrientation('Vertical')
37
+ angle = orientation.get_angle()
38
+ print(angle) # Output: 90
39
+
40
+ orientation = TextOrientation(None)
41
+ angle = orientation.get_angle(verbose=TextOrientation) # Raises ValueError
42
+ print(angle)
43
+
44
+
45
+ print(get_angle('-y'))