def __init__(self, imdisplay, config, imlist=None, number=1, parent=None): """Setup widget. *imdisplay* a `FitsDisplay` derived fits display widget, *imlist* a list of fits image filenames, *config* filename used for output configuration file, *number* image number to load on startup, *parent* parent widget. """ # Set default parameters self.imlist = imlist self.number = number self.config = config self.amp = {'target': 1, 'comparison': 1} # Set default marker self.mark_with = 'circle' # Set default search distance for recentering self.distance = 5 # Default line style parameters self.line = { 'target': { 'color': 'g', 'width': 2 }, 'comparison': { 'color': 'g', 'width': 2 } } # Import gui from ui_photometryconfigwidget import Ui_PhotometryConfigWidget # Setup widget QtGui.QWidget.__init__(self, parent) # Bind gui to widget self.ui = Ui_PhotometryConfigWidget() self.ui.setupUi(self) # Destroy widget on close self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # Connect to display window self.imdisplay = imdisplay # Connect position selected signal from display to event handler self.connect(self.imdisplay, QtCore.SIGNAL('positionSelected(float, float)'), self.selectionHandler) # Set current display widget for positionSelected signal self.xdisplay = [] self.ydisplay = [] self.rdisplay = [] # Keep track of currently displayed objects self.display = { 'target': { 'position': False, 'annulus': False, 'region': False }, 'comparison': { 'position': False, 'annulus': False, 'region': False } } # Keep track of input widgets self.parameters = ['x', 'y', 'r', 'r1', 'r2', 'x1', 'y1', 'x2', 'y2'] self.input = { 'target': { 'x': self.ui.tgtXLineEdit, 'y': self.ui.tgtYLineEdit, 'r': self.ui.tgtRLineEdit, 'r1': self.ui.tgtR1LineEdit, 'r2': self.ui.tgtR2LineEdit, 'x1': self.ui.tgtX1LineEdit, 'y1': self.ui.tgtY1LineEdit, 'x2': self.ui.tgtX2LineEdit, 'y2': self.ui.tgtY2LineEdit }, 'comparison': { 'x': self.ui.cmpXLineEdit, 'y': self.ui.cmpYLineEdit, 'r': self.ui.cmpRLineEdit, 'r1': self.ui.cmpR1LineEdit, 'r2': self.ui.cmpR2LineEdit, 'x1': self.ui.cmpX1LineEdit, 'y1': self.ui.cmpY1LineEdit, 'x2': self.ui.cmpX2LineEdit, 'y2': self.ui.cmpY2LineEdit } } # Keep track of capture buttons self.buttons = ['position', 'radius', 'annulus', 'region'] self.capture={'target' \ : {'position' : self.ui.captureTgt, 'radius' : self.ui.captureTgtRadius, 'annulus' : self.ui.captureTgtAnulusBackground, 'region' : self.ui.captureTgtRegionBackground}, 'comparison' \ : {'position' : self.ui.captureCmp, 'radius' : self.ui.captureCmpRadius, 'annulus' : self.ui.captureCmpAnulusBackground, 'region' : self.ui.captureCmpRegionBackground}} # Keep track of checkbox recenter widgets self.recenter = { 'target': self.ui.tgtRecenterCheckBox, 'comparison': self.ui.cmpRecenterCheckBox } self.centered = {'target': False, 'comparison': False} # Enable blocking of redraws self.block = { 'target': { 'x': False, 'y': False, 'r': False, 'r1': False, 'r2': False, 'x1': False, 'y1': False, 'x2': False, 'y2': False }, 'comparison': { 'x': False, 'y': False, 'r': False, 'r1': False, 'r2': False, 'x1': False, 'y1': False, 'x2': False, 'y2': False } } # Set validator to ensure valid input on lineEdit input widgets self.validator = QtGui.QDoubleValidator(self) for object in ['target', 'comparison']: for key in self.parameters: self.input[object][key].setValidator(self.validator) # Set signal mapper for lineEdit updates self.drawMapper = QtCore.QSignalMapper(self) # Connect lineEdit updates to signal mapper for object in ['target', 'comparison']: for key in self.parameters: # Add signal map entry self.drawMapper.setMapping(self.input[object][key], QtCore.QString(object + ',' + key)) # Connect to signal mapper self.connect(self.input[object][key], QtCore.SIGNAL('textChanged(QString)'), self.drawMapper, QtCore.SLOT('map()')) # Connect signal mapper to draw handler self.connect(self.drawMapper, QtCore.SIGNAL('mapped(QString)'), self.textUpdated) # Set signal mapper for capture buttons self.captureMapper = QtCore.QSignalMapper(self) # Connect capture button signals to signal mapper for object in ['target', 'comparison']: for key in self.buttons: # Add signal map entry self.captureMapper.setMapping( self.capture[object][key], QtCore.QString(object + ',' + key)) # Connect to signal mapper self.connect(self.capture[object][key], QtCore.SIGNAL('clicked()'), self.captureMapper, QtCore.SLOT('map()')) # Connect signal mapper to capture handler self.connect(self.captureMapper, QtCore.SIGNAL('mapped(QString)'), self.captureHandler) # Connect save button self.connect(self.ui.saveButton, QtCore.SIGNAL('clicked()'), self.save) # If an image list is given if self.imlist is not None: # Connect image selection spinBox to event handlers self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.loadImage) self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.redraw) # Load first image self.setImageNumber(self.number) # Hide end selection widgets (not implemented here) self.ui.tgtEndPosLabel.hide() self.ui.tgtEndXLabel.hide() self.ui.tgtEndYLabel.hide() self.ui.cmpEndPosLabel.hide() self.ui.cmpEndXLabel.hide() self.ui.cmpEndYLabel.hide() self.ui.tgtXEndLineEdit.hide() self.ui.tgtYEndLineEdit.hide() self.ui.cmpXEndLineEdit.hide() self.ui.cmpYEndLineEdit.hide() self.ui.captureTgtEnd.hide() self.ui.captureCmpEnd.hide()
def __init__(self, imdisplay, config, imlist=None, number=1, parent=None): """Setup widget. *imdisplay* a `FitsDisplay` derived fits display widget, *imlist* a list of fits image filenames, *config* filename used for output configuration file, *number* image number to load on startup, *parent* parent widget. """ # Set default parameters self.imlist=imlist self.number=number self.config=config self.amp={'target' : 1, 'comparison' : 1 } # Set default marker self.mark_with='circle' # Set default search distance for recentering self.distance=5 # Default line style parameters self.line={ 'target' : { 'color' : 'g', 'width' : 2 }, 'comparison' : { 'color' : 'g', 'width' : 2 }} # Import gui from ui_photometryconfigwidget import Ui_PhotometryConfigWidget # Setup widget QtGui.QWidget.__init__(self, parent) # Bind gui to widget self.ui = Ui_PhotometryConfigWidget() self.ui.setupUi(self) # Destroy widget on close self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # Connect to display window self.imdisplay=imdisplay # Connect position selected signal from display to event handler self.connect(self.imdisplay, QtCore.SIGNAL('positionSelected(float, float)'), self.selectionHandler) # Set current display widget for positionSelected signal self.xdisplay=[] self.ydisplay=[] self.rdisplay=[] # Keep track of currently displayed objects self.display={'target' : {'position' : False, 'annulus' : False, 'region' : False }, 'comparison' : {'position' : False, 'annulus' : False, 'region' : False }} # Keep track of input widgets self.parameters=['x','y','r','r1','r2','x1','y1','x2','y2'] self.input={'target' : { 'x' : self.ui.tgtXLineEdit, 'y' : self.ui.tgtYLineEdit, 'r' : self.ui.tgtRLineEdit, 'r1' : self.ui.tgtR1LineEdit, 'r2' : self.ui.tgtR2LineEdit, 'x1' : self.ui.tgtX1LineEdit, 'y1' : self.ui.tgtY1LineEdit, 'x2' : self.ui.tgtX2LineEdit, 'y2' : self.ui.tgtY2LineEdit}, 'comparison' : { 'x' : self.ui.cmpXLineEdit, 'y' : self.ui.cmpYLineEdit, 'r' : self.ui.cmpRLineEdit, 'r1' : self.ui.cmpR1LineEdit, 'r2' : self.ui.cmpR2LineEdit, 'x1' : self.ui.cmpX1LineEdit, 'y1' : self.ui.cmpY1LineEdit, 'x2' : self.ui.cmpX2LineEdit, 'y2' : self.ui.cmpY2LineEdit}} # Keep track of capture buttons self.buttons=['position','radius','annulus','region'] self.capture={'target' \ : {'position' : self.ui.captureTgt, 'radius' : self.ui.captureTgtRadius, 'annulus' : self.ui.captureTgtAnulusBackground, 'region' : self.ui.captureTgtRegionBackground}, 'comparison' \ : {'position' : self.ui.captureCmp, 'radius' : self.ui.captureCmpRadius, 'annulus' : self.ui.captureCmpAnulusBackground, 'region' : self.ui.captureCmpRegionBackground}} # Keep track of checkbox recenter widgets self.recenter={'target' : self.ui.tgtRecenterCheckBox, 'comparison' : self.ui.cmpRecenterCheckBox} self.centered={'target' : False, 'comparison' : False} # Enable blocking of redraws self.block={'target' : { 'x' : False, 'y' : False, 'r' : False, 'r1' : False, 'r2' : False, 'x1' : False, 'y1' : False, 'x2' : False, 'y2' : False}, 'comparison' : { 'x' : False, 'y' : False, 'r' : False, 'r1' : False, 'r2' : False, 'x1' : False, 'y1' : False, 'x2' : False, 'y2' : False}} # Set validator to ensure valid input on lineEdit input widgets self.validator = QtGui.QDoubleValidator(self) for object in ['target','comparison']: for key in self.parameters: self.input[object][key].setValidator(self.validator) # Set signal mapper for lineEdit updates self.drawMapper = QtCore.QSignalMapper(self) # Connect lineEdit updates to signal mapper for object in ['target','comparison']: for key in self.parameters: # Add signal map entry self.drawMapper.setMapping(self.input[object][key], QString(object+','+key)) # Connect to signal mapper self.connect(self.input[object][key], QtCore.SIGNAL('textChanged(QString)'), self.drawMapper, QtCore.SLOT('map()')) # Connect signal mapper to draw handler self.connect(self.drawMapper, QtCore.SIGNAL('mapped(QString)'), self.textUpdated) # Set signal mapper for capture buttons self.captureMapper = QtCore.QSignalMapper(self) # Connect capture button signals to signal mapper for object in ['target','comparison']: for key in self.buttons: # Add signal map entry self.captureMapper.setMapping(self.capture[object][key], QString(object+','+key)) # Connect to signal mapper self.connect(self.capture[object][key], QtCore.SIGNAL('clicked()'), self.captureMapper, QtCore.SLOT('map()')) # Connect signal mapper to capture handler self.connect(self.captureMapper, QtCore.SIGNAL('mapped(QString)'), self.captureHandler) # Connect save button self.connect(self.ui.saveButton, QtCore.SIGNAL('clicked()'), self.save) # If an image list is given if self.imlist is not None: # Connect image selection spinBox to event handlers self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.loadImage) self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.redraw) # Load first image self.setImageNumber(self.number) # Hide end selection widgets (not implemented here) self.ui.tgtEndPosLabel.hide() self.ui.tgtEndXLabel.hide() self.ui.tgtEndYLabel.hide() self.ui.cmpEndPosLabel.hide() self.ui.cmpEndXLabel.hide() self.ui.cmpEndYLabel.hide() self.ui.tgtXEndLineEdit.hide() self.ui.tgtYEndLineEdit.hide() self.ui.cmpXEndLineEdit.hide() self.ui.cmpYEndLineEdit.hide() self.ui.captureTgtEnd.hide() self.ui.captureCmpEnd.hide()
class PhotometryConfigWidget(QtGui.QWidget): """Configure dialog for photometry. Has settings for: * target position, size * target background * type (anulus/region) * parameters * comparison position, size * comparison background * type (anulus/region) * parameters """ def __init__(self, imdisplay, config, imlist=None, number=1, parent=None): """Setup widget. *imdisplay* a `FitsDisplay` derived fits display widget, *imlist* a list of fits image filenames, *config* filename used for output configuration file, *number* image number to load on startup, *parent* parent widget. """ # Set default parameters self.imlist = imlist self.number = number self.config = config self.amp = {'target': 1, 'comparison': 1} # Set default marker self.mark_with = 'circle' # Set default search distance for recentering self.distance = 5 # Default line style parameters self.line = { 'target': { 'color': 'g', 'width': 2 }, 'comparison': { 'color': 'g', 'width': 2 } } # Import gui from ui_photometryconfigwidget import Ui_PhotometryConfigWidget # Setup widget QtGui.QWidget.__init__(self, parent) # Bind gui to widget self.ui = Ui_PhotometryConfigWidget() self.ui.setupUi(self) # Destroy widget on close self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # Connect to display window self.imdisplay = imdisplay # Connect position selected signal from display to event handler self.connect(self.imdisplay, QtCore.SIGNAL('positionSelected(float, float)'), self.selectionHandler) # Set current display widget for positionSelected signal self.xdisplay = [] self.ydisplay = [] self.rdisplay = [] # Keep track of currently displayed objects self.display = { 'target': { 'position': False, 'annulus': False, 'region': False }, 'comparison': { 'position': False, 'annulus': False, 'region': False } } # Keep track of input widgets self.parameters = ['x', 'y', 'r', 'r1', 'r2', 'x1', 'y1', 'x2', 'y2'] self.input = { 'target': { 'x': self.ui.tgtXLineEdit, 'y': self.ui.tgtYLineEdit, 'r': self.ui.tgtRLineEdit, 'r1': self.ui.tgtR1LineEdit, 'r2': self.ui.tgtR2LineEdit, 'x1': self.ui.tgtX1LineEdit, 'y1': self.ui.tgtY1LineEdit, 'x2': self.ui.tgtX2LineEdit, 'y2': self.ui.tgtY2LineEdit }, 'comparison': { 'x': self.ui.cmpXLineEdit, 'y': self.ui.cmpYLineEdit, 'r': self.ui.cmpRLineEdit, 'r1': self.ui.cmpR1LineEdit, 'r2': self.ui.cmpR2LineEdit, 'x1': self.ui.cmpX1LineEdit, 'y1': self.ui.cmpY1LineEdit, 'x2': self.ui.cmpX2LineEdit, 'y2': self.ui.cmpY2LineEdit } } # Keep track of capture buttons self.buttons = ['position', 'radius', 'annulus', 'region'] self.capture={'target' \ : {'position' : self.ui.captureTgt, 'radius' : self.ui.captureTgtRadius, 'annulus' : self.ui.captureTgtAnulusBackground, 'region' : self.ui.captureTgtRegionBackground}, 'comparison' \ : {'position' : self.ui.captureCmp, 'radius' : self.ui.captureCmpRadius, 'annulus' : self.ui.captureCmpAnulusBackground, 'region' : self.ui.captureCmpRegionBackground}} # Keep track of checkbox recenter widgets self.recenter = { 'target': self.ui.tgtRecenterCheckBox, 'comparison': self.ui.cmpRecenterCheckBox } self.centered = {'target': False, 'comparison': False} # Enable blocking of redraws self.block = { 'target': { 'x': False, 'y': False, 'r': False, 'r1': False, 'r2': False, 'x1': False, 'y1': False, 'x2': False, 'y2': False }, 'comparison': { 'x': False, 'y': False, 'r': False, 'r1': False, 'r2': False, 'x1': False, 'y1': False, 'x2': False, 'y2': False } } # Set validator to ensure valid input on lineEdit input widgets self.validator = QtGui.QDoubleValidator(self) for object in ['target', 'comparison']: for key in self.parameters: self.input[object][key].setValidator(self.validator) # Set signal mapper for lineEdit updates self.drawMapper = QtCore.QSignalMapper(self) # Connect lineEdit updates to signal mapper for object in ['target', 'comparison']: for key in self.parameters: # Add signal map entry self.drawMapper.setMapping(self.input[object][key], QtCore.QString(object + ',' + key)) # Connect to signal mapper self.connect(self.input[object][key], QtCore.SIGNAL('textChanged(QString)'), self.drawMapper, QtCore.SLOT('map()')) # Connect signal mapper to draw handler self.connect(self.drawMapper, QtCore.SIGNAL('mapped(QString)'), self.textUpdated) # Set signal mapper for capture buttons self.captureMapper = QtCore.QSignalMapper(self) # Connect capture button signals to signal mapper for object in ['target', 'comparison']: for key in self.buttons: # Add signal map entry self.captureMapper.setMapping( self.capture[object][key], QtCore.QString(object + ',' + key)) # Connect to signal mapper self.connect(self.capture[object][key], QtCore.SIGNAL('clicked()'), self.captureMapper, QtCore.SLOT('map()')) # Connect signal mapper to capture handler self.connect(self.captureMapper, QtCore.SIGNAL('mapped(QString)'), self.captureHandler) # Connect save button self.connect(self.ui.saveButton, QtCore.SIGNAL('clicked()'), self.save) # If an image list is given if self.imlist is not None: # Connect image selection spinBox to event handlers self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.loadImage) self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.redraw) # Load first image self.setImageNumber(self.number) # Hide end selection widgets (not implemented here) self.ui.tgtEndPosLabel.hide() self.ui.tgtEndXLabel.hide() self.ui.tgtEndYLabel.hide() self.ui.cmpEndPosLabel.hide() self.ui.cmpEndXLabel.hide() self.ui.cmpEndYLabel.hide() self.ui.tgtXEndLineEdit.hide() self.ui.tgtYEndLineEdit.hide() self.ui.cmpXEndLineEdit.hide() self.ui.cmpYEndLineEdit.hide() self.ui.captureTgtEnd.hide() self.ui.captureCmpEnd.hide() def setImageNumber(self, number): """Set the image number.""" self.ui.imageSpinBox.setValue(number) def loadImage(self, number): """Loads a new image. *number* is the image number to be loaded. This function uses `saltsafeio.getexposure` to get the correct exposure from a list of fits files containing an arbitrary number of extensions. """ # Emit signal self.emit(QtCore.SIGNAL("imageNumberUpdated(int)"), number) # Load image from file self.img = saltsafeio.get_exposure(self.imlist, number) # Display image self.imdisplay.loadImage(self.img) # Redraw canvas self.imdisplay.redraw_canvas() def mark(self, *args, **kwargs): if self.mark_with == 'square': self.imdisplay.addSquare(*args, **kwargs) elif self.mark_with == 'circle': self.imdisplay.addCircle(*args, **kwargs) def textUpdated(self, key): # Get object and parameter from key obj, par = str(key).split(',') # Check block if self.block[obj][par]: return # Set block to prevent infinite repeat self.block[obj][par] = True # Recenter on object if requested if par == 'x' and self.recenter[obj].isChecked( ) and not self.centered[obj]: x = float(self.input[obj]['x'].text()) y = float(self.input[obj]['y'].text()) r = float(self.input[obj]['r'].text()) x, y = find_object(self.img, x, y, self.distance) self.input[obj]['x'].setText(str(x)) self.input[obj]['y'].setText(str(y)) self.centered[obj] = not (self.centered[obj]) # Check if object region size locking is on if self.ui.lockObjectSizes.isChecked(): if par == 'r': r = self.input[obj]['r'].text() if obj == 'target': self.input['comparison']['r'].setText(r) elif obj == 'comparison': self.input['target']['r'].setText(r) # Check if background size locking is on if self.ui.lockBackgroundSize.isChecked(): if par in ['r1', 'r2']: r = self.input[obj][par].text() if obj == 'target': self.ui.cmpAnulusRadioButton.setChecked(True) self.input['comparison'][par].setText(r) elif obj == 'comparison': self.ui.tgtAnulusRadioButton.setChecked(True) self.input['target'][par].setText(r) elif par in ['x1', 'y1', 'x2', 'y2']: c = self.input[obj][par].text() if obj == 'target': self.ui.cmpRegionRadioButton.setChecked(True) self.input['comparison'][par].setText(c) elif obj == 'comparison': self.ui.tgtRegionRadioButton.setChecked(True) self.input['target'][par].setText(c) # Check if background region centering if self.ui.allignTgtVerticalCenter.isChecked(): if par in ['y1', 'y2']: y = float(self.input[obj][par].text()) center = self.img.shape[0] / 2.0 height = abs(y - center) self.input[obj]['y1'].setText(str(center + height)) self.input[obj]['y2'].setText(str(center - height)) # Draw markers self.draw(key) # Unset block self.block[obj][par] = False def draw(self, key): """Draws markers for object positions, and backgrounds. To be called when any input widget value changes. *key* is given by the signal mapper and consists of a string with the object and parameter separated by a comma. """ # Get object and parameter from key obj, par = str(key).split(',') try: # Set amplifier self.amp[obj] = self.getCurrentAmp() # Draw markers if par == 'x' or par == 'y' or par == 'r': x = float(self.input[obj]['x'].text()) y = float(self.input[obj]['y'].text()) r = float(self.input[obj]['r'].text()) self.display[obj]['position'] = True self.mark(obj, x, y, r, color=self.line[obj]['color'], lw=self.line[obj]['width']) elif par == 'r1' or par == 'r2': # Annulus is selected so remove region marker self.imdisplay.removePatch(obj + '_region') x = float(self.input[obj]['x'].text()) y = float(self.input[obj]['y'].text()) r = float(self.input[obj][par].text()) # Keep track of the selected background mode self.display[obj]['annulus'] = True self.display[obj]['region'] = False self.mark(obj + '_' + par, x, y, r, color=self.line[obj]['color'], lw=self.line[obj]['width']) elif par == 'x1' or par == 'y1' or par == 'x2' or par == 'y2': # Region is selected so remove annulus markers self.imdisplay.removePatch(obj + '_r1') self.imdisplay.removePatch(obj + '_r2') x1 = float(self.input[obj]['x1'].text()) y1 = float(self.input[obj]['y1'].text()) x2 = float(self.input[obj]['x2'].text()) y2 = float(self.input[obj]['y2'].text()) # Keep track of the selected background mode self.display[obj]['annulus'] = False self.display[obj]['region'] = True self.imdisplay.addRectangle(obj + '_region', x1, y1, x2, y2, color=self.line[obj]['color'], lw=self.line[obj]['width']) # Redraw canvas self.imdisplay.redraw_canvas(keepzoom=True) except ValueError: pass def redraw(self, number): """Redraws object and background markers for all objects on the currently displayed amplifier *number*. """ self.imdisplay.reset() # Find wich amplifier is currently displayed amp = self.getCurrentAmp() # (Re)draw markers for obj in ['target', 'comparison']: if self.amp[obj] == amp: if self.display[obj]['position']: self.draw(obj + ',' + 'r') if self.display[obj]['annulus']: self.draw(obj + ',' + 'r1') self.draw(obj + ',' + 'r2') if self.display[obj]['region']: self.draw(obj + ',' + 'y2') def getCurrentAmp(self, namps=4): """Returns the currently displayed amplifier. *namps* is the number of amplifiers on the CCD. """ # Get exposure number n = int(self.ui.imageSpinBox.value()) # Convert exposure number to current amplifier number amp = n % namps if amp == 0: amp = namps return amp def captureHandler(self, key): """Called when a capture button is clicked. *key* is given by the signal mapper and consists of a string with the object and parameter separated by a comma. Depending on the *key* input widgets are added to the current display lists. Subsequent calls to `self.selectionHandler` get displayed in the listed widgets. """ # Get object and parameter from key obj, par = str(key).split(',') # Add input widgets to lists if par == 'position': self.xdisplay = [self.input[obj]['x']] self.ydisplay = [self.input[obj]['y']] self.rdisplay = [] elif par == 'radius': self.xdisplay = [] self.ydisplay = [] self.x = float(self.input[obj]['x'].text()) self.y = float(self.input[obj]['y'].text()) self.rdisplay = [self.input[obj]['r']] elif par == 'annulus': self.xdisplay = [] self.ydisplay = [] self.x = float(self.input[obj]['x'].text()) self.y = float(self.input[obj]['y'].text()) self.rdisplay = [self.input[obj]['r1'], self.input[obj]['r2']] elif par == 'region': self.xdisplay = [self.input[obj]['x1'], self.input[obj]['x2']] self.ydisplay = [self.input[obj]['y1'], self.input[obj]['y2']] self.rdisplay = [] def selectionHandler(self, x, y): """Event handler for click in image display window. *x*, *y* is the position (in image pixel coordinates) of the click. These positions are inserted into the first input widgets in the display lists. If a radius is requested this is calculated from the position given in (self.x, self.y) which should be set to the current object. """ if len(self.xdisplay) > 0: display = self.xdisplay.pop(0) display.setText(str(x)) if len(self.ydisplay) > 0: display = self.ydisplay.pop(0) display.setText(str(y)) if len(self.rdisplay) > 0: r = np.sqrt((x - self.x)**2 + (y - self.y)**2) display = self.rdisplay.pop(0) display.setText(str(r)) def setSearchDistance(self, distance): """Set search distance used for recentering.""" self.distance = int(distance) def setMarkerType(self, marker): """Set marker type to 'circle' or 'square'.""" if marker in ['circle', 'square']: self.mark_with = marker else: raise SaltIOError('Unknown marker type ' + str(marker)) def setLineColor(self, object, color): """Changes the default line color used for marking.""" self.line[object]['color'] = color def setLineWidth(self, object, width): """Changes the default line width used for marking.""" self.line[object]['width'] = width def save(self): """Save configuration. The format is:: For objects that use an anullus: object amp x y r r1 r2 For objects that use a region: object amp x y r x1 y1 x2 y2 """ if (self.ui.tgtAnulusRadioButton.isChecked() and self.ui.cmpRegionRadioButton.isChecked()) or \ (self.ui.tgtRegionRadioButton.isChecked() and self.ui.cmpAnulusRadioButton.isChecked()): msg = 'SLOTPREVIEW--SLOTPHOT can not handle different background types' raise SaltError(msg) # Write values to file with open(self.config, 'w') as f: for i, obj in enumerate(['target', 'comparison']): b_type = 'region' if obj == 'target': print obj, self.ui.tgtAnulusRadioButton.isChecked() if self.ui.tgtAnulusRadioButton.isChecked(): b_type = 'annulus' elif obj == 'comparison': if self.ui.cmpAnulusRadioButton.isChecked(): b_type = 'annulus' # If r1 is not zero, assumes annulus line = '%i\t%i\t' % (i + 1, self.amp[obj]) if b_type == 'annulus': line += ''.join('%3.2f\t' % float(self.input[obj][key].text()) for key in ['x', 'y', 'r', 'r1', 'r2']) else: line += ''.join( '%3.2f\t' % float(self.input[obj][key].text()) for key in ['x', 'y', 'r', 'x1', 'y2', 'x2', 'y2']) # Write string to configfile f.write(line.rstrip() + '\n') # Exit program self.close()
class PhotometryConfigWidget(QtGui.QWidget): """Configure dialog for photometry. Has settings for: * target position, size * target background * type (anulus/region) * parameters * comparison position, size * comparison background * type (anulus/region) * parameters """ def __init__(self, imdisplay, config, imlist=None, number=1, parent=None): """Setup widget. *imdisplay* a `FitsDisplay` derived fits display widget, *imlist* a list of fits image filenames, *config* filename used for output configuration file, *number* image number to load on startup, *parent* parent widget. """ # Set default parameters self.imlist=imlist self.number=number self.config=config self.amp={'target' : 1, 'comparison' : 1 } # Set default marker self.mark_with='circle' # Set default search distance for recentering self.distance=5 # Default line style parameters self.line={ 'target' : { 'color' : 'g', 'width' : 2 }, 'comparison' : { 'color' : 'g', 'width' : 2 }} # Import gui from ui_photometryconfigwidget import Ui_PhotometryConfigWidget # Setup widget QtGui.QWidget.__init__(self, parent) # Bind gui to widget self.ui = Ui_PhotometryConfigWidget() self.ui.setupUi(self) # Destroy widget on close self.setAttribute(QtCore.Qt.WA_DeleteOnClose) # Connect to display window self.imdisplay=imdisplay # Connect position selected signal from display to event handler self.connect(self.imdisplay, QtCore.SIGNAL('positionSelected(float, float)'), self.selectionHandler) # Set current display widget for positionSelected signal self.xdisplay=[] self.ydisplay=[] self.rdisplay=[] # Keep track of currently displayed objects self.display={'target' : {'position' : False, 'annulus' : False, 'region' : False }, 'comparison' : {'position' : False, 'annulus' : False, 'region' : False }} # Keep track of input widgets self.parameters=['x','y','r','r1','r2','x1','y1','x2','y2'] self.input={'target' : { 'x' : self.ui.tgtXLineEdit, 'y' : self.ui.tgtYLineEdit, 'r' : self.ui.tgtRLineEdit, 'r1' : self.ui.tgtR1LineEdit, 'r2' : self.ui.tgtR2LineEdit, 'x1' : self.ui.tgtX1LineEdit, 'y1' : self.ui.tgtY1LineEdit, 'x2' : self.ui.tgtX2LineEdit, 'y2' : self.ui.tgtY2LineEdit}, 'comparison' : { 'x' : self.ui.cmpXLineEdit, 'y' : self.ui.cmpYLineEdit, 'r' : self.ui.cmpRLineEdit, 'r1' : self.ui.cmpR1LineEdit, 'r2' : self.ui.cmpR2LineEdit, 'x1' : self.ui.cmpX1LineEdit, 'y1' : self.ui.cmpY1LineEdit, 'x2' : self.ui.cmpX2LineEdit, 'y2' : self.ui.cmpY2LineEdit}} # Keep track of capture buttons self.buttons=['position','radius','annulus','region'] self.capture={'target' \ : {'position' : self.ui.captureTgt, 'radius' : self.ui.captureTgtRadius, 'annulus' : self.ui.captureTgtAnulusBackground, 'region' : self.ui.captureTgtRegionBackground}, 'comparison' \ : {'position' : self.ui.captureCmp, 'radius' : self.ui.captureCmpRadius, 'annulus' : self.ui.captureCmpAnulusBackground, 'region' : self.ui.captureCmpRegionBackground}} # Keep track of checkbox recenter widgets self.recenter={'target' : self.ui.tgtRecenterCheckBox, 'comparison' : self.ui.cmpRecenterCheckBox} self.centered={'target' : False, 'comparison' : False} # Enable blocking of redraws self.block={'target' : { 'x' : False, 'y' : False, 'r' : False, 'r1' : False, 'r2' : False, 'x1' : False, 'y1' : False, 'x2' : False, 'y2' : False}, 'comparison' : { 'x' : False, 'y' : False, 'r' : False, 'r1' : False, 'r2' : False, 'x1' : False, 'y1' : False, 'x2' : False, 'y2' : False}} # Set validator to ensure valid input on lineEdit input widgets self.validator = QtGui.QDoubleValidator(self) for object in ['target','comparison']: for key in self.parameters: self.input[object][key].setValidator(self.validator) # Set signal mapper for lineEdit updates self.drawMapper = QtCore.QSignalMapper(self) # Connect lineEdit updates to signal mapper for object in ['target','comparison']: for key in self.parameters: # Add signal map entry self.drawMapper.setMapping(self.input[object][key], QString(object+','+key)) # Connect to signal mapper self.connect(self.input[object][key], QtCore.SIGNAL('textChanged(QString)'), self.drawMapper, QtCore.SLOT('map()')) # Connect signal mapper to draw handler self.connect(self.drawMapper, QtCore.SIGNAL('mapped(QString)'), self.textUpdated) # Set signal mapper for capture buttons self.captureMapper = QtCore.QSignalMapper(self) # Connect capture button signals to signal mapper for object in ['target','comparison']: for key in self.buttons: # Add signal map entry self.captureMapper.setMapping(self.capture[object][key], QString(object+','+key)) # Connect to signal mapper self.connect(self.capture[object][key], QtCore.SIGNAL('clicked()'), self.captureMapper, QtCore.SLOT('map()')) # Connect signal mapper to capture handler self.connect(self.captureMapper, QtCore.SIGNAL('mapped(QString)'), self.captureHandler) # Connect save button self.connect(self.ui.saveButton, QtCore.SIGNAL('clicked()'), self.save) # If an image list is given if self.imlist is not None: # Connect image selection spinBox to event handlers self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.loadImage) self.connect(self.ui.imageSpinBox, QtCore.SIGNAL('valueChanged(int)'), self.redraw) # Load first image self.setImageNumber(self.number) # Hide end selection widgets (not implemented here) self.ui.tgtEndPosLabel.hide() self.ui.tgtEndXLabel.hide() self.ui.tgtEndYLabel.hide() self.ui.cmpEndPosLabel.hide() self.ui.cmpEndXLabel.hide() self.ui.cmpEndYLabel.hide() self.ui.tgtXEndLineEdit.hide() self.ui.tgtYEndLineEdit.hide() self.ui.cmpXEndLineEdit.hide() self.ui.cmpYEndLineEdit.hide() self.ui.captureTgtEnd.hide() self.ui.captureCmpEnd.hide() def setImageNumber(self,number): """Set the image number.""" self.ui.imageSpinBox.setValue(number) def loadImage(self, number): """Loads a new image. *number* is the image number to be loaded. This function uses `saltsafeio.getexposure` to get the correct exposure from a list of fits files containing an arbitrary number of extensions. """ # Emit signal self.emit(QtCore.SIGNAL("imageNumberUpdated(int)"), number) # Load image from file self.img=saltsafeio.get_exposure(self.imlist,number) # Display image self.imdisplay.loadImage(self.img) # Redraw canvas self.imdisplay.redraw_canvas() def mark(self,*args,**kwargs): if self.mark_with=='square': self.imdisplay.addSquare(*args,**kwargs) elif self.mark_with=='circle': self.imdisplay.addCircle(*args,**kwargs) def textUpdated(self,key): # Get object and parameter from key obj,par=str(key).split(',') # Check block if self.block[obj][par]: return # Set block to prevent infinite repeat self.block[obj][par]=True # Recenter on object if requested if par=='x' and self.recenter[obj].isChecked() and not self.centered[obj]: x=float(self.input[obj]['x'].text()) y=float(self.input[obj]['y'].text()) r=float(self.input[obj]['r'].text()) x,y=find_object(self.img,x,y,self.distance) self.input[obj]['x'].setText(str(x)) self.input[obj]['y'].setText(str(y)) self.centered[obj]=not(self.centered[obj]) # Check if object region size locking is on if self.ui.lockObjectSizes.isChecked(): if par=='r': r=self.input[obj]['r'].text() if obj=='target': self.input['comparison']['r'].setText(r) elif obj=='comparison': self.input['target']['r'].setText(r) # Check if background size locking is on if self.ui.lockBackgroundSize.isChecked(): if par in ['r1','r2']: r=self.input[obj][par].text() if obj=='target': self.ui.cmpAnulusRadioButton.setChecked(True) self.input['comparison'][par].setText(r) elif obj=='comparison': self.ui.tgtAnulusRadioButton.setChecked(True) self.input['target'][par].setText(r) elif par in ['x1','y1','x2','y2']: c=self.input[obj][par].text() if obj=='target': self.ui.cmpRegionRadioButton.setChecked(True) self.input['comparison'][par].setText(c) elif obj=='comparison': self.ui.tgtRegionRadioButton.setChecked(True) self.input['target'][par].setText(c) # Check if background region centering if self.ui.allignTgtVerticalCenter.isChecked(): if par in ['y1','y2']: y=float(self.input[obj][par].text()) center=self.img.shape[0]/2.0 height=abs(y-center) self.input[obj]['y1'].setText(str(center+height)) self.input[obj]['y2'].setText(str(center-height)) # Draw markers self.draw(key) # Unset block self.block[obj][par]=False def draw(self,key): """Draws markers for object positions, and backgrounds. To be called when any input widget value changes. *key* is given by the signal mapper and consists of a string with the object and parameter separated by a comma. """ # Get object and parameter from key obj,par=str(key).split(',') try: # Set amplifier self.amp[obj]=self.getCurrentAmp() # Draw markers if par=='x' or par=='y' or par=='r': x=float(self.input[obj]['x'].text()) y=float(self.input[obj]['y'].text()) r=float(self.input[obj]['r'].text()) self.display[obj]['position']=True self.mark(obj,x,y,r,color=self.line[obj]['color'],lw=self.line[obj]['width']) elif par=='r1' or par=='r2': # Annulus is selected so remove region marker self.imdisplay.removePatch(obj+'_region') x=float(self.input[obj]['x'].text()) y=float(self.input[obj]['y'].text()) r=float(self.input[obj][par].text()) # Keep track of the selected background mode self.display[obj]['annulus']=True self.display[obj]['region']=False self.mark(obj+'_'+par,x,y,r,color=self.line[obj]['color'],lw=self.line[obj]['width']) elif par=='x1' or par=='y1' or par=='x2' or par=='y2': # Region is selected so remove annulus markers self.imdisplay.removePatch(obj+'_r1') self.imdisplay.removePatch(obj+'_r2') x1=float(self.input[obj]['x1'].text()) y1=float(self.input[obj]['y1'].text()) x2=float(self.input[obj]['x2'].text()) y2=float(self.input[obj]['y2'].text()) # Keep track of the selected background mode self.display[obj]['annulus']=False self.display[obj]['region']=True self.imdisplay.addRectangle(obj+'_region',x1,y1,x2,y2, color=self.line[obj]['color'],lw=self.line[obj]['width']) # Redraw canvas self.imdisplay.redraw_canvas(keepzoom=True) except ValueError: pass def redraw(self, number): """Redraws object and background markers for all objects on the currently displayed amplifier *number*. """ self.imdisplay.reset() # Find wich amplifier is currently displayed amp=self.getCurrentAmp() # (Re)draw markers for obj in ['target','comparison']: if self.amp[obj]==amp: if self.display[obj]['position']: self.draw(obj+','+'r') if self.display[obj]['annulus']: self.draw(obj+','+'r1') self.draw(obj+','+'r2') if self.display[obj]['region']: self.draw(obj+','+'y2') def getCurrentAmp(self, namps=4): """Returns the currently displayed amplifier. *namps* is the number of amplifiers on the CCD. """ # Get exposure number n=int(self.ui.imageSpinBox.value()) # Convert exposure number to current amplifier number amp=n%namps if amp==0: amp=namps return amp def captureHandler(self, key): """Called when a capture button is clicked. *key* is given by the signal mapper and consists of a string with the object and parameter separated by a comma. Depending on the *key* input widgets are added to the current display lists. Subsequent calls to `self.selectionHandler` get displayed in the listed widgets. """ # Get object and parameter from key obj,par=str(key).split(',') # Add input widgets to lists if par=='position': self.xdisplay=[self.input[obj]['x']] self.ydisplay=[self.input[obj]['y']] self.rdisplay=[] elif par=='radius': self.xdisplay=[] self.ydisplay=[] self.x=float(self.input[obj]['x'].text()) self.y=float(self.input[obj]['y'].text()) self.rdisplay=[self.input[obj]['r']] elif par=='annulus': self.xdisplay=[] self.ydisplay=[] self.x=float(self.input[obj]['x'].text()) self.y=float(self.input[obj]['y'].text()) self.rdisplay=[self.input[obj]['r1'], self.input[obj]['r2']] elif par=='region': self.xdisplay=[self.input[obj]['x1'], self.input[obj]['x2']] self.ydisplay=[self.input[obj]['y1'], self.input[obj]['y2']] self.rdisplay=[] def selectionHandler(self, x, y): """Event handler for click in image display window. *x*, *y* is the position (in image pixel coordinates) of the click. These positions are inserted into the first input widgets in the display lists. If a radius is requested this is calculated from the position given in (self.x, self.y) which should be set to the current object. """ if len(self.xdisplay)>0: display=self.xdisplay.pop(0) display.setText(str(x)) if len(self.ydisplay)>0: display=self.ydisplay.pop(0) display.setText(str(y)) if len(self.rdisplay)>0: r=np.sqrt((x-self.x)**2+(y-self.y)**2) display=self.rdisplay.pop(0) display.setText(str(r)) def setSearchDistance(self, distance): """Set search distance used for recentering.""" self.distance=int(distance) def setMarkerType(self, marker): """Set marker type to 'circle' or 'square'.""" if marker in ['circle','square']: self.mark_with=marker else: raise SaltIOError('Unknown marker type '+str(marker)) def setLineColor(self, object, color): """Changes the default line color used for marking.""" self.line[object]['color']=color def setLineWidth(self, object, width): """Changes the default line width used for marking.""" self.line[object]['width']=width def save(self): """Save configuration. The format is:: For objects that use an anullus: object amp x y r r1 r2 For objects that use a region: object amp x y r x1 y1 x2 y2 """ if (self.ui.tgtAnulusRadioButton.isChecked() and self.ui.cmpRegionRadioButton.isChecked()) or \ (self.ui.tgtRegionRadioButton.isChecked() and self.ui.cmpAnulusRadioButton.isChecked()): msg='SLOTPREVIEW--SLOTPHOT can not handle different background types' raise SaltError(msg) # Write values to file with open(self.config,'w') as f: for i,obj in enumerate(['target','comparison']): b_type='region' if obj=='target': print(obj, self.ui.tgtAnulusRadioButton.isChecked()) if self.ui.tgtAnulusRadioButton.isChecked(): b_type='annulus' elif obj=='comparison': if self.ui.cmpAnulusRadioButton.isChecked(): b_type='annulus' # If r1 is not zero, assumes annulus line='%i\t%i\t' % (i+1, self.amp[obj]) if b_type=='annulus': line+=''.join('%3.2f\t' % float(self.input[obj][key].text()) for key in ['x', 'y', 'r', 'r1', 'r2']) else: line+=''.join('%3.2f\t' % float(self.input[obj][key].text()) for key in ['x', 'y', 'r', 'x1', 'y2', 'x2', 'y2']) # Write string to configfile f.write(line.rstrip()+'\n') # Exit program self.close()