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.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/customdialog.py +136 -0
- batoolset/GUI/exception_handler.py +74 -0
- batoolset/GUI/list_gui.py +514 -0
- batoolset/GUI/multi_image_display.py +1038 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1369 -0
- batoolset/GUI/scrollablepaint.py +1165 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/drawings/__init__.py +0 -0
- batoolset/drawings/math_text.py +294 -0
- batoolset/drawings/pdf_svg_renderer.py +376 -0
- batoolset/drawings/shapes/Position.py +183 -0
- batoolset/drawings/shapes/__init__.py +0 -0
- batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/drawings/shapes/circle2d.py +99 -0
- batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
- batoolset/drawings/shapes/ellipse2d.py +336 -0
- batoolset/drawings/shapes/freehand2d.py +235 -0
- batoolset/drawings/shapes/group.py +1985 -0
- batoolset/drawings/shapes/image2d.py +3157 -0
- batoolset/drawings/shapes/line2d.py +579 -0
- batoolset/drawings/shapes/point2d.py +257 -0
- batoolset/drawings/shapes/polygon2d.py +357 -0
- batoolset/drawings/shapes/polyline2d.py +235 -0
- batoolset/drawings/shapes/rect2d.py +388 -0
- batoolset/drawings/shapes/rectangle2d.py +601 -0
- batoolset/drawings/shapes/scalebar.py +310 -0
- batoolset/drawings/shapes/serializablefont.py +133 -0
- batoolset/drawings/shapes/serializableguiparams.py +14 -0
- batoolset/drawings/shapes/square2d.py +102 -0
- batoolset/drawings/shapes/test_publication_style.py +220 -0
- batoolset/drawings/shapes/textorientation.py +45 -0
- batoolset/drawings/shapes/tools.py +467 -0
- batoolset/drawings/shapes/transform2D.py +350 -0
- batoolset/drawings/shapes/txt2d.py +615 -0
- batoolset/drawings/svg_clip_renderer.py +247 -0
- batoolset/drawings/svg_embed.py +165 -0
- batoolset/drawings/svg_text_renderer.py +183 -0
- batoolset/drawings/widgets/__init__.py +0 -0
- batoolset/drawings/widgets/col.py +343 -0
- batoolset/drawings/widgets/image_group.py +248 -0
- batoolset/drawings/widgets/image_organizer.py +96 -0
- batoolset/drawings/widgets/paint.py +184 -0
- batoolset/drawings/widgets/vectorial.py +256 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +822 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/icons/__init__.py +0 -0
- batoolset/icons/tools.py +248 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1308 -0
- batoolset/luts/lutcreatorfromformula.py +209 -0
- batoolset/luts/luteditor.py +243 -0
- batoolset/pyqts/__init__.py +0 -0
- batoolset/pyqts/pleasewaitdialog.py +14 -0
- batoolset/pyqts/tools.py +760 -0
- batoolset/serializations/__init__.py +0 -0
- batoolset/serializations/tools.py +1338 -0
- batoolset/theme.py +647 -0
- batoolset/uis/__init__.py +0 -0
- batoolset/uis/blinker.py +56 -0
- batoolset_gui-0.2.0.dist-info/METADATA +54 -0
- batoolset_gui-0.2.0.dist-info/RECORD +74 -0
- batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
- batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
- 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'))
|