wolfhece 2.1.109__py3-none-any.whl → 2.1.111__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.
- wolfhece/PyDraw.py +223 -45
- wolfhece/PyPalette.py +15 -0
- wolfhece/PyVertexvectors.py +114 -41
- wolfhece/apps/curvedigitizer.py +197 -141
- wolfhece/apps/version.py +1 -1
- wolfhece/matplotlib_fig.py +390 -72
- wolfhece/wolf_array.py +22 -6
- wolfhece/wolfresults_2D.py +1 -1
- {wolfhece-2.1.109.dist-info → wolfhece-2.1.111.dist-info}/METADATA +2 -2
- {wolfhece-2.1.109.dist-info → wolfhece-2.1.111.dist-info}/RECORD +13 -13
- {wolfhece-2.1.109.dist-info → wolfhece-2.1.111.dist-info}/WHEEL +0 -0
- {wolfhece-2.1.109.dist-info → wolfhece-2.1.111.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.109.dist-info → wolfhece-2.1.111.dist-info}/top_level.txt +0 -0
wolfhece/matplotlib_fig.py
CHANGED
@@ -2,6 +2,7 @@ from matplotlib.backends.backend_wx import NavigationToolbar2Wx as NavigationToo
|
|
2
2
|
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
|
3
3
|
from typing import Literal
|
4
4
|
from matplotlib.figure import Figure
|
5
|
+
from matplotlib.axes import Axes
|
5
6
|
import matplotlib.pyplot as plt
|
6
7
|
from matplotlib.gridspec import GridSpec
|
7
8
|
import numpy as np
|
@@ -11,6 +12,8 @@ from wolfhece.PyParams import Wolf_Param, new_json
|
|
11
12
|
from wolfhece.PyTranslate import _
|
12
13
|
from wolfhece.PyVertex import getRGBfromI
|
13
14
|
|
15
|
+
from PIL import Image, ImageOps
|
16
|
+
|
14
17
|
|
15
18
|
import wx
|
16
19
|
from matplotlib.backend_bases import KeyEvent, MouseEvent
|
@@ -55,7 +58,7 @@ def sanitize_fmt(fmt):
|
|
55
58
|
|
56
59
|
class Matplotlib_ax_properties():
|
57
60
|
|
58
|
-
def __init__(self, ax =None) -> None:
|
61
|
+
def __init__(self, ax:Axes =None) -> None:
|
59
62
|
|
60
63
|
self._ax = ax
|
61
64
|
self._myprops = None
|
@@ -64,29 +67,55 @@ class Matplotlib_ax_properties():
|
|
64
67
|
self._tmp_line_prop:Matplolib_line_properties = None
|
65
68
|
self._selected_line = -1
|
66
69
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
70
|
+
if ax is None:
|
71
|
+
self.title = 'Figure'
|
72
|
+
self.xtitle = 'X [m]'
|
73
|
+
self.ytitle = 'Y [m]'
|
74
|
+
self.legend = False
|
75
|
+
self.xmin = -99999
|
76
|
+
self.xmax = -99999
|
77
|
+
self.ymin = -99999
|
78
|
+
self.ymax = -99999
|
79
|
+
self.gridx_major = False
|
80
|
+
self.gridy_major = False
|
81
|
+
self.gridx_minor = False
|
82
|
+
self.gridy_minor = False
|
83
|
+
|
84
|
+
self._equal_axis = 0
|
85
|
+
self.scaling_factor = 1.
|
86
|
+
|
87
|
+
self.ticks_x = 1.
|
88
|
+
self.ticks_y = 1.
|
89
|
+
self.ticks_label_x = 1.
|
90
|
+
self.ticks_label_y = 1.
|
91
|
+
|
92
|
+
self.format_x = '.2f'
|
93
|
+
self.format_y = '.2f'
|
94
|
+
else:
|
95
|
+
self.title = ax.get_title()
|
96
|
+
self.xtitle = ax.get_xlabel()
|
97
|
+
self.ytitle = ax.get_ylabel()
|
98
|
+
self.legend = ax.get_legend() is not None
|
99
|
+
self.xmin = -99999
|
100
|
+
self.xmax = -99999
|
101
|
+
self.ymin = -99999
|
102
|
+
self.ymax = -99999
|
103
|
+
self.gridx_major = False
|
104
|
+
self.gridy_major = False
|
105
|
+
self.gridx_minor = False
|
106
|
+
self.gridy_minor = False
|
107
|
+
|
108
|
+
aspect = ax.get_aspect()
|
109
|
+
self._equal_axis = 0 if aspect == 'auto' else 1 if aspect in [1., 'equal'] else 2
|
110
|
+
self.scaling_factor = 0 if aspect == 'auto' else 1 if aspect in [1., 'equal'] else aspect
|
111
|
+
|
112
|
+
self.ticks_x = ax.get_xticks()
|
113
|
+
self.ticks_y = ax.get_yticks
|
114
|
+
self.ticks_label_x = ax.get_xticklabels()
|
115
|
+
self.ticks_label_y = ax.get_yticklabels()
|
116
|
+
|
117
|
+
self.format_x = '.2f'
|
118
|
+
self.format_y = '.2f'
|
90
119
|
|
91
120
|
self._set_props()
|
92
121
|
|
@@ -234,9 +263,10 @@ class Matplotlib_ax_properties():
|
|
234
263
|
return
|
235
264
|
|
236
265
|
lines = self._ax.get_lines()
|
266
|
+
img = self._ax.get_images()
|
237
267
|
|
238
|
-
if len(lines) == 0:
|
239
|
-
logging.warning('No lines found')
|
268
|
+
if len(lines) == 0 and len(img) == 0:
|
269
|
+
logging.warning('No lines/image found')
|
240
270
|
return
|
241
271
|
|
242
272
|
xmin = np.inf
|
@@ -253,6 +283,13 @@ class Matplotlib_ax_properties():
|
|
253
283
|
ymin = min(ymin, np.min(y))
|
254
284
|
ymax = max(ymax, np.max(y))
|
255
285
|
|
286
|
+
for im in img:
|
287
|
+
x = im.get_extent()
|
288
|
+
xmin = min(xmin, x[0])
|
289
|
+
xmax = max(xmax, x[1])
|
290
|
+
ymin = min(ymin, x[2])
|
291
|
+
ymax = max(ymax, x[3])
|
292
|
+
|
256
293
|
return xmin, xmax, ymin, ymax
|
257
294
|
|
258
295
|
def fill_property(self, verbosity= True):
|
@@ -346,8 +383,10 @@ class Matplotlib_ax_properties():
|
|
346
383
|
ax.xaxis.grid(self.gridx_major)
|
347
384
|
ax.yaxis.grid(self.gridy_major)
|
348
385
|
|
349
|
-
|
350
|
-
|
386
|
+
if len(self.ticks_x) <= 100:
|
387
|
+
ax.set_xticks(self.ticks_x, self.ticks_label_x)
|
388
|
+
if len(self.ticks_y) <= 100:
|
389
|
+
ax.set_yticks(self.ticks_y, self.ticks_label_y)
|
351
390
|
|
352
391
|
if self.legend:
|
353
392
|
update = any(line.update_legend for line in self._lines)
|
@@ -612,11 +651,62 @@ class Matplolib_line_properties():
|
|
612
651
|
|
613
652
|
self.update_legend = False
|
614
653
|
|
654
|
+
self._scales = [1.0, 1.0]
|
655
|
+
self._origin_world = [0.0, 0.0]
|
656
|
+
self._origin_local = [0.0, 0.0]
|
657
|
+
|
615
658
|
self._set_props()
|
616
659
|
|
617
660
|
if self._line is not None:
|
618
661
|
self.get_properties()
|
619
662
|
|
663
|
+
def get_xydata(self, two_columns:bool = False):
|
664
|
+
""" Get the xy data """
|
665
|
+
|
666
|
+
if self._line is None:
|
667
|
+
return None
|
668
|
+
|
669
|
+
if two_columns:
|
670
|
+
return (self._line.get_xdata() - self._origin_local[0]) * self._scales[0] + self._origin_world[0], \
|
671
|
+
(self._line.get_ydata() - self._origin_local[1]) * self._scales[1] + self._origin_world[1]
|
672
|
+
else:
|
673
|
+
return np.array([(self._line.get_xdata() - self._origin_local[0]) * self._scales[0] + self._origin_world[0],
|
674
|
+
(self._line.get_ydata() - self._origin_local[1]) * self._scales[1] + self._origin_world[1]]).T
|
675
|
+
|
676
|
+
def set_xydata(self, xy_data:np.ndarray):
|
677
|
+
""" Set the xy data """
|
678
|
+
|
679
|
+
if self._line is None:
|
680
|
+
return
|
681
|
+
|
682
|
+
self._line.set_xdata((xy_data[:,0] - self._origin_world[0]) / self._scales[0] + self._origin_local[0])
|
683
|
+
self._line.set_ydata((xy_data[:,1] - self._origin_world[1]) / self._scales[1] + self._origin_local[1])
|
684
|
+
|
685
|
+
@property
|
686
|
+
def xdata(self):
|
687
|
+
return self.get_xydata(two_columns= True)[0]
|
688
|
+
|
689
|
+
@property
|
690
|
+
def ydata(self):
|
691
|
+
return self.get_xydata(two_columns= True)[1]
|
692
|
+
|
693
|
+
@property
|
694
|
+
def xydata(self):
|
695
|
+
return self.get_xydata()
|
696
|
+
|
697
|
+
@xydata.setter
|
698
|
+
def xydata(self, value):
|
699
|
+
|
700
|
+
if not isinstance(value, np.ndarray):
|
701
|
+
logging.warning('xydata must be a numpy array')
|
702
|
+
return
|
703
|
+
|
704
|
+
if value.shape[1] != 2:
|
705
|
+
logging.warning('xydata must have 2 columns')
|
706
|
+
return
|
707
|
+
|
708
|
+
self.set_xydata(value)
|
709
|
+
|
620
710
|
@property
|
621
711
|
def ax_props(self):
|
622
712
|
return self._ax_props
|
@@ -821,6 +911,14 @@ class Matplolib_line_properties():
|
|
821
911
|
self._myprops.addparam('Picker', 'Picker', self.picker, 'Logical', 'Picker')
|
822
912
|
self._myprops.addparam('Picker', 'Picker radius', self.picker_radius, 'Float', 'Picker radius')
|
823
913
|
|
914
|
+
self._myprops.addparam('Scales', 'X scale', self._scales[0], 'Float', 'X scale')
|
915
|
+
self._myprops.addparam('Scales', 'Y scale', self._scales[1], 'Float', 'Y scale')
|
916
|
+
|
917
|
+
self._myprops.addparam('Origin', 'X world', self._origin_world[0], 'Float', 'X origin into world')
|
918
|
+
self._myprops.addparam('Origin', 'Y world', self._origin_world[1], 'Float', 'Y origin into world')
|
919
|
+
self._myprops.addparam('Origin', 'X local', self._origin_local[0], 'Float', 'X origin into local references')
|
920
|
+
self._myprops.addparam('Origin', 'Y local', self._origin_local[1], 'Float', 'Y origin into local references')
|
921
|
+
|
824
922
|
self._myprops.Populate()
|
825
923
|
# self._myprops.Layout()
|
826
924
|
# self._myprops.SetSizeHints(500,500)
|
@@ -848,6 +946,14 @@ class Matplolib_line_properties():
|
|
848
946
|
self._myprops[('Picker', 'Picker')] = self.picker
|
849
947
|
self._myprops[('Picker', 'Picker radius')] = self.picker_radius
|
850
948
|
|
949
|
+
self._myprops[('Scales', 'X scale')] = self._scales[0]
|
950
|
+
self._myprops[('Scales', 'Y scale')] = self._scales[1]
|
951
|
+
|
952
|
+
self._myprops[('Origin', 'X world')] = self._origin_world[0]
|
953
|
+
self._myprops[('Origin', 'Y world')] = self._origin_world[1]
|
954
|
+
self._myprops[('Origin', 'X local')] = self._origin_local[0]
|
955
|
+
self._myprops[('Origin', 'Y local')] = self._origin_local[1]
|
956
|
+
|
851
957
|
self._myprops.Populate()
|
852
958
|
|
853
959
|
def ui(self):
|
@@ -902,6 +1008,15 @@ class Matplolib_line_properties():
|
|
902
1008
|
self.picker = self._myprops[('Picker', 'Picker')]
|
903
1009
|
self.picker_radius = self._myprops[('Picker', 'Picker radius')]
|
904
1010
|
|
1011
|
+
self._scales[0] = self._myprops[('Scales', 'X scale')]
|
1012
|
+
self._scales[1] = self._myprops[('Scales', 'Y scale')]
|
1013
|
+
|
1014
|
+
self._origin_world[0] = self._myprops[('Origin', 'X world')]
|
1015
|
+
self._origin_world[1] = self._myprops[('Origin', 'Y world')]
|
1016
|
+
|
1017
|
+
self._origin_local[0] = self._myprops[('Origin', 'X local')]
|
1018
|
+
self._origin_local[1] = self._myprops[('Origin', 'Y local')]
|
1019
|
+
|
905
1020
|
self.set_properties()
|
906
1021
|
|
907
1022
|
def set_properties(self, line:Line2D = None):
|
@@ -946,13 +1061,19 @@ class Matplolib_line_properties():
|
|
946
1061
|
def show_properties(self):
|
947
1062
|
self.ui()
|
948
1063
|
|
1064
|
+
@property
|
1065
|
+
def has_world_transfer(self):
|
1066
|
+
return self._scales[0] != 1.0 or self._scales[1] != 1.0 or self._origin_world[0] != 0. or self._origin_world[1] != 0. or self._origin_local[0] != 0. or self._origin_local[1] != 0.
|
1067
|
+
|
949
1068
|
def to_dict(self) -> str:
|
950
1069
|
""" properties to dict """
|
951
1070
|
|
952
|
-
|
953
|
-
|
1071
|
+
# We need to store the local data
|
1072
|
+
xy = self._line.get_xydata()
|
1073
|
+
xdata = xy[:,0].tolist()
|
1074
|
+
ydata = xy[:,1].tolist()
|
954
1075
|
|
955
|
-
|
1076
|
+
locdict = {'color':self.color,
|
956
1077
|
'linewidth':self.linewidth,
|
957
1078
|
'linestyle':self.linestyle,
|
958
1079
|
'marker':self.marker,
|
@@ -967,12 +1088,31 @@ class Matplolib_line_properties():
|
|
967
1088
|
'picker':self.picker,
|
968
1089
|
'picker_radius':self.picker_radius,
|
969
1090
|
'xdata':xdata,
|
970
|
-
'ydata':ydata
|
1091
|
+
'ydata':ydata,
|
1092
|
+
'xscale':self._scales[0],
|
1093
|
+
'yscale':self._scales[1],
|
1094
|
+
'xorigin_world':self._origin_world[0],
|
1095
|
+
'yorigin_world':self._origin_world[1],
|
1096
|
+
'xorigin_local':self._origin_local[0],
|
1097
|
+
'yorigin_local':self._origin_local[1]}
|
1098
|
+
|
1099
|
+
if self.has_world_transfer:
|
1100
|
+
world_xdata, world_ydata = self.get_xydata(two_columns = True)
|
1101
|
+
world_xdata = world_xdata.tolist()
|
1102
|
+
world_ydata = world_ydata.tolist()
|
1103
|
+
|
1104
|
+
locdict['world_xdata'] = world_xdata
|
1105
|
+
locdict['world_ydata'] = world_ydata
|
1106
|
+
|
1107
|
+
return locdict
|
971
1108
|
|
972
1109
|
def from_dict(self, props:dict):
|
973
1110
|
""" properties from dict """
|
974
1111
|
|
975
|
-
keys = ['color', 'linewidth', 'linestyle', 'marker', 'markersize', 'alpha',
|
1112
|
+
keys = ['color', 'linewidth', 'linestyle', 'marker', 'markersize', 'alpha',
|
1113
|
+
'label', 'markerfacecolor', 'markeredgecolor', 'markeredgewidth',
|
1114
|
+
'visible', 'zorder', 'picker', 'picker_radius',
|
1115
|
+
'xscale', 'yscale', 'xorigin_world', 'yorigin_world', 'xorigin_local', 'yorigin_local']
|
976
1116
|
|
977
1117
|
for key in keys:
|
978
1118
|
try:
|
@@ -981,11 +1121,63 @@ class Matplolib_line_properties():
|
|
981
1121
|
logging.warning('Key not found in properties dict')
|
982
1122
|
pass
|
983
1123
|
|
1124
|
+
# ATTENTION : The next 2 lines are done in the to_dict method of the axes
|
1125
|
+
# xydata = np.array([props['xdata'], props['ydata']]).T
|
1126
|
+
# self.set_xydata(xydata)
|
1127
|
+
|
984
1128
|
self.populate()
|
985
1129
|
self.set_properties()
|
986
1130
|
|
987
1131
|
return self
|
988
1132
|
|
1133
|
+
@property
|
1134
|
+
def xscale(self):
|
1135
|
+
return self._scales[0]
|
1136
|
+
|
1137
|
+
@xscale.setter
|
1138
|
+
def xscale(self, value):
|
1139
|
+
self._scales[0] = value
|
1140
|
+
|
1141
|
+
@property
|
1142
|
+
def yscale(self):
|
1143
|
+
return self._scales[1]
|
1144
|
+
|
1145
|
+
@yscale.setter
|
1146
|
+
def yscale(self, value):
|
1147
|
+
self._scales[1] = value
|
1148
|
+
|
1149
|
+
@property
|
1150
|
+
def xorigin_world(self):
|
1151
|
+
return self._origin_world[0]
|
1152
|
+
|
1153
|
+
@xorigin_world.setter
|
1154
|
+
def xorigin_world(self, value):
|
1155
|
+
self._origin_world[0] = value
|
1156
|
+
|
1157
|
+
@property
|
1158
|
+
def yorigin_world(self):
|
1159
|
+
return self._origin_world[1]
|
1160
|
+
|
1161
|
+
@yorigin_world.setter
|
1162
|
+
def yorigin_world(self, value):
|
1163
|
+
self._origin_world[1] = value
|
1164
|
+
|
1165
|
+
@property
|
1166
|
+
def xorigin_local(self):
|
1167
|
+
return self._origin_local[0]
|
1168
|
+
|
1169
|
+
@xorigin_local.setter
|
1170
|
+
def xorigin_local(self, value):
|
1171
|
+
self._origin_local[0] = value
|
1172
|
+
|
1173
|
+
@property
|
1174
|
+
def yorigin_local(self):
|
1175
|
+
return self._origin_local[1]
|
1176
|
+
|
1177
|
+
@yorigin_local.setter
|
1178
|
+
def yorigin_local(self, value):
|
1179
|
+
self._origin_local[1] = value
|
1180
|
+
|
989
1181
|
def add_props_to_sizer(self, frame:wx.Frame, sizer:wx.BoxSizer):
|
990
1182
|
""" Add the properties to a sizer """
|
991
1183
|
|
@@ -1013,8 +1205,9 @@ class Matplolib_line_properties():
|
|
1013
1205
|
self._line = None
|
1014
1206
|
|
1015
1207
|
class PRESET_LAYOUTS(Enum):
|
1016
|
-
DEFAULT = (1,1)
|
1017
|
-
MAT2X2 = (2,2)
|
1208
|
+
DEFAULT = (1,1, 'auto')
|
1209
|
+
MAT2X2 = (2,2, 'auto')
|
1210
|
+
DEFAULT_EQUAL = (1,1, 'equal')
|
1018
1211
|
class Matplotlib_Figure(wx.Frame):
|
1019
1212
|
""" Matplotlib Figure with wx Frame """
|
1020
1213
|
|
@@ -1043,7 +1236,8 @@ class Matplotlib_Figure(wx.Frame):
|
|
1043
1236
|
|
1044
1237
|
self.wx_exists = wx.App.Get() is not None
|
1045
1238
|
|
1046
|
-
self.fig =
|
1239
|
+
self.fig = Figure()
|
1240
|
+
# self.fig.set_visible(False)
|
1047
1241
|
dpi = self.fig.get_dpi()
|
1048
1242
|
size_x, size_y = self.fig.get_size_inches()
|
1049
1243
|
|
@@ -1055,9 +1249,20 @@ class Matplotlib_Figure(wx.Frame):
|
|
1055
1249
|
self.shown_props = None # shown properties
|
1056
1250
|
self._shiftdown = False
|
1057
1251
|
|
1252
|
+
self._action = None
|
1253
|
+
self._keep_first_point = True
|
1254
|
+
|
1058
1255
|
self.apply_layout(layout) # apply the layout
|
1059
1256
|
pass
|
1060
1257
|
|
1258
|
+
@property
|
1259
|
+
def action(self):
|
1260
|
+
return self._action
|
1261
|
+
|
1262
|
+
@action.setter
|
1263
|
+
def action(self, value):
|
1264
|
+
self._action = value
|
1265
|
+
|
1061
1266
|
def presets(self, which:PRESET_LAYOUTS = PRESET_LAYOUTS.DEFAULT):
|
1062
1267
|
""" Presets """
|
1063
1268
|
|
@@ -1103,12 +1308,12 @@ class Matplotlib_Figure(wx.Frame):
|
|
1103
1308
|
# store the axes in a list -- So we can access them by index, not only by name
|
1104
1309
|
self.ax = [ax for ax in self.ax_dict.values()]
|
1105
1310
|
else:
|
1106
|
-
# Tuple or list of
|
1107
|
-
if len(layout) !=
|
1108
|
-
logging.warning('Layout must be a tuple or a list of
|
1311
|
+
# Tuple or list of 3 elements - subplots
|
1312
|
+
if len(layout) != 3:
|
1313
|
+
logging.warning('Layout must be a tuple or a list of 3 elements (nbrows:int, nbcols:int, aspect_ratio:str|float)')
|
1109
1314
|
return
|
1110
1315
|
|
1111
|
-
self.nbrows, self.nbcols = layout
|
1316
|
+
self.nbrows, self.nbcols, ratio = layout
|
1112
1317
|
if self.nbrows*self.nbcols == 1:
|
1113
1318
|
# Convert to list -- subplots returns a single Axes but we want a list
|
1114
1319
|
self.ax = [self.fig.subplots(self.nbrows, self.nbcols)]
|
@@ -1116,6 +1321,14 @@ class Matplotlib_Figure(wx.Frame):
|
|
1116
1321
|
# Flatten the axes -- sbplots returns a 2D array of Axes but we want a list
|
1117
1322
|
self.ax = self.fig.subplots(self.nbrows, self.nbcols).flatten()
|
1118
1323
|
|
1324
|
+
for curax in self.ax:
|
1325
|
+
if ratio == 'auto':
|
1326
|
+
curax.set_aspect('auto')
|
1327
|
+
elif ratio == 'equal':
|
1328
|
+
curax.set_aspect('equal')
|
1329
|
+
else:
|
1330
|
+
curax.set_aspect(ratio)
|
1331
|
+
|
1119
1332
|
# store the axes in a dict -- So we can access them by name, not only by index
|
1120
1333
|
self.ax_dict = {f'{i}':ax for i, ax in enumerate(self.ax)}
|
1121
1334
|
for key,ax in self.ax_dict.items():
|
@@ -1410,7 +1623,11 @@ class Matplotlib_Figure(wx.Frame):
|
|
1410
1623
|
|
1411
1624
|
@property
|
1412
1625
|
def cur_line(self) -> Line2D:
|
1413
|
-
|
1626
|
+
|
1627
|
+
if self._line_current.GetSelection() == -1:
|
1628
|
+
return None
|
1629
|
+
else:
|
1630
|
+
return self.cur_ax.get_lines()[int(self._line_current.GetSelection())]
|
1414
1631
|
|
1415
1632
|
def get_figax(self):
|
1416
1633
|
|
@@ -1461,6 +1678,10 @@ class Matplotlib_Figure(wx.Frame):
|
|
1461
1678
|
def show_curline_properties(self):
|
1462
1679
|
# self.cur_line_properties.ui()
|
1463
1680
|
self._hide_all_props()
|
1681
|
+
|
1682
|
+
if self._line_current.GetSelection() == -1:
|
1683
|
+
return
|
1684
|
+
|
1464
1685
|
self.cur_line_properties.show_props()
|
1465
1686
|
# self.Layout()
|
1466
1687
|
self._sizer_grid_props.Layout()
|
@@ -1535,6 +1756,11 @@ class Matplotlib_Figure(wx.Frame):
|
|
1535
1756
|
self._axes_properties[int(self._ax_current.GetSelection())].reset_selection()
|
1536
1757
|
elif event.key == 'shift':
|
1537
1758
|
self._shiftdown = True
|
1759
|
+
elif event.key == 'enter':
|
1760
|
+
if self.action is not None:
|
1761
|
+
action, callback = self.action
|
1762
|
+
if action == 'Digitize':
|
1763
|
+
callback((0,0), 'End Digitize')
|
1538
1764
|
|
1539
1765
|
def OnKeyRelease(self, event:KeyEvent):
|
1540
1766
|
if event.key == 'shift':
|
@@ -1544,8 +1770,9 @@ class Matplotlib_Figure(wx.Frame):
|
|
1544
1770
|
|
1545
1771
|
rclick = event.button == 3
|
1546
1772
|
lclick = event.button == 1
|
1773
|
+
middle = event.button == 2
|
1547
1774
|
|
1548
|
-
if not rclick:
|
1775
|
+
if not (rclick or middle):
|
1549
1776
|
return
|
1550
1777
|
|
1551
1778
|
if event.inaxes:
|
@@ -1553,33 +1780,81 @@ class Matplotlib_Figure(wx.Frame):
|
|
1553
1780
|
idx= ax.get_figure().axes.index(event.inaxes)
|
1554
1781
|
x, y = event.xdata, event.ydata
|
1555
1782
|
|
1556
|
-
if
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
if
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
self.
|
1582
|
-
|
1783
|
+
if middle:
|
1784
|
+
if self.action is not None:
|
1785
|
+
action, callback = self.action
|
1786
|
+
if action == 'Digitize':
|
1787
|
+
callback((0,0), 'End Digitize')
|
1788
|
+
|
1789
|
+
if rclick:
|
1790
|
+
|
1791
|
+
if self._shiftdown:
|
1792
|
+
# add a point to the current line, update the grid and plot
|
1793
|
+
xy = self.cur_line.get_xydata()
|
1794
|
+
|
1795
|
+
if xy.shape[0] == 1:
|
1796
|
+
if self._keep_first_point:
|
1797
|
+
xy = np.vstack((xy, [x,y]))
|
1798
|
+
else:
|
1799
|
+
xy = np.asarray([[x,y]])
|
1800
|
+
self._keep_first_point = True
|
1801
|
+
else:
|
1802
|
+
xy = np.vstack((xy, [x,y]))
|
1803
|
+
|
1804
|
+
self.cur_line.set_data(xy[:,0], xy[:,1])
|
1805
|
+
self.fill_grid_with_xy_np(self.cur_line_properties.get_xydata())
|
1806
|
+
self._canvas.draw()
|
1807
|
+
|
1808
|
+
if self.action is not None:
|
1809
|
+
|
1810
|
+
action, callback = self.action
|
1811
|
+
|
1812
|
+
if action == 'Digitize':
|
1813
|
+
if not self._shiftdown:
|
1814
|
+
logging.warning('Shift must be down to digitize')
|
1815
|
+
elif action == 'Ref X':
|
1816
|
+
if not self._shiftdown:
|
1817
|
+
logging.warning('Shift must be down to set X reference')
|
1818
|
+
else:
|
1819
|
+
callback((x,y), action)
|
1820
|
+
elif action == 'Ref Y':
|
1821
|
+
if not self._shiftdown:
|
1822
|
+
logging.warning('Shift must be down to set Y reference')
|
1823
|
+
else:
|
1824
|
+
callback((x,y), action)
|
1825
|
+
elif action == 'Origin':
|
1826
|
+
if not self._shiftdown:
|
1827
|
+
logging.warning('Shift must be down to set origin')
|
1828
|
+
else:
|
1829
|
+
callback((x,y), action)
|
1830
|
+
|
1831
|
+
elif not self._shiftdown:
|
1832
|
+
# Find the closest line and select it
|
1833
|
+
|
1834
|
+
lines = ax.get_lines()
|
1835
|
+
if len(lines) == 0:
|
1836
|
+
logging.warning('No lines !')
|
1837
|
+
return
|
1838
|
+
|
1839
|
+
dist_min = 1e6
|
1840
|
+
line_min = None
|
1841
|
+
|
1842
|
+
for line in lines:
|
1843
|
+
xy = line.get_xydata()
|
1844
|
+
if xy.shape[0] == 0:
|
1845
|
+
continue
|
1846
|
+
|
1847
|
+
dist = np.linalg.norm(xy - np.array([x,y]), axis=1)
|
1848
|
+
idx_min = np.argmin(dist)
|
1849
|
+
if dist[idx_min] < dist_min:
|
1850
|
+
dist_min = dist[idx_min]
|
1851
|
+
line_min = line
|
1852
|
+
|
1853
|
+
self._ax_current.SetSelection(idx)
|
1854
|
+
self._fill_lines_ax(idx = lines.index(line_min))
|
1855
|
+
self._axes_properties[idx].select_line(lines.index(line_min))
|
1856
|
+
|
1857
|
+
self.fill_grid_with_xy_np(self.cur_line_properties.get_xydata())
|
1583
1858
|
|
1584
1859
|
self.show_curline_properties()
|
1585
1860
|
|
@@ -1604,6 +1879,27 @@ class Matplotlib_Figure(wx.Frame):
|
|
1604
1879
|
grid.SetCellValue(i, colx, str(xy[i,0]))
|
1605
1880
|
grid.SetCellValue(i, coly, str(xy[i,1]))
|
1606
1881
|
|
1882
|
+
def fill_grid_with_xy_np(self, xy:np.ndarray, grid:CpGrid = None, colx:int= 0, coly:int= 1):
|
1883
|
+
|
1884
|
+
if grid is None:
|
1885
|
+
grid = self._xls
|
1886
|
+
|
1887
|
+
grid.ClearGrid()
|
1888
|
+
|
1889
|
+
if grid.GetNumberRows() < len(xy):
|
1890
|
+
grid.AppendRows(len(xy)-grid.GetNumberRows())
|
1891
|
+
elif grid.GetNumberRows() > len(xy):
|
1892
|
+
grid.DeleteRows(len(xy), grid.GetNumberRows()-len(xy))
|
1893
|
+
|
1894
|
+
for i in range(len(xy)):
|
1895
|
+
grid.SetCellValue(i, colx, str(xy[i,0]))
|
1896
|
+
grid.SetCellValue(i, coly, str(xy[i,1]))
|
1897
|
+
|
1898
|
+
def get_xy_from_grid(self):
|
1899
|
+
""" Get the xy from the grid """
|
1900
|
+
|
1901
|
+
return self._get_xy_from_grid(self._xls)
|
1902
|
+
|
1607
1903
|
def update_line_from_grid(self, event):
|
1608
1904
|
|
1609
1905
|
line = self.cur_line
|
@@ -1620,7 +1916,8 @@ class Matplotlib_Figure(wx.Frame):
|
|
1620
1916
|
xy[i,0] = float(self._xls.GetCellValue(i, 0))
|
1621
1917
|
xy[i,1] = float(self._xls.GetCellValue(i, 1))
|
1622
1918
|
|
1623
|
-
|
1919
|
+
self.cur_line_properties.set_xydata(xy)
|
1920
|
+
self._canvas.draw()
|
1624
1921
|
self.update_layout()
|
1625
1922
|
|
1626
1923
|
def add_row_to_grid(self, event):
|
@@ -1643,6 +1940,10 @@ class Matplotlib_Figure(wx.Frame):
|
|
1643
1940
|
|
1644
1941
|
def onnew_line(self, event):
|
1645
1942
|
""" Add a plot to the current ax """
|
1943
|
+
self.new_line()
|
1944
|
+
|
1945
|
+
def new_line(self, ax:Axes=None, **kwargs) -> Matplolib_line_properties:
|
1946
|
+
""" Add a plot to the current ax """
|
1646
1947
|
|
1647
1948
|
curline = self.cur_line
|
1648
1949
|
if curline is not None:
|
@@ -1650,7 +1951,9 @@ class Matplotlib_Figure(wx.Frame):
|
|
1650
1951
|
xy = np.asarray([[xy[0,0],xy[0,1]]])
|
1651
1952
|
else:
|
1652
1953
|
xy = np.asarray([[0,0]])
|
1653
|
-
|
1954
|
+
|
1955
|
+
self._keep_first_point = False
|
1956
|
+
return self.add_line(xy, ax, **kwargs)
|
1654
1957
|
|
1655
1958
|
def _get_xy_from_grid(self, grid:CpGrid, colx:int= 0, coly:int= 1):
|
1656
1959
|
""" Get the xy from a grid """
|
@@ -1670,7 +1973,7 @@ class Matplotlib_Figure(wx.Frame):
|
|
1670
1973
|
|
1671
1974
|
return xy
|
1672
1975
|
|
1673
|
-
def add_line(self, xy:np.ndarray, ax:Axes=None, **kwargs):
|
1976
|
+
def add_line(self, xy:np.ndarray, ax:Axes=None, **kwargs) -> Matplolib_line_properties:
|
1674
1977
|
""" Add a plot to the current ax """
|
1675
1978
|
|
1676
1979
|
ax, idx_ax = self.get_ax_idx(ax)
|
@@ -1686,6 +1989,21 @@ class Matplotlib_Figure(wx.Frame):
|
|
1686
1989
|
self.update_layout()
|
1687
1990
|
self._canvas.SetFocus()
|
1688
1991
|
|
1992
|
+
return self.cur_ax_properties._lines[-1]
|
1993
|
+
|
1994
|
+
def add_image(self, image:np.ndarray | str, ax:Axes= None, **kwargs):
|
1995
|
+
|
1996
|
+
ax, idx_ax = self.get_ax_idx(ax)
|
1997
|
+
|
1998
|
+
if isinstance(image, str):
|
1999
|
+
image = ImageOps.flip(Image.open(image))
|
2000
|
+
ax.axis('off') # clear x-axis and y-axis
|
2001
|
+
|
2002
|
+
ax.imshow(image, **kwargs)
|
2003
|
+
|
2004
|
+
self.update_layout()
|
2005
|
+
self._canvas.SetFocus()
|
2006
|
+
|
1689
2007
|
def ondel_line(self, event):
|
1690
2008
|
""" Remove a plot from the current ax """
|
1691
2009
|
|