1 | import os |
---|
2 | import wx |
---|
3 | from sas.sascalc.data_util.calcthread import CalcThread |
---|
4 | from sas.sascalc.dataloader.data_info import Data2D |
---|
5 | from sas.sascalc.file_converter.bsl_loader import BSLLoader |
---|
6 | from sas.sascalc.dataloader.readers.red2d_reader import Reader as Red2DWriter |
---|
7 | |
---|
8 | class ConvertBSLThread(CalcThread): |
---|
9 | |
---|
10 | def __init__(self, parent, in_file, out_file, |
---|
11 | updatefn=None, completefn=None): |
---|
12 | CalcThread.__init__(self, updatefn=updatefn, completefn=completefn) |
---|
13 | self.parent = parent |
---|
14 | self.in_file = in_file |
---|
15 | self.out_file = out_file |
---|
16 | |
---|
17 | def compute(self): |
---|
18 | self.ready(delay=0.0) |
---|
19 | self.update(msg="Extracting data...") |
---|
20 | |
---|
21 | try: |
---|
22 | x, y, frame_data = self._extract_bsl_data(self.in_file) |
---|
23 | except Exception as e: |
---|
24 | self.ready() |
---|
25 | self.update(exception=e) |
---|
26 | self.complete(success=False) |
---|
27 | return |
---|
28 | |
---|
29 | if x == None and y == None and frame_data == None: |
---|
30 | # Cancelled by user |
---|
31 | self.ready() |
---|
32 | self.update(msg="Conversion cancelled") |
---|
33 | self.complete(success=False) |
---|
34 | return |
---|
35 | |
---|
36 | if self.isquit(): |
---|
37 | self.complete(success=False) |
---|
38 | return |
---|
39 | |
---|
40 | self.ready(delay=0.0) |
---|
41 | self.update(msg="Exporting data...") |
---|
42 | |
---|
43 | try: |
---|
44 | completed = self._convert_to_red2d(self.out_file, x, y, frame_data) |
---|
45 | except Exception as e: |
---|
46 | self.ready() |
---|
47 | self.update(exception=e) |
---|
48 | self.complete(success=False) |
---|
49 | return |
---|
50 | |
---|
51 | self.complete(success=completed) |
---|
52 | |
---|
53 | |
---|
54 | def _extract_bsl_data(self, filename): |
---|
55 | """ |
---|
56 | Extracts data from a 2D BSL file |
---|
57 | |
---|
58 | :param filename: The header file to extract the data from |
---|
59 | :return x_data: A 1D array containing all the x coordinates of the data |
---|
60 | :return y_data: A 1D array containing all the y coordinates of the data |
---|
61 | :return frame_data: A dictionary of the form frame_number: data, where |
---|
62 | data is a 2D numpy array containing the intensity data |
---|
63 | """ |
---|
64 | loader = BSLLoader(filename) |
---|
65 | frames = [0] |
---|
66 | should_continue = True |
---|
67 | |
---|
68 | if loader.n_frames > 1: |
---|
69 | params = self.parent.ask_frame_range(loader.n_frames) |
---|
70 | frames = params['frames'] |
---|
71 | elif loader.n_rasters == 1 and loader.n_frames == 1: |
---|
72 | message = ("The selected file is an OTOKO file. Please select the " |
---|
73 | "'OTOKO 1D' option if you wish to convert it.") |
---|
74 | dlg = wx.MessageDialog(self.parent, |
---|
75 | message, |
---|
76 | 'Error!', |
---|
77 | wx.OK | wx.ICON_WARNING) |
---|
78 | dlg.ShowModal() |
---|
79 | should_continue = False |
---|
80 | dlg.Destroy() |
---|
81 | else: |
---|
82 | message = ("The selected data file only has 1 frame, it might be" |
---|
83 | " a multi-frame OTOKO file.\nContinue conversion?") |
---|
84 | dlg = wx.MessageDialog(self.parent, |
---|
85 | message, |
---|
86 | 'Warning!', |
---|
87 | wx.YES_NO | wx.ICON_WARNING) |
---|
88 | should_continue = (dlg.ShowModal() == wx.ID_YES) |
---|
89 | dlg.Destroy() |
---|
90 | |
---|
91 | if not should_continue: |
---|
92 | return None, None, None |
---|
93 | |
---|
94 | frame_data = {} |
---|
95 | |
---|
96 | for frame in frames: |
---|
97 | loader.frame = frame |
---|
98 | frame_data[frame] = loader.load_data() |
---|
99 | |
---|
100 | # TODO: Tidy this up |
---|
101 | # Prepare axes values (arbitrary scale) |
---|
102 | x_data = [] |
---|
103 | y_data = range(loader.n_pixels) * loader.n_rasters |
---|
104 | for i in range(loader.n_rasters): |
---|
105 | x_data += [i] * loader.n_pixels |
---|
106 | |
---|
107 | return x_data, y_data, frame_data |
---|
108 | |
---|
109 | def _convert_to_red2d(self, filepath, x, y, frame_data): |
---|
110 | """ |
---|
111 | Writes Data2D objects to Red2D .dat files. If more than one frame is |
---|
112 | provided, the frame number will be appended to the filename of each |
---|
113 | file written. |
---|
114 | |
---|
115 | :param filepath: The filepath to write to |
---|
116 | :param x: The x column of the data |
---|
117 | :param y: The y column of the data |
---|
118 | :param frame_data: A dictionary of the form frame_number: data, where |
---|
119 | data is a 2D numpy array containing the intensity data |
---|
120 | |
---|
121 | :return: True if export completed, False if export cancelled by user |
---|
122 | """ |
---|
123 | filename = os.path.split(filepath)[-1] |
---|
124 | filepath = os.path.split(filepath)[0] |
---|
125 | writer = Red2DWriter() |
---|
126 | |
---|
127 | for i, frame in frame_data.iteritems(): |
---|
128 | # If more than 1 frame is being exported, append the frame |
---|
129 | # number to the filename |
---|
130 | if self.isquit(): |
---|
131 | return False |
---|
132 | |
---|
133 | if len(frame_data) > 1: |
---|
134 | frame_filename = filename.split('.') |
---|
135 | frame_filename[0] += str(i+1) |
---|
136 | frame_filename = '.'.join(frame_filename) |
---|
137 | else: |
---|
138 | frame_filename = filename |
---|
139 | |
---|
140 | data_i = frame.reshape((len(x),1)) |
---|
141 | data_info = Data2D(data=data_i, qx_data=x, qy_data=y) |
---|
142 | writer.write(os.path.join(filepath, frame_filename), data_info) |
---|
143 | self.ready() |
---|
144 | self.update(msg="Written file: {}".format(frame_filename)) |
---|
145 | |
---|
146 | return True |
---|