def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()): QDialog.__init__(self,parent,flags); self.setModal(modal); self.setWindowTitle("Export Karma annotations"); lo = QVBoxLayout(self); lo.setMargin(10); lo.setSpacing(5); # file selector self.wfile = FileSelector(self,label="Filename:",dialog_label="Karma annotations filename",default_suffix="ann",file_types="Karma annotations (*.ann)"); lo.addWidget(self.wfile); # selected sources checkbox self.wsel = QCheckBox("selected sources only",self); lo.addWidget(self.wsel); # OK/cancel buttons lo.addSpacing(10); lo2 = QHBoxLayout(); lo.addLayout(lo2); lo2.setContentsMargins(0,0,0,0); lo2.setMargin(5); self.wokbtn = QPushButton("OK",self); self.wokbtn.setMinimumWidth(128); QObject.connect(self.wokbtn,SIGNAL("clicked()"),self.accept); self.wokbtn.setEnabled(False); cancelbtn = QPushButton("Cancel",self); cancelbtn.setMinimumWidth(128); QObject.connect(cancelbtn,SIGNAL("clicked()"),self.reject); lo2.addWidget(self.wokbtn); lo2.addStretch(1); lo2.addWidget(cancelbtn); self.setMinimumWidth(384); # signals QObject.connect(self.wfile,SIGNAL("valid"),self.wokbtn.setEnabled); # internal state self.qerrmsg = QErrorMessage(self); self._model_filename = None;
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Export Karma annotations") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="Filename:", dialog_label="Karma annotations filename", default_suffix="ann", file_types="Karma annotations (*.ann)") lo.addWidget(self.wfile) # selected sources checkbox self.wsel = QCheckBox("selected sources only", self) lo.addWidget(self.wsel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("valid"), self.wokbtn.setEnabled) # internal state self.qerrmsg = QErrorMessage(self) self._model_filename = None
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox( "Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip( """<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), self.wpad.setEnabled) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox( "Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("filenameSelected"), self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self)
class MakeBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox( "Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip( """<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), self.wpad.setEnabled) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox( "Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("filenameSelected"), self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model pb = self.model.primaryBeam() if pb: self.wpb_exp.setText(pb) else: self.wpb_apply.setChecked(False) self.wpb_exp.setText("") if model.filename(): self._model_dir = os.path.dirname(os.path.abspath( model.filename())) else: self._model_dir = os.path.abspath('.') self.wfile.setDirectory(self._model_dir) self._fileSelected(self.wfile.filename(), quiet=True) def _fileSelected(self, filename, quiet=False): self.wokbtn.setEnabled(False) if not filename: return None # check that filename matches model if not os.path.samefile(self._model_dir, os.path.dirname(filename)): self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Directory mismatch", """<P>The FITS file must reside in the same directory as the current sky model.</P>""") self.wfile.setDirectory(self._model_dir) return None # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] hdr = input_hdu.header # get frequency, if specified for axis in range(1, hdr['NAXIS'] + 1): if hdr['CTYPE%d' % axis].upper() == 'FREQ': self.wfreq.setText(str(hdr['CRVAL%d' % axis] / 1e+6)) break except Exception as err: busy = None self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return None self.wokbtn.setEnabled(True) # if filename is not in model already, enable the "add to model" control for src in self.model.sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) \ and os.path.exists(src.shape.filename) and os.path.exists(filename) \ and os.path.samefile(src.shape.filename, filename): self.wadd.setChecked(True) self.wadd.setEnabled(False) self.wadd.setText("image already in sky model") break else: self.wadd.setText("add image to sky model") return filename def accept(self): """Tries to make a brick, and closes the dialog if successful.""" sources = [ src for src in self.model.sources if src.selected and src.typecode == 'pnt' ] filename = self.wfile.filename() if not self._fileSelected(filename): return # get PB expression pbfunc = None if self.wpb_apply.isChecked(): pbexp = str(self.wpb_exp.text()) try: pbfunc = eval("lambda r,fq:" + pbexp) except Exception as err: QMessageBox.warning( self, "Error parsing PB experssion", "Error parsing primary beam expression %s: %s" % (pbexp, str(err))) return # get frequency freq = str(self.wfreq.text()) freq = float(freq) * 1e+6 if freq else None # get pad factor pad = str(self.wpad.text()) pad = max(float(pad), 1) if pad else 1 # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy = None QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # reset data if asked to if self.woverwrite.isChecked(): input_hdu.data[...] = 0 # insert sources Imaging.restoreSources(input_hdu, sources, 0, primary_beam=pbfunc, freq=freq) # save fits file try: # pyfits seems to produce an exception: # TypeError: formatwarning() takes exactly 4 arguments (5 given) # when attempting to overwrite a file. As a workaround, remove the file first. if os.path.exists(filename): os.remove(filename) input_hdu.writeto(filename) except Exception as err: traceback.print_exc() busy = None QMessageBox.warning( self, "Error writing FITS", "Error writing FITS file %s: %s" % (filename, str(err))) return changed = False sources = self.model.sources # remove sources from model if asked to if self.wdel.isChecked(): sources = [ src for src in sources if not (src.selected and src.typecode == 'pnt') ] changed = True # add image to model if asked to if self.wadd.isChecked(): hdr = input_hdu.header # get image parameters max_flux = float(input_hdu.data.max()) wcs = WCS(hdr, mode='pyfits') # Get reference pixel coordinates # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image # So scan the header to get the CRPIX values ra0 = dec0 = 1 for iaxis in range(hdr['NAXIS']): axs = str(iaxis + 1) name = hdr.get('CTYPE' + axs, axs).upper() if name.startswith("RA"): ra0 = hdr.get('CRPIX' + axs, 1) - 1 elif name.startswith("DEC"): dec0 = hdr.get('CRPIX' + axs, 1) - 1 # convert pixel to degrees ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] # check if this image is already contained in the model for src in sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) and os.path.samefile( src.shape.filename, filename): # update source parameters src.pos.ra, src.pos.dec = ra0, dec0 src.flux.I = max_flux src.shape.ex, src.shape.ey = sx, sy src.shape.nx, src.shape.ny = nx, ny src.shape.pad = pad break # not contained, make new source object else: pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=pad) img_src = SkyModel.Source(os.path.splitext( os.path.basename(filename))[0], pos, flux, shape=shape) sources.append(img_src) changed = True if changed: self.model.setSources(sources) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) self.parent().showMessage("Wrote %d sources to FITS file %s" % (len(sources), filename)) busy = None return QDialog.accept(self)
class ExportKarmaDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Export Karma annotations") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="Filename:", dialog_label="Karma annotations filename", default_suffix="ann", file_types="Karma annotations (*.ann)") lo.addWidget(self.wfile) # selected sources checkbox self.wsel = QCheckBox("selected sources only", self) lo.addWidget(self.wsel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("valid"), self.wokbtn.setEnabled) # internal state self.qerrmsg = QErrorMessage(self) self._model_filename = None def setModel(self, model): self.model = model # set the default annotations filename, whenever a new model filename is set filename = self.model.filename() if filename and filename != self._model_filename: self._model_filename = filename self.wfile.setFilename(os.path.splitext(filename)[0] + ".ann") def accept(self): """Tries to export annotations, and closes the dialog if successful.""" try: filename = self.wfile.filename() if os.path.exists(filename) and QMessageBox.question( self, "Exporting Karma annotations", "<P>Overwrite the file %s?</P>" % filename, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes: return f = file(self.wfile.filename(), "wt") f.write('COORD W\nPA STANDARD\nCOLOR GREEN\nFONT hershey12\n') # source list if self.wsel.isChecked(): sources = [src for src in self.model.sources if src.selected] else: sources = self.model.sources # calculate basis size for crosses (TODO: replace min_size with something more sensible, as this value is in degrees) brightnesses = [ abs(src.brightness()) for src in sources if src.brightness() != 0 ] min_bright = brightnesses and min(brightnesses) min_size = 0.01 # loop over sources busy = BusyIndicator() for src in sources: ra = src.pos.ra / DEG dec = src.pos.dec / DEG # figure out source size if src.brightness() and min_bright: ysize = (math.log10(abs(src.brightness())) - math.log10(min_bright) + 1) * min_size else: ysize = min_size xsize = ysize / (math.cos(src.pos.dec) or 1) # figure out source style style, label = self.model.getSourcePlotStyle(src) if style: f.write('# %s\n' % src.name) # write symbol for source f.write('COLOR %s\n' % style.symbol_color) if style.symbol == "plus": f.write('CROSS %.12f %.12f %f %f\n' % (ra, dec, xsize, ysize)) elif style.symbol == "cross": f.write('CROSS %.12f %.12f %f %f 45\n' % (ra, dec, ysize, ysize)) elif style.symbol == "circle": f.write('CIRCLE %.12f %.12f %f\n' % (ra, dec, ysize)) elif style.symbol == "dot": f.write('DOT %.12f %.12f\n' % (ra, dec)) elif style.symbol == "square": f.write('CBOX %.12f %.12f %f %f\n' % (ra, dec, xsize, ysize)) elif style.symbol == "diamond": f.write('CBOX %.12f %.12f %f %f 45\n' % (ra, dec, xsize, ysize)) # write label if label: f.write('FONT hershey%d\n' % (style.label_size * 2)) f.write('COLOR %s\n' % style.label_color) f.write('TEXT %.12f %.12f %s\n' % (ra, dec, label)) f.close() except IOError as err: busy = None self.qerrmsg.showMessage( "Error writing Karma annotations file %s: %s" % (filename, str(err))) return busy = None self.parent().showMessage( "Wrote Karma annotations for %d sources to file %s" % (len(sources), filename)) return QDialog.accept(self)
def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()): QDialog.__init__(self,parent,flags); self.setModal(modal); self.setWindowTitle("Restore model into image"); lo = QVBoxLayout(self); lo.setMargin(10); lo.setSpacing(5); # file selector self.wfile_in = FileSelector(self,label="Input FITS file:",dialog_label="Input FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.ExistingFile); lo.addWidget(self.wfile_in); self.wfile_out = FileSelector(self,label="Output FITS file:",dialog_label="Output FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.AnyFile); lo.addWidget(self.wfile_out); # beam size lo1 = QHBoxLayout(); lo.addLayout(lo1); lo1.setContentsMargins(0,0,0,0); lo1.addWidget(QLabel("Restoring beam FWHM, major axis:",self)); self.wbmaj = QLineEdit(self); lo1.addWidget(self.wbmaj); lo1.addWidget(QLabel("\" minor axis:",self)); self.wbmin = QLineEdit(self); lo1.addWidget(self.wbmin); lo1.addWidget(QLabel("\" P.A.:",self)); self.wbpa = QLineEdit(self); lo1.addWidget(self.wbpa); lo1.addWidget(QLabel(u"\u00B0",self)); for w in self.wbmaj,self.wbmin,self.wbpa: w.setValidator(QDoubleValidator(self)); lo1 = QHBoxLayout(); lo.addLayout(lo1); lo1.setContentsMargins(0,0,0,0); self.wfile_psf = FileSelector(self,label="Set restoring beam by fitting PSF image:",dialog_label="PSF FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.ExistingFile); lo1.addSpacing(32); lo1.addWidget(self.wfile_psf); # selection only self.wselonly = QCheckBox("restore selected model sources only",self); lo.addWidget(self.wselonly ); # OK/cancel buttons lo.addSpacing(10); lo2 = QHBoxLayout(); lo.addLayout(lo2); lo2.setContentsMargins(0,0,0,0); lo2.setMargin(5); self.wokbtn = QPushButton("OK",self); self.wokbtn.setMinimumWidth(128); QObject.connect(self.wokbtn,SIGNAL("clicked()"),self.accept); self.wokbtn.setEnabled(False); cancelbtn = QPushButton("Cancel",self); cancelbtn.setMinimumWidth(128); QObject.connect(cancelbtn,SIGNAL("clicked()"),self.reject); lo2.addWidget(self.wokbtn); lo2.addStretch(1); lo2.addWidget(cancelbtn); self.setMinimumWidth(384); # signals QObject.connect(self.wfile_in,SIGNAL("filenameSelected"),self._fileSelected); QObject.connect(self.wfile_in,SIGNAL("filenameSelected"),self._inputFileSelected); QObject.connect(self.wfile_out,SIGNAL("filenameSelected"),self._fileSelected); QObject.connect(self.wfile_psf,SIGNAL("filenameSelected"),self._psfFileSelected); # internal state self.qerrmsg = QErrorMessage(self);
class RestoreImageDialog (QDialog): def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()): QDialog.__init__(self,parent,flags); self.setModal(modal); self.setWindowTitle("Restore model into image"); lo = QVBoxLayout(self); lo.setMargin(10); lo.setSpacing(5); # file selector self.wfile_in = FileSelector(self,label="Input FITS file:",dialog_label="Input FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.ExistingFile); lo.addWidget(self.wfile_in); self.wfile_out = FileSelector(self,label="Output FITS file:",dialog_label="Output FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.AnyFile); lo.addWidget(self.wfile_out); # beam size lo1 = QHBoxLayout(); lo.addLayout(lo1); lo1.setContentsMargins(0,0,0,0); lo1.addWidget(QLabel("Restoring beam FWHM, major axis:",self)); self.wbmaj = QLineEdit(self); lo1.addWidget(self.wbmaj); lo1.addWidget(QLabel("\" minor axis:",self)); self.wbmin = QLineEdit(self); lo1.addWidget(self.wbmin); lo1.addWidget(QLabel("\" P.A.:",self)); self.wbpa = QLineEdit(self); lo1.addWidget(self.wbpa); lo1.addWidget(QLabel(u"\u00B0",self)); for w in self.wbmaj,self.wbmin,self.wbpa: w.setValidator(QDoubleValidator(self)); lo1 = QHBoxLayout(); lo.addLayout(lo1); lo1.setContentsMargins(0,0,0,0); self.wfile_psf = FileSelector(self,label="Set restoring beam by fitting PSF image:",dialog_label="PSF FITS file",default_suffix="fits",file_types="FITS files (*.fits *.FITS)",file_mode=QFileDialog.ExistingFile); lo1.addSpacing(32); lo1.addWidget(self.wfile_psf); # selection only self.wselonly = QCheckBox("restore selected model sources only",self); lo.addWidget(self.wselonly ); # OK/cancel buttons lo.addSpacing(10); lo2 = QHBoxLayout(); lo.addLayout(lo2); lo2.setContentsMargins(0,0,0,0); lo2.setMargin(5); self.wokbtn = QPushButton("OK",self); self.wokbtn.setMinimumWidth(128); QObject.connect(self.wokbtn,SIGNAL("clicked()"),self.accept); self.wokbtn.setEnabled(False); cancelbtn = QPushButton("Cancel",self); cancelbtn.setMinimumWidth(128); QObject.connect(cancelbtn,SIGNAL("clicked()"),self.reject); lo2.addWidget(self.wokbtn); lo2.addStretch(1); lo2.addWidget(cancelbtn); self.setMinimumWidth(384); # signals QObject.connect(self.wfile_in,SIGNAL("filenameSelected"),self._fileSelected); QObject.connect(self.wfile_in,SIGNAL("filenameSelected"),self._inputFileSelected); QObject.connect(self.wfile_out,SIGNAL("filenameSelected"),self._fileSelected); QObject.connect(self.wfile_psf,SIGNAL("filenameSelected"),self._psfFileSelected); # internal state self.qerrmsg = QErrorMessage(self); def setModel (self,model): nsel = len([ src for src in model.sources if src.selected ]); self.wselonly.setVisible(nsel>0 and nsel<len(model.sources)); self.model = model; self._fileSelected(None); def _fileSelected (self,filename): self.wokbtn.setEnabled(bool(self.wfile_in.filename() and self.wfile_out.filename())); def _inputFileSelected (self,filename): if filename: try: header = pyfits.open(filename)[0].header; except: self.qerrmsg.showMessage("Error reading FITS file %s: %s"%(filename,str(err))); self.wfile_in.setFilename(""); return; # try to get beam extents gx,gy,grot = [ header.get(x,None) for x in 'BMAJ','BMIN','BPA' ]; if all([x is not None for x in gx,gy,grot]): # if beam size is already set, ask before overwriting print [ str(x.text()) for x in self.wbmaj,self.wbmin,self.wbpa ]
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Restore model into image") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile_in) self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.AnyFile) lo.addWidget(self.wfile_out) # beam size lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self)) self.wbmaj = QLineEdit(self) lo1.addWidget(self.wbmaj) lo1.addWidget(QLabel("\" minor axis:", self)) self.wbmin = QLineEdit(self) lo1.addWidget(self.wbmin) lo1.addWidget(QLabel("\" P.A.:", self)) self.wbpa = QLineEdit(self) lo1.addWidget(self.wbpa) lo1.addWidget(QLabel("\u00B0", self)) for w in self.wbmaj, self.wbmin, self.wbpa: w.setValidator(QDoubleValidator(self)) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wfile_psf = FileSelector(self, label="Set restoring beam by fitting PSF image:", dialog_label="PSF FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo1.addSpacing(32) lo1.addWidget(self.wfile_psf) # selection only self.wselonly = QCheckBox("restore selected model sources only", self) lo.addWidget(self.wselonly) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._fileSelected) QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._inputFileSelected) QObject.connect(self.wfile_out, SIGNAL("filenameSelected"), self._fileSelected) QObject.connect(self.wfile_psf, SIGNAL("filenameSelected"), self._psfFileSelected) # internal state self.qerrmsg = QErrorMessage(self)
class RestoreImageDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Restore model into image") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile_in = FileSelector(self, label="Input FITS file:", dialog_label="Input FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile_in) self.wfile_out = FileSelector(self, label="Output FITS file:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.AnyFile) lo.addWidget(self.wfile_out) # beam size lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Restoring beam FWHM, major axis:", self)) self.wbmaj = QLineEdit(self) lo1.addWidget(self.wbmaj) lo1.addWidget(QLabel("\" minor axis:", self)) self.wbmin = QLineEdit(self) lo1.addWidget(self.wbmin) lo1.addWidget(QLabel("\" P.A.:", self)) self.wbpa = QLineEdit(self) lo1.addWidget(self.wbpa) lo1.addWidget(QLabel("\u00B0", self)) for w in self.wbmaj, self.wbmin, self.wbpa: w.setValidator(QDoubleValidator(self)) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wfile_psf = FileSelector(self, label="Set restoring beam by fitting PSF image:", dialog_label="PSF FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo1.addSpacing(32) lo1.addWidget(self.wfile_psf) # selection only self.wselonly = QCheckBox("restore selected model sources only", self) lo.addWidget(self.wselonly) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._fileSelected) QObject.connect(self.wfile_in, SIGNAL("filenameSelected"), self._inputFileSelected) QObject.connect(self.wfile_out, SIGNAL("filenameSelected"), self._fileSelected) QObject.connect(self.wfile_psf, SIGNAL("filenameSelected"), self._psfFileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): nsel = len([src for src in model.sources if src.selected]) self.wselonly.setVisible(nsel > 0 and nsel < len(model.sources)) self.model = model self._fileSelected(None) def _fileSelected(self, filename): self.wokbtn.setEnabled(bool(self.wfile_in.filename() and self.wfile_out.filename())) def _inputFileSelected(self, filename): if filename: try: header = pyfits.open(filename)[0].header except Exception as err: self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (filename, str(err))) self.wfile_in.setFilename("") return # try to get beam extents gx, gy, grot = [header.get(x, None) for x in ('BMAJ', 'BMIN', 'BPA')] if all([x is not None for x in (gx, gy, grot)]): # if beam size is already set, ask before overwriting print([str(x.text()) for x in (self.wbmaj, self.wbmin, self.wbpa)]) if any([bool(str(x.text())) for x in (self.wbmaj, self.wbmin, self.wbpa)]) and \ QMessageBox.question(self, "Set restoring beam", "Also reset restoring beam size from this FITS file?", QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes: return self.wbmaj.setText("%.2f" % (gx * 3600)) self.wbmin.setText("%.2f" % (gy * 3600)) self.wbpa.setText("%.2f" % grot) def _psfFileSelected(self, filename): busy = BusyIndicator() filename = str(filename) self.parent().showMessage("Fitting gaussian to PSF file %s" % filename) try: bmaj, bmin, pa = [x / DEG for x in Imaging.fitPsf(filename)] except Exception as err: busy = None self.qerrmsg.showMessage("Error fitting PSF file %s: %s" % (filename, str(err))) return bmaj *= 3600 * Imaging.FWHM bmin *= 3600 * Imaging.FWHM self.wbmaj.setText(str(bmaj)) self.wbmin.setText(str(bmin)) self.wbpa.setText(str(pa)) def accept(self): """Tries to restore the image, and closes the dialog if successful.""" # get list of sources to restore sources = self.model.sources sel_sources = [src for src in sources if src.selected] if len(sel_sources) > 0 and len(sel_sources) < len(sources) and self.wselonly.isChecked(): sources = sel_sources if not sources: self.qerrmsg.showMessage("No sources to restore.") return busy = BusyIndicator() # get filenames infile = self.wfile_in.filename() outfile = self.wfile_out.filename() self.parent().showMessage( "Restoring %d model sources to image %s, writing to %s" % (len(sources), infile, outfile)) # read fits file try: input_hdu = pyfits.open(infile)[0] except Exception as err: busy = None self.qerrmsg.showMessage("Error reading FITS file %s: %s" % (infile, str(err))) return # get beam sizes try: bmaj = float(str(self.wbmaj.text())) bmin = float(str(self.wbmin.text())) pa = float(str(self.wbpa.text()) or "0") except Exception as err: busy = None self.qerrmsg.showMessage("Invalid beam size specified") return bmaj = bmaj / (Imaging.FWHM * 3600) * DEG bmin = bmin / (Imaging.FWHM * 3600) * DEG pa = pa * DEG # restore try: Imaging.restoreSources(input_hdu, sources, bmaj, bmin, pa) except Exception as err: busy = None self.qerrmsg.showMessage("Error restoring model into image: %s" % str(err)) return # save fits file try: input_hdu.writeto(outfile, clobber=True) except Exception as err: busy = None self.qerrmsg.showMessage("Error writing FITS file %s: %s" % (outfile, str(err))) return self.parent().loadImage(outfile) busy = None return QDialog.accept(self)
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox("Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip("""<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), self.wpad.setEnabled) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox("Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("filenameSelected"), self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self)
class MakeBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.setModal(modal) self.setWindowTitle("Convert sources to FITS brick") lo = QVBoxLayout(self) lo.setMargin(10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="Output FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # reference frequency lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) label = QLabel("Frequency, MHz:", self) lo1.addWidget(label) tip = """<P>If your sky model contains spectral information (such as spectral indices), then a brick may be generated for a specific frequency. If a frequency is not specified here, the reference frequency of the model sources will be assumed.</P>""" self.wfreq = QLineEdit(self) self.wfreq.setValidator(QDoubleValidator(self)) label.setToolTip(tip) self.wfreq.setToolTip(tip) lo1.addWidget(self.wfreq) # beam gain lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpb_apply = QCheckBox("Apply primary beam expression:", self) self.wpb_apply.setChecked(True) lo1.addWidget(self.wpb_apply) tip = """<P>If this option is specified, a primary power beam gain will be applied to the sources before inserting them into the brick. This can be any valid Python expression making use of the variables 'r' (corresponding to distance from field centre, in radians) and 'fq' (corresponding to frequency.)</P>""" self.wpb_exp = QLineEdit(self) self.wpb_apply.setToolTip(tip) self.wpb_exp.setToolTip(tip) lo1.addWidget(self.wpb_exp) # overwrite or add mode lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.woverwrite = QRadioButton("overwrite image", self) self.woverwrite.setChecked(True) lo1.addWidget(self.woverwrite) self.waddinto = QRadioButton("add into image", self) lo1.addWidget(self.waddinto) # add to model self.wadd = QCheckBox("Add resulting brick to sky model as a FITS image component", self) lo.addWidget(self.wadd) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) self.wpad = QLineEdit(self) self.wpad.setValidator(QDoubleValidator(self)) self.wpad.setText("1.1") lab = QLabel("...with padding factor:", self) lab.setToolTip("""<P>The padding factor determines the amount of null padding inserted around the image during the prediction stage. Padding alleviates the effects of tapering and detapering in the uv-brick, which can show up towards the edges of the image. For a factor of N, the image will be padded out to N times its original size. This increases memory use, so if you have no flux at the edges of the image anyway, then a pad factor of 1 is perfectly fine.</P>""") self.wpad.setToolTip(lab.toolTip()) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), self.wpad.setEnabled) QObject.connect(self.wadd, SIGNAL("toggled(bool)"), lab.setEnabled) self.wpad.setEnabled(False) lab.setEnabled(False) lo1.addStretch(1) lo1.addWidget(lab, 0) lo1.addWidget(self.wpad, 1) self.wdel = QCheckBox("Remove from the sky model sources that go into the brick", self) lo.addWidget(self.wdel) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setMargin(5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals QObject.connect(self.wfile, SIGNAL("filenameSelected"), self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model pb = self.model.primaryBeam() if pb: self.wpb_exp.setText(pb) else: self.wpb_apply.setChecked(False) self.wpb_exp.setText("") if model.filename(): self._model_dir = os.path.dirname(os.path.abspath(model.filename())) else: self._model_dir = os.path.abspath('.') self.wfile.setDirectory(self._model_dir) self._fileSelected(self.wfile.filename(), quiet=True) def _fileSelected(self, filename, quiet=False): self.wokbtn.setEnabled(False) if not filename: return None # check that filename matches model if not os.path.samefile(self._model_dir, os.path.dirname(filename)): self.wfile.setFilename('') if not quiet: QMessageBox.warning(self, "Directory mismatch", """<P>The FITS file must reside in the same directory as the current sky model.</P>""") self.wfile.setDirectory(self._model_dir) return None # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] hdr = input_hdu.header # get frequency, if specified for axis in range(1, hdr['NAXIS'] + 1): if hdr['CTYPE%d' % axis].upper() == 'FREQ': self.wfreq.setText(str(hdr['CRVAL%d' % axis] / 1e+6)) break except Exception as err: busy = None self.wfile.setFilename('') if not quiet: QMessageBox.warning(self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return None self.wokbtn.setEnabled(True) # if filename is not in model already, enable the "add to model" control for src in self.model.sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) \ and os.path.exists(src.shape.filename) and os.path.exists(filename) \ and os.path.samefile(src.shape.filename, filename): self.wadd.setChecked(True) self.wadd.setEnabled(False) self.wadd.setText("image already in sky model") break else: self.wadd.setText("add image to sky model") return filename def accept(self): """Tries to make a brick, and closes the dialog if successful.""" sources = [src for src in self.model.sources if src.selected and src.typecode == 'pnt'] filename = self.wfile.filename() if not self._fileSelected(filename): return # get PB expression pbfunc = None if self.wpb_apply.isChecked(): pbexp = str(self.wpb_exp.text()) try: pbfunc = eval("lambda r,fq:" + pbexp) except Exception as err: QMessageBox.warning(self, "Error parsing PB experssion", "Error parsing primary beam expression %s: %s" % (pbexp, str(err))) return # get frequency freq = str(self.wfreq.text()) freq = float(freq) * 1e+6 if freq else None # get pad factor pad = str(self.wpad.text()) pad = max(float(pad), 1) if pad else 1 # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy = None QMessageBox.warning(self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # reset data if asked to if self.woverwrite.isChecked(): input_hdu.data[...] = 0 # insert sources Imaging.restoreSources(input_hdu, sources, 0, primary_beam=pbfunc, freq=freq) # save fits file try: # pyfits seems to produce an exception: # TypeError: formatwarning() takes exactly 4 arguments (5 given) # when attempting to overwrite a file. As a workaround, remove the file first. if os.path.exists(filename): os.remove(filename) input_hdu.writeto(filename) except Exception as err: traceback.print_exc() busy = None QMessageBox.warning(self, "Error writing FITS", "Error writing FITS file %s: %s" % (filename, str(err))) return changed = False sources = self.model.sources # remove sources from model if asked to if self.wdel.isChecked(): sources = [src for src in sources if not (src.selected and src.typecode == 'pnt')] changed = True # add image to model if asked to if self.wadd.isChecked(): hdr = input_hdu.header # get image parameters max_flux = float(input_hdu.data.max()) wcs = WCS(hdr, mode='pyfits') # Get reference pixel coordinates # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image # So scan the header to get the CRPIX values ra0 = dec0 = 1 for iaxis in range(hdr['NAXIS']): axs = str(iaxis + 1) name = hdr.get('CTYPE' + axs, axs).upper() if name.startswith("RA"): ra0 = hdr.get('CRPIX' + axs, 1) - 1 elif name.startswith("DEC"): dec0 = hdr.get('CRPIX' + axs, 1) - 1 # convert pixel to degrees ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] # check if this image is already contained in the model for src in sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) and os.path.samefile( src.shape.filename, filename): # update source parameters src.pos.ra, src.pos.dec = ra0, dec0 src.flux.I = max_flux src.shape.ex, src.shape.ey = sx, sy src.shape.nx, src.shape.ny = nx, ny src.shape.pad = pad break # not contained, make new source object else: pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=pad) img_src = SkyModel.Source(os.path.splitext(os.path.basename(filename))[0], pos, flux, shape=shape) sources.append(img_src) changed = True if changed: self.model.setSources(sources) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) self.parent().showMessage("Wrote %d sources to FITS file %s" % (len(sources), filename)) busy = None return QDialog.accept(self)
class AddBrickDialog(QDialog): def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self._model_dir = None self.setModal(modal) self.setWindowTitle("Add FITS brick") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # overwrite or add mode lo1 = QGridLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Padding factor:", self), 0, 0) self.wpad = QLineEdit("2", self) self.wpad.setValidator(QDoubleValidator(self)) lo1.addWidget(self.wpad, 0, 1) lo1.addWidget(QLabel("Assign source name:", self), 1, 0) self.wname = QLineEdit(self) lo1.addWidget(self.wname, 1, 1) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile.filenameSelected.connect(self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self) def setModel(self, model): self.model = model if model.filename(): self._model_dir = os.path.dirname(os.path.abspath( model.filename())) else: self._model_dir = os.path.abspath('.') self.wfile.setDirectory(self._model_dir) self._fileSelected(self.wfile.filename(), quiet=True) def _fileSelected(self, filename, quiet=False): self.wokbtn.setEnabled(False) if not filename: return None # check that filename matches model if not os.path.samefile(self._model_dir, os.path.dirname(filename)): self.wfile.setFilename('') if not quiet: QMessageBox.warning( self, "Directory mismatch", """<P>The FITS file must reside in the same directory as the current sky model.</P>""") self.wfile.setDirectory(self._model_dir) return None # if filename is not in model already, enable the "add to model" control for src in self.model.sources: if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage): if os.path.exists(src.shape.filename) and os.path.samefile( src.shape.filename, filename): if not quiet: QMessageBox.warning( self, "Already in model", "This FITS brick is already present in the model.") self.wfile.setFilename('') return None if not str(self.wname.text()): self.wname.setText( os.path.splitext(os.path.basename(str(filename)))[0]) self.wokbtn.setEnabled(True) return filename def accept(self): """Tries to add brick, and closes the dialog if successful.""" filename = self.wfile.filename() # read fits file busy = BusyIndicator() try: input_hdu = pyfits.open(filename)[0] except Exception as err: busy.reset_cursor() QMessageBox.warning( self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err))) return # check name srcname = str(self.wname.text()) or os.path.splitext( os.path.basename(str(filename)))[0] if srcname in set([src.name for src in self.model.sources]): QMessageBox.warning( self, "Already in model", "<p>The model already contains a source named '%s'. Please select a different name.</p>" % srcname) return # get image parameters hdr = input_hdu.header max_flux = float(input_hdu.data.max()) wcs = WCS(hdr, mode='pyfits') # Get reference pixel coordinates # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image # So scan the header to get the CRPIX values ra0 = dec0 = 1 for iaxis in range(hdr['NAXIS']): axs = str(iaxis + 1) name = hdr.get('CTYPE' + axs, axs).upper() if name.startswith("RA"): ra0 = hdr.get('CRPIX' + axs, 1) - 1 elif name.startswith("DEC"): dec0 = hdr.get('CRPIX' + axs, 1) - 1 # convert pixel to degrees # print ra0,dec0 ra0, dec0 = wcs.pix2wcs(ra0, dec0) ra0 *= DEG dec0 *= DEG # print ModelClasses.Position.ra_hms_static(ra0) # print ModelClasses.Position.dec_sdms_static(dec0) sx, sy = wcs.getHalfSizeDeg() sx *= DEG sy *= DEG nx, ny = input_hdu.data.shape[-1:-3:-1] pos = ModelClasses.Position(ra0, dec0) flux = ModelClasses.Flux(max_flux) shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=float(str(self.wpad.text()) or "1")) img_src = SkyModel.Source(srcname, pos, flux, shape=shape) self.model.setSources(self.model.sources + [img_src]) self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self) busy.reset_cursor() return QDialog.accept(self)
class ExportKarmaDialog (QDialog): def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()): QDialog.__init__(self,parent,flags); self.setModal(modal); self.setWindowTitle("Export Karma annotations"); lo = QVBoxLayout(self); lo.setMargin(10); lo.setSpacing(5); # file selector self.wfile = FileSelector(self,label="Filename:",dialog_label="Karma annotations filename",default_suffix="ann",file_types="Karma annotations (*.ann)"); lo.addWidget(self.wfile); # selected sources checkbox self.wsel = QCheckBox("selected sources only",self); lo.addWidget(self.wsel); # OK/cancel buttons lo.addSpacing(10); lo2 = QHBoxLayout(); lo.addLayout(lo2); lo2.setContentsMargins(0,0,0,0); lo2.setMargin(5); self.wokbtn = QPushButton("OK",self); self.wokbtn.setMinimumWidth(128); QObject.connect(self.wokbtn,SIGNAL("clicked()"),self.accept); self.wokbtn.setEnabled(False); cancelbtn = QPushButton("Cancel",self); cancelbtn.setMinimumWidth(128); QObject.connect(cancelbtn,SIGNAL("clicked()"),self.reject); lo2.addWidget(self.wokbtn); lo2.addStretch(1); lo2.addWidget(cancelbtn); self.setMinimumWidth(384); # signals QObject.connect(self.wfile,SIGNAL("valid"),self.wokbtn.setEnabled); # internal state self.qerrmsg = QErrorMessage(self); self._model_filename = None; def setModel (self,model): self.model = model; # set the default annotations filename, whenever a new model filename is set filename = self.model.filename(); if filename and filename != self._model_filename: self._model_filename = filename; self.wfile.setFilename(os.path.splitext(filename)[0] + ".ann"); def accept (self): """Tries to export annotations, and closes the dialog if successful."""; try: filename = self.wfile.filename(); if os.path.exists(filename) and QMessageBox.question(self,"Exporting Karma annotations","<P>Overwrite the file %s?</P>"%filename, QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes) != QMessageBox.Yes: return; f = file(self.wfile.filename(),"wt"); f.write('COORD W\nPA STANDARD\nCOLOR GREEN\nFONT hershey12\n'); # source list if self.wsel.isChecked(): sources = [ src for src in self.model.sources if src.selected ]; else: sources = self.model.sources; # calculate basis size for crosses (TODO: replace min_size with something more sensible, as this value is in degrees) brightnesses = [ abs(src.brightness()) for src in sources if src.brightness() != 0 ]; min_bright = brightnesses and min(brightnesses) ; min_size = 0.01; # loop over sources busy = BusyIndicator(); for src in sources: ra = src.pos.ra/DEG; dec = src.pos.dec/DEG; # figure out source size if src.brightness() and min_bright: ysize = (math.log10(abs(src.brightness())) - math.log10(min_bright) + 1)*min_size; else: ysize = min_size; xsize = ysize/(math.cos(src.pos.dec) or 1); # figure out source style style,label = self.model.getSourcePlotStyle(src); if style: f.write('# %s\n'%src.name); # write symbol for source f.write('COLOR %s\n'%style.symbol_color); if style.symbol == "plus": f.write('CROSS %.12f %.12f %f %f\n'%(ra,dec,xsize,ysize)); elif style.symbol == "cross": f.write('CROSS %.12f %.12f %f %f 45\n'%(ra,dec,ysize,ysize)); elif style.symbol == "circle": f.write('CIRCLE %.12f %.12f %f\n'%(ra,dec,ysize)); elif style.symbol == "dot": f.write('DOT %.12f %.12f\n'%(ra,dec)); elif style.symbol == "square": f.write('CBOX %.12f %.12f %f %f\n'%(ra,dec,xsize,ysize)); elif style.symbol == "diamond": f.write('CBOX %.12f %.12f %f %f 45\n'%(ra,dec,xsize,ysize)); # write label if label: f.write('FONT hershey%d\n'%(style.label_size*2)); f.write('COLOR %s\n'%style.label_color); f.write('TEXT %.12f %.12f %s\n'%(ra,dec,label)); f.close(); except IOError,err: busy = None; self.qerrmsg.showMessage("Error writing Karma annotations file %s: %s"%(filename,str(err))); return; busy = None; self.parent().showMessage("Wrote Karma annotations for %d sources to file %s"%(len(sources),filename)); return QDialog.accept(self);
def __init__(self, parent, modal=True, flags=Qt.WindowFlags()): QDialog.__init__(self, parent, flags) self.model = None self._model_dir = None self.setModal(modal) self.setWindowTitle("Add FITS brick") lo = QVBoxLayout(self) lo.setContentsMargins(10, 10, 10, 10) lo.setSpacing(5) # file selector self.wfile = FileSelector(self, label="FITS filename:", dialog_label="FITS file", default_suffix="fits", file_types="FITS files (*.fits *.FITS)", file_mode=QFileDialog.ExistingFile) lo.addWidget(self.wfile) # overwrite or add mode lo1 = QGridLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lo1.addWidget(QLabel("Padding factor:", self), 0, 0) self.wpad = QLineEdit("2", self) self.wpad.setValidator(QDoubleValidator(self)) lo1.addWidget(self.wpad, 0, 1) lo1.addWidget(QLabel("Assign source name:", self), 1, 0) self.wname = QLineEdit(self) lo1.addWidget(self.wname, 1, 1) # OK/cancel buttons lo.addSpacing(10) lo2 = QHBoxLayout() lo.addLayout(lo2) lo2.setContentsMargins(0, 0, 0, 0) lo2.setContentsMargins(5, 5, 5, 5) self.wokbtn = QPushButton("OK", self) self.wokbtn.setMinimumWidth(128) self.wokbtn.clicked.connect(self.accept) self.wokbtn.setEnabled(False) cancelbtn = QPushButton("Cancel", self) cancelbtn.setMinimumWidth(128) cancelbtn.clicked.connect(self.reject) lo2.addWidget(self.wokbtn) lo2.addStretch(1) lo2.addWidget(cancelbtn) self.setMinimumWidth(384) # signals self.wfile.filenameSelected.connect(self._fileSelected) # internal state self.qerrmsg = QErrorMessage(self)