1 | #-------------------------------------------------------------------------------
|
---|
2 | # Name: ApplicationWindow
|
---|
3 | # Purpose:
|
---|
4 | #
|
---|
5 | # Author: Kelvin
|
---|
6 | #
|
---|
7 | # Created: 02/01/2014
|
---|
8 | #-------------------------------------------------------------------------------
|
---|
9 |
|
---|
10 | from PyQt4 import QtGui, QtCore
|
---|
11 | from Skymap import *
|
---|
12 | from SpectralGraph import *
|
---|
13 | from ProgressBar import *
|
---|
14 | from DuchampResults import *
|
---|
15 |
|
---|
16 | import pyfits
|
---|
17 | import os
|
---|
18 |
|
---|
19 | """Application Window Class Definition"""
|
---|
20 | # This handles the main window.
|
---|
21 | class ApplicationWindow(QtGui.QMainWindow):
|
---|
22 |
|
---|
23 | # Main Window Initialisation
|
---|
24 | def __init__(self):
|
---|
25 |
|
---|
26 | # QMainWindow Initialisation
|
---|
27 | QtGui.QMainWindow.__init__(self)
|
---|
28 | self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
---|
29 | self.setWindowTitle("Duchamp")
|
---|
30 |
|
---|
31 | # File Menu
|
---|
32 | self.file_menu = QtGui.QMenu('&File', self)
|
---|
33 | self.file_menu.addAction('&Open File', self.openFiles, QtCore.Qt.CTRL + QtCore.Qt.Key_O)
|
---|
34 | self.file_menu.addAction('&Save as parameters file', self.saveParameters, QtCore.Qt.CTRL + QtCore.Qt.Key_P)
|
---|
35 | self.file_menu.addSeparator()
|
---|
36 | self.file_menu.addAction('&Quit', self.close, QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
|
---|
37 | self.menuBar().addMenu(self.file_menu)
|
---|
38 |
|
---|
39 | # Options Menu
|
---|
40 | self.options_menu = QtGui.QMenu('&Options', self)
|
---|
41 | self.menuBar().addSeparator()
|
---|
42 | self.menuBar().addMenu(self.options_menu)
|
---|
43 | self.options_menu.addAction('&Full Screen Mode', self.showFullScreen, QtCore.Qt.CTRL + QtCore.Qt.Key_F)
|
---|
44 | self.options_menu.addAction('&Maximized Mode', self.showMaximized, QtCore.Qt.CTRL + QtCore.Qt.Key_M)
|
---|
45 | self.options_menu.addAction('&Normal Mode', self.showNormal, QtCore.Qt.CTRL + QtCore.Qt.Key_N)
|
---|
46 |
|
---|
47 | # Help Menu
|
---|
48 | self.help_menu = QtGui.QMenu('&Help', self)
|
---|
49 | self.menuBar().addSeparator()
|
---|
50 | self.menuBar().addMenu(self.help_menu)
|
---|
51 | self.help_menu.addAction('&About', self.about, QtCore.Qt.CTRL + QtCore.Qt.Key_A)
|
---|
52 |
|
---|
53 | # Main Widget
|
---|
54 | self.main_widget = QtGui.QWidget(self)
|
---|
55 | self.main_widget.setFocus()
|
---|
56 | self.setCentralWidget(self.main_widget)
|
---|
57 | self.layout = QtGui.QGridLayout(self.main_widget)
|
---|
58 |
|
---|
59 | # Main Window Status Bar
|
---|
60 | self.statusBar().showMessage("Duchamp Output Window", 2000)
|
---|
61 |
|
---|
62 | # Window location and size
|
---|
63 | self.center = QtGui.QDesktopWidget().availableGeometry().center()
|
---|
64 | self.move(0, 0)
|
---|
65 | self.xsize = 2*self.center.x()*0.9
|
---|
66 | self.ysize = 2*self.center.y()*0.9
|
---|
67 | self.resize(self.xsize, self.ysize)
|
---|
68 |
|
---|
69 | # About Message
|
---|
70 | def about(self):
|
---|
71 |
|
---|
72 | QtGui.QMessageBox.about(self, "About Duchamp",
|
---|
73 | """
|
---|
74 | <i>Duchamp</i> is an object-finder for use
|
---|
75 | on spectral-line data cubes. The basic execution of <i>Duchamp</i> is to read in a
|
---|
76 | FITS data cube, find sources in the cube, and produce a text file of positions,
|
---|
77 | velocities and fluxes of the detections, as well as a postscript file of the spectra
|
---|
78 | of each detection.
|
---|
79 |
|
---|
80 | <i>Duchamp</i> has been designed to search for objects in particular sorts of data:
|
---|
81 | those with relatively small, isolated objects in a large amount of background or
|
---|
82 | noise. Examples of such data are extragalactic HI surveys, or maser surveys.
|
---|
83 | <i>Duchamp</i> searches for groups of connected voxels (or pixels) that are all above
|
---|
84 | some flux threshold. No assumption is made as to the shape of detections, and
|
---|
85 | the only size constraints applied are those specified by the user.
|
---|
86 |
|
---|
87 | <i>Duchamp</i> has been written as a three-dimensional source finder, but it is possible
|
---|
88 | to run it on a two-dimensional image (i.e. one with no frequency or velocity
|
---|
89 | information), or indeed a one-dimensional array, and many of the features of
|
---|
90 | the program will work fine. The focus, however, is on object detection in three
|
---|
91 | dimensions, one of which is a spectral dimension. Note, in particular, that it
|
---|
92 | does not do any fitting of source profiles, a feature common (and desirable)
|
---|
93 | for many two-dimensional source finders. This is beyond the current scope of
|
---|
94 | <i>Duchamp</i>, whose aim is reliable detection of spectral-line objects.
|
---|
95 |
|
---|
96 | <i>Duchamp</i> provides the ability to pre-process the data prior to searching.
|
---|
97 | Spectral baselines can be removed, and either smoothing or multi-resolution
|
---|
98 | wavelet reconstruction can be performed to enhance the completeness and
|
---|
99 | reliability of the resulting catalogue.
|
---|
100 | """)
|
---|
101 |
|
---|
102 | # This prompts the user to open required files.
|
---|
103 | def openFiles(self):
|
---|
104 |
|
---|
105 | # Prompt for results file name.
|
---|
106 | resultsFileName = QtGui.QFileDialog.getOpenFileName(self, 'Open results File', '.', "text files (*.txt);;All files (*)")
|
---|
107 |
|
---|
108 | # If the name is empty, then we did not load any files.
|
---|
109 | if not resultsFileName:
|
---|
110 | QtGui.QMessageBox.information(self, 'Message', 'No file loaded.', QtGui.QMessageBox.Ok)
|
---|
111 | return
|
---|
112 |
|
---|
113 | # Open up the results file.
|
---|
114 | resultsFile = open(resultsFileName)
|
---|
115 |
|
---|
116 | # Get the first line of the results file.
|
---|
117 | for line in resultsFile:
|
---|
118 | firstline = line
|
---|
119 | break
|
---|
120 |
|
---|
121 | # Determine if the content of the results file is correct.
|
---|
122 | if "# Results of the Duchamp source finder" not in firstline:
|
---|
123 | QtGui.QMessageBox.information(self, 'Message', 'Incorrect content for results file. No file loaded.', QtGui.QMessageBox.Ok)
|
---|
124 | return
|
---|
125 |
|
---|
126 | # Find the maskfits and fits file name.
|
---|
127 | maskfitsFileName = ''
|
---|
128 | fitsFileName = ''
|
---|
129 | for line in resultsFile:
|
---|
130 |
|
---|
131 | if 'imageFile' in line and ' = ' in line:
|
---|
132 |
|
---|
133 | fitsFileName = line.split(' = ')[-1].split()[0].strip()
|
---|
134 |
|
---|
135 | if 'flagOutputMask' in line and '>' in line:
|
---|
136 |
|
---|
137 | maskfitsFileName = line.split('>')[-1].split()[0].strip()
|
---|
138 |
|
---|
139 | # If we cannot find the fits file name, prompt the user to find it.
|
---|
140 | if not fitsFileName:
|
---|
141 | QtGui.QMessageBox.information(self, 'Message', 'Results file do not contain input "imageFile" name. Please specify the original input fits file used in source finding.', QtGui.QMessageBox.Ok)
|
---|
142 |
|
---|
143 | # Prompt for the input cube fits file name.
|
---|
144 | fitsFileName = QtGui.QFileDialog.getOpenFileName(self, 'Open input File', '.', "fits files (*.fits);;All files (*)")
|
---|
145 |
|
---|
146 | # If the name is empty, then we did not load any files.
|
---|
147 | if not fitsFileName:
|
---|
148 | QtGui.QMessageBox.information(self, 'Message', 'No file loaded.', QtGui.QMessageBox.Ok)
|
---|
149 | return
|
---|
150 |
|
---|
151 | # If the file found does not exist, then let the user know and give the user a final chance.
|
---|
152 | while not os.path.isfile(fitsFileName):
|
---|
153 | QtGui.QMessageBox.information(self, 'Message', 'Input "imageFile" <%s> specified in results file cannot be found in current directory. Please specify the original input fits file used in source finding.'%fitsFileName, QtGui.QMessageBox.Ok)
|
---|
154 |
|
---|
155 | # Prompt for the input cube fits file name.
|
---|
156 | fitsFileName = QtGui.QFileDialog.getOpenFileName(self, 'Open input File', '.', "fits files (*.fits);;All files (*)")
|
---|
157 |
|
---|
158 | # If the user cancelled the loading, then do not prompt the user anymore.
|
---|
159 | if not fitsFileName:
|
---|
160 | return
|
---|
161 |
|
---|
162 | # If we cannot find the maskfits file name, prompt the user to find it.
|
---|
163 | if not maskfitsFileName:
|
---|
164 | QtGui.QMessageBox.information(self, 'Message', 'Results file do not contain MASK fits file name. Please specify MASK fits file.', QtGui.QMessageBox.Ok)
|
---|
165 |
|
---|
166 | # Prompt for the mask cube fits file name.
|
---|
167 | maskfitsFileName = QtGui.QFileDialog.getOpenFileName(self, 'Open mask File', '.', "MASK fits files (*.MASK.fits);;fits files (*.fits);;All files (*)")
|
---|
168 |
|
---|
169 | # If the name is empty, then we did not load any files.
|
---|
170 | if not maskfitsFileName:
|
---|
171 | QtGui.QMessageBox.information(self, 'Message', 'No file loaded.', QtGui.QMessageBox.Ok)
|
---|
172 | return
|
---|
173 |
|
---|
174 | # If the file found does not exist, then let the user know and give the user a final chance.
|
---|
175 | while not os.path.isfile(maskfitsFileName):
|
---|
176 | QtGui.QMessageBox.information(self, 'Message', 'MASK fits file <%s> specified in results file cannot be found in current directory. Please specify the MASK fits file.'%maskfitsFileName, QtGui.QMessageBox.Ok)
|
---|
177 |
|
---|
178 | # Prompt for the mask cube fits file name.
|
---|
179 | maskfitsFileName = QtGui.QFileDialog.getOpenFileName(self, 'Open mask File', '.', "MASK fits files (*.MASK.fits);;fits files (*.fits);;All files (*)")
|
---|
180 |
|
---|
181 | # If the user cancelled the loading, then do not prompt the user anymore.
|
---|
182 | if not maskfitsFileName:
|
---|
183 | return
|
---|
184 |
|
---|
185 | # Store the results from the results file as a class
|
---|
186 | self.Results = DuchampResults(resultsFileName)
|
---|
187 |
|
---|
188 | # Set the names of the files
|
---|
189 | self.Results.maskfitsFileName = maskfitsFileName
|
---|
190 | self.Results.fitsFileName = fitsFileName
|
---|
191 |
|
---|
192 | # If the file names are okay, then check and load the files using those names.
|
---|
193 | self.checkAndLoadFiles(maskfitsFileName, fitsFileName)
|
---|
194 |
|
---|
195 | # This makes sure that the files are correct and in the correct format.
|
---|
196 | # If everything is good, it will start the software.
|
---|
197 | def checkAndLoadFiles(self, maskfitsFileName, fitsFileName):
|
---|
198 |
|
---|
199 | # Try reading the files.
|
---|
200 | try:
|
---|
201 | # Open the mask cube fits file.
|
---|
202 | maskFits = pyfits.open(maskfitsFileName)
|
---|
203 |
|
---|
204 | # If the 'BUNIT' option in the mask cube fits header is not 'Object ID', then the program will not start.
|
---|
205 | if maskFits[0].header.get('BUNIT') != 'Object ID':
|
---|
206 | QtGui.QMessageBox.warning(self, 'Incorrect specification in .MASK.fits file', 'The "BUNIT" option in "%s" is not set to "Object ID". Files not loaded.'%maskfitsFileName, QtGui.QMessageBox.Ok)
|
---|
207 | return(0)
|
---|
208 |
|
---|
209 | # Open the input cube fits file.
|
---|
210 | realFits = pyfits.open(fitsFileName)
|
---|
211 |
|
---|
212 | # If the two cubes do not have matching shapes, then they are obviously not meant for each other. The program will not start.
|
---|
213 | if maskFits[0].data.shape != realFits[0].data.shape:
|
---|
214 | QtGui.QMessageBox.warning(self, 'Cube sizes mismatch', 'Fits cube sizes differ in shape in files "%s" and "%s". Files not loaded.'%(maskfitsFileName, fitsFileName), QtGui.QMessageBox.Ok)
|
---|
215 | return(0)
|
---|
216 |
|
---|
217 | # If everything is fine, we will confirm the choice of files with the user.
|
---|
218 | SuccessMsg = QtGui.QMessageBox()
|
---|
219 | SuccessMsg.setWindowTitle("Files Loaded")
|
---|
220 | SuccessMsg.setText('Files\n"%s" and \n"%s"\n are loaded successfully!'%(maskfitsFileName, fitsFileName))
|
---|
221 | SuccessMsg.setInformativeText('Please confirm that those are the correct files.')
|
---|
222 | SuccessMsg.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.Discard)
|
---|
223 | SuccessMsg.setDefaultButton(QtGui.QMessageBox.Yes)
|
---|
224 |
|
---|
225 | # If the user is fine with the choice, then start the program with those files.
|
---|
226 | if SuccessMsg.exec_() == QtGui.QMessageBox.Yes:
|
---|
227 |
|
---|
228 | # This is the name of the input file
|
---|
229 | self.fitsName = fitsFileName.split('/')[-1]
|
---|
230 |
|
---|
231 | # This is the name of the maskfits file
|
---|
232 | self.maskfitsName = maskfitsFileName.split('/')[-1]
|
---|
233 |
|
---|
234 | # Those are the fits data
|
---|
235 | self.maskFits = maskFits
|
---|
236 | self.realFits = realFits
|
---|
237 |
|
---|
238 | # Now we can start processing the data
|
---|
239 | self.processData()
|
---|
240 | return(1)
|
---|
241 |
|
---|
242 | # Otherwise, discard the files.
|
---|
243 | QtGui.QMessageBox.information(self, 'Message', 'Files discarded.', QtGui.QMessageBox.Ok)
|
---|
244 | return(0)
|
---|
245 |
|
---|
246 | # If anything went wrong in the above process, then file reading has failed.
|
---|
247 | except Exception:
|
---|
248 | QtGui.QMessageBox.warning(self, 'Error', 'Reading of fits files failed. Files not loaded.', QtGui.QMessageBox.Ok)
|
---|
249 |
|
---|
250 | return(0)
|
---|
251 |
|
---|
252 | # This loads the required data from the files that contains the data.
|
---|
253 | def processData(self):
|
---|
254 |
|
---|
255 | # Use a progress bar to indicate the loading process
|
---|
256 | self.pbar = ProgressBar(title = "Processing data", total = 100)
|
---|
257 | self.pbar.show()
|
---|
258 |
|
---|
259 | # Obtain the two cubes
|
---|
260 | self.maskCube = self.maskFits[0].data
|
---|
261 | self.realCube = self.realFits[0].data
|
---|
262 |
|
---|
263 | # Those are the size of our data cubes
|
---|
264 | [self.lenZ, self.lenY, self.lenX] = self.maskCube.shape
|
---|
265 |
|
---|
266 | # This is the cube used to produce the final image
|
---|
267 | self.finalCube = zeros([self.lenZ, self.lenY, self.lenX])
|
---|
268 |
|
---|
269 | # Those are our mask image, real image, and final image respectively
|
---|
270 | self.maskImage = zeros([self.lenY, self.lenX])
|
---|
271 | self.realImage = zeros([self.lenY, self.lenX])
|
---|
272 | self.finalImage = zeros([self.lenY, self.lenX])
|
---|
273 |
|
---|
274 | # This will contain all the moment maps with their index matching its object ID
|
---|
275 | self.momentMaps = ['Moment Maps']
|
---|
276 |
|
---|
277 | # Here we compute the final cube from the mask cube and real cube
|
---|
278 |
|
---|
279 | # Go through each object we found
|
---|
280 | for ObjID in range(1, self.Results.totalDetections + 1):
|
---|
281 |
|
---|
282 | # Obtain information regarding that source
|
---|
283 | ObjStats = self.Results.SkyObjects[ObjID].stats
|
---|
284 |
|
---|
285 | # Obtain the spatial and spectral bound of that source
|
---|
286 | x1 = ObjStats['X1']
|
---|
287 | x2 = ObjStats['X2']
|
---|
288 | y1 = ObjStats['Y1']
|
---|
289 | y2 = ObjStats['Y2']
|
---|
290 | z1 = ObjStats['Z1']
|
---|
291 | z2 = ObjStats['Z2']
|
---|
292 |
|
---|
293 | # Calculate the moment map of the source as well as add the source to the final cube
|
---|
294 | momentMap = zeros([self.lenY, self.lenX])
|
---|
295 |
|
---|
296 | for z in range(z1, z2 + 1):
|
---|
297 |
|
---|
298 | momentMap += self.realCube[z]
|
---|
299 |
|
---|
300 | for y in range(y1, y2 + 1):
|
---|
301 | for x in range(x1, x2 + 1):
|
---|
302 |
|
---|
303 | if self.maskCube[z][y][x] == ObjID:
|
---|
304 | self.finalCube[z][y][x] = self.realCube[z][y][x]
|
---|
305 |
|
---|
306 | # Add this moment map to the list of moment maps
|
---|
307 | self.momentMaps += [momentMap]
|
---|
308 |
|
---|
309 | # And also compute the mask image from the mask cube
|
---|
310 | for z in range(self.lenZ):
|
---|
311 | self.maskImage[self.maskCube[z] == ObjID] = ObjID
|
---|
312 |
|
---|
313 | # Update the progress bar accordingly
|
---|
314 | self.pbar.update_progressbar(100*ObjID/self.Results.totalDetections + 1)
|
---|
315 |
|
---|
316 | # Compute the real image from the real cube
|
---|
317 | for z in range(self.lenZ):
|
---|
318 | self.realImage += self.realCube[z]
|
---|
319 |
|
---|
320 | # Compute the final image from the final cube
|
---|
321 | for z in range(self.lenZ):
|
---|
322 | self.finalImage += self.finalCube[z]
|
---|
323 |
|
---|
324 | # The data processing has finished
|
---|
325 | self.pbar.close()
|
---|
326 |
|
---|
327 | # The program can now start
|
---|
328 | self.start()
|
---|
329 |
|
---|
330 | # This defines what happens when the software starts.
|
---|
331 | def start(self):
|
---|
332 |
|
---|
333 | # Use a progress bar to indicate the start up progress
|
---|
334 | self.pbar = ProgressBar(title = "Loading Skymap", total = 100)
|
---|
335 | self.pbar.show()
|
---|
336 |
|
---|
337 | # Start the skymap.
|
---|
338 | self.startSkymap()
|
---|
339 | self.pbar.update_progressbar(26)
|
---|
340 |
|
---|
341 | # Start the spectral graph.
|
---|
342 | self.startSpectralGraph()
|
---|
343 | self.pbar.update_progressbar(51)
|
---|
344 |
|
---|
345 | # Start the zoom graph
|
---|
346 | self.startZoomGraph()
|
---|
347 | self.pbar.update_progressbar(76)
|
---|
348 |
|
---|
349 | # Start the moment map
|
---|
350 | self.startMomentMap()
|
---|
351 | self.pbar.update_progressbar(100)
|
---|
352 |
|
---|
353 | self.pbar.close()
|
---|
354 |
|
---|
355 | # This starts the skymap.
|
---|
356 | def startSkymap(self):
|
---|
357 |
|
---|
358 | # Initialise the skymap.
|
---|
359 | self.skymap = Skymap(self.Results, maskImage = self.maskImage, finalImage = self.finalImage)
|
---|
360 | self.skymap.drawImage()
|
---|
361 | self.skymap.enableInteractivity()
|
---|
362 | self.skymap.fig.suptitle(self.fitsName)
|
---|
363 |
|
---|
364 | # Determine its size and location.
|
---|
365 | self.skymap.setMinimumHeight(0.5*self.ysize)
|
---|
366 | self.skymap.setMinimumWidth(0.3*self.xsize)
|
---|
367 | self.layout.addWidget(self.skymap, 0, 0, 4, 4)
|
---|
368 |
|
---|
369 | # Connect the signal from skymap to update the spectral graph.
|
---|
370 | self.skymap.graphSignal.connect(self.updateSpectralGraph)
|
---|
371 | self.skymap.graphSignal.connect(self.updateZoomGraph)
|
---|
372 | self.skymap.graphSignal.connect(self.updateMomentMap)
|
---|
373 |
|
---|
374 | # This starts the spectral graph.
|
---|
375 | def startSpectralGraph(self):
|
---|
376 |
|
---|
377 | # Initialise the spectral graph.
|
---|
378 | self.spectralGraph = SpectralGraph(self.Results, self.maskFits, self.realFits, self.skymap.maskImage)
|
---|
379 |
|
---|
380 | # Determine its size and location.
|
---|
381 | self.spectralGraph.setMinimumHeight(0.3*self.ysize)
|
---|
382 | self.spectralGraph.setMinimumWidth(self.xsize)
|
---|
383 |
|
---|
384 | self.layout.addWidget(self.spectralGraph, 4, 0, 2, 8)
|
---|
385 |
|
---|
386 | # Initialise the radio buttons for display options.
|
---|
387 | self.displayMaxButton = QtGui.QRadioButton("Peak Detected Intensity")
|
---|
388 | self.displaySumButton = QtGui.QRadioButton("Total Detected Intensity")
|
---|
389 |
|
---|
390 | # Determine its location.
|
---|
391 | self.layout.addWidget(self.displayMaxButton, 0, 4, 1, 1)
|
---|
392 | self.layout.addWidget(self.displaySumButton, 0, 5, 1, 1)
|
---|
393 |
|
---|
394 | # Determine its functionality.
|
---|
395 | self.displayMaxButton.toggled.connect(self.spectralGraph.plotMax)
|
---|
396 | self.displaySumButton.toggled.connect(self.spectralGraph.plotSum)
|
---|
397 | self.displayMaxButton.setChecked(True)
|
---|
398 |
|
---|
399 |
|
---|
400 | # This updates the spectral graph.
|
---|
401 | def updateSpectralGraph(self):
|
---|
402 |
|
---|
403 | # Set the current object to plot the spectral graph with as the highlighted object in the skymap, and plot the spectral graph.
|
---|
404 | self.spectralGraph.setCurrentObjID(self.skymap.highlightedObj)
|
---|
405 | self.spectralGraph.plotSource()
|
---|
406 |
|
---|
407 | # This starts the zoom graph.
|
---|
408 | def startZoomGraph(self):
|
---|
409 |
|
---|
410 | # Initialise the zoom graph
|
---|
411 | self.zoomGraph = ZoomGraph(self.Results, self.maskFits, self.realFits, self.skymap.maskImage)
|
---|
412 |
|
---|
413 | # Determine its location
|
---|
414 | self.layout.addWidget(self.zoomGraph, 1, 4, 3, 2)
|
---|
415 |
|
---|
416 | # Link the radio button functionalities to the zoom graph
|
---|
417 | self.displayMaxButton.toggled.connect(self.zoomGraph.plotMax)
|
---|
418 | self.displaySumButton.toggled.connect(self.zoomGraph.plotSum)
|
---|
419 |
|
---|
420 | # This updates the zoom graph.
|
---|
421 | def updateZoomGraph(self):
|
---|
422 |
|
---|
423 | # Set the current object to plot the zoom graph with as the highlighted object in the skymap, and plot the zoom graph.
|
---|
424 | self.zoomGraph.setCurrentObjID(self.skymap.highlightedObj)
|
---|
425 | self.zoomGraph.plotSource()
|
---|
426 |
|
---|
427 | # This starts the moment map.
|
---|
428 | def startMomentMap(self):
|
---|
429 |
|
---|
430 | # Initialise the moment map
|
---|
431 | self.momentMap = MomentMap(self.Results, maskImage = self.maskImage, finalImage = self.finalImage, momentMaps = self.momentMaps)
|
---|
432 |
|
---|
433 | # Determine its location
|
---|
434 | self.layout.addWidget(self.momentMap, 1, 6, 3, 2)
|
---|
435 |
|
---|
436 | # This updates the moment map.
|
---|
437 | def updateMomentMap(self):
|
---|
438 |
|
---|
439 | # Set the current object to draw the moment map with as the highlighted object in the skymap, and draw the moment map
|
---|
440 | self.momentMap.setCurrentObjID(self.skymap.highlightedObj)
|
---|
441 | self.momentMap.drawImage()
|
---|
442 |
|
---|
443 | # This saves the parameters used to generate this output into a parameter file.
|
---|
444 | def saveParameters(self):
|
---|
445 |
|
---|
446 | # Prompt for the name of the file.
|
---|
447 | parameterFilePath = QtGui.QFileDialog.getSaveFileName(self, 'Save parameters as', '.', "text files (*.txt);;All files (*)")
|
---|
448 |
|
---|
449 | # Write the parameter.
|
---|
450 | try:
|
---|
451 | writeParamFile(parameterFilePath, self.Results.parameters, self.Results.parameterName)
|
---|
452 |
|
---|
453 | except Exceptions:
|
---|
454 | pass |
---|