コード例 #1
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
コード例 #2
0
 def validate(self):
     host = self._hostLine.text()
     port = self._portLine.text()
     database = self._databaseLine.text()
     user = self._userLine.text()
     password = self._passwordLine.text()
     e = self.parent().controller().connectDatabase(host, port, database, user, password)
     if not e:
         self.parent().statusBar().showMessage(self.tr("Connected to database <" + database + ">"))
         self.close()
     else:
         err = QErrorMessage(self)
         err.setWindowTitle(self.tr("Unable to connect database"))
         err.showMessage("Connection to database failed \n Error : " + str(e))
         err.show()
コード例 #3
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)
コード例 #4
0
ファイル: export_karma.py プロジェクト: ska-sa/tigger
 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
コード例 #5
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)
コード例 #6
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 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)
コード例 #7
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("\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)
コード例 #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("\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)
コード例 #9
0
 def __init__(self, parent, flags=Qt.WindowFlags()):
     QDialog.__init__(self, parent, flags)
     self.setModal(False)
     self.setWindowTitle("Select sources by...")
     lo = QVBoxLayout(self)
     lo.setMargin(10)
     lo.setSpacing(5)
     # select by
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     lo1.setContentsMargins(0, 0, 0, 0)
     #    lab = QLabel("Select:")
     #   lo1.addWidget(lab)
     self.wselby = QComboBox(self)
     lo1.addWidget(self.wselby, 0)
     QObject.connect(self.wselby, SIGNAL("activated(const QString &)"),
                     self._setup_selection_by)
     # under/over
     self.wgele = QComboBox(self)
     lo1.addWidget(self.wgele, 0)
     self.wgele.addItems([">", ">=", "<=", "<", "sum<=", "sum>"])
     QObject.connect(self.wgele, SIGNAL("activated(const QString &)"),
                     self._select_threshold)
     # threshold value
     self.wthreshold = QLineEdit(self)
     QObject.connect(self.wthreshold, SIGNAL("editingFinished()"),
                     self._select_threshold)
     lo1.addWidget(self.wthreshold, 1)
     # min and max label
     self.wminmax = QLabel(self)
     lo.addWidget(self.wminmax)
     # selection slider
     lo1 = QHBoxLayout()
     lo.addLayout(lo1)
     self.wpercent = QSlider(self)
     self.wpercent.setTracking(False)
     QObject.connect(self.wpercent, SIGNAL("valueChanged(int)"),
                     self._select_percentile)
     QObject.connect(self.wpercent, SIGNAL("sliderMoved(int)"),
                     self._select_percentile_threshold)
     self.wpercent.setRange(0, 100)
     self.wpercent.setOrientation(Qt.Horizontal)
     lo1.addWidget(self.wpercent)
     self.wpercent_lbl = QLabel("0%", self)
     self.wpercent_lbl.setMinimumWidth(64)
     lo1.addWidget(self.wpercent_lbl)
     #    # hide button
     #    lo.addSpacing(10)
     #    lo2 = QHBoxLayout()
     #    lo.addLayout(lo2)
     #    lo2.setContentsMargins(0,0,0,0)
     #    hidebtn = QPushButton("Close",self)
     #    hidebtn.setMinimumWidth(128)
     #    QObject.connect(hidebtn,SIGNAL("clicked()"),self.hide)
     #    lo2.addStretch(1)
     #    lo2.addWidget(hidebtn)
     #    lo2.addStretch(1)
     #    self.setMinimumWidth(384)
     self._in_select_threshold = False
     self._sort_index = None
     self.qerrmsg = QErrorMessage(self)
コード例 #10
0
ファイル: export_karma.py プロジェクト: ska-sa/tigger
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)
コード例 #11
0
    def __init__(self, parent, hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self.setWindowIcon(pixmaps.tigger_starface.icon())
        self._currier = PersistentCurrier()
        self.hide()
        # init column constants
        for icol, col in enumerate(self.ViewModelColumns):
            setattr(self, "Column%s" % col.capitalize(), icol)
        # init GUI
        self.setWindowTitle("Tigger")
        # self.setIcon(pixmaps.purr_logo.pm())
        cw = QWidget(self)
        self.setCentralWidget(cw)
        cwlo = QVBoxLayout(cw)
        cwlo.setMargin(5)
        # make splitter
        spl1 = self._splitter1 = QSplitter(Qt.Vertical, cw)
        spl1.setOpaqueResize(False)
        cwlo.addWidget(spl1)
        # Create listview of LSM entries
        self.tw = SkyModelTreeWidget(spl1)
        self.tw.hide()

        # split bottom pane
        spl2 = self._splitter2 = QSplitter(Qt.Horizontal, spl1)
        spl2.setOpaqueResize(False)
        self._skyplot_stack = QWidget(spl2)
        self._skyplot_stack_lo = QVBoxLayout(self._skyplot_stack)
        self._skyplot_stack_lo.setContentsMargins(0, 0, 0, 0)

        # add plot
        self.skyplot = SkyModelPlotter(self._skyplot_stack, self)
        self.skyplot.resize(128, 128)
        self.skyplot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        self._skyplot_stack_lo.addWidget(self.skyplot, 1000)
        self.skyplot.hide()
        QObject.connect(self.skyplot, SIGNAL("imagesChanged"), self._imagesChanged)
        QObject.connect(self.skyplot, SIGNAL("showMessage"), self.showMessage)
        QObject.connect(self.skyplot, SIGNAL("showErrorMessage"), self.showErrorMessage)

        self._grouptab_stack = QWidget(spl2)
        self._grouptab_stack_lo = lo = QVBoxLayout(self._grouptab_stack)
        self._grouptab_stack_lo.setContentsMargins(0, 0, 0, 0)
        # add groupings table
        self.grouptab = ModelGroupsTable(self._grouptab_stack)
        self.grouptab.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        QObject.connect(self, SIGNAL("hasSkyModel"), self.grouptab.setEnabled)
        lo.addWidget(self.grouptab, 1000)
        lo.addStretch(1)
        self.grouptab.hide()

        # add image controls -- parentless for now (setLayout will reparent them anyway)
        self.imgman = ImageManager()
        self.skyplot.setImageManager(self.imgman)
        QObject.connect(self.imgman, SIGNAL("imagesChanged"), self._imagesChanged)
        QObject.connect(self.imgman, SIGNAL("showMessage"), self.showMessage)
        QObject.connect(self.imgman, SIGNAL("showErrorMessage"), self.showErrorMessage)

        # enable status line
        self.statusBar().show()
        # Create and populate main menu
        menubar = self.menuBar()
        # File menu
        file_menu = menubar.addMenu("&File")
        qa_open = file_menu.addAction("&Open model...", self._openFileCallback, Qt.CTRL + Qt.Key_O)
        qa_merge = file_menu.addAction("&Merge in model...", self._mergeFileCallback, Qt.CTRL + Qt.SHIFT + Qt.Key_O)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_merge.setEnabled)
        file_menu.addSeparator()
        qa_save = file_menu.addAction("&Save model", self.saveFile, Qt.CTRL + Qt.Key_S)
        QObject.connect(self, SIGNAL("isUpdated"), qa_save.setEnabled)
        qa_save_as = file_menu.addAction("Save model &as...", self.saveFileAs)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_save_as.setEnabled)
        qa_save_selection_as = file_menu.addAction("Save selection as...", self.saveSelectionAs)
        QObject.connect(self, SIGNAL("hasSelection"), qa_save_selection_as.setEnabled)
        file_menu.addSeparator()
        qa_close = file_menu.addAction("&Close model", self.closeFile, Qt.CTRL + Qt.Key_W)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_close.setEnabled)
        qa_quit = file_menu.addAction("Quit", self.close, Qt.CTRL + Qt.Key_Q)

        # Image menu
        menubar.addMenu(self.imgman.getMenu())
        # Plot menu
        menubar.addMenu(self.skyplot.getMenu())

        # LSM Menu
        em = QMenu("&LSM", self)
        self._qa_em = menubar.addMenu(em)
        self._qa_em.setVisible(False)
        QObject.connect(self, SIGNAL("hasSkyModel"), self._qa_em.setVisible)
        self._column_view_menu = QMenu("&Show columns", self)
        self._qa_cv_menu = em.addMenu(self._column_view_menu)
        em.addSeparator()
        em.addAction("Select &all", self._selectAll, Qt.CTRL + Qt.Key_A)
        em.addAction("&Invert selection", self._selectInvert, Qt.CTRL + Qt.Key_I)
        em.addAction("Select b&y attribute...", self._showSourceSelector, Qt.CTRL + Qt.Key_Y)
        em.addSeparator()
        qa_add_tag = em.addAction("&Tag selection...", self.addTagToSelection, Qt.CTRL + Qt.Key_T)
        QObject.connect(self, SIGNAL("hasSelection"), qa_add_tag.setEnabled)
        qa_del_tag = em.addAction("&Untag selection...", self.removeTagsFromSelection, Qt.CTRL + Qt.Key_U)
        QObject.connect(self, SIGNAL("hasSelection"), qa_del_tag.setEnabled)
        qa_del_sel = em.addAction("&Delete selection", self._deleteSelection)
        QObject.connect(self, SIGNAL("hasSelection"), qa_del_sel.setEnabled)

        # Tools menu
        tm = self._tools_menu = QMenu("&Tools", self)
        self._qa_tm = menubar.addMenu(tm)
        self._qa_tm.setVisible(False)
        QObject.connect(self, SIGNAL("hasSkyModel"), self._qa_tm.setVisible)

        # Help menu
        menubar.addSeparator()
        hm = self._help_menu = menubar.addMenu("&Help")
        hm.addAction("&About...", self._showAboutDialog)
        self._about_dialog = None

        # message handlers
        self.qerrmsg = QErrorMessage(self)

        # set initial state
        self.setAcceptDrops(True)
        self.model = None
        self.filename = None
        self._display_filename = None
        self._open_file_dialog = self._merge_file_dialog = self._save_as_dialog = self._save_sel_as_dialog = self._open_image_dialog = None
        self.emit(SIGNAL("isUpdated"), False)
        self.emit(SIGNAL("hasSkyModel"), False)
        self.emit(SIGNAL("hasSelection"), False)
        self._exiting = False

        # set initial layout
        self._current_layout = None
        self.setLayout(self.LayoutEmpty)
        dprint(1, "init complete")
コード例 #12
0
class MainWindow(QMainWindow):
    ViewModelColumns = ["name", "RA", "Dec", "type", "Iapp", "I", "Q", "U", "V", "RM", "spi", "shape"]

    def __init__(self, parent, hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self.setWindowIcon(pixmaps.tigger_starface.icon())
        self._currier = PersistentCurrier()
        self.hide()
        # init column constants
        for icol, col in enumerate(self.ViewModelColumns):
            setattr(self, "Column%s" % col.capitalize(), icol)
        # init GUI
        self.setWindowTitle("Tigger")
        # self.setIcon(pixmaps.purr_logo.pm())
        cw = QWidget(self)
        self.setCentralWidget(cw)
        cwlo = QVBoxLayout(cw)
        cwlo.setMargin(5)
        # make splitter
        spl1 = self._splitter1 = QSplitter(Qt.Vertical, cw)
        spl1.setOpaqueResize(False)
        cwlo.addWidget(spl1)
        # Create listview of LSM entries
        self.tw = SkyModelTreeWidget(spl1)
        self.tw.hide()

        # split bottom pane
        spl2 = self._splitter2 = QSplitter(Qt.Horizontal, spl1)
        spl2.setOpaqueResize(False)
        self._skyplot_stack = QWidget(spl2)
        self._skyplot_stack_lo = QVBoxLayout(self._skyplot_stack)
        self._skyplot_stack_lo.setContentsMargins(0, 0, 0, 0)

        # add plot
        self.skyplot = SkyModelPlotter(self._skyplot_stack, self)
        self.skyplot.resize(128, 128)
        self.skyplot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        self._skyplot_stack_lo.addWidget(self.skyplot, 1000)
        self.skyplot.hide()
        QObject.connect(self.skyplot, SIGNAL("imagesChanged"), self._imagesChanged)
        QObject.connect(self.skyplot, SIGNAL("showMessage"), self.showMessage)
        QObject.connect(self.skyplot, SIGNAL("showErrorMessage"), self.showErrorMessage)

        self._grouptab_stack = QWidget(spl2)
        self._grouptab_stack_lo = lo = QVBoxLayout(self._grouptab_stack)
        self._grouptab_stack_lo.setContentsMargins(0, 0, 0, 0)
        # add groupings table
        self.grouptab = ModelGroupsTable(self._grouptab_stack)
        self.grouptab.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        QObject.connect(self, SIGNAL("hasSkyModel"), self.grouptab.setEnabled)
        lo.addWidget(self.grouptab, 1000)
        lo.addStretch(1)
        self.grouptab.hide()

        # add image controls -- parentless for now (setLayout will reparent them anyway)
        self.imgman = ImageManager()
        self.skyplot.setImageManager(self.imgman)
        QObject.connect(self.imgman, SIGNAL("imagesChanged"), self._imagesChanged)
        QObject.connect(self.imgman, SIGNAL("showMessage"), self.showMessage)
        QObject.connect(self.imgman, SIGNAL("showErrorMessage"), self.showErrorMessage)

        # enable status line
        self.statusBar().show()
        # Create and populate main menu
        menubar = self.menuBar()
        # File menu
        file_menu = menubar.addMenu("&File")
        qa_open = file_menu.addAction("&Open model...", self._openFileCallback, Qt.CTRL + Qt.Key_O)
        qa_merge = file_menu.addAction("&Merge in model...", self._mergeFileCallback, Qt.CTRL + Qt.SHIFT + Qt.Key_O)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_merge.setEnabled)
        file_menu.addSeparator()
        qa_save = file_menu.addAction("&Save model", self.saveFile, Qt.CTRL + Qt.Key_S)
        QObject.connect(self, SIGNAL("isUpdated"), qa_save.setEnabled)
        qa_save_as = file_menu.addAction("Save model &as...", self.saveFileAs)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_save_as.setEnabled)
        qa_save_selection_as = file_menu.addAction("Save selection as...", self.saveSelectionAs)
        QObject.connect(self, SIGNAL("hasSelection"), qa_save_selection_as.setEnabled)
        file_menu.addSeparator()
        qa_close = file_menu.addAction("&Close model", self.closeFile, Qt.CTRL + Qt.Key_W)
        QObject.connect(self, SIGNAL("hasSkyModel"), qa_close.setEnabled)
        qa_quit = file_menu.addAction("Quit", self.close, Qt.CTRL + Qt.Key_Q)

        # Image menu
        menubar.addMenu(self.imgman.getMenu())
        # Plot menu
        menubar.addMenu(self.skyplot.getMenu())

        # LSM Menu
        em = QMenu("&LSM", self)
        self._qa_em = menubar.addMenu(em)
        self._qa_em.setVisible(False)
        QObject.connect(self, SIGNAL("hasSkyModel"), self._qa_em.setVisible)
        self._column_view_menu = QMenu("&Show columns", self)
        self._qa_cv_menu = em.addMenu(self._column_view_menu)
        em.addSeparator()
        em.addAction("Select &all", self._selectAll, Qt.CTRL + Qt.Key_A)
        em.addAction("&Invert selection", self._selectInvert, Qt.CTRL + Qt.Key_I)
        em.addAction("Select b&y attribute...", self._showSourceSelector, Qt.CTRL + Qt.Key_Y)
        em.addSeparator()
        qa_add_tag = em.addAction("&Tag selection...", self.addTagToSelection, Qt.CTRL + Qt.Key_T)
        QObject.connect(self, SIGNAL("hasSelection"), qa_add_tag.setEnabled)
        qa_del_tag = em.addAction("&Untag selection...", self.removeTagsFromSelection, Qt.CTRL + Qt.Key_U)
        QObject.connect(self, SIGNAL("hasSelection"), qa_del_tag.setEnabled)
        qa_del_sel = em.addAction("&Delete selection", self._deleteSelection)
        QObject.connect(self, SIGNAL("hasSelection"), qa_del_sel.setEnabled)

        # Tools menu
        tm = self._tools_menu = QMenu("&Tools", self)
        self._qa_tm = menubar.addMenu(tm)
        self._qa_tm.setVisible(False)
        QObject.connect(self, SIGNAL("hasSkyModel"), self._qa_tm.setVisible)

        # Help menu
        menubar.addSeparator()
        hm = self._help_menu = menubar.addMenu("&Help")
        hm.addAction("&About...", self._showAboutDialog)
        self._about_dialog = None

        # message handlers
        self.qerrmsg = QErrorMessage(self)

        # set initial state
        self.setAcceptDrops(True)
        self.model = None
        self.filename = None
        self._display_filename = None
        self._open_file_dialog = self._merge_file_dialog = self._save_as_dialog = self._save_sel_as_dialog = self._open_image_dialog = None
        self.emit(SIGNAL("isUpdated"), False)
        self.emit(SIGNAL("hasSkyModel"), False)
        self.emit(SIGNAL("hasSelection"), False)
        self._exiting = False

        # set initial layout
        self._current_layout = None
        self.setLayout(self.LayoutEmpty)
        dprint(1, "init complete")

    # layout identifiers
    LayoutEmpty = "empty"
    LayoutImage = "image"
    LayoutImageModel = "model"
    LayoutSplit = "split"

    def _getFilenamesFromDropEvent(self, event):
        """Checks if drop event is valid (i.e. contains a local URL to a FITS file), and returns list of filenames contained therein."""
        dprint(1, "drop event:", event.mimeData().text())
        if not event.mimeData().hasUrls():
            dprint(1, "drop event: no urls")
            return None
        filenames = []
        for url in event.mimeData().urls():
            name = str(url.toLocalFile())
            dprint(2, "drop event: name is", name)
            if name and Images.isFITS(name):
                filenames.append(name)
        dprint(2, "drop event: filenames are", filenames)
        return filenames

    def dragEnterEvent(self, event):
        if self._getFilenamesFromDropEvent(event):
            dprint(1, "drag-enter accepted")
            event.acceptProposedAction()
        else:
            dprint(1, "drag-enter rejected")

    def dropEvent(self, event):
        filenames = self._getFilenamesFromDropEvent(event)
        dprint(1, "dropping", filenames)
        if filenames:
            event.acceptProposedAction()
            busy = BusyIndicator()
            for name in filenames:
                self.imgman.loadImage(name)

    def saveSizes(self):
        if self._current_layout is not None:
            dprint(1, "saving sizes for layout", self._current_layout)
            # save main window size and splitter dimensions
            sz = self.size()
            Config.set('%s-main-window-width' % self._current_layout, sz.width())
            Config.set('%s-main-window-height' % self._current_layout, sz.height())
            for spl, name in ((self._splitter1, "splitter1"), (self._splitter2, "splitter2")):
                ssz = spl.sizes()
                for i, sz in enumerate(ssz):
                    Config.set('%s-%s-size%d' % (self._current_layout, name, i), sz)

    def loadSizes(self):
        if self._current_layout is not None:
            dprint(1, "loading sizes for layout", self._current_layout)
            # get main window size and splitter dimensions
            w = Config.getint('%s-main-window-width' % self._current_layout, 0)
            h = Config.getint('%s-main-window-height' % self._current_layout, 0)
            dprint(2, "window size is", w, h)
            if not (w and h):
                return None
            self.resize(QSize(w, h))
            for spl, name in (self._splitter1, "splitter1"), (self._splitter2, "splitter2"):
                ssz = [Config.getint('%s-%s-size%d' % (self._current_layout, name, i), -1) for i in (0, 1)]
                dprint(2, "splitter", name, "sizes", ssz)
                if all([sz >= 0 for sz in ssz]):
                    spl.setSizes(ssz)
                else:
                    return None
        return True

    def setLayout(self, layout):
        """Changes the current window layout. Restores sizes etc. from config file."""
        if self._current_layout is layout:
            return
        dprint(1, "switching to layout", layout)
        # save sizes to config file
        self.saveSizes()
        # remove imgman widget from all layouts
        for lo in self._skyplot_stack_lo, self._grouptab_stack_lo:
            if lo.indexOf(self.imgman) >= 0:
                lo.removeWidget(self.imgman)
        # assign it to appropriate parent and parent's layout
        if layout is self.LayoutImage or layout is self.LayoutEmpty:
            lo = self._skyplot_stack_lo
        else:
            lo = self._grouptab_stack_lo
        self.imgman.setParent(lo.parentWidget())
        lo.addWidget(self.imgman, 0)
        # show/hide panels
        if layout is self.LayoutEmpty:
            self.tw.hide()
            self.grouptab.hide()
            self.skyplot.show()
        elif layout is self.LayoutImage:
            self.tw.hide()
            self.grouptab.hide()
            self.skyplot.show()
        elif layout is self.LayoutImageModel:
            self.tw.show()
            self.grouptab.show()
            self.skyplot.show()
        # reload sizes
        self._current_layout = layout
        if not self.loadSizes():
            dprint(1, "no sizes loaded, setting defaults")
            if layout is self.LayoutEmpty:
                self.resize(QSize(512, 256))
            elif layout is self.LayoutImage:
                self.resize(QSize(512, 512))
                self._splitter2.setSizes([512, 0])
            elif layout is self.LayoutImageModel:
                self.resize(QSize(1024, 512))
                self._splitter1.setSizes([256, 256])
                self._splitter2.setSizes([256, 256])

    def enableUpdates(self, enable=True):
        """Enables updates of the child widgets. Usually called after startup is completed (i.e. all data loaded)"""
        self.skyplot.enableUpdates(enable)
        if enable:
            if self.model:
                self.setLayout(self.LayoutImageModel)
            elif self.imgman.getImages():
                self.setLayout(self.LayoutImage)
            else:
                self.setLayout(self.LayoutEmpty)
            self.show()

    def _showAboutDialog(self):
        if not self._about_dialog:
            self._about_dialog = AboutDialog.AboutDialog(self)
        self._about_dialog.show()

    def addTool(self, name, callback):
        """Adds a tool to the Tools menu"""
        self._tools_menu.addAction(name, self._currier.curry(self._callTool, callback))

    def _callTool(self, callback):
        callback(self, self.model)

    def _imagesChanged(self):
        """Called when the set of loaded images has changed"""
        if self.imgman.getImages():
            if self._current_layout is self.LayoutEmpty:
                self.setLayout(self.LayoutImage)
        else:
            if not self.model:
                self.setLayout(self.LayoutEmpty)

    def _selectAll(self):
        if not self.model:
            return
        busy = BusyIndicator()
        for src in self.model.sources:
            src.selected = True
        self.model.emitSelection(self)

    def _selectInvert(self):
        if not self.model:
            return
        busy = BusyIndicator()
        for src in self.model.sources:
            src.selected = not src.selected
        self.model.emitSelection(self)

    def _deleteSelection(self):
        unselected = [src for src in self.model.sources if not src.selected]
        nsel = len(self.model.sources) - len(unselected)
        if QMessageBox.question(self, "Delete selection", """<P>Really deleted %d selected source(s)?
        %d unselected sources will remain in the model.</P>""" % (nsel, len(unselected)),
                                QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Cancel) != QMessageBox.Ok:
            return
        self.model.setSources(unselected)
        self.showMessage("""Deleted %d sources""" % nsel)
        self.model.emitUpdate(SkyModel.UpdateAll, origin=self)

    def _showSourceSelector(self):
        TigGUI.Tools.source_selector.show_source_selector(self, self.model)

    def _updateModelSelection(self, num, origin=None):
        """Called when the model selection has been updated."""
        self.emit(SIGNAL("hasSelection"), bool(num))

    _formats = [f[1] for f in Tigger.Models.Formats.listFormatsFull()]

    _load_file_types = [(doc, ["*" + ext for ext in extensions], load) for load, save, doc, extensions in _formats if
                        load]
    _save_file_types = [(doc, ["*" + ext for ext in extensions], save) for load, save, doc, extensions in _formats if
                        save]

    def showMessage(self, msg, time=3000):
        self.statusBar().showMessage(msg, 3000)

    def showErrorMessage(self, msg, time=3000):
        self.qerrmsg.showMessage(msg)

    def loadImage(self, filename):
        return self.imgman.loadImage(filename)

    def setModel(self, model):
        self.emit(SIGNAL("modelChanged"), model)
        if model:
            self.model = model
            self.emit(SIGNAL("hasSkyModel"), True)
            self.emit(SIGNAL("hasSelection"), False)
            self.emit(SIGNAL("isUpdated"), False)
            self.model.enableSignals()
            self.model.connect("updated", self._indicateModelUpdated)
            self.model.connect("selected", self._updateModelSelection)
            # pass to children
            self.tw.setModel(self.model)
            self.grouptab.setModel(self.model)
            self.skyplot.setModel(self.model)
            # add items to View menu
            self._column_view_menu.clear()
            self.tw.addColumnViewActionsTo(self._column_view_menu)
        else:
            self.model = None
            self.setWindowTitle("Tigger")
            self.emit(SIGNAL("hasSelection"), False)
            self.emit(SIGNAL("isUpdated"), False)
            self.emit(SIGNAL("hasSkyModel"), False)
            self.tw.clear()
            self.grouptab.clear()
            self.skyplot.setModel(None)

    def _openFileCallback(self):
        if not self._open_file_dialog:
            filters = ";;".join(
                ["%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._load_file_types])
            dialog = self._open_file_dialog = QFileDialog(self, "Open sky model", ".", filters)
            dialog.setFileMode(QFileDialog.ExistingFile)
            dialog.setModal(True)
            QObject.connect(dialog, SIGNAL("filesSelected(const QStringList &)"), self.openFile)
        self._open_file_dialog.exec_()
        return

    def _mergeFileCallback(self):
        if not self._merge_file_dialog:
            filters = ";;".join(
                ["%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._load_file_types])
            dialog = self._merge_file_dialog = QFileDialog(self, "Merge in sky model", ".", filters)
            dialog.setFileMode(QFileDialog.ExistingFile)
            dialog.setModal(True)
            QObject.connect(dialog, SIGNAL("filesSelected(const QStringList &)"),
                            self._currier.curry(self.openFile, merge=True))
        self._merge_file_dialog.exec_()
        return

    def openFile(self, filename=None, format=None, merge=False, show=True):
        # check that we can close existing model
        if not merge and not self._canCloseExistingModel():
            return False
        if isinstance(filename, QStringList):
            filename = filename[0]
        filename = str(filename)
        # try to determine the file type
        filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat(filename, format)
        if import_func is None:
            self.showErrorMessage("""Error loading model file %s: unknown file format""" % filename)
            return
        # try to load the specified file
        busy = BusyIndicator()
        self.showMessage("""Reading %s file %s""" % (filetype, filename), 3000)
        QApplication.flush()
        try:
            model = import_func(filename)
            model.setFilename(filename)
        except:
            busy = None
            self.showErrorMessage("""Error loading '%s' file %s: %s""" % (filetype, filename, str(sys.exc_info()[1])))
            return
        # set the layout
        if show:
            self.setLayout(self.LayoutImageModel)
        # add to content
        if merge and self.model:
            self.model.addSources(model.sources)
            self.showMessage("""Merged in %d sources from '%s' file %s""" % (len(model.sources), filetype, filename),
                             3000)
            self.model.emitUpdate(SkyModel.UpdateAll)
        else:
            self.showMessage("""Loaded %d sources from '%s' file %s""" % (len(model.sources), filetype, filename),
                             3000)
            self._display_filename = os.path.basename(filename)
            self.setModel(model)
            self._indicateModelUpdated(updated=False)
            # only set self.filename if an export function is available for this format. Otherwise set it to None, so that trying to save
            # the file results in a save-as operation (so that we don't save to a file in an unsupported format).
            self.filename = filename if export_func else None

    def closeEvent(self, event):
        dprint(1, "closing")
        self._exiting = True
        self.saveSizes()
        if not self.closeFile():
            self._exiting = False
            event.ignore()
            return
        self.skyplot.close()
        self.imgman.close()
        self.emit(SIGNAL("closing"))
        dprint(1, "invoking os._exit(0)")
        os._exit(0)
        QMainWindow.closeEvent(self, event)

    def _canCloseExistingModel(self):
        # save model if modified
        if self.model and self._model_updated:
            res = QMessageBox.question(self, "Closing sky model",
                                       "<P>Model has been modified, would you like to save the changes?</P>",
                                       QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save)
            if res == QMessageBox.Cancel:
                return False
            elif res == QMessageBox.Save:
                if not self.saveFile(confirm=False, overwrite=True):
                    return False
        # unload model images, unless we are already exiting anyway
        if not self._exiting:
            self.imgman.unloadModelImages()
        return True

    def closeFile(self):
        if not self._canCloseExistingModel():
            return False
        # close model
        self._display_filename = None
        self.setModel(None)
        # set the layout
        self.setLayout(self.LayoutImage if self.imgman.getTopImage() else self.LayoutEmpty)
        return True

    def saveFile(self, filename=None, confirm=False, overwrite=True, non_native=False):
        """Saves file using the specified 'filename'. If filename is None, uses current filename, if
        that is not set, goes to saveFileAs() to open dialog and get a filename.
        If overwrite=False, will ask for confirmation before overwriting an existing file.
        If non_native=False, will ask for confirmation before exporting in non-native format.
        If confirm=True, will ask for confirmation regardless.
        Returns True if saving succeeded, False on error (or if cancelled by user).
        """
        if isinstance(filename, QStringList):
            filename = filename[0]
        filename = (filename and str(filename)) or self.filename
        if filename is None:
            return self.saveFileAs()
        else:
            warning = ''
            # try to determine the file type
            filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat(filename, None)
            if export_func is None:
                self.showErrorMessage("""Error saving model file %s: unsupported output format""" % filename)
                return
            if os.path.exists(filename) and not overwrite:
                warning += "<P>The file already exists and will be overwritten.</P>"
            if filetype != 'Tigger' and not non_native:
                warning += """<P>Please note that you are exporting the model using the external format '%s'.
              Source types, tags and other model features not supported by this
              format will be omitted during the export.</P>""" % filetype
            # get confirmation
            if confirm or warning:
                dialog = QMessageBox.warning if warning else QMessageBox.question
                if dialog(self, "Saving sky model", "<P>Save model to %s?</P>%s" % (filename, warning),
                          QMessageBox.Save | QMessageBox.Cancel, QMessageBox.Save) != QMessageBox.Save:
                    return False
            busy = BusyIndicator()
            try:
                export_func(self.model, filename)
                self.model.setFilename(filename)
            except:
                busy = None
                self.showErrorMessage("""Error saving model file %s: %s""" % (filename, str(sys.exc_info()[1])))
                return False
            self.showMessage("""Saved model to file %s""" % filename, 3000)
            self._display_filename = os.path.basename(filename)
            self._indicateModelUpdated(updated=False)
            self.filename = filename
            return True

    def saveFileAs(self, filename=None):
        """Saves file using the specified 'filename'. If filename is None, opens dialog to get a filename.
        Returns True if saving succeeded, False on error (or if cancelled by user).
        """
        if filename is None:
            if not self._save_as_dialog:
                filters = ";;".join(
                    ["%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._save_file_types])
                dialog = self._save_as_dialog = QFileDialog(self, "Save sky model", ".", filters)
                dialog.setDefaultSuffix(ModelHTML.DefaultExtension)
                dialog.setFileMode(QFileDialog.AnyFile)
                dialog.setAcceptMode(QFileDialog.AcceptSave)
                dialog.setConfirmOverwrite(False)
                dialog.setModal(True)
                QObject.connect(dialog, SIGNAL("filesSelected(const QStringList &)"), self.saveFileAs)
            return self._save_as_dialog.exec_() == QDialog.Accepted
        # filename supplied, so save
        return self.saveFile(filename, confirm=False)

    def saveSelectionAs(self, filename=None, force=False):
        if not self.model:
            return
        if filename is None:
            if not self._save_sel_as_dialog:
                filters = ";;".join(
                    ["%s (%s)" % (name, " ".join(patterns)) for name, patterns, func in self._save_file_types])
                dialog = self._save_sel_as_dialog = QFileDialog(self, "Save sky model", ".", filters)
                dialog.setDefaultSuffix(ModelHTML.DefaultExtension)
                dialog.setFileMode(QFileDialog.AnyFile)
                dialog.setAcceptMode(QFileDialog.AcceptSave)
                dialog.setConfirmOverwrite(True)
                dialog.setModal(True)
                QObject.connect(dialog, SIGNAL("filesSelected(const QStringList &)"), self.saveSelectionAs)
            return self._save_sel_as_dialog.exec_() == QDialog.Accepted
        # save selection
        if isinstance(filename, QStringList):
            filename = filename[0]
        filename = str(filename)
        selmodel = self.model.copy()
        sources = [src for src in self.model.sources if src.selected]
        if not sources:
            self.showErrorMessage("""You have not selected any sources to save.""")
            return
        # try to determine the file type
        filetype, import_func, export_func, doc = Tigger.Models.Formats.resolveFormat(filename, None)
        if export_func is None:
            self.showErrorMessage("""Error saving model file %s: unsupported output format""" % filename)
            return
        busy = BusyIndicator()
        try:
            export_func(self.model, filename, sources=sources)
        except:
            busy = None
            self.showErrorMessage(
                """Error saving selection to model file %s: %s""" % (filename, str(sys.exc_info()[1])))
            return False
        self.showMessage("""Wrote %d selected source%s to file %s""" % (
        len(selmodel.sources), "" if len(selmodel.sources) == 1 else "s", filename), 3000)
        pass

    def addTagToSelection(self):
        if not hasattr(self, '_add_tag_dialog'):
            self._add_tag_dialog = Widgets.AddTagDialog(self, modal=True)
        self._add_tag_dialog.setTags(self.model.tagnames)
        self._add_tag_dialog.setValue(True)
        if self._add_tag_dialog.exec_() != QDialog.Accepted:
            return
        tagname, value = self._add_tag_dialog.getTag()
        if tagname is None or value is None:
            return None
        dprint(1, "tagging selected sources with", tagname, value)
        # tag selected sources
        for src in self.model.sources:
            if src.selected:
                src.setAttribute(tagname, value)
        # If tag is not new, set a UpdateSelectionOnly flag on the signal
        dprint(1, "adding tag to model")
        self.model.addTag(tagname)
        dprint(1, "recomputing totals")
        self.model.getTagGrouping(tagname).computeTotal(self.model.sources)
        dprint(1, "emitting update signal")
        what = SkyModel.UpdateSourceContent + SkyModel.UpdateTags + SkyModel.UpdateSelectionOnly
        self.model.emitUpdate(what, origin=self)

    def removeTagsFromSelection(self):
        if not hasattr(self, '_remove_tag_dialog'):
            self._remove_tag_dialog = Widgets.SelectTagsDialog(self, modal=True, caption="Remove Tags",
                                                               ok_button="Remove")
        # get set of all tags in selected sources
        tags = set()
        for src in self.model.sources:
            if src.selected:
                tags.update(src.getTagNames())
        if not tags:
            return
        tags = list(tags)
        tags.sort()
        # show dialog
        self._remove_tag_dialog.setTags(tags)
        if self._remove_tag_dialog.exec_() != QDialog.Accepted:
            return
        tags = self._remove_tag_dialog.getSelectedTags()
        if not tags:
            return
        # ask for confirmation
        plural = (len(tags) > 1 and "s") or ""
        if QMessageBox.question(self, "Removing tags", "<P>Really remove the tag%s '%s' from selected sources?</P>" % (
        plural, "', '".join(tags)),
                                QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) != QMessageBox.Yes:
            return
        # remove the tags
        for src in self.model.sources:
            if src.selected:
                for tag in tags:
                    src.removeAttribute(tag)
        # update model
        self.model.scanTags()
        self.model.initGroupings()
        # emit signal
        what = SkyModel.UpdateSourceContent + SkyModel.UpdateTags + SkyModel.UpdateSelectionOnly
        self.model.emitUpdate(what, origin=self)

    def _indicateModelUpdated(self, what=None, origin=None, updated=True):
        """Marks model as updated."""
        self._model_updated = updated
        self.emit(SIGNAL("isUpdated"), updated)
        if self.model:
            self.setWindowTitle(
                "Tigger - %s%s" % ((self._display_filename or "(unnamed)", " (modified)" if updated else "")))