def process_frame(self, frame): """ Prepare a child thread to do the reduction of a single frame. (Will run in main thread) """ try: if self.pipethread.isAlive(): # If busy reducing a frame, say so! raise Busy except AttributeError: # Will be raised if pipethread does not exist. This is OK, because # is means no reduction is going on pass # Make sure the input frames exists if not os.path.exists(frame): messageLog.put_error('Error processing %s' % frame) messageLog.put_error('Frame does not exist (anymore?). Skipped!') return # Put all the selected tasks (by their checkboxes) in the object 'todotasks' todotasks = [] for task in self.tasklist: if self.selectedtasks[task.name].get(): todotasks.append(task.name) # Start a child thread that will do the data 'crunching' messageLog.put('Starting pipeline thread', 9) self.pipethread = threading.Thread(None, self.run_pipe, args=(frame, self.outQueue, todotasks, self.GUIupdater, self.currentmode)) self.pipethread.start()
def dump(optionlist=[]): """ Create a quick dump of the current options in the logfile. Mainly for debugging purposes """ # Only do this for the options given in optionlist if optionlist: keys = optionlist # ...or for all options if not list was defined else: keys = options.keys() # Output header messageLog.put("="*80) messageLog.put("Dumping current settings to logfile") messageLog.put("="*80) # Write one by one the name and values to the logfile for key in keys: try: messageLog.put("%s: %s" % (options[key].name, options[key].value)) except KeyError: messageLog.put_error("Cannot find value of '%s'." % key) # Output footer messageLog.put("="*80)
def save_raw(outfile=None, optionlist=[], ignore_options=[]): "Save the current values of options to disk (raw format using cPickle)" # Only proceed if outfile is defined if outfile: # Create an object that will only contain the elements that are # supposed to be written to disk outoptions = dict() # If optionlist is defined, only select the listed items if optionlist: keys = optionlist # Otherwise, select all else: keys = options.keys() # If ignore_options was defined, modify the keylist if ignore_options: for ignore_key in ignore_options: if ignore_key in keys: keys.remove(ignore_key) # Loop over options (well... the corresponding keys) for key in keys: # Copy the options into the output object try: outoptions[key] = options[key] messageLog.put("Wrote value for %s to %s" % (key, outfile), 9) except KeyError: messageLog.put_error("Cannot write configuration option '%s'." % key) # Now, open the output file in write mode filehandle = open(outfile, mode='w') # Do the actual writing to disk. cPickle takes care of creating # a textual representation of the contents of outoptions try: cPickle.dump(outoptions, filehandle) except Exception, errstr: messageLog.put_error("Could not save config to %s : %s" % (infile, errstr)) raise # Close the output file filehandle.close() # Inform user messageLog.put("Wrote configuration to %s" % outfile, 5)
def load_raw(infile=None, optionlist=[], ignore_options=[]): "Restore the values of options from disk (Raw format using cPickle)" # Only proceed if infile is defined if infile: # Test if file exists if not os.path.exists(infile): messageLog.put_error("Requested config file does not exist: %s" % infile) return # Open the input file for reading filehandle = open(infile, mode='r') # Use cPickle to read the newoptions object from disk try: newoptions = cPickle.load(filehandle) except Exception, errstr: messageLog.put_error("Could not load config from %s : %s" % (infile, errstr)) raise # Close the input file filehandle.close() # Get the list of keys to read: keys = newoptions.keys() # If ignore_options was defined, modify the keylist if ignore_options: for ignore_key in ignore_options: if ignore_key in keys: keys.remove(ignore_key) # Loop over keylist (i.e. the options) for key in keys: # Copy the read options into the current set of options try: options[key] = newoptions[key] messageLog.put("Restored value for %s from %s" % (key, infile), 9) except KeyError: messageLog.put_error("Cannot restore configuration option '%s'." % key) # Inform user messageLog.put("Restored configuration from %s" % infile, 5)
def makeFrame(self): "Construct the frame from scratch" # Put all data in a root frame that is embedded in the 'self' Frame object. # In this way, it is possible to destroy and rebuid the frame object, # without destroying the 'self' frame object. self.root = Frame(self, bg=self.defaultbgcolor) # The root frame should be flexible in width self.root.columnconfigure(0, weight=1) # Get the value of the current reduction mode and save in the config option self.currentmode = self.mode.get() config.options['currentmode'].set(self.currentmode) # Put the info on the tasks and modes into a variable that will be # 'unpacked' below modesandtasks = config.options['reductionmodes'].value self.tasklist = [] task_is_selected = {} try: modesandtasks[self.currentmode] except KeyError: messageLog.put_error('No reduction modes are defined!') messageLog.put_error('You will not be able to do any reductions') messageLog.put_error('Update or reinstall your default configuration file') return # Obtain direct references to the class objects that are named in # 'tasknames' for the current mode, and if they are selected or not for (taskname, selected) in modesandtasks[self.currentmode]: self.tasklist.append( getattr(tasks, taskname) ) task_is_selected[taskname] = selected # Row counter i = 1 # Loop over tasks to create the taskBars for task in self.tasklist: # Create an integer that will be bound to the checkbutton in the taskBar self.selectedtasks[task.name] = IntVar(master=self.root) # Set the task to 'selected' or not if task_is_selected[task.name]: self.selectedtasks[task.name].set(1) # Create a 'taskBar' object, which consists of a checkbutton and a # status line. Store the taskBar in 'self.taskbars' for future reference self.taskbars[task.name] = taskBar.taskBar(self.root, task, self.selectedtasks[task.name], bgcolor=self.defaultbgcolor) self.taskbars[task.name].grid(sticky=E+W) # Next row i = i + 1 # And display the contents of the root frame self.root.grid(sticky=E+W)
messageLog.put( 'Task manager invoked for %s with following keywords :' % task.name, 7) messageLog.put('%s %s' % (args, kwds), 7) try: # Execute the task task().run(*args, **kwds) except tasks.taskAbort, errString: return '' except tasks.taskError, errString: # Catch the 'standard' taskError, which indicates an 'expected' error messageLog.put('TaskManager: Encountered a taskError', 7) messageLog.put_error('An error occured in task "%s" :' % task.name) messageLog.put_error(errString) return 'Terminated by error' except Exception, inst: # Also catch all other errors and display the complete traceback log. # These errors are more severe and should not occur, although they will. messageLog.put('TaskManager: Encountered an unknown error', 7) messageLog.put_error('An unknown error occured in "%s" :' % task.name) # Get the error message in standard traceback format (exc_type, exc_value, exc_traceback) = sys.exc_info() errormessage = traceback.format_exception(exc_type, exc_value, exc_traceback) for message in errormessage:
def load(infile=None, optionlist=[], ignore_options=[]): "Restore the values of from disk" # Only proceed if infile is defined if infile: # Test if file exists if not os.path.exists(infile): raise IOError try: newoptions = configobj.ConfigObj(infile) except Exception, errstr: messageLog.put_error("Syntax error in configuration file : %s" % infile) messageLog.put_error("Error: %s " % errstr) return # Get the list of keys to read: try: keys = newoptions['main'].keys() except KeyError: messageLog.put_error("Syntax error in config file: no 'main' section defined") raise # If ignore_options was defined, modify the keylist if ignore_options: for ignore_key in ignore_options: if ignore_key in keys: keys.remove(ignore_key) # Loop over keylist (i.e. the options) and process the different formats for key in keys: # Convert to integer if possible try: newvalue = newoptions['main'].as_int(key) except: newvalue = newoptions['main'][key] try: options[key] except: messageLog.put_error("Option %s in %s does not exist - ignored" % (key, infile)) return # Process arrays of files if options[key].type in ('filelist', 'fitsfilelist'): # Cast to array of strings if type(newvalue) != type([]): newvalue = [newvalue] # And attach directory name is necessary filelist = newvalue newvalue = [] for filename in filelist: if not os.path.dirname(filename): filename = (os.path.join(options[key].indir.value, filename)) newvalue.append(filename) # Process arrays of files if options[key].type in ('fitsfile', 'outfitsfile'): # Cast to string if type(newvalue) == type([]): newvalue = newvalue[0] if newvalue != "": if not os.path.dirname(newvalue): newvalue = os.path.join(options[key].indir.value, newvalue) # Now copy the new values into the option try: options[key].value = newvalue messageLog.put("Restored value for %s from %s" % (key, infile), 9) except KeyError: messageLog.put_error("Cannot restore configuration option '%s'." % key) # Now, store the available reduction tasks options['reductionmodes'].value = {} options['availablemodes'].value = newoptions.keys() options['availablemodes'].value.remove('main') for mode in options['availablemodes'].value: options['reductionmodes'].value[mode] = [] tasks = newoptions[mode].keys() for task in tasks: options['reductionmodes'].value[mode].append((task, newoptions[mode].as_bool(task))) messageLog.put("Read list of tasks for mode: %s" % mode, 9) # Inform user messageLog.put("Restored configuration from %s" % infile, 5)
def save(outfile=None, optionlist=[], ignore_options=[]): "Save the current values of options to disk" # Only proceed if outfile is defined if outfile: # Create an object that will only contain the elements that are # supposed to be written to disk outconfig = configobj.ConfigObj(options['default_savefile'].value) # If optionlist is defined, only select the listed items if optionlist: keys = optionlist # Otherwise, select all else: keys = outconfig['main'].keys() # If ignore_options was defined, modify the keylist if ignore_options: for ignore_key in ignore_options: if ignore_key in keys: keys.remove(ignore_key) # Loop over options for key in keys: try: outvalue = options[key].value except KeyError: messageLog.put_warning("Could not write value for %s - continuing" % key) return # Process arrays of files if options[key].type in ('filelist', 'fitsfilelist'): # Cast to array of strings if type(outvalue) != type([]): outvalue = [outvalue] # And attach directory name is necessary filelist = outvalue outvalue = [] for filename in filelist: if os.path.dirname(filename) == options[key].indir.value : filename = os.path.basename(filename) outvalue.append(filename) if options[key].type in ('fitsfile', 'outfitsfile'): if os.path.dirname(outvalue) == options[key].indir.value : outvalue = os.path.basename(outvalue) # Copy the options into the output object try: outconfig['main'][key] = outvalue messageLog.put("Wrote value for %s to %s" % (key, outfile), 9) except KeyError: messageLog.put_error("Cannot write configuration option '%s'." % key) try: # Now, open the output file in write mode filehandle = open(outfile, mode='w') # Write the data outconfig.write(filehandle) # Close the output file filehandle.close() except Exception, errstr: messageLog.put_error("Could not save config to %s : %s" % (outfile, errstr)) # Inform user messageLog.put("Wrote configuration to %s" % outfile, 5)
def makeFrame(self): "Construct the frame from scratch" # Put all data in a root frame that is embedded in the 'self' Frame object. # In this way, it is possible to destroy and rebuid the frame object, # without destroying the 'self' frame object. self.root = Frame(self) # All columns of the root frame should be flexible in width self.root.columnconfigure(0, weight=1, minsize=200) self.root.columnconfigure(1, weight=1, minsize=self.minwidth) self.root.columnconfigure(2, weight=1, minsize=self.minwidth) # Initialize (reset) the list of button objects (for future reference) self.optionbuttonlist = {} # Row counter i = 1 # If a title is given, display a label spanning the entire width of # the frame if self.title: label = Label(self.root, text=self.title) label.grid(columnspan=3, row=i, sticky=E + W) # Next row i = i + 1 # Loop through the list of options for key in self.optionlist: # Get the value of the named option try: option = config.options[key] except KeyError: # Diaplay an error in the log if the option does not exist messageLog.put_error( "Cannot find option '%s' in current configuration!" % key) # Skip the creation of an optionButton and continue with the next option continue # Create an optionButton (button and value field) for this option button = optionButton(self.root, option, width=self.minwidth) button.grid(column=0, columnspan=3, row=i, sticky=E + W) # Save this Tkinter object in 'optionbuttonlist' for future reference self.optionbuttonlist[key] = button # Newt row i = i + 1 # Only if this is a top-level window, add 'Save', 'Load' and 'Reset' # buttons, and an 'Close window' button to hide the window. if not self.embed: button = Button(self.root, text='Save settings') # Attach the 'saveConfig' method to this button button.config(command=self.saveConfig) button.grid(column=0, row=i, sticky=E + W) # Add pop-up help to this button popUp.popUp( self.root, button, title='Save settings', text="""Save the settings above (and only these) to file. """) button = Button(self.root, text='Load settings') # Attach the 'loadConfig' method to this button button.config(command=self.loadConfig) button.grid(column=1, row=i, sticky=E + W) # Add pop-up help to this button popUp.popUp(self.root, button, title='Load settings', text="""Load settings above (and only these) from file. """) button = Button(self.root, text='Reset') # Attach the 'resetConfig' method to this button button.config(command=self.resetConfig) button.grid(column=2, row=i, sticky=E + W) # Add pop-up help to this button popUp.popUp( self.root, button, title='Reset', text="""Revert to the most recently saved or loaded settings. """) # Next row i = i + 1 # Add the 'Close window' button, and attach the hide method button = Button(self.root, text='Close window', command=self.hide) button.grid(row=i, columnspan=3, sticky=E + W) # Display the contents of the roor window self.root.grid(sticky=N + S + E + W)
def makeFrame(self): "Construct the frame from scratch" # Put all data in a root frame that is embedded in the 'self' Frame object. # In this way, it is possible to destroy and rebuid the frame object, # without destroying the 'self' frame object. self.root = Frame(self) # All columns of the root frame should be flexible in width self.root.columnconfigure(0, weight=1, minsize=200) self.root.columnconfigure(1, weight=1, minsize=self.minwidth) self.root.columnconfigure(2, weight=1, minsize=self.minwidth) # Initialize (reset) the list of button objects (for future reference) self.optionbuttonlist = {} # Row counter i = 1 # If a title is given, display a label spanning the entire width of # the frame if self.title: label = Label(self.root, text=self.title) label.grid(columnspan=3, row=i, sticky=E+W) # Next row i = i + 1 # Loop through the list of options for key in self.optionlist: # Get the value of the named option try: option = config.options[key] except KeyError: # Diaplay an error in the log if the option does not exist messageLog.put_error("Cannot find option '%s' in current configuration!" % key) # Skip the creation of an optionButton and continue with the next option continue # Create an optionButton (button and value field) for this option button = optionButton(self.root, option, width=self.minwidth) button.grid(column=0, columnspan=3, row=i, sticky=E+W) # Save this Tkinter object in 'optionbuttonlist' for future reference self.optionbuttonlist[key] = button # Newt row i = i + 1 # Only if this is a top-level window, add 'Save', 'Load' and 'Reset' # buttons, and an 'Close window' button to hide the window. if not self.embed: button = Button(self.root, text='Save settings') # Attach the 'saveConfig' method to this button button.config(command=self.saveConfig) button.grid(column=0, row=i, sticky=E+W) # Add pop-up help to this button popUp.popUp(self.root, button, title='Save settings', text="""Save the settings above (and only these) to file. """) button = Button(self.root, text='Load settings') # Attach the 'loadConfig' method to this button button.config(command = self.loadConfig) button.grid(column=1, row=i, sticky=E+W) # Add pop-up help to this button popUp.popUp(self.root, button, title='Load settings', text="""Load settings above (and only these) from file. """) button = Button(self.root, text='Reset') # Attach the 'resetConfig' method to this button button.config(command = self.resetConfig) button.grid(column=2, row=i, sticky=E+W) # Add pop-up help to this button popUp.popUp(self.root, button, title='Reset', text="""Revert to the most recently saved or loaded settings. """) # Next row i = i + 1 # Add the 'Close window' button, and attach the hide method button = Button(self.root, text='Close window', command=self.hide) button.grid(row=i, columnspan=3, sticky=E+W) # Display the contents of the roor window self.root.grid(sticky=N+S+E+W)
# Display status message that task is to be executed messageLog.put('Task manager invoked for %s with following keywords :' % task.name, 7) messageLog.put('%s %s' % (args, kwds), 7) try: # Execute the task task().run(*args, **kwds) except tasks.taskAbort, errString: return '' except tasks.taskError, errString: # Catch the 'standard' taskError, which indicates an 'expected' error messageLog.put('TaskManager: Encountered a taskError', 7) messageLog.put_error('An error occured in task "%s" :' % task.name) messageLog.put_error(errString) return 'Terminated by error' except Exception, inst: # Also catch all other errors and display the complete traceback log. # These errors are more severe and should not occur, although they will. messageLog.put('TaskManager: Encountered an unknown error', 7) messageLog.put_error('An unknown error occured in "%s" :' % task.name) # Get the error message in standard traceback format (exc_type, exc_value, exc_traceback) = sys.exc_info() errormessage = traceback.format_exception(exc_type, exc_value, exc_traceback) for message in errormessage: # Show all lines of traceback log
def autoCheckFiles(self): """ Invoked when the autocheck checkbox is toggled. Depending upon whether it was selected or deselected, will start or stop the automatic checking for new files for the unprocessed queue. """ # Is the autocheck button (still) selected? if self.autocheck.get(): # Yes, it was... # Make sure that inpath and outpath are different. If they are the # identical, a newly reduced file in the 'output' directory would appear # to be a new, unreduced file in the 'input' directory, triggering a bad # runaway. inpath = os.path.normpath(config.options['inpath'].value) outpath = os.path.normpath(config.options['outpath'].value) if inpath == outpath: messageLog.put_error( 'Putting "Output directory" equal to "Input directory" will cause' ) messageLog.put_error( 'runaway problems in combination with "Autocheck for new files".' ) messageLog.put_error('Autochecking stopped!') # In this case, force an untoggle of the button and return self.autocheck.set(0) return # If this is the first time this routine is called, prepare the # directory list. if (self.checkID is None): # Store the current contents of the input directory for future reference self.dirlist = [ os.path.join(inpath, file) for file in os.listdir(inpath) ] messageLog.put('Autochecking started', 5) # Check for new files in the input directory self.findNewFiles() # Call this routine again later self.checkID = self.after(autocheckwaitingtime, self.autoCheckFiles) else: # Well, if the autocheck button is not selected (anymore) # Try to cancel the next call to this routine (if there is one scheduled) try: self.after_cancel(self.checkID) except: pass # Reset the ID of the waiting loop self.checkID = None messageLog.put('Autochecking stopped', 5)
def createdatacube(framelist, extension=0): """ Read data for a list of images, and store this in a 3-dimensional array (numpy type). A 3-D array is practical for performing efficient calculations on a large number of images (for example, averaging). """ # Determine the number of frames in the list (will be z-size of cube) nframes = len(framelist) # Counter to keep track of position in the datacube n = 0 # Read first frame in list to determine frame size (x- and y-size of cube) messageLog.put('Reading frame %i of %i (%s)' % (n+1, nframes, os.path.basename(framelist[0]))) try: image = pyfits.open(framelist[0]) except IOError: messageLog.put_error('Cannot open %s' % framelist[0]) return None image = extractMEF(image, extension=extension) # THIS IS NOT THE BEST PLACE TO PUT THESE, BUT IT SHOULD GO SOMEWHERE flipframe(image, config.options['frameorientation'].value) clipframe(image) data = image[0].data framesize = data.shape # Create am empty 3D dataset and insert first frame # (Use .copy() to keep the data in memory after the image is closed) datacube = numpy.zeros((nframes, framesize[0], framesize[1]), data.dtype) datacube[n, :, :] = data.copy() # Close the file image.close() # Next frame... n = n + 1 # Loop over remaining frames (start from index 1, not 0) for frame in framelist[1:]: messageLog.put('Reading frame %i of %i (%s)' % (n+1, nframes, os.path.basename(frame))) # Read a frame try: image = pyfits.open(frame) except IOError: messageLog.put_error('Cannot open %s' % frame) return None # THIS IS NOT THE BEST PLACE TO PUT THESE, BUT IT SHOULD GO SOMEWHERE image = extractMEF(image, extension=extension) flipframe(image, config.options['frameorientation'].value) clipframe(image) data = image[0].data # Check that the frame size is compatible with first frame if data.shape != framesize: messageLog.put_error('Not all frames have identical sizes') return None # Insert the frame in the cube # (Use .copy() to keep the data in memory after the image is closed) datacube[n, :, :] = data.copy() # Close the file image.close() # Next frame... n = n + 1 # Return the cube return datacube
def WCSvector(frame, extension=0): """ Return vectors of the WCS (World Coordinate System) x and y pixel coordinates in this frame. Particularly useful for binned or windowed frames. """ # 'frame' is assumed to be an object created by pyfits.open(). Failure # to read any of the header keywords generates a KeyError exception, which # is supposed to be handled by the calling routine. However, for properly # defined frames, exceptions should not need to occur # Get the number of pixels for the two axes from the FITS headers # No try/except needed, because these headers MUST be defined. try: xsize = frame[extension].header['NAXIS1'] ysize = frame[extension].header['NAXIS2'] except KeyError: messageLog.put_error('NAXIS1 or NAXIS2 keyword not defined. Are you working with the correct extension?') return # Get the remaining vector values, in a truly failsafe way. try: x0 = frame[extension].header['CRVAL1'] except KeyError: x0 = 1 try: y0 = frame[extension].header['CRVAL2'] except KeyError: y0 = 1 try: xref = frame[extension].header['CRPIX1'] except KeyError: xref = 1 try: yref = frame[extension].header['CRPIX2'] except KeyError: yref = 1 try: xstep, dummy = frame[extension].header['CCDSUM'].split() xstep = int(xstep) except: raise try: xstep = frame[extension].header['CDELT1'] except KeyError: xstep = 1 try: dummy, ystep = frame[extension].header['CCDSUM'].split() ystep = int(ystep) except: raise try: ystep = frame[extension].header['CDELT2'] except KeyError: ystep = 1 # Construct the actual vectors with x and y pixel coordinates. # numpy.arange(n) gives a vector with n numbered elements xvec = (x0 + ( (numpy.arange(xsize) - xref + 1) * xstep)) yvec = (y0 + ( (numpy.arange(ysize) - yref + 1) * ystep)) # Return a tuple with the two vectors return (xvec, yvec)
def run(): """ Main program entry, initializing and starting the main window and managing its 'mainloop' """ # Get and check command-line options # # NB: The -c (enable-calib) option is now deprecated # try: opts, args = getopt.getopt(sys.argv[1:], "cs", ["calib", "scrollbar"]) except getopt.GetoptError: # print help information and exit: print "Unknown command line parameter. Allowed optional parameters are : " #print "-c [file] Load calibration file at startup" print "-s / --scrollbar Add vertical scrollbar is screen does not fit" sys.exit(2) #enablecalib = False hasscrollbar = False for option, parameter in opts: if option in ("-c", "--calib"): print "The -c option is deprecated. You can now always access the calibs submenu." # enablecalib = True if option in ("-s", "--scrollbar"): hasscrollbar = True # Display a welcome message in the message log messageLog.put('=' * 65) messageLog.put("Welcome to FIEStool - the automatic data processing tool") messageLog.put("for the FIES spectrograph at the Nordic Optical Telescope") messageLog.put("Copyright (c) 2005 NOTSA; All Rights Reserved") messageLog.put("This is version %s (%s), written by %s" % (_version, _date, _author)) messageLog.put('=' * 65) messageLog.put("Right-click on any item to obtain instantaneous help") messageLog.put('=' * 65) # And tell the user what warnings and errors look like messageLog.put_warning("This is a sample warning") messageLog.put_error("This is a sample error") messageLog.put('=' * 65) # Now, check if a default config file exists; if yes, load it. try: config.load(config.options['default_savefile'].value) except IOError: messageLog.put_warning('No default configuration file found!') pass # Now, try to load the autoLoader config file. try: config.load_raw(config.options['default_autoload_savefile'].value) except IOError: messageLog.put_warning( 'No default autoloader configuration file found!') pass # Create the absolute top-level root window. Destroying this kills 'everything'! root = Tk() # Make the root window variable in width and height root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) # Give it a title root.title('FIEStool - Data reduction for the FIES spectrograph') # Initialize the main window, with 'root' as its parent main = mainWindow(root, hasscrollbar) if hasscrollbar: # This puts the focus right, and will give the canvas child real sizes root.deiconify() root.focus_set() root.grab_set() root.wait_visibility() # Show the canvas main.canvas.grid(row=0, column=0, sticky=N + S + E + W) # Determine width and heigth of the contained frame canvaschild = main.canvas.children.values()[0] neededwidth = canvaschild.winfo_width() neededheight = canvaschild.winfo_height() maxheight = main.canvas.winfo_screenheight() maxwidth = main.canvas.winfo_screenwidth() neededheight = min(maxheight, neededheight) neededwidth = min(maxwidth, neededwidth) # Set the canvas size and enable scrolling main.canvas.config(width=neededwidth, height=neededheight) main.canvas.config(scrollregion=main.canvas.bbox("all")) else: # Display the main window main.grid(sticky=N + E + S + W) # Catch the 'delete window' signal that may be triggered by the window # manager if the user clicks on its 'close' button root.protocol("WM_DELETE_WINDOW", main.really_quit) # Start the mainloop. The program executing pauses here, only to execute events # generated by the main window (which in essence is everything) ############### main.mainloop() ############### # Apparently an event triggered an exit out of the mainloop (see main.reallyquit) messageLog.put("Destroying widgets") # Destroy the root window and all its children root.destroy() # This message will never arrive, though... messageLog.put("Program finished - EXIT FIEStool") # Return to caller (main) return
def run(): """ Main program entry, initializing and starting the main window and managing its 'mainloop' """ # Get and check command-line options # # NB: The -c (enable-calib) option is now deprecated # try: opts, args = getopt.getopt(sys.argv[1:], "cs", ["calib", "scrollbar"]) except getopt.GetoptError: # print help information and exit: print "Unknown command line parameter. Allowed optional parameters are : " #print "-c [file] Load calibration file at startup" print "-s / --scrollbar Add vertical scrollbar is screen does not fit" sys.exit(2) #enablecalib = False hasscrollbar = False for option, parameter in opts: if option in ("-c", "--calib"): print "The -c option is deprecated. You can now always access the calibs submenu." # enablecalib = True if option in ("-s", "--scrollbar"): hasscrollbar = True # Display a welcome message in the message log messageLog.put('='*65) messageLog.put("Welcome to FIEStool - the automatic data processing tool") messageLog.put("for the FIES spectrograph at the Nordic Optical Telescope") messageLog.put("Copyright (c) 2005 NOTSA; All Rights Reserved") messageLog.put("This is version %s (%s), written by %s" % (_version, _date, _author)) messageLog.put('='*65) messageLog.put("Right-click on any item to obtain instantaneous help") messageLog.put('='*65) # And tell the user what warnings and errors look like messageLog.put_warning("This is a sample warning") messageLog.put_error("This is a sample error") messageLog.put('='*65) # Now, check if a default config file exists; if yes, load it. try: config.load(config.options['default_savefile'].value) except IOError: messageLog.put_warning('No default configuration file found!') pass # Now, try to load the autoLoader config file. try: config.load_raw(config.options['default_autoload_savefile'].value) except IOError: messageLog.put_warning('No default autoloader configuration file found!') pass # Create the absolute top-level root window. Destroying this kills 'everything'! root = Tk() # Make the root window variable in width and height root.rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) # Give it a title root.title('FIEStool - Data reduction for the FIES spectrograph') # Initialize the main window, with 'root' as its parent main = mainWindow(root, hasscrollbar) if hasscrollbar: # This puts the focus right, and will give the canvas child real sizes root.deiconify() root.focus_set() root.grab_set() root.wait_visibility() # Show the canvas main.canvas.grid(row=0, column=0, sticky=N+S+E+W) # Determine width and heigth of the contained frame canvaschild = main.canvas.children.values()[0] neededwidth = canvaschild.winfo_width() neededheight = canvaschild.winfo_height() maxheight = main.canvas.winfo_screenheight() maxwidth = main.canvas.winfo_screenwidth() neededheight = min(maxheight, neededheight) neededwidth = min(maxwidth, neededwidth) # Set the canvas size and enable scrolling main.canvas.config(width=neededwidth, height=neededheight) main.canvas.config(scrollregion=main.canvas.bbox("all")) else: # Display the main window main.grid(sticky=N+E+S+W) # Catch the 'delete window' signal that may be triggered by the window # manager if the user clicks on its 'close' button root.protocol("WM_DELETE_WINDOW", main.really_quit) # Start the mainloop. The program executing pauses here, only to execute events # generated by the main window (which in essence is everything) ############### main.mainloop() ############### # Apparently an event triggered an exit out of the mainloop (see main.reallyquit) messageLog.put("Destroying widgets") # Destroy the root window and all its children root.destroy() # This message will never arrive, though... messageLog.put("Program finished - EXIT FIEStool") # Return to caller (main) return
def run_pipe(self, inframe, outQueue, todotasks, GUIupdater, mode): "Do the reduction a single frame. (Will run in child thread)" # Prepare local variables doneTasks = [] outframe = '' exit = 0 # Display an initial message in the logging window nchars = len('Processing %s' % (inframe)) messageLog.put('='*nchars) messageLog.put('Processing %s' % (inframe)) messageLog.put('='*nchars) # Remember original name of frame firstinframe = inframe # Split filename into path, base name and extension (path, base) = os.path.split(inframe) (outfilebase, extn) = os.path.splitext(base) # Remove existing output files with same basename. fileUtils.removefiles(os.path.join(config.options['outpath'].value, '%s_step*.fits' % outfilebase)) # Disable the contents of the 'self' frame GUIupdater.put((self.disable, (), {})) # Clear the old messages in the taskBars GUIupdater.put((self.clear, (), {})) # Can this safely be commented out? ONLY if the mode change is correctly # adjusted upon rebuilding the frame after a change of mode. # # Define a (new) configuration option that will contain the name # of the current reduction mode. This paramter will be referenced by the # IRAF-based tasks, and will search for the calibration files in the # corresponding directory "tasks/[mode]". #config.options['currentmode'].set(mode) # Keep track of number of reduction steps step = 0 # Loop over the tasks for task in self.tasklist: # Increase task counter step = step + 1 # Highlight the status bar corresponding to this task GUIupdater.put((self.taskbars[task.name].highlight, (), {})) # Skip the tasks if it wasn't in 'todotasks' (i.e. it was not selected # by the user). if not task.name in todotasks: GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':'Skipped'})) GUIupdater.put((self.taskbars[task.name].highlight_off, (), {})) continue # Give the GUIupdater time to adjust the screen if task.inthread: while not GUIupdater.empty(): sleep(0.1) # Make sure any previous reduction steps required by the current task # have been performed before for neededTask in task.prereq: if not neededTask in doneTasks: # If the reduction cannot continue, make it clear to the user messageLog.put_error('Cannot execute "%s" without "%s"' % (task.buttonText, getattr(tasks, neededTask).buttonText)) GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':'Stopped'})) GUIupdater.put((self.taskbars[task.name].highlight, (), {'color':'red'})) exit = 1 # And make sure we exit the reduction loop (only 'break' # would not be enough here) # Exit if needed if exit: break # Create name of output frame from step counter and step suffix if task.suffix: outframe = os.path.join(config.options['outpath'].value, '%s_step%03i_%s.fits' % (outfilebase, step, task.suffix)) else: outframe = None # OK to start to run the task; reflect this in the taskBar label and log newlabel = 'Running "%s" with %s' % (task.buttonText, os.path.basename(inframe)) GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':newlabel} )) messageLog.put(newlabel, 7) # Call the taskManager to do the task executing and retrieve possible # errors encountered. Any errors occuring while executing the task will # never end the reduction interface. Instead, a (descriptive?) error # will be shown in the log ###################################################################### errorstring = taskManager.runTask(task, inframe, outframe) ###################################################################### # Errors encountered? If yes, show them! if errorstring != None: if errorstring == '': GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':'No further processing of this frame'})) GUIupdater.put((self.taskbars[task.name].highlight, (), {'color':'orange'})) else: GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':errorstring})) GUIupdater.put((self.taskbars[task.name].highlight, (), {'color':'red'})) break else: # Everything went smoothly GUIupdater.put((self.taskbars[task.name].set_label, (), {'text':'Finished'})) GUIupdater.put((self.taskbars[task.name].highlight_off, (), {})) # Append task name to the list of successfully finished tasks doneTasks.append(task.name) # Only if the task we just executed returned an output file name, the # next task should use this as its input file name. if outframe and os.path.exists(outframe) and task.output: inframe = outframe else: outframe = inframe # After the loop over all tasks terminates, fill the outQueue, so that the # calling routine knows the reduction of the current frame is finished. self.outQueue.put((firstinframe, outframe)) # Re-enable the 'self' frame GUIupdater.put((self.enable, (), {})) # And bring the good news to the user messageLog.put('Finished processing %s' % os.path.basename(firstinframe))
def getwavedef2D(frame): """ Extract and return the wavelength definition vector [ordernumber, zero wavelength offset, wavelength step] using the FITS header keywords of a 2-dimensional frame, following the IRAF definition. It is a rather dirty routine. """ # Make sure I can find the appropriate headers if not (frame[0].header.get('WAT0_001', None)): messageLog.put_warning('Could not find FITS headers defining the wavelength solution') return None # Get the number of pixels per order and the number of orders from the headers npixels = frame[0].header['NAXIS1'] norders = frame[0].header['NAXIS2'] # Create an (empty) output array wavearray = numpy.zeros((norders, 4), dtype=numpy.float64) # Get all the FITS header keywords in an array fullhlist = frame[0].header.items() wavedef = "" # And loop over the headers to extract any header that contains (part of) # the wavelength definition, and store these in one long string object for name, value in fullhlist: # Only 68 characters can be part of FITS header value if name[0:4] == 'WAT2': wavedef = wavedef + value.ljust(68) # Now it gets very IRAF-specific! Bin the first 16 characters (garbage) wavedef = wavedef[16:] # and split the string object into smaller pieces, one for each order wavedef = wavedef.split('spec') # Bin the first entry (also garbage) del(wavedef[0]) # Check that we got the correct number of order definitions from the headers if len(wavedef) != norders: messageLog.put_warning('No wavelength solution found', 5) return None # Initialize order number counter linecounter = 0 # Get rid of " and = characters in the string and isolate the parameter values for line in wavedef: line = line.replace('"', '') line = line.replace('=', '') line = line.split() # According to IRAF's definition the following parameters can be extracted # from the line number = int(line[1]) orderno = int(line[2]) woffset = float(line[4]) wstep = float(line[5]) # Store the order number, zero pixel wavelength and wavelength step in the # output array wavearray[linecounter, :] = [number, orderno, woffset, wstep] linecounter = linecounter + 1 # If an incompatible format was used to define the order locations, complain! if int(line[3]) != 0: messageLog.put_error('Error in interpreting spectrum definition') messageLog.put_warning('Dispersion solution format seems to be non-linear. Trying to make ') messageLog.put_warning('the best of it. Wavelength scale may be terribly wrong.') # Finally, return the result to the caller return wavearray