ararpy 0.0.1a1__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.
ararpy/files/export.py ADDED
@@ -0,0 +1,1181 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ # ==========================================
4
+ # Copyright 2023 Yang
5
+ # webarar - export
6
+ # ==========================================
7
+ #
8
+ #
9
+ #
10
+ import pickle
11
+ import traceback
12
+
13
+ from xlsxwriter.workbook import Workbook
14
+ from xlsxwriter.worksheet import Worksheet
15
+ from xlsxwriter.chartsheet import Chartsheet
16
+ import os
17
+ import sys
18
+
19
+ from re import findall
20
+ from ..calc import arr, isochron
21
+ from ..smp import (basic, samples, Sample, Plot, consts)
22
+
23
+ try:
24
+ from webarar.settings import SETTINGS_ROOT
25
+ except ModuleNotFoundError:
26
+ SETTINGS_ROOT = ""
27
+
28
+
29
+ class ExcelTemplate:
30
+ def __init__(self, **kwargs):
31
+ self.name = ""
32
+ self.worksheets = {}
33
+ self.save_path = ''
34
+ for key, value in kwargs.items():
35
+ setattr(self, key, value)
36
+
37
+ def sheetnames(self):
38
+ return self.worksheets.keys()
39
+
40
+ def sheet(self):
41
+ # sheet name, sheet property name, (col, row, name, arr cls property name), (), ...
42
+ return self.worksheets.items()
43
+
44
+ def save(self):
45
+ with open(self.save_path, 'wb') as f:
46
+ f.write(pickle.dumps(self))
47
+
48
+
49
+ class WritingWorkbook:
50
+ def __init__(self, **kwargs):
51
+ self.style = None
52
+ self.filepath = None #
53
+ self.template = None
54
+ self.template_filepath = None #
55
+ self.sample = None #
56
+
57
+ self.chartarea_color = '#FFFFFF' # white = '#FFFFFF'
58
+ self.plotarea_color = '#FFFFFF' # yellow = '#FFE4B5'
59
+ self.border_color = '#000000' # black = '#000000'
60
+ self.font_color = '#000000' # black = '#000000'
61
+ self.color_map = ['#d61a1a', '#0268bd', '#f3f0eb', '#ffffff']
62
+ self.plot_border_width = 1.25
63
+ self.font_name = 'Microsoft Sans Serif'
64
+ self.title_font_size = 16
65
+ self.axis_font_size = 14
66
+ self.axis_font_bold = False
67
+ self.axis_num_font_size = 10
68
+ self.line_width = 1.25
69
+
70
+ for key, value in kwargs.items():
71
+ setattr(self, key, value)
72
+
73
+ if self.template_filepath is not None:
74
+ self.read_template()
75
+ if self.style is None:
76
+ self.style = {
77
+ 'font_size': 10, 'font_name': 'Microsoft Sans Serif', 'bold': False,
78
+ 'bg_color': '#FFFFFF', # back ground
79
+ 'font_color': '#000000', 'align': 'left',
80
+ 'top': 1, 'left': 1, 'right': 1, 'bottom': 1 # border width
81
+ }
82
+
83
+ def read_template(self):
84
+ # print(self.template_filepath)
85
+ self.template = CustomUnpickler(open(self.template_filepath, 'rb')).load()
86
+
87
+ def get_xls(self):
88
+ # TypeError: NAN/INF not supported in write_number() without 'nan_inf_to_errors' Workbook() option
89
+ xls = Workbook(self.filepath, {"nan_inf_to_errors": True})
90
+ style = xls.add_format(self.style)
91
+
92
+ sht_reference = xls.add_worksheet("Reference") # Data used to plot
93
+ sht_reference.hide_gridlines(2) # 0 = show grids, 1 = hide print grid, else = hide print and screen grids
94
+ start_row = 3
95
+ reference_header = [
96
+ "x1", "y1", "y2", "x2", "y1", "y2", "x3", "y1", "y2", "x[bar]", "y[bar]", None, None, None, # age spectra
97
+ "x[set1]", "y[set1]", "x[set2]", "y[set2]", "x[unselected]", "y[unselected]",
98
+ "point1[set1]", "point2[set1]", "point1[set2]", "point2[set2]",
99
+ "x[set1]", "y[set1]", "x[set2]", "y[set2]", "x[unselected]", "y[unselected]",
100
+ "point1[set1]", "point2[set1]", "point1[set2]", "point2[set2]",
101
+ "x[set1]", "y[set1]", "x[set2]", "y[set2]", "x[unselected]", "y[unselected]",
102
+ "point1[set1]", "point2[set1]", "point1[set2]", "point2[set2]",
103
+ "x[set1]", "y[set1]", "x[set2]", "y[set2]", "x[unselected]", "y[unselected]",
104
+ "point1[set1]", "point2[set1]", "point1[set2]", "point2[set2]",
105
+ "x[set1]", "y[set1]", "x[set2]", "y[set2]", "x[unselected]", "y[unselected]",
106
+ "point1[set1]", "point2[set1]", "point1[set2]", "point2[set2]",
107
+ ]
108
+ # writing header
109
+ sht_reference.write_row(row=start_row - 3, col=0, data=reference_header, cell_format=style)
110
+ # Data for age spectra
111
+ spectra_data = arr.transpose(self.sample.AgeSpectraPlot.data)
112
+ spectra_set1_data = arr.transpose(self.sample.AgeSpectraPlot.set1.data) or [[]] * 3
113
+ spectra_set2_data = arr.transpose(self.sample.AgeSpectraPlot.set2.data) or [[]] * 3
114
+ sht_reference.write_column(f"A{start_row}", spectra_data[0], style)
115
+ sht_reference.write_column(f"B{start_row}", spectra_data[1], style)
116
+ sht_reference.write_column(f"C{start_row}", spectra_data[2], style)
117
+ sht_reference.write_column(f"D{start_row}", spectra_set1_data[0], style)
118
+ sht_reference.write_column(f"E{start_row}", spectra_set1_data[1], style)
119
+ sht_reference.write_column(f"F{start_row}", spectra_set1_data[2], style)
120
+ sht_reference.write_column(f"G{start_row}", spectra_set2_data[0], style)
121
+ sht_reference.write_column(f"H{start_row}", spectra_set2_data[1], style)
122
+ sht_reference.write_column(f"I{start_row}", spectra_set2_data[2], style)
123
+ sht_reference.write_column(f"J{start_row}", [], style)
124
+ sht_reference.write_column(f"K{start_row}", [], style)
125
+ # Data for normal isochron
126
+ set_data = [set1_data, set2_data, set3_data] = isochron.get_set_data(
127
+ self.sample.NorIsochronPlot.data, self.sample.SelectedSequence1, self.sample.SelectedSequence2,
128
+ self.sample.UnselectedSequence)
129
+ sht_reference.write_column(f"O{start_row}", set1_data[0], style)
130
+ sht_reference.write_column(f"P{start_row}", set1_data[2], style)
131
+ sht_reference.write_column(f"Q{start_row}", set2_data[0], style)
132
+ sht_reference.write_column(f"R{start_row}", set2_data[2], style)
133
+ sht_reference.write_column(f"S{start_row}", set3_data[0], style)
134
+ sht_reference.write_column(f"T{start_row}", set3_data[2], style)
135
+ sht_reference.write_column(f"U{start_row}", self.sample.NorIsochronPlot.line1.data[0], style)
136
+ sht_reference.write_column(f"V{start_row}", self.sample.NorIsochronPlot.line1.data[1], style)
137
+ sht_reference.write_column(f"W{start_row}", self.sample.NorIsochronPlot.line2.data[0], style)
138
+ sht_reference.write_column(f"X{start_row}", self.sample.NorIsochronPlot.line2.data[1], style)
139
+ # Data for inverse isochron
140
+ set_data = [set1_data, set2_data, set3_data] = isochron.get_set_data(
141
+ self.sample.InvIsochronPlot.data, self.sample.SelectedSequence1, self.sample.SelectedSequence2,
142
+ self.sample.UnselectedSequence)
143
+ sht_reference.write_column(f"Y{start_row}", set1_data[0], style)
144
+ sht_reference.write_column(f"Z{start_row}", set1_data[2], style)
145
+ sht_reference.write_column(f"AA{start_row}", set2_data[0], style)
146
+ sht_reference.write_column(f"AB{start_row}", set2_data[2], style)
147
+ sht_reference.write_column(f"AC{start_row}", set3_data[0], style)
148
+ sht_reference.write_column(f"AD{start_row}", set3_data[2], style)
149
+ sht_reference.write_column(f"AE{start_row}", self.sample.InvIsochronPlot.line1.data[0], style)
150
+ sht_reference.write_column(f"AF{start_row}", self.sample.InvIsochronPlot.line1.data[1], style)
151
+ sht_reference.write_column(f"AG{start_row}", self.sample.InvIsochronPlot.line2.data[0], style)
152
+ sht_reference.write_column(f"AH{start_row}", self.sample.InvIsochronPlot.line2.data[1], style)
153
+ # Data for Cl 1 isochron
154
+ set_data = [set1_data, set2_data, set3_data] = isochron.get_set_data(
155
+ self.sample.KClAr1IsochronPlot.data, self.sample.SelectedSequence1, self.sample.SelectedSequence2,
156
+ self.sample.UnselectedSequence)
157
+ sht_reference.write_column(f"AI{start_row}", set1_data[0], style)
158
+ sht_reference.write_column(f"AJ{start_row}", set1_data[2], style)
159
+ sht_reference.write_column(f"AK{start_row}", set2_data[0], style)
160
+ sht_reference.write_column(f"AL{start_row}", set2_data[2], style)
161
+ sht_reference.write_column(f"AM{start_row}", set3_data[0], style)
162
+ sht_reference.write_column(f"AN{start_row}", set3_data[2], style)
163
+ sht_reference.write_column(f"AO{start_row}", self.sample.KClAr1IsochronPlot.line1.data[0], style)
164
+ sht_reference.write_column(f"AP{start_row}", self.sample.KClAr1IsochronPlot.line1.data[1], style)
165
+ sht_reference.write_column(f"AQ{start_row}", self.sample.KClAr1IsochronPlot.line2.data[0], style)
166
+ sht_reference.write_column(f"AR{start_row}", self.sample.KClAr1IsochronPlot.line2.data[1], style)
167
+ # Data for Cl 2 isochron
168
+ set_data = [set1_data, set2_data, set3_data] = isochron.get_set_data(
169
+ self.sample.KClAr2IsochronPlot.data, self.sample.SelectedSequence1, self.sample.SelectedSequence2,
170
+ self.sample.UnselectedSequence)
171
+ sht_reference.write_column(f"AS{start_row}", set1_data[0], style)
172
+ sht_reference.write_column(f"AT{start_row}", set1_data[2], style)
173
+ sht_reference.write_column(f"AU{start_row}", set2_data[0], style)
174
+ sht_reference.write_column(f"AV{start_row}", set2_data[2], style)
175
+ sht_reference.write_column(f"AW{start_row}", set3_data[0], style)
176
+ sht_reference.write_column(f"AX{start_row}", set3_data[2], style)
177
+ sht_reference.write_column(f"AY{start_row}", self.sample.KClAr2IsochronPlot.line1.data[0], style)
178
+ sht_reference.write_column(f"AZ{start_row}", self.sample.KClAr2IsochronPlot.line1.data[1], style)
179
+ sht_reference.write_column(f"BA{start_row}", self.sample.KClAr2IsochronPlot.line2.data[0], style)
180
+ sht_reference.write_column(f"BB{start_row}", self.sample.KClAr2IsochronPlot.line2.data[1], style)
181
+ # Data for Cl 3 isochron
182
+ set_data = [set1_data, set2_data, set3_data] = isochron.get_set_data(
183
+ self.sample.KClAr3IsochronPlot.data, self.sample.SelectedSequence1, self.sample.SelectedSequence2,
184
+ self.sample.UnselectedSequence)
185
+ sht_reference.write_column(f"BC{start_row}", set1_data[0], style)
186
+ sht_reference.write_column(f"BD{start_row}", set1_data[2], style)
187
+ sht_reference.write_column(f"BE{start_row}", set2_data[0], style)
188
+ sht_reference.write_column(f"BF{start_row}", set2_data[2], style)
189
+ sht_reference.write_column(f"BG{start_row}", set3_data[0], style)
190
+ sht_reference.write_column(f"BH{start_row}", set3_data[2], style)
191
+ sht_reference.write_column(f"BI{start_row}", self.sample.KClAr3IsochronPlot.line1.data[0], style)
192
+ sht_reference.write_column(f"BJ{start_row}", self.sample.KClAr3IsochronPlot.line1.data[1], style)
193
+ sht_reference.write_column(f"BK{start_row}", self.sample.KClAr3IsochronPlot.line2.data[0], style)
194
+ sht_reference.write_column(f"BL{start_row}", self.sample.KClAr3IsochronPlot.line2.data[1], style)
195
+ # Data for degas pattern
196
+ degas_data = self.sample.DegasPatternPlot.data
197
+ sht_reference.write_column(f"BM{start_row}", degas_data[0], style)
198
+ sht_reference.write_column(f"BN{start_row}", degas_data[1], style)
199
+ sht_reference.write_column(f"BO{start_row}", degas_data[2], style)
200
+ sht_reference.write_column(f"BP{start_row}", degas_data[3], style)
201
+ sht_reference.write_column(f"BQ{start_row}", degas_data[4], style)
202
+ sht_reference.write_column(f"BR{start_row}", degas_data[5], style)
203
+ sht_reference.write_column(f"BS{start_row}", degas_data[6], style)
204
+ sht_reference.write_column(f"BT{start_row}", degas_data[7], style)
205
+ sht_reference.write_column(f"BU{start_row}", degas_data[8], style)
206
+ sht_reference.write_column(f"BV{start_row}", degas_data[9], style)
207
+ sht_reference.write_column(f"BW{start_row}", [i+1 for i in range(len(self.sample.SequenceName))], style)
208
+
209
+ sht_result = xls.add_worksheet('Results')
210
+ title_style = xls.add_format({
211
+ 'font_size': 10, 'font_name': 'Microsoft Sans Serif', 'bold': True,
212
+ 'bg_color': '#ccffff', 'font_color': '#0000ff', 'align': 'vcenter',
213
+ 'top': 2, 'bottom': 2 # border width
214
+ })
215
+ title_style_2 = xls.add_format({
216
+ 'font_size': 10, 'font_name': 'Microsoft Sans Serif', 'bold': True,
217
+ 'font_color': '#000000', 'align': 'vcenter', 'top': 0, 'left': 0, 'bottom': 0, 'right': 0 # border width
218
+ })
219
+ data_style = xls.add_format({
220
+ 'font_size': 10, 'font_name': 'Microsoft Sans Serif', 'bold': False,
221
+ 'font_color': '#000000', 'align': 'vcenter',
222
+ 'top': 0, 'left': 0, 'bottom': 0, 'right': 0 # border width
223
+ })
224
+ data_style.set_align('left')
225
+ data_style.set_text_wrap()
226
+ sht_result.set_column(0, 21, width=8.5) # column width
227
+ sht_result.set_column(10, 11, width=3) # column width
228
+ sht_result.merge_range(0, 0, 1, 9, 'Sample Information', title_style)
229
+ title_list = [
230
+ [7, 0, 8, 0, 'Result'], [7, 1, 8, 1, ''], [7, 2, 8, 2, '40(r)/39(k)'], [7, 3, 8, 3, '1σ'],
231
+ [7, 4, 8, 4, 'Age'], [7, 5, 8, 5, '1σ'],
232
+ [7, 6, 8, 6, 'MSWD'], [7, 7, 8, 7, '39Ar(K)'], [7, 8, 8, 8, 'Ca/K'], [7, 9, 8, 9, '1σ'],
233
+ [7, 12, 8, 12, 'Result'], [7, 13, 8, 13, ''], [7, 14, 8, 14, '40(r)/39(k)'], [7, 15, 8, 15, '1σ'],
234
+ [7, 16, 8, 16, 'Age'], [7, 17, 8, 17, '1σ'],
235
+ [7, 18, 8, 18, 'MSWD'], [7, 19, 8, 19, '39Ar(K)'], [7, 20, 8, 20, 'Ca/K'], [7, 21, 8, 21, '1σ'],
236
+ ]
237
+ data_list = [
238
+ [3, 0, 3, 2, f"Sample = {self.sample.Info.sample.name}"],
239
+ [3, 3, 3, 5, f"Material = {self.sample.Info.sample.material}"],
240
+ [3, 6, 3, 8, f"Irradiation = {self.sample.TotalParam[28][0]}"],
241
+ [4, 0, 4, 2, f"Analyst = {self.sample.Info.laboratory.analyst}"],
242
+ [4, 3, 4, 5, f"Researcher = {self.sample.Info.researcher.name}"],
243
+ [4, 6, 4, 8,
244
+ f"J = {self.sample.TotalParam[67][0]} ± {round(self.sample.TotalParam[68][0] * self.sample.TotalParam[67][0] / 100, len(str(self.sample.TotalParam[67][0])))}"],
245
+ ]
246
+ for each in title_list:
247
+ sht_result.merge_range(*each, title_style)
248
+ for each in data_list:
249
+ sht_result.merge_range(*each, data_style)
250
+
251
+ def _write_results(sht: Worksheet, start_row: int, start_col: int, title: str, data: list):
252
+ if len(data) < 11:
253
+ return
254
+ sht.merge_range(start_row, start_col, start_row + 1, start_col + 1, title, title_style_2)
255
+ sht.merge_range(start_row, start_col + 2, start_row + 1, start_col + 2, data[0], data_style)
256
+ sht.write_column(start_row, start_col + 3, data[1:3], data_style)
257
+ sht.merge_range(start_row, start_col + 4, start_row + 1, start_col + 4, data[3], data_style)
258
+ sht.write_column(start_row, start_col + 5, data[4:6], data_style)
259
+ sht.merge_range(start_row, start_col + 6, start_row + 1, start_col + 6, data[6], data_style)
260
+ sht.write_column(start_row, start_col + 7, data[7:9], data_style)
261
+ sht.merge_range(start_row, start_col + 8, start_row + 1, start_col + 8, data[9], data_style)
262
+ sht.write_column(start_row, start_col + 9, data[9:11], data_style)
263
+
264
+ try:
265
+ _write_results(sht_result, 10, 0, 'Total Age', [
266
+ *self.sample.AgeSpectraPlot.info[0:2], '', self.sample.AgeSpectraPlot.info[4],
267
+ self.sample.AgeSpectraPlot.info[6], '',
268
+ '', 1, len(self.sample.SequenceName), '', '', ''])
269
+ _write_results(sht_result, 10, 12, 'Total Age', [
270
+ *self.sample.AgeSpectraPlot.info[0:2], '', self.sample.AgeSpectraPlot.info[4],
271
+ self.sample.AgeSpectraPlot.info[6], '',
272
+ '', 1, len(self.sample.SequenceName), '', '', ''])
273
+ except TypeError:
274
+ pass
275
+ try:
276
+ _write_results(sht_result, 17, 0, 'Set 1 Age Plateau', [
277
+ *self.sample.AgeSpectraPlot.set1.info[0:2], '',
278
+ self.sample.AgeSpectraPlot.set1.info[4], self.sample.AgeSpectraPlot.set1.info[6], '',
279
+ self.sample.AgeSpectraPlot.set1.info[3], '', self.sample.AgeSpectraPlot.set1.info[2],
280
+ '', '', ''
281
+ ])
282
+ except (IndexError, TypeError):
283
+ pass
284
+ try:
285
+ _write_results(sht_result, 17, 12, 'Set 2 Age Plateau', [
286
+ *self.sample.AgeSpectraPlot.set2.info[0:2], '',
287
+ self.sample.AgeSpectraPlot.set2.info[4], self.sample.AgeSpectraPlot.set2.info[6], '',
288
+ self.sample.AgeSpectraPlot.set2.info[2], '', self.sample.AgeSpectraPlot.set2.info[3],
289
+ '', '', ''
290
+ ])
291
+ except (IndexError, TypeError):
292
+ pass
293
+ for row_index, figure in enumerate(['figure_2', 'figure_3', 'figure_4', 'figure_5', 'figure_6']):
294
+ for col_index, set in enumerate(['set1', 'set2']):
295
+ try:
296
+ _isochron_results = getattr(basic.get_component_byid(self.sample, figure), set)
297
+ _name = \
298
+ ['Normal Ioschron', 'Inverse Isochron', 'K-Cl-Ar Plot 1', 'K-Cl-Ar Plot 2', 'K-Cl-Ar Plot 3'][
299
+ row_index]
300
+ _write_results(sht_result, 24 + row_index * 7, 0 + col_index * 12,
301
+ f'{["Set 1", "Set 2"][col_index]} {_name}', [
302
+ *_isochron_results.info[2], '',
303
+ _isochron_results.info[3][0], _isochron_results.info[3][2], '',
304
+ _isochron_results.info[0][4], '',
305
+ len([self.sample.SelectedSequence1, self.sample.SelectedSequence2][col_index]),
306
+ '', '', ''
307
+ ])
308
+ except (IndexError, TypeError):
309
+ continue
310
+
311
+ for sht_name, [prop_name, sht_type, row, col, _, smp_attr_name, header_name] in self.template.sheet():
312
+ if sht_type == "table":
313
+ data = arr.transpose(getattr(self.sample, smp_attr_name, None).data)
314
+ sht = xls.add_worksheet(sht_name)
315
+ sht.hide_gridlines(2) # 0 = show grids, 1 = hide print grid, else = hide print and screen grids
316
+ sht.hide() # default hidden table sheet
317
+ sht.set_column(0, len(data), width=12) # column width
318
+ header = getattr(samples, header_name)
319
+ sht.write_row(row=row - 1, col=col, data=header, cell_format=style)
320
+ for each_col in data:
321
+ res = sht.write_column(row=row, col=col, data=each_col, cell_format=style)
322
+ if res:
323
+ xls.close()
324
+ return None
325
+ col += 1
326
+ elif sht_type == "chart":
327
+ sht = xls.add_chartsheet(sht_name)
328
+ sht.set_paper(1) # US letter = 1, A4 = 9, letter is more rectangular
329
+ num_unselected = len(self.sample.UnselectedSequence)
330
+ num_set1 = len(self.sample.SelectedSequence1)
331
+ num_set2 = len(self.sample.SelectedSequence2)
332
+ if "spectra" in prop_name.lower():
333
+ figure = self.sample.AgeSpectraPlot
334
+ data_area = [
335
+ # Spectra lines
336
+ f"A{start_row}:A{len(spectra_data[0]) + start_row - 1}",
337
+ f"B{start_row}:B{len(spectra_data[0]) + start_row - 1}",
338
+ f"C{start_row}:C{len(spectra_data[0]) + start_row - 1}",
339
+ # set 1
340
+ f"D{start_row}:D{len(spectra_data[0]) + start_row - 1}",
341
+ f"E{start_row}:E{len(spectra_data[0]) + start_row - 1}",
342
+ f"F{start_row}:F{len(spectra_data[0]) + start_row - 1}",
343
+ # set 2
344
+ f"G{start_row}:G{len(spectra_data[0]) + start_row - 1}",
345
+ f"H{start_row}:H{len(spectra_data[0]) + start_row - 1}",
346
+ f"I{start_row}:I{len(spectra_data[0]) + start_row - 1}",
347
+ ]
348
+ axis_range = [figure.xaxis.min, figure.xaxis.max, figure.yaxis.min, figure.yaxis.max]
349
+ self.get_chart_age_spectra(xls, sht, data_area, axis_range)
350
+ elif "normal_isochron" in prop_name.lower():
351
+ data_area = [
352
+ f"O{start_row}:O{num_set1 + start_row - 1}", f"P{start_row}:P{num_set1 + start_row - 1}",
353
+ f"Q{start_row}:Q{num_set2 + start_row - 1}", f"R{start_row}:R{num_set2 + start_row - 1}",
354
+ f"S{start_row}:S{num_unselected + start_row - 1}",
355
+ f"T{start_row}:T{num_unselected + start_row - 1}",
356
+ f"U{start_row}:V{start_row}", f"U{start_row + 1}:V{start_row + 1}",
357
+ f"W{start_row}:X{start_row}", f"W{start_row + 1}:X{start_row + 1}",
358
+ ]
359
+ axis_range = [
360
+ basic.get_component_byid(self.sample, 'figure_2').xaxis.min,
361
+ basic.get_component_byid(self.sample, 'figure_2').xaxis.max,
362
+ basic.get_component_byid(self.sample, 'figure_2').yaxis.min,
363
+ basic.get_component_byid(self.sample, 'figure_2').yaxis.max,
364
+ ]
365
+ self.get_chart_isochron(
366
+ xls, sht, data_area, axis_range, title_name="Normal Isochron",
367
+ x_axis_name=f"{consts.sup_39}Ar / {consts.sup_36}Ar",
368
+ y_axis_name=f"{consts.sup_40}Ar / {consts.sup_36}Ar")
369
+ elif "inverse_isochron" in prop_name.lower():
370
+ data_area = [
371
+ f"Y{start_row}:Y{num_set1 + start_row - 1}", f"Z{start_row}:Z{num_set1 + start_row - 1}",
372
+ f"AA{start_row}:AA{num_set2 + start_row - 1}", f"AB{start_row}:AB{num_set2 + start_row - 1}",
373
+ f"AC{start_row}:AC{num_unselected + start_row - 1}",
374
+ f"AD{start_row}:AD{num_unselected + start_row - 1}",
375
+ f"AE{start_row}:AF{start_row}", f"AE{start_row + 1}:AF{start_row + 1}",
376
+ f"AG{start_row}:AH{start_row}", f"AG{start_row + 1}:AH{start_row + 1}",
377
+ ]
378
+ axis_range = [
379
+ basic.get_component_byid(self.sample, 'figure_3').xaxis.min,
380
+ basic.get_component_byid(self.sample, 'figure_3').xaxis.max,
381
+ basic.get_component_byid(self.sample, 'figure_3').yaxis.min,
382
+ basic.get_component_byid(self.sample, 'figure_3').yaxis.max,
383
+ ]
384
+ self.get_chart_isochron(
385
+ xls, sht, data_area, axis_range, title_name="Inverse Isochron",
386
+ x_axis_name=f"{consts.sup_39}Ar / {consts.sup_40}Ar",
387
+ y_axis_name=f"{consts.sup_36}Ar / {consts.sup_40}Ar")
388
+ elif "k-cl-ar_1_isochron" in prop_name.lower():
389
+ data_area = [
390
+ f"AI{start_row}:AI{num_set1 + start_row - 1}", f"AJ{start_row}:AJ{num_set1 + start_row - 1}",
391
+ f"AK{start_row}:AK{num_set2 + start_row - 1}", f"AL{start_row}:AL{num_set2 + start_row - 1}",
392
+ f"AM{start_row}:AM{num_unselected + start_row - 1}",
393
+ f"AN{start_row}:AN{num_unselected + start_row - 1}",
394
+ f"AO{start_row}:AP{start_row}", f"AO{start_row + 1}:AP{start_row + 1}",
395
+ f"AQ{start_row}:AR{start_row}", f"AQ{start_row + 1}:AR{start_row + 1}",
396
+ ]
397
+ axis_range = [
398
+ basic.get_component_byid(self.sample, 'figure_4').xaxis.min,
399
+ basic.get_component_byid(self.sample, 'figure_4').xaxis.max,
400
+ basic.get_component_byid(self.sample, 'figure_4').yaxis.min,
401
+ basic.get_component_byid(self.sample, 'figure_4').yaxis.max,
402
+ ]
403
+ self.get_chart_isochron(
404
+ xls, sht, data_area, axis_range, title_name="K-Cl-Ar 1 Isochron",
405
+ x_axis_name=f"{consts.sup_39}Ar / {consts.sup_38}Ar",
406
+ y_axis_name=f"{consts.sup_40}Ar / {consts.sup_38}Ar")
407
+ elif "k-cl-ar_2_isochron" in prop_name.lower():
408
+ data_area = [
409
+ f"AS{start_row}:AS{num_set1 + start_row - 1}", f"AT{start_row}:AT{num_set1 + start_row - 1}",
410
+ f"AU{start_row}:AU{num_set2 + start_row - 1}", f"AV{start_row}:AV{num_set2 + start_row - 1}",
411
+ f"AW{start_row}:AW{num_unselected + start_row - 1}",
412
+ f"AX{start_row}:AX{num_unselected + start_row - 1}",
413
+ f"AY{start_row}:AZ{start_row}", f"AY{start_row + 1}:AZ{start_row + 1}",
414
+ f"BA{start_row}:BB{start_row}", f"BA{start_row + 1}:BB{start_row + 1}",
415
+ ]
416
+ axis_range = [
417
+ basic.get_component_byid(self.sample, 'figure_5').xaxis.min,
418
+ basic.get_component_byid(self.sample, 'figure_5').xaxis.max,
419
+ basic.get_component_byid(self.sample, 'figure_5').yaxis.min,
420
+ basic.get_component_byid(self.sample, 'figure_5').yaxis.max,
421
+ ]
422
+ self.get_chart_isochron(
423
+ xls, sht, data_area, axis_range, title_name="K-Cl-Ar 2 Isochron",
424
+ x_axis_name=f"{consts.sup_39}Ar / {consts.sup_40}Ar",
425
+ y_axis_name=f"{consts.sup_38}Ar / {consts.sup_40}Ar")
426
+ elif "k-cl-ar_3_isochron" in prop_name.lower():
427
+ data_area = [
428
+ f"BC{start_row}:BC{num_set1 + start_row - 1}", f"BD{start_row}:BD{num_set1 + start_row - 1}",
429
+ f"BE{start_row}:BE{num_set2 + start_row - 1}", f"BF{start_row}:BF{num_set2 + start_row - 1}",
430
+ f"BG{start_row}:BG{num_unselected + start_row - 1}",
431
+ f"BH{start_row}:BH{num_unselected + start_row - 1}",
432
+ f"BI{start_row}:BJ{start_row}", f"BI{start_row + 1}:BJ{start_row + 1}",
433
+ f"BK{start_row}:BL{start_row}", f"BK{start_row + 1}:BL{start_row + 1}",
434
+ ]
435
+ axis_range = [
436
+ basic.get_component_byid(self.sample, 'figure_6').xaxis.min,
437
+ basic.get_component_byid(self.sample, 'figure_6').xaxis.max,
438
+ basic.get_component_byid(self.sample, 'figure_6').yaxis.min,
439
+ basic.get_component_byid(self.sample, 'figure_6').yaxis.max,
440
+ ]
441
+ self.get_chart_isochron(
442
+ xls, sht, data_area, axis_range, title_name="K-Cl-Ar 3 Isochron",
443
+ x_axis_name=f"{consts.sup_38}Ar / {consts.sup_39}Ar",
444
+ y_axis_name=f"{consts.sup_40}Ar / {consts.sup_39}Ar")
445
+ elif "degas_pattern" in prop_name.lower():
446
+ data_area = [
447
+ f"BM{start_row}:BM{len(degas_data[0]) + start_row - 1}",
448
+ f"BN{start_row}:BN{len(degas_data[1]) + start_row - 1}",
449
+ f"BO{start_row}:BO{len(degas_data[2]) + start_row - 1}",
450
+ f"BP{start_row}:BP{len(degas_data[3]) + start_row - 1}",
451
+ f"BQ{start_row}:BQ{len(degas_data[4]) + start_row - 1}",
452
+ f"BR{start_row}:BR{len(degas_data[5]) + start_row - 1}",
453
+ f"BS{start_row}:BS{len(degas_data[6]) + start_row - 1}",
454
+ f"BT{start_row}:BT{len(degas_data[7]) + start_row - 1}",
455
+ f"BU{start_row}:BU{len(degas_data[8]) + start_row - 1}",
456
+ f"BV{start_row}:BV{len(degas_data[9]) + start_row - 1}",
457
+ f"BW{start_row}:BW{len(degas_data[9]) + start_row - 1}",
458
+ ]
459
+ axis_range = [
460
+ basic.get_component_byid(self.sample, 'figure_8').xaxis.min,
461
+ basic.get_component_byid(self.sample, 'figure_8').xaxis.max,
462
+ basic.get_component_byid(self.sample, 'figure_8').yaxis.min,
463
+ basic.get_component_byid(self.sample, 'figure_8').yaxis.max,
464
+ ]
465
+ self.get_chart_degas_pattern(
466
+ xls, sht, data_area, axis_range,
467
+ title_name="Degas Pattern", x_axis_name=f"Sequence",
468
+ y_axis_name=f"Argon Isotopes (%)")
469
+ else:
470
+ xls.close()
471
+ return None
472
+ xls.get_worksheet_by_name("Reference").hide()
473
+ xls.get_worksheet_by_name("Isochrons").hidden = 0 # unhiden isochrons worksheet
474
+ xls.get_worksheet_by_name("Publish").activate()
475
+ xls.close()
476
+ print('导出完毕,文件路径:%s' % self.filepath)
477
+ return True
478
+
479
+ def create_initial_chart(self, xls: Workbook, chart_type: str = 'scatter'):
480
+ # chartarea
481
+ chartarea_dict = {'border': {'none': True}, 'fill': {'color': self.chartarea_color}}
482
+ # plotarea
483
+ plotarea_dict = {
484
+ 'border': {'color': self.border_color, 'width': self.plot_border_width},
485
+ 'fill': {'color': self.plotarea_color}
486
+ }
487
+ # title, No title
488
+ title_dict = {'none': True, 'name': '',
489
+ 'name_font': {'name': self.font_name, 'size': self.title_font_size, 'color': self.font_color}}
490
+ # axis
491
+ axis_dict = {
492
+ 'name': 'Axis Name', # axis name
493
+ 'name_font': {'name': self.font_name, 'size': self.axis_font_size, 'bold': self.axis_font_bold,
494
+ 'color': self.font_color},
495
+ # Setting number font
496
+ 'num_font': {'name': self.font_name, 'size': self.axis_num_font_size, 'color': self.font_color,
497
+ 'italic': False},
498
+ 'major_gridlines': {'visible': False, 'line': {'width': 1.25, 'dash_type': 'dash'}},
499
+ # Setting line (ticks) properties of chart axes
500
+ 'line': {'width': self.plot_border_width, 'color': self.border_color},
501
+ 'crossing': 'min',
502
+ }
503
+ # Not display legend
504
+ legend_dict = {'none': True}
505
+
506
+ chart = xls.add_chart({'type': chart_type})
507
+ chart.set_chartarea(chartarea_dict)
508
+ chart.set_plotarea(plotarea_dict)
509
+ chart.set_title(title_dict)
510
+ chart.set_x_axis(axis_dict)
511
+ chart.set_y_axis(axis_dict)
512
+ chart.set_legend(legend_dict)
513
+
514
+ return chart
515
+
516
+ def get_chart_age_spectra(self, xls: Workbook, sht: Chartsheet, data_area: list, axis_range: list):
517
+ # ==============SpectraDiagram===============
518
+ [xMin, xMax, yMin, yMax] = axis_range
519
+ # Initializing a chart
520
+ chart = self.create_initial_chart(xls, 'scatter')
521
+ # Set data
522
+ series = [
523
+ {
524
+ 'name': 'Line 1-1',
525
+ 'categories': f'Reference!{data_area[0]}',
526
+ 'values': f'Reference!{data_area[1]}',
527
+ 'line': {'color': self.border_color, 'width': self.line_width},
528
+ 'marker': {'type': 'none'},
529
+ },
530
+ {
531
+ 'name': 'Line 1-2',
532
+ 'categories': f'Reference!{data_area[0]}',
533
+ 'values': f'Reference!{data_area[2]}',
534
+ 'line': {'color': self.border_color, 'width': self.line_width},
535
+ 'marker': {'type': 'none'},
536
+ },
537
+ {
538
+ 'name': 'Line 2-1',
539
+ 'categories': f'Reference!{data_area[3]}',
540
+ 'values': f'Reference!{data_area[4]}',
541
+ 'line': {'color': self.color_map[0], 'width': self.line_width},
542
+ 'marker': {'type': 'none'},
543
+ },
544
+ {
545
+ 'name': 'Line 2-2',
546
+ 'categories': f'Reference!{data_area[3]}',
547
+ 'values': f'Reference!{data_area[5]}',
548
+ 'line': {'color': self.color_map[0], 'width': self.line_width},
549
+ 'marker': {'type': 'none'},
550
+ },
551
+ {
552
+ 'name': 'Line 3-1',
553
+ 'categories': f'Reference!{data_area[6]}',
554
+ 'values': f'Reference!{data_area[7]}',
555
+ 'line': {'color': self.color_map[1], 'width': self.line_width},
556
+ 'marker': {'type': 'none'},
557
+ },
558
+ {
559
+ 'name': 'Line 3-2',
560
+ 'categories': f'Reference!{data_area[6]}',
561
+ 'values': f'Reference!{data_area[8]}',
562
+ 'line': {'color': self.color_map[1], 'width': self.line_width},
563
+ 'marker': {'type': 'none'},
564
+ },
565
+ ]
566
+ for each in series:
567
+ chart.add_series(each)
568
+ # Title
569
+ # chart.title_name = 'Age Spectra'
570
+ # Axis title
571
+ chart.x_axis.update({'name': f'Cumulative {consts.sup_39}Ar released (%)', 'min': xMin, 'max': xMax})
572
+ chart.y_axis.update({'name': 'Apparent age (Ma)', 'min': yMin, 'max': yMax})
573
+ sht.set_chart(chart)
574
+
575
+ def get_chart_isochron(self, xls: Workbook, sht: Chartsheet, data_areas, axis_range,
576
+ title_name="", x_axis_name="", y_axis_name=""):
577
+ # Initializing a chart
578
+ isochron = self.create_initial_chart(xls, 'scatter')
579
+ series = [
580
+ {
581
+ 'name': 'Set 1 Selected Points',
582
+ 'categories': f'Reference!{data_areas[0]}',
583
+ 'values': f'Reference!{data_areas[1]}',
584
+ 'line': {'none': True},
585
+ 'marker': {
586
+ 'type': 'square', 'size': 6,
587
+ 'border': {'color': self.border_color},
588
+ 'fill': {'color': self.color_map[0]}}
589
+ },
590
+ {
591
+ 'name': 'Set 2 Selected Points',
592
+ 'categories': f'Reference!{data_areas[2]}',
593
+ 'values': f'Reference!{data_areas[3]}',
594
+ 'line': {'none': True},
595
+ 'marker': {'type': 'square', 'size': 6,
596
+ 'border': {'color': self.border_color},
597
+ 'fill': {'color': self.color_map[1]}}
598
+ },
599
+ {
600
+ 'name': 'Unselected Points',
601
+ 'categories': f'Reference!{data_areas[4]}',
602
+ 'values': f'Reference!{data_areas[5]}',
603
+ 'line': {'none': True},
604
+ 'marker': {'type': 'square', 'size': 6,
605
+ 'border': {'color': self.border_color},
606
+ 'fill': {'color': self.color_map[2]}}
607
+ },
608
+ {
609
+ 'name': 'Line Set 1',
610
+ 'categories': f'Reference!{data_areas[6]}',
611
+ 'values': f'Reference!{data_areas[7]}',
612
+ 'line': {'color': self.color_map[0], 'width': self.line_width},
613
+ 'marker': {'type': 'none'}
614
+ },
615
+ {
616
+ 'name': 'Line Set 2',
617
+ 'categories': f'Reference!{data_areas[8]}',
618
+ 'values': f'Reference!{data_areas[9]}',
619
+ 'line': {'color': self.color_map[1], 'width': self.line_width},
620
+ 'marker': {'type': 'none'}
621
+ }
622
+ ]
623
+ # isochron.title_name = title_name
624
+ for each in series:
625
+ isochron.add_series(each)
626
+ isochron.x_axis.update({'name': x_axis_name, 'min': axis_range[0], 'max': axis_range[1]})
627
+ isochron.y_axis.update({'name': y_axis_name, 'min': axis_range[2], 'max': axis_range[3]})
628
+ sht.set_chart(isochron)
629
+
630
+ def get_chart_degas_pattern(self, xls: Workbook, sht: Chartsheet, data_areas, axis_range,
631
+ title_name="", x_axis_name="", y_axis_name=""):
632
+ # unicode for supscript numbers
633
+ # series name
634
+ series_name = [
635
+ f'{consts.sup_36}Ara', f'{consts.sup_37}ArCa', f'{consts.sup_38}ArCl',
636
+ f'{consts.sup_39}ArK', f'{consts.sup_40}Arr', f'{consts.sup_36}Ar',
637
+ f'{consts.sup_37}Ar', f'{consts.sup_38}Ar', f'{consts.sup_39}Ar', f'{consts.sup_40}Ar',
638
+ ]
639
+ # Initializing a chart
640
+ chart = self.create_initial_chart(xls, 'scatter')
641
+ series = []
642
+ for i in range(len(data_areas) - 1):
643
+ series.append({
644
+ 'name': series_name[i],
645
+ 'categories': f'Reference!{data_areas[10]}',
646
+ 'values': f'Reference!{data_areas[i]}',
647
+ 'line': {'color': self.border_color, 'width': self.line_width},
648
+ 'marker': {
649
+ 'type': 'square', 'size': 6,
650
+ 'border': {'color': self.border_color},
651
+ # 'fill': {'color': self.color_map[0]},
652
+ }
653
+ })
654
+ # chart.title_name = title_name
655
+ for each in series:
656
+ chart.add_series(each)
657
+ # chart.x_axis.update({'name': x_axis_name, 'min': axis_range[0], 'max': axis_range[1]})
658
+ # chart.y_axis.update({'name': y_axis_name, 'min': axis_range[2], 'max': axis_range[3]})
659
+
660
+ chart.set_legend({'none': False, 'position': 'top'})
661
+
662
+ sht.set_chart(chart)
663
+
664
+
665
+ class CreateOriginGraph:
666
+ def __init__(self, **kwargs):
667
+ self.name = "Origin"
668
+ self.sample = Sample()
669
+ self.export_filepath = ""
670
+ self.spectra_data = [[]] * 3
671
+ self.set1_spectra_data = [[]] * 3
672
+ self.set2_spectra_data = [[]] * 3
673
+ self.isochron_data = [[]] * 36
674
+ self.isochron_lines_data = [[]] * 20
675
+ for key, value in kwargs.items():
676
+ setattr(self, key, value)
677
+
678
+ def get_graphs(self):
679
+ import originpro as op
680
+ import OriginExt
681
+ def origin_shutdown_exception_hook(exctype, value, traceback):
682
+ """Ensures Origin gets shut down if an uncaught exception"""
683
+ op.exit()
684
+ sys.__excepthook__(exctype, value, traceback)
685
+
686
+ if op and op.oext:
687
+ sys.excepthook = origin_shutdown_exception_hook
688
+
689
+ # Set Origin instance visibility.
690
+ # Important for only external Python.
691
+ # Should not be used with embedded Python.
692
+ if op.oext:
693
+ op.set_show(True)
694
+
695
+ # Spectra lines
696
+ linePoints = self.spectra_data if self.spectra_data != [] else [[]] * 3
697
+ linePoints += self.set1_spectra_data if self.set1_spectra_data != [] else [[]] * 3
698
+ linePoints += self.set2_spectra_data if self.set2_spectra_data != [] else [[]] * 3
699
+ age_spectra_wks = op.new_sheet(lname='Age data')
700
+ age_spectra_wks.from_list(0, linePoints[0], lname="X values")
701
+ age_spectra_wks.from_list(1, linePoints[1], lname="Y1 values")
702
+ age_spectra_wks.from_list(2, linePoints[2], lname="Y2 values")
703
+ age_spectra_wks.from_list(3, linePoints[3], lname="Set 1 X values")
704
+ age_spectra_wks.from_list(4, linePoints[4], lname="Set 1 Y1 values")
705
+ age_spectra_wks.from_list(5, linePoints[5], lname="Set 1 Y2 values")
706
+ age_spectra_wks.from_list(6, linePoints[6], lname="Set 2 X values")
707
+ age_spectra_wks.from_list(7, linePoints[7], lname="Set 2 Y1 values")
708
+ age_spectra_wks.from_list(8, linePoints[8], lname="Set 2 Y2 values")
709
+ # Isochron scatter plots
710
+ isochron_wb = op.new_book('w', lname='Isochron data')
711
+ isochron_set1_ws = isochron_wb.add_sheet(name='Isochron Set 1', active=False)
712
+ isochron_set2_ws = isochron_wb.add_sheet(name='Isochron Set 2', active=False)
713
+ isochron_set3_ws = isochron_wb.add_sheet(name='Isochron Unselected', active=False)
714
+ isochron_lines_ws = isochron_wb.add_sheet(name='Isochron Lines', active=False)
715
+
716
+ for index, item in enumerate(self.isochron_lines_data):
717
+ isochron_lines_ws.from_list(index, item, lname='')
718
+ for index, each_col in enumerate(arr.transpose(
719
+ [arr.transpose(self.isochron_data)[i] for i in self.sample.SelectedSequence1])):
720
+ isochron_set1_ws.from_list(index, each_col) # Normal, invers, K-Cl-Ar 1, K-Cl-Ar 2, K-Cl-Ar 3, 3D
721
+ for index, each_col in enumerate(arr.transpose(
722
+ [arr.transpose(self.isochron_data)[i] for i in self.sample.SelectedSequence2])):
723
+ isochron_set2_ws.from_list(index, each_col) # Normal, invers, K-Cl-Ar 1, K-Cl-Ar 2, K-Cl-Ar 3, 3D
724
+ for index, each_col in enumerate(arr.transpose(
725
+ [arr.transpose(self.isochron_data)[i] for i in self.sample.UnselectedSequence])):
726
+ isochron_set3_ws.from_list(index, each_col) # Normal, invers, K-Cl-Ar 1, K-Cl-Ar 2, K-Cl-Ar 3, 3D
727
+
728
+ # Age spectra plot
729
+ graph = self.sample.AgeSpectraPlot
730
+ gr = op.new_graph(lname='Age Spectra',
731
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
732
+ gl_1 = gr[0]
733
+ pl_1 = gl_1.add_plot(age_spectra_wks, coly='B', colx='A', type='l') # 'l'(Line Plot)
734
+ pl_2 = gl_1.add_plot(age_spectra_wks, coly='C', colx='A', type='l') # 'l'(Line Plot)
735
+ pl_3 = gl_1.add_plot(age_spectra_wks, coly='E', colx='D', type='l') # set 1
736
+ pl_4 = gl_1.add_plot(age_spectra_wks, coly='F', colx='D', type='l') # set 1
737
+ pl_5 = gl_1.add_plot(age_spectra_wks, coly='H', colx='G', type='l') # set 2
738
+ pl_6 = gl_1.add_plot(age_spectra_wks, coly='I', colx='G', type='l') # set 2
739
+ pl_1.color = pl_2.color = graph.line1.color
740
+ pl_3.color = pl_4.color = graph.line3.color
741
+ pl_5.color = pl_6.color = graph.line5.color
742
+ gl_1.axis('y').title = 'Apparent age (Ma)'
743
+ gl_1.axis('x').title = 'Cumulative \+(39)Ar released (%)'
744
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
745
+ gl_1.axis('x').set_limits(0, 100, 20)
746
+ # Normal Isochron plots
747
+ graph = self.sample.NorIsochronPlot
748
+ gr = op.new_graph(lname='Normal Isochron',
749
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
750
+ gl_1 = gr[0]
751
+ pl_1 = gl_1.add_plot(isochron_set1_ws, colx='A', coly='C', type='s') # 's'(Scatter Plot)
752
+ pl_2 = gl_1.add_plot(isochron_set2_ws, colx='A', coly='C', type='s') # 's'(Scatter Plot)
753
+ pl_3 = gl_1.add_plot(isochron_set3_ws, colx='A', coly='C', type='s') # 's'(Scatter Plot)
754
+ for plt in gl_1.plot_list():
755
+ plt.color = [graph.set1.color, graph.set2.color, '#333333'][plt.index()]
756
+ plt.symbol_kind = 2
757
+ plt.symbol_interior = 1
758
+ plt.symbol_size = 3
759
+ pl_1 = gl_1.add_plot(isochron_lines_ws, coly='B', colx='A', type='l') # 'l'(Line Plot)
760
+ pl_2 = gl_1.add_plot(isochron_lines_ws, coly='D', colx='C', type='l') # 'l'(Line Plot)
761
+ pl_1.color = graph.line1.color
762
+ pl_2.color = graph.line2.color
763
+ gl_1.axis('x').title = '\+(39)Ar / \+(36)Ar'
764
+ gl_1.axis('y').title = '\+(40)Ar / \+(36)Ar'
765
+ gl_1.axis('x').set_limits(float(graph.xaxis.min), float(graph.xaxis.max))
766
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
767
+ # print(pl_1.layer.GetNumProp(f"{gl_1.axis('x')}.inc"))
768
+ # pl_1.layer.SetNumProp(pl_1._format_property('symbol.size'), 10)
769
+ # print(pl_1.layer.GetNumProp(pl_1._format_property('symbol.size')))
770
+ # Inverse Isochron plots
771
+ graph = self.sample.InvIsochronPlot
772
+ gr = op.new_graph(lname='Inverse Isochron',
773
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
774
+ gl_1 = gr[0]
775
+ pl_1 = gl_1.add_plot(isochron_set1_ws, colx='G', coly='I', type='s') # 's'(Scatter Plot)
776
+ pl_2 = gl_1.add_plot(isochron_set2_ws, colx='G', coly='I', type='s') # 's'(Scatter Plot)
777
+ pl_3 = gl_1.add_plot(isochron_set3_ws, colx='G', coly='I', type='s') # 's'(Scatter Plot)
778
+ for plt in gl_1.plot_list():
779
+ plt.color = [graph.set1.color, graph.set2.color, '#333333'][plt.index()]
780
+ plt.symbol_kind = 2
781
+ plt.symbol_interior = 1
782
+ plt.symbol_size = 3
783
+ pl_1 = gl_1.add_plot(isochron_lines_ws, coly='F', colx='E', type='l') # 'l'(Line Plot)
784
+ pl_2 = gl_1.add_plot(isochron_lines_ws, coly='H', colx='G', type='l') # 'l'(Line Plot)
785
+ pl_1.color = graph.line1.color
786
+ pl_2.color = graph.line2.color
787
+ gl_1.axis('x').title = '\+(39)Ar / \+(40)Ar'
788
+ gl_1.axis('y').title = '\+(36)Ar / \+(40)Ar'
789
+ gl_1.axis('x').set_limits(float(graph.xaxis.min), float(graph.xaxis.max))
790
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
791
+ # Cl correlation 1
792
+ graph = self.sample.KClAr1IsochronPlot
793
+ gr = op.new_graph(lname='Cl correlation 1',
794
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
795
+ gl_1 = gr[0]
796
+ pl_1 = gl_1.add_plot(isochron_set1_ws, colx='M', coly='O', type='s') # 's'(Scatter Plot)
797
+ pl_2 = gl_1.add_plot(isochron_set2_ws, colx='M', coly='O', type='s') # 's'(Scatter Plot)
798
+ pl_3 = gl_1.add_plot(isochron_set3_ws, colx='M', coly='O', type='s') # 's'(Scatter Plot)
799
+ for plt in gl_1.plot_list():
800
+ plt.color = [graph.set1.color, graph.set2.color, '#333333'][plt.index()]
801
+ plt.symbol_kind = 2
802
+ plt.symbol_interior = 1
803
+ plt.symbol_size = 3
804
+ pl_1 = gl_1.add_plot(isochron_lines_ws, coly='J', colx='I', type='l') # 'l'(Line Plot)
805
+ pl_2 = gl_1.add_plot(isochron_lines_ws, coly='L', colx='K', type='l') # 'l'(Line Plot)
806
+ pl_1.color = graph.line1.color
807
+ pl_2.color = graph.line2.color
808
+ gl_1.axis('x').title = '\+(39)Ar / \+(38)Ar'
809
+ gl_1.axis('y').title = '\+(40)Ar / \+(38)Ar'
810
+ gl_1.axis('x').set_limits(float(graph.xaxis.min), float(graph.xaxis.max))
811
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
812
+ # Cl correlation 2
813
+ graph = self.sample.KClAr2IsochronPlot
814
+ gr = op.new_graph(lname='Cl correlation 2',
815
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
816
+ gl_1 = gr[0]
817
+ pl_1 = gl_1.add_plot(isochron_set1_ws, colx='S', coly='U', type='s') # 's'(Scatter Plot)
818
+ pl_2 = gl_1.add_plot(isochron_set2_ws, colx='S', coly='U', type='s') # 's'(Scatter Plot)
819
+ pl_3 = gl_1.add_plot(isochron_set3_ws, colx='S', coly='U', type='s') # 's'(Scatter Plot)
820
+ for plt in gl_1.plot_list():
821
+ plt.color = [graph.set1.color, graph.set2.color, '#333333'][plt.index()]
822
+ plt.symbol_kind = 2
823
+ plt.symbol_interior = 1
824
+ plt.symbol_size = 3
825
+ pl_1 = gl_1.add_plot(isochron_lines_ws, coly='N', colx='M', type='l') # 'l'(Line Plot)
826
+ pl_2 = gl_1.add_plot(isochron_lines_ws, coly='P', colx='O', type='l') # 'l'(Line Plot)
827
+ pl_1.color = graph.line1.color
828
+ pl_2.color = graph.line2.color
829
+ gl_1.axis('x').title = '\+(39)Ar / \+(40)Ar'
830
+ gl_1.axis('y').title = '\+(38)Ar / \+(40)Ar'
831
+ gl_1.axis('x').set_limits(float(graph.xaxis.min), float(graph.xaxis.max))
832
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
833
+ # Cl correlation 3
834
+ graph = self.sample.KClAr3IsochronPlot
835
+ gr = op.new_graph(lname='Cl correlation 3',
836
+ template=os.path.join(SETTINGS_ROOT, 'OriginExportTemplate.otpu'))
837
+ gl_1 = gr[0]
838
+ pl_1 = gl_1.add_plot(isochron_set1_ws, colx='Y', coly='AA', type='s') # 's'(Scatter Plot)
839
+ pl_2 = gl_1.add_plot(isochron_set2_ws, colx='Y', coly='AA', type='s') # 's'(Scatter Plot)
840
+ pl_3 = gl_1.add_plot(isochron_set3_ws, colx='Y', coly='AA', type='s') # 's'(Scatter Plot)
841
+ for plt in gl_1.plot_list():
842
+ plt.color = [graph.set1.color, graph.set2.color, '#333333'][plt.index()]
843
+ plt.symbol_kind = 2
844
+ plt.symbol_interior = 1
845
+ plt.symbol_size = 3
846
+ pl_1 = gl_1.add_plot(isochron_lines_ws, coly='R', colx='Q', type='l') # 'l'(Line Plot)
847
+ pl_2 = gl_1.add_plot(isochron_lines_ws, coly='T', colx='S', type='l') # 'l'(Line Plot)
848
+ pl_1.color = graph.line1.color
849
+ pl_2.color = graph.line2.color
850
+ gl_1.axis('x').title = '\+(38)Ar / \+(39)Ar'
851
+ gl_1.axis('y').title = '\+(40)Ar / \+(39)Ar'
852
+ gl_1.axis('x').set_limits(float(graph.xaxis.min), float(graph.xaxis.max))
853
+ gl_1.axis('y').set_limits(float(graph.yaxis.min), float(graph.yaxis.max))
854
+
855
+ # Save the opju to your UFF.
856
+ op.save(self.export_filepath)
857
+
858
+ # Exit running instance of Origin.
859
+ # Required for external Python but don't use with embedded Python.
860
+ if op.oext:
861
+ op.exit()
862
+ return 0
863
+
864
+
865
+ class CreatePDF:
866
+ def __init__(self, **kwargs):
867
+ self.name = "PDF"
868
+ self.sample = Sample()
869
+ self.figure = Plot()
870
+ self.export_filepath = ""
871
+ self.page_size = [595, 842]
872
+ self.data_bytes = b""
873
+ self.component = []
874
+ self.text = []
875
+ self.frame = []
876
+ self.axis_area = [138, 400, 360, 270] # x0, y0, w, h
877
+ with open(os.path.join(SETTINGS_ROOT, 'PDF_Template.txt'), 'rb') as f:
878
+ self.data_str: str = f.read().decode('utf-8')
879
+ for key, value in kwargs.items():
880
+ setattr(self, key, value)
881
+
882
+ def _xmin(self):
883
+ return float(self.figure.xaxis.min)
884
+
885
+ def _xmax(self):
886
+ return float(self.figure.xaxis.max)
887
+
888
+ def _ymin(self):
889
+ return float(self.figure.yaxis.min)
890
+
891
+ def _ymax(self):
892
+ return float(self.figure.yaxis.max)
893
+
894
+ def _get_transfer_pos(self, x, y):
895
+ x0, y0, w, h = self.axis_area
896
+ x = (x - self._xmin()) / (self._xmax() - self._xmin()) * w + x0
897
+ y = (y - self._ymin()) / (self._ymax() - self._ymin()) * h + y0
898
+ return [x, y]
899
+
900
+ def _get_isochron_line(self, point1, point2, color='1 0 0', width=1):
901
+ line_str = ''
902
+ if not len(point1) == len(point2) == 2:
903
+ return line_str
904
+ x0, y0, w, h = self.axis_area
905
+
906
+ def _get_line_points(k, m):
907
+ if k == 0:
908
+ return [
909
+ [x0, m], [x0 + w, m]
910
+ ]
911
+ return [
912
+ [x0, x0 * k + m], [x0 + w, (x0 + w) * k + m],
913
+ [(y0 - m) / k, y0], [(y0 + h - m) / k, y0 + h]
914
+ ]
915
+
916
+ point_1 = self._get_transfer_pos(*point1)
917
+ point_2 = self._get_transfer_pos(*point2)
918
+ k = (point_2[1] - point_1[1]) / (point_2[0] - point_1[0])
919
+ m = point_2[1] - point_2[0] * k
920
+ line = []
921
+ for point in _get_line_points(k, m):
922
+ if self.is_in_area(*point):
923
+ line.append(point)
924
+ if len(line) == 2:
925
+ line_str = f'{width} w\r{color} RG\r{line[0][0]} {line[0][1]} m {line[1][0]} {line[1][1]} l S\r'
926
+ return line_str
927
+
928
+ def _get_spectra_line(self, data, width=1, color='1 0 0'):
929
+ """
930
+ data = [[x1, x2, ..., xn], [y1, y2, ..., yn]]
931
+ """
932
+ x0, y0, w, h = self.axis_area
933
+ num = 0
934
+ line_str = ''
935
+ if not data:
936
+ return line_str
937
+ data = arr.transpose(data)
938
+ for index, point in enumerate(data):
939
+ point = self._get_transfer_pos(*point)
940
+ if not self.is_in_area(*point):
941
+ if index == 0 and self.is_in_area(*self._get_transfer_pos(*data[index + 1])):
942
+ point[0] = x0
943
+ elif index == (len(data) - 1) and self.is_in_area(*self._get_transfer_pos(*data[index - 1])):
944
+ point[0] = x0 + w
945
+ elif 0 < index < (len(data) - 1) and (
946
+ self.is_in_area(*self._get_transfer_pos(*data[index + 1])) or self.is_in_area(
947
+ *self._get_transfer_pos(*data[index - 1]))):
948
+ if x0 < point[0] < (x0 + w):
949
+ point[1] = [y0, y0 + h][point[1] >= y0 + h]
950
+ else:
951
+ point[0] = [x0, x0 + w][point[0] >= x0 + w]
952
+ else:
953
+ continue
954
+ line_str = line_str + f'{point[0]} {point[1]} {"m " if num == 0 else "l "}'
955
+ num += 1
956
+ line_str = f'{width} w\r{color} RG\r' + line_str + 'S\r'
957
+ return line_str
958
+
959
+ def is_in_area(self, x, y):
960
+ x0, y0, w, h = self.axis_area
961
+ if x == -999:
962
+ x = x0
963
+ if y == -999:
964
+ y = y0
965
+ return x0 <= x <= x0 + w and y0 <= y <= y0 + h
966
+
967
+ def set_axis_frame(self):
968
+ from decimal import Decimal
969
+ frame = ''
970
+ x0, y0, w, h = self.axis_area
971
+ frame += f'1 w\r0 0 0 RG\r{x0} {y0} {w} {h} re S\r' # % 四个参数:最小x,最小y,宽度和高度
972
+
973
+ xmin, xmax = float(self.figure.xaxis.min), float(self.figure.xaxis.max)
974
+ nx, dx = int(self.figure.xaxis.split_number), float(self.figure.xaxis.interval)
975
+ ymin, ymax = float(self.figure.yaxis.min), float(self.figure.yaxis.max)
976
+ ny, dy = int(self.figure.yaxis.split_number), float(self.figure.yaxis.interval)
977
+
978
+ for i in range(ny + 1):
979
+ yi = y0 + i * h * dy / (ymax - ymin)
980
+ if self.is_in_area(-999, yi):
981
+ frame += f'{x0} {yi} m {x0 - 4} {yi} l S\r'
982
+ frame += f'BT\r1 0 0 1 {x0 - 4 - 32} {yi - 4} Tm\r/F1 12 Tf\r0 0 0 rg\r({Decimal(str(ymin)) + i * Decimal(str(dy))}) Tj\rET\r'
983
+ for i in range(nx + 1):
984
+ xi = x0 + i * w * dx / (xmax - xmin)
985
+ if self.is_in_area(xi, -999):
986
+ frame += f'{xi} {y0} m {xi} {y0 - 4} l S\r'
987
+ frame += f'BT\r1 0 0 1 {xi - 12} {y0 - 16} Tm\r/F1 12 Tf\r0 0 0 rg\r({Decimal(str(xmin)) + i * Decimal(str(dx))}) Tj\rET\r'
988
+ self.frame.append(frame)
989
+ return frame
990
+
991
+ def set_main_content(self):
992
+ content = ''
993
+ if self.figure.type == 'isochron':
994
+ scatter_w, scatter_h = 5, 5
995
+ if not arr.is_empty(self.figure.line1.info):
996
+ content += self._get_isochron_line(*self.figure.line1.data, width=1, color='1 0 0')
997
+ if not arr.is_empty(self.figure.line2.info):
998
+ content += self._get_isochron_line(*self.figure.line2.data, width=1, color='0 0 1')
999
+ for point in arr.transpose(self.figure.data):
1000
+ x, y = self._get_transfer_pos(point[0], point[2])
1001
+ if self.is_in_area(x, y):
1002
+ if int(point[5]) - 1 in self.sample.SelectedSequence1:
1003
+ color = '0 0 0 RG\r1 0 0 rg\r'
1004
+ elif int(point[5]) - 1 in self.sample.SelectedSequence2:
1005
+ color = '0 0 0 RG\r0 0 1 rg\r'
1006
+ else:
1007
+ color = '0 0 0 RG\r1 1 1 rg\r'
1008
+ content = content + color + \
1009
+ f'{x - scatter_w / 2} {y - scatter_h / 2} {scatter_w} {scatter_h} re b\r'
1010
+ elif self.figure.type == 'spectra':
1011
+ for index, data in enumerate([self.figure.data, self.figure.set1.data, self.figure.set2.data]):
1012
+ color = ['0 0 0', '1 0 0', '0 0 1'][index]
1013
+ data = arr.transpose(data)
1014
+ content = content + self._get_spectra_line(data[:2], color=color) + \
1015
+ self._get_spectra_line([data[0], data[2]], color=color)
1016
+ self.component.append(content)
1017
+ return content
1018
+
1019
+ def set_text(self):
1020
+ text = ''
1021
+ x0, y0, w, h = self.axis_area
1022
+ # Figure Title
1023
+ text += f'BT\r1 0 0 1 {x0 + 10} {y0 - 20 + h} Tm\n/F1 12 Tf\r({self.sample.Info.sample.name}) Tj\rET\r'
1024
+ if self.figure.type == 'isochron':
1025
+ xaxis_title_number = ''.join(list(filter(str.isdigit, self.figure.xaxis.title.text)))
1026
+ yaxis_title_number = ''.join(list(filter(str.isdigit, self.figure.yaxis.title.text)))
1027
+ # X axis title
1028
+ x_title_length = 5 * 12 # length * font point size
1029
+ text += '\n'.join([
1030
+ 'BT', f'1 0 0 1 {x0 + w / 2 - x_title_length / 2} {y0 - 30} Tm',
1031
+ # % 使用Tm将文本位置设置为(35,530)前四个参数是cosx, sinx, -sinx, cosx表示逆时针旋转弧度
1032
+ '/F1 8 Tf', '5 Ts', f'({xaxis_title_number[:2]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar / ) Tj',
1033
+ '/F1 8 Tf', '5 Ts', f'({xaxis_title_number[2:4]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar) Tj', 'ET',
1034
+ ])
1035
+ # Y axis title
1036
+ y_title_length = 5 * 12 # length * font point size
1037
+ text += '\n'.join([
1038
+ 'BT', f'0 1 -1 0 {x0 - 40} {y0 + h / 2 - y_title_length / 2} Tm',
1039
+ # % 使用Tm将文本位置设置为(35,530)前四个参数是cosx, sinx, -sinx, cosx表示逆时针旋转弧度
1040
+ '/F1 8 Tf', '5 Ts', f'({yaxis_title_number[:2]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar / ) Tj',
1041
+ '/F1 8 Tf', '5 Ts', f'({yaxis_title_number[2:4]}) Tj', '/F1 12 Tf', '0 Ts', '(Ar) Tj', 'ET',
1042
+ ])
1043
+
1044
+ elif self.figure.type == 'spectra':
1045
+ # X axis title
1046
+ x_title_length = 13 * 12 # length * font point size
1047
+ text += '\n'.join([
1048
+ 'BT', f'1 0 0 1 {x0 + w / 2 - x_title_length / 2} {y0 - 30} Tm',
1049
+ '/F1 12 Tf', '0 Ts', '(Cumulative ) Tj', '/F1 8 Tf', '5 Ts', f'(39) Tj',
1050
+ '/F1 12 Tf', '0 Ts', '(Ar Released (%)) Tj', 'ET',
1051
+ ])
1052
+ # Y axis title
1053
+ y_title_length = 9 * 12 # length * font point size
1054
+ text += '\n'.join([
1055
+ 'BT', f'0 1 -1 0 {x0 - 40} {y0 + h / 2 - y_title_length / 2} Tm',
1056
+ '/F1 12 Tf', '0 Ts', f'(Apparent Age (Ma)) Tj', 'ET',
1057
+ ])
1058
+ # Text 1
1059
+ info = self.figure.set1.info
1060
+ if len(info) == 8 and self.figure.text1.text != '':
1061
+ sum39 = findall('∑{sup|39}Ar = (.*)', self.figure.text1.text)[1]
1062
+ text += '\n'.join([
1063
+ 'BT', f'1 0 0 1 {x0 + w / 4} {y0 + h / 2} Tm',
1064
+ '/F1 12 Tf', '0 Ts', f'(t) Tj', '/F1 8 Tf', '-2 Ts', f'(p) Tj',
1065
+ '/F1 12 Tf', '0 Ts',
1066
+ f'( = {info[4]:.2f} <261> {info[6]:.2f} Ma, MSMD = {info[3]:.2f}, ∑) Tj',
1067
+ '/F1 8 Tf', '5 Ts', f'(39) Tj',
1068
+ '/F1 12 Tf', '0 Ts',
1069
+ f'(Ar = {sum39}) Tj',
1070
+ 'ET',
1071
+ ])
1072
+ # Text 2
1073
+ text2 = findall('∑{sup|39}Ar = (.*)', self.figure.text2.text)[1]
1074
+
1075
+ self.text.append(text)
1076
+ return text
1077
+
1078
+ def set_split_line(self):
1079
+ others = []
1080
+ for i in range(200):
1081
+ if i * 50 >= self.page_size[0]:
1082
+ break
1083
+ others.append(f'[2] 0 d\n{i * 50} 0 m {i * 50} {self.page_size[1]} l S')
1084
+ for i in range(200):
1085
+ if i * 50 >= self.page_size[1]:
1086
+ break
1087
+ others.append(f'[2] 0 d\n0 {i * 50} m {self.page_size[0]} {i * 50} l S')
1088
+ self.data_str = self.data_str.replace(
1089
+ '% <flag: others>\r',
1090
+ '% <flag: others>\r' + '0.75 G\n' + '\n'.join(others),
1091
+ )
1092
+
1093
+ def set_info(self):
1094
+ from datetime import datetime, timezone, timedelta
1095
+ date = str(datetime.now(tz=timezone(offset=timedelta(hours=8))))
1096
+ date = findall('(.*)-(.*)-(.*) (.*):(.*):(.*)\.(.*)', date)[0]
1097
+ date = ''.join(date[0:6])
1098
+ date = 'D:' + date + "+08'00'"
1099
+ self.data_str = self.data_str.replace(
1100
+ '% <flag: info CreationDate>',
1101
+ f"{date}",
1102
+ )
1103
+ self.data_str = self.data_str.replace(
1104
+ '% <flag: info ModDate>',
1105
+ f"{date}",
1106
+ )
1107
+
1108
+ self.data_str = self.data_str.replace(
1109
+ '% <flag: info Title>',
1110
+ f'{self.sample.Info.sample.name} - {self.figure.name}'
1111
+ )
1112
+ self.data_str = self.data_str.replace(
1113
+ '% <flag: page title>\r',
1114
+ '% <flag: page title>\r' +
1115
+ f'(<This is a demo of the exported PDF.>) Tj T*\n'
1116
+ f'(<The PDFs can be freely edited in Adobe Illustrator.>) Tj\n'
1117
+ )
1118
+
1119
+ def set_replace(self):
1120
+ self.data_str = self.data_str.replace(
1121
+ '% <main contents>\r',
1122
+ '% <main contents>\r' + '\r\n'.join(self.component)
1123
+ )
1124
+ self.data_str = self.data_str.replace(
1125
+ '% <frames>\r',
1126
+ '% <frames>\r' + '\r\n'.join(self.frame)
1127
+ )
1128
+ self.data_str = self.data_str.replace(
1129
+ '% <texts>\r',
1130
+ '% <texts>\r' + '\r\n'.join(self.text)
1131
+ )
1132
+
1133
+ def get_pdf(self):
1134
+ self.do_function(
1135
+ self.set_main_content,
1136
+ self.set_axis_frame,
1137
+ self.set_text,
1138
+ self.set_info,
1139
+ self.set_replace,
1140
+ # self.set_split_line,
1141
+ self.toBetys,
1142
+ self.save,
1143
+ )
1144
+
1145
+ def get_contents(self):
1146
+ self.do_function(
1147
+ self.set_main_content,
1148
+ self.set_axis_frame,
1149
+ self.set_text,
1150
+ )
1151
+ return {
1152
+ 'component': self.component,
1153
+ 'frame': self.frame,
1154
+ 'text': self.text,
1155
+ }
1156
+
1157
+ def toBetys(self):
1158
+ self.data_bytes = self.data_str.encode('utf-8')
1159
+ return self.data_bytes
1160
+
1161
+ def save(self):
1162
+ with open(self.export_filepath, 'wb') as f:
1163
+ f.write(self.data_bytes)
1164
+
1165
+ def do_function(self, *handlers):
1166
+ for handler in handlers:
1167
+ try:
1168
+ handler()
1169
+ except Exception:
1170
+ print(traceback.format_exc())
1171
+ continue
1172
+
1173
+
1174
+ class CustomUnpickler(pickle.Unpickler):
1175
+ """https://blog.csdn.net/weixin_43236007/article/details/109506191"""
1176
+
1177
+ def find_class(self, module, name):
1178
+ try:
1179
+ return super().find_class(__name__, name)
1180
+ except AttributeError:
1181
+ return super().find_class(module, name)