Пример #1
0
 def selectSources(self, predicate):
     """Selects sources according to predicate(src)"""
     busy = BusyIndicator()
     for src in self.model.sources:
         src.selected = predicate(src)
     self.model.emitSelection(origin=self)
     busy = None
Пример #2
0
 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;
Пример #3
0
 def execute_tdl_job(self, _tdlmod, ns, func, name, job_id):
     """executes a predefined TDL job given by func"""
     self._tdlexec_menu.hide()
     try:
         # log job
         TDLOptions.dump_log(
             "running TDL job '%s' (%s)" %
             (name, ("job id '%s'" % job_id if job_id else "no job id")))
         busy = BusyIndicator()
         func(meqds.mqs(), self)
         # no errors, so clear error list, if any
         self.clear_errors()
         self.show_message("TDL job '" + name + "' executed.",
                           transient=True)
         busy = None
     except:
         (etype, exc, tb) = sys.exc_info()
         _dprint(0, 'exception running TDL job', func.__name__)
         traceback.print_exception(etype, exc, tb)
         # use TDL add_error() to process the error, since this automatically
         # adds location information. However, we want to remove ourselves
         # from the stack traceback first
         tb = traceback.extract_tb(tb)
         # pop everything leading up to our filename
         while tb[0][0] != _MODULE_FILENAME:
             tb.pop(0)
         # pop frame with our filename, this should leave only TDL-code frames
         tb.pop(0)
         ns.AddError(exc, tb, error_limit=None)
         msg = "TDL job '" + name + "' failed"
         self._error_window.set_errors(ns.GetErrors(), message=msg)
         self.emit(PYSIGNAL("showEditor()"), self)
         busy = None
Пример #4
0
 def _refreshModel(self, what=SkyModel.UpdateAll, origin=None):
     if origin is self or not what & (SkyModel.UpdateSourceList
                                      | SkyModel.UpdateSourceContent):
         return
     # if only selection was changed, take shortcut
     if what & SkyModel.UpdateSelectionOnly:
         dprint(2, "model update -- selection only")
         return self._refreshSelectedItems(origin)
     busy = BusyIndicator()
     # else repopulate widget completely
     dprint(2, "model update -- complete")
     Kittens.widgets.ClickableTreeWidget.clear(self)
     dprint(2, "creating model items")
     items = [SkyModelTreeWidgetItem(src) for src in self.model.sources]
     self._itemdict = dict(
         zip([src.name for src in self.model.sources], items))
     dprint(2, "adding to tree widget")
     self.addTopLevelItems(items)
     self.header().updateGeometry()
     # show/hide columns based on tag availability
     self._enableColumn(ColumnIapp, 'Iapp' in self.model.tagnames)
     self._enableColumn(ColumnR, 'r' in self.model.tagnames)
     dprint(2, "re-sorting")
     self.sortItems(('Iapp' in self.model.tagnames and ColumnIapp)
                    or ColumnI, Qt.DescendingOrder)
     busy = None
Пример #5
0
 def raiseImage (self,imagecon):
   # reshuffle image stack, if more than one image image
   if len(self._imagecons) > 1:
     busy = BusyIndicator();
     # reshuffle image stack
     self._imagecons.remove(imagecon);
     self._imagecons.insert(0,imagecon);
     # notify imagecons
     for i,ic in enumerate(self._imagecons):
       label = "%d"%(i+1) if i else "<B>1</B>";
       ic.setZ(self._z0-i*10,top=not i,depthlabel=label,can_raise=True);
     # adjust visibility
     for j,ic in enumerate(self._imagecons):
       ic.setImageVisible(not j or bool(self._qa_plot_all.isChecked()));
     # issue replot signal
     self.emit(SIGNAL("imageRaised"));
     self.fastReplot();
   # else simply update labels
   else:
     self._imagecons[0].setZ(self._z0,top=True,depthlabel=None,can_raise=False);
     self._imagecons[0].setImageVisible(True);
   # update slice menus
   img = imagecon.image;
   axes = imagecon.renderControl().slicedAxes();
   for i,(next,prev) in enumerate(self._qa_slices):
     next.setVisible(False);
     prev.setVisible(False);
     if i < len(axes):
       iaxis,name,labels = axes[i];
       next.setVisible(True);
       prev.setVisible(True);
       next.setText("Show next slice along %s axis"%name);
       prev.setText("Show previous slice along %s axis"%name);
   # emit signasl
   self.emit(SIGNAL("imageRaised"),img);
Пример #6
0
 def _select_percentile_threshold(self, percent, do_select=False):
     # ignore if no sort index set up, or if _select_threshold() is being called
     if self._sort_index is None or self._in_select_threshold:
         return
     dprint(1, "select_precentile_threshold", percent)
     busy = BusyIndicator()
     # number of objects to select
     nsrc = len(self._sort_index)
     nsel = int(math.ceil(nsrc * float(percent) / 100))
     # get comparison operator
     opstr = str(self.wgele.currentText())
     op, select = self.Operators[opstr]
     # select head or tail of list, depending on direction of operator
     if select:
         thr = self._sort_index[min(nsel, nsrc - 1)]
         slc1 = slice(0, nsel)
         slc2 = slice(nsel, None)
     else:
         thr = self._sort_index[-min(nsel + 1, nsrc)]
         slc1 = slice(nsrc - nsel, None)
         slc2 = slice(0, nsrc - nsel)
     if do_select:
         for val, src, cumsum in self._sort_index[slc1]:
             src.selected = True
         for val, src, cumsum in self._sort_index[slc2]:
             src.selected = False
         self.model.emitSelection(self)
     self.wpercent_lbl.setText("%3d%%" % percent)
     self.wthreshold.setText(
         "%g" % (thr[2] if opstr.startswith("sum") else thr[0]))
     return nsel
Пример #7
0
 def setColorMapNumber(self, index, write_config=True):
     busy = BusyIndicator()
     self._current_cmap_index = index
     cmap = self._cmap_list[index]
     self.image.setColorMap(cmap)
     self.emit(SIGNAL("colorMapChanged"), cmap)
     if self._config and write_config:
         self._config.set("colour-map-number", index)
Пример #8
0
 def _refreshSelectedItems(self, origin=None):
     busy = BusyIndicator()
     dprint(3, "refreshing selected items")
     for item in self.iterator():
         if item.isSelected():
             dprint(4, "resetting item", item._src.name)
             item.setSource(item._src)
     dprint(3, "refreshing selected items done")
     busy = None
Пример #9
0
 def setIntensityMapNumber(self, index, write_config=True):
     busy = BusyIndicator()
     self._current_imap_index = index
     imap = self._imap_list[index][1]
     imap.setDataSubset(self._displaydata, self._displaydata_minmax)
     imap.setDataRange(*self._displayrange)
     self.image.setIntensityMap(imap)
     self.emit(SIGNAL("intensityMapChanged"), imap, index)
     if self._config and write_config:
         self._config.set("intensity-map-number", index)
Пример #10
0
 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,err:
     busy = None;
     self.qerrmsg.showMessage("Error fitting PSF file %s: %s"%(filename,str(err)));
     return;
Пример #11
0
 def _displayAllImages (self,enabled):
   busy = BusyIndicator();
   if enabled:
     for ic in self._imagecons:
       ic.setImageVisible(True);
   else:
     self._imagecons[0].setImageVisible(True);
     for ic in self._imagecons[1:]:
       ic.setImageVisible(False);
   self.replot();
Пример #12
0
 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
Пример #13
0
 def loadImage (self,filename=None,duplicate=True,to_top=True,model=None):
   """Loads image. Returns ImageControlBar object.
   If image is already loaded: returns old ICB if duplicate=False (raises to top if to_top=True),
   or else makes a new control bar.
   If model is set to a source name, marks the image as associated with a model source. These can be unloaded en masse by calling
   unloadModelImages().
   """;
   if filename is None:
       if not self._load_image_dialog:
           dialog = self._load_image_dialog = QFileDialog(self,"Load FITS image",".","FITS images (%s);;All files (*)"%(" ".join(["*"+ext for ext in FITS_ExtensionList])));
           dialog.setFileMode(QFileDialog.ExistingFile);
           dialog.setModal(True);
           QObject.connect(dialog,SIGNAL("filesSelected(const QStringList &)"),self.loadImage);
       self._load_image_dialog.exec_();
       return None;
   if isinstance(filename,QStringList):
     filename = filename[0];
   filename = str(filename);
   # report error if image does not exist
   if not os.path.exists(filename):
     self.showErrorMessage("""FITS image %s does not exist."""%filename);
     return None;
   # see if image is already loaded
   if not duplicate:
     for ic in self._imagecons:
       if ic.getFilename() and os.path.samefile(filename,ic.getFilename()):
         if to_top:
           self.raiseImage(ic);
         if model:
           self._model_imagecons.add(id(ic));
         return ic;
   # load the FITS image
   busy = BusyIndicator();
   dprint(2,"reading FITS image",filename);
   self.showMessage("""Reading FITS image %s"""%filename,3000);
   QApplication.flush();
   try:
     image = SkyImage.FITSImagePlotItem(str(filename));
   except KeyboardInterrupt:
     raise;
   except:
       busy = None;
       traceback.print_exc();
       self.showErrorMessage("""<P>Error loading FITS image %s: %s. This may be due to a bug in Tigger; if the FITS file loads fine in another viewer,
         please send the FITS file, along with a copy of any error messages from the text console, to [email protected].</P>"""%(filename,str(sys.exc_info()[1])));
       return None;
   # create control bar, add to widget stack
   ic = self._createImageController(image,"model source '%s'"%model if model else filename,model or image.name,model=model);
   self.showMessage("""Loaded FITS image %s"""%filename,3000);
   dprint(2,"image loaded");
   return ic;
Пример #14
0
 def setIntensityMapLogCycles(self,
                              cycles,
                              notify_image=True,
                              write_config=True):
     busy = BusyIndicator()
     imap = self.currentIntensityMap()
     if isinstance(imap, Colormaps.LogIntensityMap):
         imap.log_cycles = cycles
         if notify_image:
             self.image.setIntensityMap()
         self.emit(SIGNAL("intensityMapChanged"), imap,
                   self._current_imap_index)
     if self._config and write_config:
         self._config.set("intensity-log-cycles", cycles)
Пример #15
0
 def setDisplayRange(self,
                     dmin,
                     dmax,
                     notify_image=True,
                     write_config=True):
     if dmax < dmin:
         dmin, dmax = dmax, dmin
     if (dmin, dmax) != self._displayrange:
         self._displayrange = dmin, dmax
         self.image.intensityMap().setDataRange(dmin, dmax)
         if notify_image:
             busy = BusyIndicator()
             self.image.setIntensityMap(emit=True)
         self.emit(SIGNAL("displayRangeChanged"), dmin, dmax)
         if self._config and write_config:
             self._config.set("range-min", dmin, save=False)
             self._config.set("range-max", dmax)
Пример #16
0
 def _updateSlice(self, write_config=True):
     """Common internal method called to finalize changes to _current_slice"""
     busy = BusyIndicator()
     dprint(2, "_updateSlice", self._current_slice,
            time.time() % 60)
     indices = tuple(self._current_slice)
     self.image.selectSlice(*indices)
     dprint(2, "image slice selected",
            time.time() % 60)
     img = self.image.image()
     self._slicerange = self._sliceranges.get(indices)
     if self._slicerange is None:
         self._slicerange = self._sliceranges[
             indices] = self.image.imageMinMax()[:2]
     dprint(2, "min/max updated",
            time.time() % 60)
     self.setSliceSubset(set_display_range=False)
     if write_config and self._config:
         self._config.set("slice", " ".join(map(str, indices)))
Пример #17
0
 def _saveImage(self):
     filename = QFileDialog.getSaveFileName(
         self, "Save FITS file", self._save_dir,
         "FITS files(*.fits *.FITS *fts *FTS)")
     filename = str(filename)
     if not filename:
         return
     busy = BusyIndicator()
     self._imgman.showMessage("""Writing FITS image %s""" % filename, 3000)
     QApplication.flush()
     try:
         self.image.save(filename)
     except Exception, exc:
         busy = None
         traceback.print_exc()
         self._imgman.showErrorMessage(
             """Error writing FITS image %s: %s""" %
             (filename, str(sys.exc_info()[1])))
         return None
Пример #18
0
 def compile_content(self):
     # import content first, and return if failed
     if not self.import_content(force=True):
         return None
     _dprint(1, self._filename, "compiling forest")
     # clear predefined functions
     self._tb_tdlexec.hide()
     # try the compilation
     busy = BusyIndicator()
     try:
         (_tdlmod,ns,msg) = \
    TDL.Compile.run_forest_definition(
         meqds.mqs(),self._filename,self._tdlmod,self._tdltext,
         parent=self,wait=False)
     # catch compilation errors
     except TDL.CumulativeError, value:
         _dprint(0, "caught cumulative error, length", len(value.args))
         self._error_window.set_errors(value.args,
                                       message="TDL import failed")
         busy = None
         return None
Пример #19
0
 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 = filter(lambda src:src.selected,sources);
   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,err:
     busy = None;
     self.qerrmsg.showMessage("Error reading FITS file %s: %s"%(infile,str(err)));
     return;
Пример #20
0
 def _select_threshold(self, *dum):
     dprint(1, "select_threshold", dum)
     self._in_select_threshold = True
     busy = BusyIndicator()
     try:
         # get threshold, ignore if not set
         threshold = str(self.wthreshold.text())
         if not threshold:
             self._reset_percentile()
             return
         # try to parse threshold, ignore if invalid
         try:
             threshold = float(threshold)
         except:
             self._reset_percentile()
             return
         # get comparison operator
         op, select = self.Operators[str(self.wgele.currentText())]
         # apply to initial segment (that matches operator)
         for num, entry in enumerate(self._sort_index):
             if not op(entry, threshold):
                 break
             entry[1].selected = select
         else:
             num = len(self._sort_index)
         # apply to remaining segment
         for val, src, cumsum in self._sort_index[num:]:
             src.selected = not select
         # set percentile
         percent = round(float(num * 100) / len(self._sort_index))
         if not select:
             percent = 100 - percent
         self.wpercent.setValue(percent)
         self.wpercent_lbl.setText("%3d%%" % percent)
         # emit signal
         self.model.emitSelection(self)
     finally:
         self._in_select_threshold = False
         busy = None
Пример #21
0
 def _exportImageToPNG(self, filename=None):
     if not filename:
         if not self._export_png_dialog:
             dialog = self._export_png_dialog = QFileDialog(
                 self, "Export image to PNG", ".", "*.png")
             dialog.setDefaultSuffix("png")
             dialog.setFileMode(QFileDialog.AnyFile)
             dialog.setAcceptMode(QFileDialog.AcceptSave)
             dialog.setModal(True)
             QObject.connect(dialog,
                             SIGNAL("filesSelected(const QStringList &)"),
                             self._exportImageToPNG)
         return self._export_png_dialog.exec_() == QDialog.Accepted
     busy = BusyIndicator()
     if isinstance(filename, QStringList):
         filename = filename[0]
     filename = str(filename)
     # make QPixmap
     nx, ny = self.image.imageDims()
     (l0, l1), (m0, m1) = self.image.getExtents()
     pixmap = QPixmap(nx, ny)
     painter = QPainter(pixmap)
     # use QwtPlot implementation of draw canvas, since we want to avoid caching
     xmap = QwtScaleMap()
     xmap.setPaintInterval(0, nx)
     xmap.setScaleInterval(l1, l0)
     ymap = QwtScaleMap()
     ymap.setPaintInterval(ny, 0)
     ymap.setScaleInterval(m0, m1)
     self.image.draw(painter, xmap, ymap, pixmap.rect())
     painter.end()
     # save to file
     try:
         pixmap.save(filename, "PNG")
     except Exception, exc:
         self.emit(SIGNAL("showErrorMessage"),
                   "Error writing %s: %s" % (filename, str(exc)))
         return
Пример #22
0
 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
Пример #23
0
class ImageManager (QWidget):
  """An ImageManager manages a stack of images (and associated ImageControllers)"""
  def __init__ (self,*args):
    QWidget.__init__(self,*args);
    # init layout
    self._lo = QVBoxLayout(self);
    self._lo.setContentsMargins(0,0,0,0);
    self._lo.setSpacing(0);
    # init internal state
    self._currier = PersistentCurrier();
    self._z0 = 0;  # z-depth of first image, the rest count down from it
    self._updating_imap = False;
    self._locked_display_range = False;
    self._imagecons = [];
    self._imagecon_loadorder = [];
    self._center_image = None;
    self._plot = None;
    self._border_pen = None;
    self._drawing_key = None;
    self._load_image_dialog = None;
    self._model_imagecons = set();
    # init menu and standard actions
    self._menu = QMenu("&Image",self);
    qag = QActionGroup(self);
    # exclusive controls for plotting topmost or all images
    self._qa_plot_top = qag.addAction("Display topmost image only");
    self._qa_plot_all   = qag.addAction("Display all images");
    self._qa_plot_top.setCheckable(True);
    self._qa_plot_all.setCheckable(True);
    self._qa_plot_top.setChecked(True);
    QObject.connect(self._qa_plot_all,SIGNAL("toggled(bool)"),self._displayAllImages);
    self._closing = False;
    
    self._qa_load_clipboard = None;
    self._clipboard_mode = QClipboard.Clipboard;
    QObject.connect(QApplication.clipboard(),SIGNAL("changed(QClipboard::Mode)"),self._checkClipboardPath);
    # populate the menu
    self._repopulateMenu();

  def close (self):
    dprint(1,"closing Manager");
    self._closing = True;
    for ic in self._imagecons:
      ic.close();

  def loadImage (self,filename=None,duplicate=True,to_top=True,model=None):
    """Loads image. Returns ImageControlBar object.
    If image is already loaded: returns old ICB if duplicate=False (raises to top if to_top=True),
    or else makes a new control bar.
    If model is set to a source name, marks the image as associated with a model source. These can be unloaded en masse by calling
    unloadModelImages().
    """;
    if filename is None:
        if not self._load_image_dialog:
            dialog = self._load_image_dialog = QFileDialog(self,"Load FITS image",".","FITS images (%s);;All files (*)"%(" ".join(["*"+ext for ext in FITS_ExtensionList])));
            dialog.setFileMode(QFileDialog.ExistingFile);
            dialog.setModal(True);
            QObject.connect(dialog,SIGNAL("filesSelected(const QStringList &)"),self.loadImage);
        self._load_image_dialog.exec_();
        return None;
    if isinstance(filename,QStringList):
      filename = filename[0];
    filename = str(filename);
    # report error if image does not exist
    if not os.path.exists(filename):
      self.showErrorMessage("""FITS image %s does not exist."""%filename);
      return None;
    # see if image is already loaded
    if not duplicate:
      for ic in self._imagecons:
        if ic.getFilename() and os.path.samefile(filename,ic.getFilename()):
          if to_top:
            self.raiseImage(ic);
          if model:
            self._model_imagecons.add(id(ic));
          return ic;
    # load the FITS image
    busy = BusyIndicator();
    dprint(2,"reading FITS image",filename);
    self.showMessage("""Reading FITS image %s"""%filename,3000);
    QApplication.flush();
    try:
      image = SkyImage.FITSImagePlotItem(str(filename));
    except KeyboardInterrupt:
      raise;
    except:
        busy = None;
        traceback.print_exc();
        self.showErrorMessage("""<P>Error loading FITS image %s: %s. This may be due to a bug in Tigger; if the FITS file loads fine in another viewer,
          please send the FITS file, along with a copy of any error messages from the text console, to [email protected].</P>"""%(filename,str(sys.exc_info()[1])));
        return None;
    # create control bar, add to widget stack
    ic = self._createImageController(image,"model source '%s'"%model if model else filename,model or image.name,model=model);
    self.showMessage("""Loaded FITS image %s"""%filename,3000);
    dprint(2,"image loaded");
    return ic;

  def showMessage (self,message,time=None):
    self.emit(SIGNAL("showMessage"),message,time);

  def showErrorMessage (self,message,time=None):
    self.emit(SIGNAL("showErrorMessage"),message,time);

  def setZ0 (self,z0):
    self._z0 = z0;
    if self._imagecons:
      self.raiseImage(self._imagecons[0]);

  def enableImageBorders (self,border_pen,label_color,label_bg_brush):
    self._border_pen,self._label_color,self._label_bg_brush = \
      border_pen,label_color,label_bg_brush;

  def lockAllDisplayRanges (self,rc0):
    """Locks all display ranges, and sets the intensity from rc0""";
    if not self._updating_imap:
      self._updating_imap = True;
      rc0.lockDisplayRange();
      try:
        for ic in self._imagecons:
          rc1 = ic.renderControl();
          if rc1 is not rc0:
            rc1.setDisplayRange(*rc0.displayRange());
            rc1.lockDisplayRange();
      finally:
        self._updating_imap = False;

  def unlockAllDisplayRanges (self):
    """Unlocks all display range.""";
    for ic in self._imagecons:
      ic.renderControl().lockDisplayRange(False);

  def _lockDisplayRange (self,rc0,lock):
    """Locks or unlocks the display range of a specific controller."""
    if lock and not self._updating_imap:
      self._updating_imap = True;
      try:
        # if something is already locked, copy display range from it
        for ic in self._imagecons:
          rc1 = ic.renderControl();
          if rc1 is not rc0 and rc1.isDisplayRangeLocked():
            rc0.setDisplayRange(*rc1.displayRange());
      finally:
        self._updating_imap = False;


  def _updateDisplayRange (self,rc,dmin,dmax):
    """This is called whenever one of the images (or rather, its associated RenderControl object) changes its display range.""";
    if not rc.isDisplayRangeLocked():
      return;
    # If the display range is locked, propagate it to all images.
    # but don't do it if we're already propagating (otherwise we may get called in an infinte loop)
    if not self._updating_imap:
      self._updating_imap = True;
      try:
        for ic in self._imagecons:
          rc1 = ic.renderControl();
          if rc1 is not rc and rc1.isDisplayRangeLocked():
            rc1.setDisplayRange(dmin,dmax);
      finally:
        self._updating_imap = False;

  def getImages (self):
    return [ ic.image for ic in self._imagecons ];

  def getTopImage (self):
    return (self._imagecons or None) and self._imagecons[0].image;

  def cycleImages (self):
    index = self._imagecon_loadorder.index(self._imagecons[0]);
    index = (index+1)%len(self._imagecon_loadorder);
    self.raiseImage(self._imagecon_loadorder[index]);

  def blinkImages (self):
    if len(self._imagecons)>1:
      self.raiseImage(self._imagecons[1]);

  def incrementSlice (self,extra_axis,incr):
    if self._imagecons:
      rc = self._imagecons[0].renderControl();
      sliced_axes = rc.slicedAxes();
      if extra_axis < len(sliced_axes):
        rc.incrementSlice(sliced_axes[extra_axis][0],incr);

  def setLMRectSubset (self,rect):
    if self._imagecons:
      self._imagecons[0].setLMRectSubset(rect);

  def getLMRectStats (self,rect):
    if self._imagecons:
      return self._imagecons[0].renderControl().getLMRectStats(rect);

  def unloadModelImages (self):
    """Unloads images associated with model (i.e. loaded with the model=True flag)""";
    for ic in [ ic for ic in self._imagecons if id(ic) in self._model_imagecons ]:
      self.unloadImage(ic);

  def unloadImage (self,imagecon):
    """Unloads the given imagecon object.""";
    if imagecon not in self._imagecons:
      return;
    # recenter if needed
    self._imagecons.remove(imagecon);
    self._imagecon_loadorder.remove(imagecon);
    self._model_imagecons.discard(id(imagecon));
    # reparent widget and release it
    imagecon.setParent(None);
    imagecon.close();
    # recenter image, if unloaded the center image
    if self._center_image is imagecon.image:
      self.centerImage(self._imagecons[0] if self._imagecons else None,emit=False);
    # emit signal
    self._repopulateMenu();
    self.emit(SIGNAL("imagesChanged"));
    if self._imagecons:
      self.raiseImage(self._imagecons[0]);

  def getCenterImage (self):
    return self._center_image;

  def centerImage (self,imagecon,emit=True):
    self._center_image = imagecon and imagecon.image;
    for ic in self._imagecons:
      ic.setPlotProjection(self._center_image.projection);
    if emit:
      self.emit(SIGNAL("imagesChanged"));

  def raiseImage (self,imagecon):
    # reshuffle image stack, if more than one image image
    if len(self._imagecons) > 1:
      busy = BusyIndicator();
      # reshuffle image stack
      self._imagecons.remove(imagecon);
      self._imagecons.insert(0,imagecon);
      # notify imagecons
      for i,ic in enumerate(self._imagecons):
        label = "%d"%(i+1) if i else "<B>1</B>";
        ic.setZ(self._z0-i*10,top=not i,depthlabel=label,can_raise=True);
      # adjust visibility
      for j,ic in enumerate(self._imagecons):
        ic.setImageVisible(not j or bool(self._qa_plot_all.isChecked()));
      # issue replot signal
      self.emit(SIGNAL("imageRaised"));
      self.fastReplot();
    # else simply update labels
    else:
      self._imagecons[0].setZ(self._z0,top=True,depthlabel=None,can_raise=False);
      self._imagecons[0].setImageVisible(True);
    # update slice menus
    img = imagecon.image;
    axes = imagecon.renderControl().slicedAxes();
    for i,(next,prev) in enumerate(self._qa_slices):
      next.setVisible(False);
      prev.setVisible(False);
      if i < len(axes):
        iaxis,name,labels = axes[i];
        next.setVisible(True);
        prev.setVisible(True);
        next.setText("Show next slice along %s axis"%name);
        prev.setText("Show previous slice along %s axis"%name);
    # emit signasl
    self.emit(SIGNAL("imageRaised"),img);

  def resetDrawKey (self):
    """Makes and sets the current plot's drawing key"""
    if self._plot:
      key = [];
      for ic in self._imagecons:
        key.append(id(ic));
        key += ic.currentSlice();
        self._plot.setDrawingKey(tuple(key));

  def fastReplot (self,*dum):
    """Fast replot -- called when flipping images or slices. Uses the plot cache, if possible.""";
    if self._plot:
      self.resetDrawKey();
      dprint(2,"calling replot",time.time()%60);
      self._plot.replot();
      dprint(2,"replot done",time.time()%60);

  def replot (self,*dum):
    """Proper replot -- called when an image needs to be properly redrawn. Cleares the plot's drawing cache.""";
    if self._plot:
      self._plot.clearDrawCache();
      self.resetDrawKey();
      self._plot.replot();

  def attachImagesToPlot (self,plot):
    self._plot = plot;
    self.resetDrawKey();
    for ic in self._imagecons:
      ic.attachToPlot(plot);

  def getMenu (self):
    return self._menu;

  def _displayAllImages (self,enabled):
    busy = BusyIndicator();
    if enabled:
      for ic in self._imagecons:
        ic.setImageVisible(True);
    else:
      self._imagecons[0].setImageVisible(True);
      for ic in self._imagecons[1:]:
        ic.setImageVisible(False);
    self.replot();

  def _checkClipboardPath (self,mode=QClipboard.Clipboard):
    if self._qa_load_clipboard:
      self._clipboard_mode = mode;
      try:
        path = str(QApplication.clipboard().text(mode));
      except:
        path = None;
      self._qa_load_clipboard.setEnabled(bool(path and os.path.isfile(path)));
      
  def _loadClipboardPath (self):
    try:
      path = QApplication.clipboard().text(self._clipboard_mode);
    except:
      return;
    self.loadImage(path);
    
  def _repopulateMenu (self):
    self._menu.clear();
    self._menu.addAction("&Load image...",self.loadImage,Qt.CTRL+Qt.Key_L);
    self._menu.addAction("&Compute image...",self.computeImage,Qt.CTRL+Qt.Key_M);
    self._qa_load_clipboard = self._menu.addAction("Load from clipboard &path",self._loadClipboardPath,Qt.CTRL+Qt.Key_P);
    self._checkClipboardPath();
    if self._imagecons:
      self._menu.addSeparator();
      # add controls to cycle images and planes
      for i,imgcon in enumerate(self._imagecons[::-1]):
        self._menu.addMenu(imgcon.getMenu());
      self._menu.addSeparator();
      if len(self._imagecons) > 1:
        self._menu.addAction("Cycle images",self.cycleImages,Qt.Key_F5);
        self._menu.addAction("Blink images",self.blinkImages,Qt.Key_F6);
      self._qa_slices = ((  self._menu.addAction("Next slice along axis 1",self._currier.curry(self.incrementSlice,0,1),Qt.Key_F7),
                                            self._menu.addAction("Previous slice along axis 1",self._currier.curry(self.incrementSlice,0,-1),Qt.SHIFT+Qt.Key_F7)),
                                        (  self._menu.addAction("Next slice along axis 2",self._currier.curry(self.incrementSlice,1,1),Qt.Key_F8),
                                            self._menu.addAction("Previous slice along axis 2",self._currier.curry(self.incrementSlice,1,-1),Qt.SHIFT+Qt.Key_F8)));
      self._menu.addSeparator();
      self._menu.addAction(self._qa_plot_top);
      self._menu.addAction(self._qa_plot_all);

  def computeImage (self,expression=None):
    """Computes image from expression (if expression is None, pops up dialog)""";
    if expression is None:
      (expression,ok) = QInputDialog.getText(self,"Compute image",
      """Enter an image expression to compute.
Any valid numpy expression is supported, and
all functions from the numpy module are available (including sub-modules such as fft).
Use 'a', 'b', 'c' to refer to images.
Examples:  "(a+b)/2", "cos(a)+sin(b)", "a-a.mean()", "fft.fft2(a)", etc.""");
#      (expression,ok) = QInputDialog.getText(self,"Compute image","""<P>Enter an expression to compute.
#        Use 'a', 'b', etc. to refer to loaded images. Any valid numpy expression is supported, and all the
#       functions from the numpy module are available. Examples of valid expressions include "(a+b)/2",
#       "cos(a)+sin(b)", "a-a.mean()", etc.
#        </P>
#      """);
      expression = str(expression);
      if not ok or not expression:
        return;
    # try to parse expression
    arglist = [ (chr(ord('a')+ic.getNumber()),ic.image) for ic in self._imagecons ];
    try:
      exprfunc = eval("lambda "+(",".join([ x[0] for x in arglist ]))+":"+expression,
                      numpy.__dict__,{});
    except Exception,exc:
      self.showErrorMessage("""Error parsing expression "%s": %s."""%(expression,str(exc)));
      return None;
    # try to evaluate expression
    self.showMessage("Computing expression \"%s\""%expression,10000);
    busy = BusyIndicator();
    QApplication.flush();
    # trim trivial trailing dimensions. This avoids the problem of when an NxMx1 and an NxMx1x1 arrays are added,
    # the result is promoted to NxMxMx1 following the numpy rules.
    def trimshape (shape):
      out = shape;
      while out and out[-1] == 1:
        out = out[:-1];
      return out;
    def trimarray (array):
      return array.reshape(trimshape(array.shape));
    try:
      result = exprfunc(*[trimarray(x[1].data()) for x in arglist]);
    except Exception,exc:
      busy = None;
      traceback.print_exc();
      self.showErrorMessage("""Error evaluating "%s": %s."""%(expression,str(exc)));
      return None;
Пример #24
0
 def _enableColumn(self, column, enable=True):
     busy = BusyIndicator()
     self._column_enabled[column] = enable
     self._showColumn(column, enable and self._column_shown[column])
Пример #25
0
 def _showColumnCategory(self, columns, show):
     busy = BusyIndicator()
     for col in columns:
         self._column_shown[col] = show
         self._showColumn(col, self._column_enabled[col] and show)
Пример #26
0
 # look for an image in the arglist with the same projection, and with a valid dirname
 # (for the where-to-save hint)
 template = arglist[0][1];
 # if all images in arglist have the same projection, then it doesn't matter what we use
 # else ask
 if len([x for x in arglist[1:] if x[1].projection == template.projection]) != len(arglist)-1:
   options = [ x[0] for x in arglist ];
   (which,ok) = QInputDialog.getItem(self,"Compute image","Coordinate system to use for the result of \"%s\":"%expression,options,0,False);
   if not ok:
     return None;
   try:
     template = arglist[options.index(which)][1];
   except:
     pass;
 # create a FITS image
 busy = BusyIndicator();
 dprint(2,"creating FITS image",expression);
 self.showMessage("""Creating image for %s"""%expression,3000);
 QApplication.flush();
 try:
   hdu = pyfits.PrimaryHDU(result.transpose(),template.fits_header);
   skyimage = SkyImage.FITSImagePlotItem(name=expression,filename=None,hdu=hdu);
 except:
   busy = None;
   traceback.print_exc();
   self.showErrorMessage("""Error creating FITS image %s: %s"""%(expression,str(sys.exc_info()[1])));
   return None;
 # get directory name for save-to hint
 dirname = getattr(template,'filename',None);
 if not dirname:
   dirnames = [ getattr(img,'filename') for x,img in arglist if hasattr(img,'filename') ];
Пример #27
0
 def import_content(self, force=False, show_options=False):
     """imports TDL module but does not run _define_forest().
 Depending on autosync/modified state, asks to save or revert.
 If module is already imported, does nothing, unless force=True,
 in which case it imports unconditionally.
 If do_compile=True, proceeds to show compile-time options on success,
 or to compile directly if there are no options
 Return value:
   True on successful import
   None if cancelled by user.
 Import errors are posted to the error dialog.
 """
     # save list of publishers (since forest will be cleared on import)
     if self._pub_nodes is None:
         if TDL.Compile.last_imported_file() == self._filename:
             self._pub_nodes = [
                 node.name for node in meqds.nodelist.iternodes()
                 if node.is_publishing()
             ]
             _dprint(2, "last imported file matches, saving",
                     len(self._pub_nodes), "publishers")
         else:
             self._pub_nodes = []
             _dprint(2, "last imported file doesn't match:",
                     TDL.Compile.last_imported_file(), self._filename)
     _dprint(1, self._filename, "importing")
     self.clear_message()
     self.clear_errors()
     self._options_menu.hide()
     self._tb_tdlexec.hide()
     self._tdlexec_menu.hide()
     # change the current directory to where the file is
     # os.chdir(os.path.dirname(self._filename));
     # The Python imp module expects text to reside in a disk file, which is
     # a pain in the ass for us if we're dealing with modified text or text
     # entered on-the-fly. So, either save or sync before proceeding
     global _external_sync
     if self._document.isModified() or not self._filename:
         if not self._save_file():
             return None
     else:
         if not self.sync_external_file(ask=False):
             return None
     # if we already have an imported module and disk file hasn't changed, skip
     # the importing step
     busy = BusyIndicator()
     if force or self._tdlmod is None or self._tdlmod_filetime == self._file_disktime:
         # reset data members
         _dprint(2, self._filename,
                 "emitting signal for 0 compile-time options")
         self.emit(PYSIGNAL("hasCompileOptions()"), self, 0)
         self._options_menu.hide()
         self._options_menu.clear()
         self._tdlmod = None
         # get text from editor
         tdltext = str(self._document.toPlainText())
         try:
             tdlmod, tdltext = TDL.Compile.import_tdl_module(
                 self._filename, tdltext)
         # catch import errors
         except TDL.CumulativeError as value:
             _dprint(0, "caught cumulative error, length", len(value.args))
             self._error_window.set_errors(value.args,
                                           message="TDL import failed")
             busy = None
             return None
         except Exception as value:
             _dprint(0, "caught other error, traceback follows")
             traceback.print_exc()
             self._error_window.set_errors([value],
                                           message="TDL import failed")
             busy = None
             return None
         # remember module and nodescope
         self._tdlmod = tdlmod
         self._tdltext = tdltext
         self._tdlmod_filetime = self._file_disktime
         # build options menu from compile-time options
         opt_tw = self._options_menu.treeWidget()
         opts = TDLOptions.get_compile_options()
         if opts:
             # add options
             try:
                 TDLOptions.populate_option_treewidget(opt_tw, opts)
             except Exception as value:
                 _dprint(0, "error setting up TDL options GUI")
                 traceback.print_exc()
                 self._error_window.set_errors(
                     [value], message="Error setting up TDL options GUI")
                 busy = None
                 return None
             # self._tb_opts.show();
             _dprint(2, self._filename, "emitting signal for", len(opts),
                     "compile-time options")
             self.emit(PYSIGNAL("hasCompileOptions()"), self, len(opts))
     # success, show options or compile
     if show_options and self.has_compile_options():
         self._options_menu.adjustSizes()
         self._options_menu.show()
     busy = None
     return True
Пример #28
0
    def compile_content(self):
        # import content first, and return if failed
        if not self.import_content(force=True):
            return None
        _dprint(1, self._filename, "compiling forest")
        # clear predefined functions
        self._tb_tdlexec.hide()
        # try the compilation
        busy = BusyIndicator()
        try:
            (_tdlmod,ns,msg) = \
       TDL.Compile.run_forest_definition(
            meqds.mqs(),self._filename,self._tdlmod,self._tdltext,
            parent=self,wait=False)
        # catch compilation errors
        except TDL.CumulativeError as value:
            _dprint(0, "caught cumulative error, length", len(value.args))
            self._error_window.set_errors(value.args,
                                          message="TDL import failed")
            busy = None
            return None
        except Exception as value:
            _dprint(0, "caught other error, traceback follows")
            traceback.print_exc()
            self._error_window.set_errors([value])
            busy = None
            return None
        # refresh the nodelist
        meqds.request_nodelist(sync=True)
        # restore publishing nodes
        for name in (self._pub_nodes or []):
            if name in ns.AllNodes():
                _dprint(2, "reenabling publishing of", name)
                meqds.enable_node_publish_by_name(name,
                                                  sync=True,
                                                  get_state=True)
            else:
                _dprint(2, "not reenabling publishing of", name,
                        ", as it no longer exists?")
        # clear publisher list so it's re-saved next time in import_content()
        self._pub_nodes = None
        ### NB: presume this all was successful for now
        # insert node scope into TDL module
        setattr(_tdlmod, '_tdl_nodescope', ns)

        # does the script define an explicit job list?
        joblist = getattr(_tdlmod, '_tdl_job_list', [])
        if not joblist:
            joblist = []
            # try to build it from implicit function names
            for (name, func) in _tdlmod.__dict__.items():
                if name.startswith("_tdl_job_") and callable(
                        func) and not TDLOptions.is_jobfunc_defined(func):
                    joblist.append(func)
        # does the script define a testing function?
        testfunc = getattr(_tdlmod, '_test_forest', None)
        if not callable(testfunc):
            testfunc = getattr(_tdlmod, 'test_forest', None)
            if callable(testfunc):
                res = QMessageBox.warning(
                    self, "Deprecated method",
                    """Your script contains a test_forest() method. This is deprecated
          and will be disabled in the future. Please rename it to
          _test_forest().
          """, QMessageBox.Ok)
        if callable(testfunc):
            joblist.append(testfunc)

        from past.builtins import cmp
        from functools import cmp_to_key
        joblist.sort(key=cmp_to_key(lambda a, b: cmp(str(a), str(b))))

        # create list of job actions
        opts = TDLOptions.get_runtime_options()
        self._tdlexec_menu.clear()
        if joblist or opts:
            if opts:
                self._job_executor = curry(self.execute_tdl_job, _tdlmod, ns)
                ## new style:
                try:
                    TDLOptions.populate_option_treewidget(
                        self._tdlexec_menu.treeWidget(),
                        opts,
                        executor=self._job_executor)
                except Exception as value:
                    _dprint(0, "error setting up TDL options GUI")
                    traceback.print_exc()
                    self._error_window.set_errors(
                        [value], message="Error setting up TDL options GUI")
                    busy = None
                    return None
            if joblist:
                for func in joblist:
                    name = re.sub("^_tdl_job_", "", func.__name__)
                    name = name.replace('_', ' ')
                    self._tdlexec_menu.addAction(name,
                                                 curry(self.execute_tdl_job,
                                                       _tdlmod, ns, func, name,
                                                       func.__name__),
                                                 icon=pixmaps.gear)
            self.emit(PYSIGNAL("hasRuntimeOptions()"), self, True)
            self._tdlexec_menu.adjustSizes()
            self._tb_tdlexec.show()
        else:
            self.emit(PYSIGNAL("hasRuntimeOptions()"), self, False)
            self._tb_tdlexec.hide()

        if joblist:
            msg += " %d predefined function(s) available, please use the Exec menu to run them." % (
                len(joblist), )

        self.show_message(msg, transient=True)
        busy = None
        return True
Пример #29
0
 def updateColorMapParameters(self):
     """Call this when the colormap parameters have changed"""
     busy = BusyIndicator()
     self.image.updateCurrentColorMap()
     if self._config:
         self._cmap_list[self._current_cmap_index].saveConfig(self._config)