Пример #1
0
 def __init__(self, parent, modal=True, flags=Qt.WindowFlags()):
     QDialog.__init__(self, parent, flags)
     self.setModal(modal)
     self.setWindowTitle("Add FITS brick")
     lo = QVBoxLayout(self)
     lo.setMargin(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.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)
Пример #2
0
 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;
Пример #3
0
 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
Пример #4
0
 def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()):
   QDialog.__init__(self,parent,flags);
   self.setModal(modal);
   self.setWindowTitle("Add FITS brick");
   lo = QVBoxLayout(self);
   lo.setMargin(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.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);
Пример #5
0
class AddBrickDialog(QDialog):
    def __init__(self, parent, modal=True, flags=Qt.WindowFlags()):
        QDialog.__init__(self, parent, flags)
        self.setModal(modal)
        self.setWindowTitle("Add FITS brick")
        lo = QVBoxLayout(self)
        lo.setMargin(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.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
        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, err:
            busy = None
            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 = None
        return QDialog.accept(self)
Пример #6
0
 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);
Пример #7
0
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 ]
        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);
Пример #8
0
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 ]
Пример #9
0
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)
Пример #10
0
class AddBrickDialog (QDialog):
  def __init__ (self,parent,modal=True,flags=Qt.WindowFlags()):
    QDialog.__init__(self,parent,flags);
    self.setModal(modal);
    self.setWindowTitle("Add FITS brick");
    lo = QVBoxLayout(self);
    lo.setMargin(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.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;
    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,err:
      busy = None;
      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 = None;
    return QDialog.accept(self);
Пример #11
0
 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);
Пример #12
0
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,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;
Пример #13
0
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);