Source code for ginga.rv.plugins.Info

# This is open-source software licensed under a BSD license.
# Please see the file LICENSE.txt for details.
"""
The ``Info`` plugin provides a pane of commonly useful metadata about the
focused channel image.  Common information includes some metadata
header values, coordinates, dimensions of the image, minimum and
maximum values, etc.  As the cursor is moved around the image, the X, Y,
Value, RA, and DEC values are updated to reflect the value under the cursor.

**Plugin Type: Global**

``Info`` is a global plugin.  Only one instance can be opened.

**Usage**

At the bottom of the ``Info`` interface are the color distribution
and cut levels controls.  The selector above the cut levels boxes lets
you chose from several distribution algorithms that map the values in
the image to the color map.  Choices are "linear", "log", "power", "sqrt",
"squared", "asinh", "sinh", and "histeq" (histogram equalization).

Below this, the low and high cut levels are shown and can be adjusted.
Pressing the "Auto Levels" button will recalculate cut levels based on
the current auto cut levels algorithm and parameters defined in the channel
preferences.

Below the "Auto Levels" button, the status of the settings for
"Cut New", "Zoom New", and "Center New" are shown for the currently active
channel.  These indicate how new images that are added to the channel
will be affected by auto cut levels, fitting to the window and panning
to the center of the image.

The "Follow New" checkbox controls whether the viewer will automatically
display new images added to the channel.  The "Raise New" checkbox controls
whether an image viewer window is raised when a new image is added.  These
two controls can be useful, for example, if an external program is adding
images to the viewer, and you wish to prevent interruption of your work
examining a particular image.

As a global plugin, ``Info`` responds to a change of focus to a new channel
by displaying the metadata from the new channel.
It typically appears under the "Synopsis" tab in the user interface.

This plugin is not usually configured to be closeable, but the user can
make it so by setting the "closeable" setting to True in the configuration
file--then Close and Help buttons will be added to the bottom of the UI.

"""
import time
import numpy as np

from ginga.gw import Widgets
from ginga import GingaPlugin, ColorDist
from ginga.rv.plugins.Toolbar import Toolbar
from ginga.table.AstroTable import AstroTable
from ginga.plot.Plotable import Plotable
from ginga.canvas.CanvasObject import get_canvas_types

__all__ = ['Info', 'Info_Ginga_Image',
           'Info_Ginga_Plot', 'Info_Ginga_Table']


class Info(Toolbar):

    def __init__(self, fv):
        # superclass defines some variables for us, like logger
        super().__init__(fv)

        self.opname_prefix = 'Info_'

    def __str__(self):
        return 'info'


class Info_Common(GingaPlugin.LocalPlugin):

    def __init__(self, fv, chviewer):
        # superclass defines some variables for us, like logger
        super().__init__(fv, chviewer)

        # truncate names after this size
        self.maxstr = 60

    def trunc(self, s):
        if len(s) > self.maxstr:
            return s[:self.maxstr - 3] + '...'
        else:
            return s


[docs] class Info_Ginga_Image(Info_Common): """Info sidebar for the Ginga Image viewer. """ def __init__(self, fv, chviewer): # superclass defines some variables for us, like logger super().__init__(fv, chviewer) self.autozoom_options = ['on', 'override', 'once', 'off'] self.autocut_options = self.autozoom_options self.autocenter_options = self.autozoom_options self.fv.add_callback('field-info', self.field_info_cb) self.gui_up = False
[docs] def build_gui(self, container): sw = Widgets.ScrollArea() vbox = Widgets.VBox() sw.set_widget(vbox) captions = (('Channel:', 'label', 'Channel', 'llabel'), ('Name:', 'label', 'Name', 'llabel'), ('Object:', 'label', 'Object', 'llabel'), ('X:', 'label', 'X', 'llabel'), ('Y:', 'label', 'Y', 'llabel'), ('Image-X:', 'label', 'Image_X', 'llabel'), ('Image-Y:', 'label', 'Image_Y', 'llabel'), ('Value:', 'label', 'Value', 'llabel'), ('RA:', 'label', 'RA', 'llabel'), ('DEC:', 'label', 'DEC', 'llabel'), ('Equinox:', 'label', 'Equinox', 'llabel'), ('Dimensions:', 'label', 'Dimensions', 'llabel'), ('Min:', 'label', 'Min', 'llabel'), ('Max:', 'label', 'Max', 'llabel'), ) w, b = Widgets.build_info(captions) self.w = b col = Widgets.VBox() row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(w, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) col.add_widget(row, stretch=0) col.add_widget(Widgets.Label(''), stretch=1) sw2 = Widgets.ScrollArea() sw2.set_widget(col) vbox.add_widget(sw2, stretch=1) captions = (('Zoom:', 'label', 'Zoom', 'llabel', 'Color Dist', 'combobox'), ('Cut Low:', 'label', 'Cut Low Value', 'llabel', 'Cut Low', 'entry'), ('Cut High:', 'label', 'Cut High Value', 'llabel', 'Cut High', 'entry'), ('Auto Levels', 'button', 'spacer_1', 'spacer', 'Cut Levels', 'button'), ('Cut New:', 'label', 'Cut New', 'combobox'), ('Zoom New:', 'label', 'Zoom New', 'combobox', 'Follow New', 'checkbutton'), ('Center New:', 'label', 'Center New', 'combobox', 'Raise New', 'checkbutton'), ) w, b = Widgets.build_info(captions) self.w.update(b) b.cut_levels.set_tooltip("Set cut levels manually") loval, hival = self.fitsimage.get_cut_levels() b.auto_levels.set_tooltip("Set cut levels by algorithm") b.cut_low.set_tooltip("Set low cut level (press Enter)") b.cut_low.set_length(9) b.cut_low.set_text(str(loval)) b.cut_low_value.set_text(str(loval)) b.cut_high.set_tooltip("Set high cut level (press Enter)") b.cut_high.set_length(9) b.cut_high.set_text(str(hival)) b.cut_high_value.set_text(str(hival)) combobox = b.color_dist for name in ColorDist.get_dist_names(): combobox.append_text(name) b.color_dist.set_tooltip("Set distribution (stretching) algorithm") combobox = b.cut_new for name in self.autocut_options: combobox.append_text(name) b.cut_new.set_tooltip("Automatically set cut levels when switching images") combobox = b.zoom_new for name in self.autozoom_options: combobox.append_text(name) b.zoom_new.set_tooltip("Automatically fit image to window when switching images") combobox = b.center_new for name in self.autocenter_options: combobox.append_text(name) b.center_new.set_tooltip("Automatically center image in window when switching images") b.follow_new.set_tooltip("Automatically switch to new images in channel") b.raise_new.set_tooltip("Automatically raise channel viewer for new images") row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(w, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) vbox.add_widget(row, stretch=0) self.w.channel.set_text(self.channel.name) self.w.color_dist.add_callback('activated', self.set_color_dist) self.w.cut_low.add_callback('activated', self.cut_levels) self.w.cut_high.add_callback('activated', self.cut_levels) self.w.cut_levels.add_callback('activated', self.cut_levels) self.w.auto_levels.add_callback('activated', self.auto_levels) self.w.cut_new.add_callback('activated', self.set_autocuts_cb) self.w.zoom_new.add_callback('activated', self.set_autozoom_cb) self.w.center_new.add_callback('activated', self.set_autocenter_cb) self.w.follow_new.add_callback('activated', self.set_follow_cb) self.w.raise_new.add_callback('activated', self.set_raise_cb) fitssettings = self.fitsimage.get_settings() for name in ['cuts']: fitssettings.get_setting(name).add_callback('set', self.cutset_cb) for name in ['scale']: fitssettings.get_setting(name).add_callback('set', self.zoomset_cb) fitssettings.get_setting('color_algorithm').add_callback('set', self.cdistset_cb) fitssettings.get_setting('autocuts').add_callback('set', self.autocuts_cb) fitssettings.get_setting('autozoom').add_callback('set', self.autozoom_cb) fitssettings.get_setting('autocenter').add_callback('set', self.autocenter_cb) fitssettings.get_setting('switchnew').add_callback('set', self.follow_cb) fitssettings.get_setting('raisenew').add_callback('set', self.raise_cb) container.add_widget(sw, stretch=1) self.gui_up = True
[docs] def start(self): self.redo()
[docs] def stop(self): self.gui_up = False
[docs] def close(self): # NOTE: this shouldn't be called under normal usage self.fv.stop_local_plugin(self.chname, str(self))
[docs] def redo(self): self.set_info()
[docs] def zoomset_cb(self, setting, value): """This callback is called when the main window is zoomed. """ if not self.gui_up: return #scale_x, scale_y = self.fitsimage.get_scale_xy() scale_x, scale_y = value # Set text showing zoom factor (1X, 2X, etc.) if scale_x == scale_y: text = self.fv.scale2text(scale_x) else: textx = self.fv.scale2text(scale_x) texty = self.fv.scale2text(scale_y) text = "X: %s Y: %s" % (textx, texty) self.w.zoom.set_text(text)
[docs] def cutset_cb(self, setting, value): if not self.gui_up: return loval, hival = value #self.w.cut_low.set_text('%.4g' % (loval)) self.w.cut_low_value.set_text('%.4g' % (loval)) #self.w.cut_high.set_text('%.4g' % (hival)) self.w.cut_high_value.set_text('%.4g' % (hival))
[docs] def cdistset_cb(self, setting, value): if not self.gui_up: return name = value self.w.color_dist.set_text(name)
[docs] def autocuts_cb(self, setting, option): if not self.gui_up: return self.logger.debug("autocuts changed to %s" % option) index = self.autocut_options.index(option) self.w.cut_new.set_index(index)
[docs] def autozoom_cb(self, setting, option): if not self.gui_up: return index = self.autozoom_options.index(option) self.w.zoom_new.set_index(index)
[docs] def autocenter_cb(self, setting, option): if not self.gui_up: return # Hack to convert old values that used to be T/F if isinstance(option, bool): choice = {True: 'on', False: 'off'} option = choice[option] index = self.autocenter_options.index(option) self.w.center_new.set_index(index)
[docs] def follow_cb(self, setting, option): if not self.gui_up: return self.w.follow_new.set_state(option)
[docs] def raise_cb(self, setting, option): if not self.gui_up: return self.w.raise_new.set_state(option)
[docs] def set_autocuts_cb(self, w, index): if not self.gui_up: return option = self.autocut_options[index] self.fitsimage.enable_autocuts(option)
[docs] def set_autozoom_cb(self, w, index): if not self.gui_up: return option = self.autozoom_options[index] self.fitsimage.enable_autozoom(option)
[docs] def set_autocenter_cb(self, w, index): if not self.gui_up: return option = self.autocenter_options[index] self.fitsimage.enable_autocenter(option)
[docs] def set_follow_cb(self, w, tf): if not self.gui_up: return self.fitsimage.get_settings().set(switchnew=tf)
[docs] def set_raise_cb(self, w, tf): if not self.gui_up: return self.fitsimage.get_settings().set(raisenew=tf)
# LOGIC
[docs] def set_info(self): if not self.gui_up: return # Show cut levels loval, hival = self.fitsimage.get_cut_levels() #self.w.cut_low.set_text('%.4g' % (loval)) self.w.cut_low_value.set_text('%.4g' % (loval)) #self.w.cut_high.set_text('%.4g' % (hival)) self.w.cut_high_value.set_text('%.4g' % (hival)) # update zoom indicator scalefactor = self.fitsimage.get_scale() text = self.fv.scale2text(scalefactor) self.w.zoom.set_text(text) # update cut new/zoom new indicators t_ = self.fitsimage.get_settings() index = self.autocut_options.index(t_['autocuts']) self.w.cut_new.set_index(index) index = self.autozoom_options.index(t_['autozoom']) self.w.zoom_new.set_index(index) option = t_['autocenter'] # Hack to convert old values that used to be T/F if isinstance(option, bool): choice = {True: 'on', False: 'off'} option = choice[option] index = self.autocenter_options.index(option) self.w.center_new.set_index(index) self.w.follow_new.set_state(t_['switchnew']) self.w.raise_new.set_state(t_['raisenew']) # Set color distribution indicator name = t_['color_algorithm'] self.w.color_dist.set_text(name) image = self.fitsimage.get_image() if image is None: return header = image.get_header() # Update info panel name = self.trunc(image.get('name', 'Noname')) self.w.name.set_text(name) objtext = self.trunc(header.get('OBJECT', 'UNKNOWN')) self.w.object.set_text(objtext) equinox = header.get('EQUINOX', '') self.w.equinox.set_text(str(equinox)) # Show min, max values width, height = self.fitsimage.get_data_size() minval, maxval = image.get_minmax(noinf=False) self.w.max.set_text(str(maxval)) self.w.min.set_text(str(minval)) # Show dimensions dim_txt = "%dx%d" % (width, height) self.w.dimensions.set_text(dim_txt)
[docs] def field_info_cb(self, fv, channel, info): if not self.gui_up: return self.w.x.set_text("%.3f" % info.x) self.w.y.set_text("%.3f" % info.y) if 'image_x' in info: self.w.image_x.set_text("%.3f" % info.image_x) else: self.w.image_x.set_text("") if 'image_y' in info: self.w.image_y.set_text("%.3f" % info.image_y) else: self.w.image_y.set_text("") self.w.value.set_text(str(info.value)) if 'ra_txt' in info: self.w.ra.set_text(info.ra_txt) self.w.dec.set_text(info.dec_txt) if 'ra_lbl' in info: self.w.lbl_ra.set_text(info.ra_lbl + ':') self.w.lbl_dec.set_text(info.dec_lbl + ':')
[docs] def cut_levels(self, w): loval, hival = self.fitsimage.get_cut_levels() try: lostr = self.w.cut_low.get_text().strip() if lostr != '': loval = float(lostr) histr = self.w.cut_high.get_text().strip() if histr != '': hival = float(histr) self.logger.debug("locut=%f hicut=%f" % (loval, hival)) return self.fitsimage.cut_levels(loval, hival) except Exception as e: self.fv.show_error("Error cutting levels: %s" % (str(e))) return True
[docs] def auto_levels(self, w): self.fitsimage.auto_levels()
[docs] def set_color_dist(self, w, idx): name = w.get_text() self.fitsimage.set_color_algorithm(name)
def __str__(self): return 'info_ginga_image'
[docs] class Info_Ginga_Plot(Info_Common): """Info sidebar for the Ginga Plot viewer. """ def __init__(self, fv, fitsimage): # superclass defines some variables for us, like logger super().__init__(fv, fitsimage) # Plugin preferences prefs = self.fv.get_preferences() self.settings = prefs.create_category('info_Ginga_Plot') self.settings.add_defaults(linewidth=1, linestyle='-', linecolor='blue', markersize=6, markerwidth=0.5, markerstyle='o', markercolor='red', show_marker=False, file_suffix='.png') self.settings.load(onError='silent') viewer = self.channel.get_viewer('Ginga Plot') self.plot_viewer = viewer viewer.add_callback('range-set', self.range_set_cb) viewer.add_callback('motion', self.motion_cb) self.gui_up = False
[docs] def build_gui(self, container): # if not have_mpl: # raise ImportError('Install matplotlib to use this plugin') t_ = self.plot_viewer.get_settings() sw = Widgets.ScrollArea() vbox = Widgets.VBox() sw.set_widget(vbox) captions = (('Channel:', 'label', 'channel', 'llabel'), ('Name:', 'label', 'name', 'llabel'), ('X:', 'label', 'x_val', 'llabel'), ('Y:', 'label', 'y_val', 'llabel'), ) w, b = Widgets.build_info(captions) self.w = b self.w.channel.set_text(self.channel.name) col = Widgets.VBox() row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(w, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) col.add_widget(row, stretch=0) col.add_widget(Widgets.Label(''), stretch=1) sw2 = Widgets.ScrollArea() sw2.set_widget(col) vbox.add_widget(sw2, stretch=1) captions = (('spacer_1', 'spacer', 'X', 'llabel', 'Y', 'llabel'), ('Dist:', 'label', 'x_dist', 'combobox', 'y_dist', 'combobox'), ('Low:', 'label', 'x_lo', 'entry', 'y_lo', 'entry'), ('High:', 'label', 'x_hi', 'entry', 'y_hi', 'entry'), ('spacer_3', 'spacer', 'Show marker', 'checkbox', 'Save Plot', 'button'), ) w, b = Widgets.build_info(captions) self.w.update(b) # Controls for X-axis scaling combobox = b.x_dist for name in ['linear', 'log']: combobox.append_text(name) combobox.set_tooltip('Select a mapping to plot on X-axis') combobox.set_text(t_['plot_dist_axis'][0]) combobox.add_callback('activated', self.x_dist_cb) # Controls for Y-axis column listing combobox = b.y_dist for name in ['linear', 'log']: combobox.append_text(name) combobox.set_tooltip('Select a mapping to plot on Y-axis') combobox.set_text(t_['plot_dist_axis'][1]) combobox.add_callback('activated', self.y_dist_cb) self.set_xlimits_widgets() self.set_ylimits_widgets() b.x_lo.add_callback('activated', lambda w: self.set_xlim_lo_cb()) b.x_lo.set_tooltip('Set X lower limit') b.x_hi.add_callback('activated', lambda w: self.set_xlim_hi_cb()) b.x_hi.set_tooltip('Set X upper limit') b.y_lo.add_callback('activated', lambda w: self.set_ylim_lo_cb()) b.y_lo.set_tooltip('Set Y lower limit') b.y_hi.add_callback('activated', lambda w: self.set_ylim_hi_cb()) b.y_hi.set_tooltip('Set Y upper limit') b.show_marker.set_state(t_.get('plot_show_marker', False)) b.show_marker.add_callback('activated', self.set_marker_cb) b.show_marker.set_tooltip('Mark data points') # Button to save plot b.save_plot.set_tooltip("Save plot to file") b.save_plot.add_callback('activated', lambda w: self.save_cb()) b.save_plot.set_enabled(True) row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(w, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) vbox.add_widget(row, stretch=0) container.add_widget(sw, stretch=1) self.gui_up = True
[docs] def redo(self): """This is called when a new image arrives or the data in the existing image changes. """ if not self.gui_up: return dataobj = self.plot_viewer.get_dataobj() name = self.trunc(dataobj.get('name', 'Noname')) self.w.name.set_text(name)
[docs] def set_xlimits_widgets(self, set_min=True, set_max=True): """Populate axis limits GUI with current plot values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges if set_min: self.w.x_lo.set_text('{0}'.format(x_lo)) if set_max: self.w.x_hi.set_text('{0}'.format(x_hi))
[docs] def set_ylimits_widgets(self, set_min=True, set_max=True): """Populate axis limits GUI with current plot values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges if set_min: self.w.y_lo.set_text('{0}'.format(y_lo)) if set_max: self.w.y_hi.set_text('{0}'.format(y_hi))
[docs] def x_dist_cb(self, w, index): _dist = w.get_text().lower() self.plot_viewer.set_dist_axis(x_axis=_dist)
[docs] def y_dist_cb(self, w, index): _dist = w.get_text().lower() self.plot_viewer.set_dist_axis(y_axis=_dist)
[docs] def set_xlim_lo_cb(self, redraw=True): """Set plot limit based on user values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges try: x_min = float(self.w.x_lo.get_text()) except Exception as e: self.fv.show_error(f"error setting X low limit: {e}") return self.plot_viewer.set_ranges(x_range=(x_min, x_hi))
[docs] def set_xlim_hi_cb(self, redraw=True): """Set plot limit based on user values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges try: x_max = float(self.w.x_hi.get_text()) except Exception as e: self.fv.show_error(f"error setting X high limit: {e}") return self.plot_viewer.set_ranges(x_range=(x_lo, x_max))
[docs] def set_ylim_lo_cb(self, redraw=True): """Set plot limit based on user values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges try: y_min = float(self.w.y_lo.get_text()) except Exception as e: self.fv.show_error(f"error setting Y low limit: {e}") return self.plot_viewer.set_ranges(y_range=(y_min, y_hi))
[docs] def set_ylim_hi_cb(self, redraw=True): """Set plot limit based on user values.""" ranges = self.plot_viewer.get_ranges() (x_lo, x_hi), (y_lo, y_hi) = ranges try: y_max = float(self.w.y_hi.get_text()) except Exception as e: self.fv.show_error(f"error setting Y high limit: {e}") return self.plot_viewer.set_ranges(y_range=(y_lo, y_max))
[docs] def range_set_cb(self, viewer, ranges): (xmin, xmax), (ymin, ymax) = ranges if self.gui_up: self.set_xlimits_widgets() self.set_ylimits_widgets()
[docs] def motion_cb(self, viewer, event): if not self.gui_up: return data_x, data_y = event.data_x, event.data_y x_str = str(data_x) y_str = str(data_y) self.w.x_val.set_text(x_str) self.w.y_val.set_text(y_str)
[docs] def set_marker_cb(self, w, tf): """Toggle show/hide data point markers.""" settings = self.plot_viewer.get_settings() settings.set(plot_show_marker=tf)
[docs] def save_cb(self): """Save plot to file.""" # This just defines the basename. # Extension has to be explicitly defined or things can get messy. w = Widgets.SaveDialog(title='Save plot') target = w.get_path() if target is None: # Save canceled return plot_ext = self.settings.get('file_suffix', '.png') try: self.plot_viewer.save_rgb_image_as_file(target, format='png') except Exception as e: self.logger.error(str(e)) else: self.logger.info('Table plot saved as {0}'.format(target))
[docs] def start(self): self.redo()
[docs] def stop(self): self.gui_up = False
[docs] def close(self): # NOTE: this shouldn't be called under normal usage self.fv.stop_local_plugin(self.chname, str(self))
def __str__(self): return 'info_ginga_plot'
[docs] class Info_Ginga_Table(Info_Common): """Info sidebar for the Ginga Table viewer. """ def __init__(self, fv, chviewer): # superclass defines some variables for us, like logger super().__init__(fv, chviewer) viewer = self.channel.get_viewer('Ginga Table') self.table_viewer = viewer # To store all active table info self.tab = None self.cols = [] self._idx = [] self._idxname = '_idx' # To store selected columns names of active table self.x_col = '' self.y_col = '' self.gui_up = False
[docs] def build_gui(self, container): sw = Widgets.ScrollArea() vbox = Widgets.VBox() sw.set_widget(vbox) captions = (('Channel:', 'label', 'channel', 'llabel'), ('Name:', 'label', 'name', 'llabel'), ) w, b = Widgets.build_info(captions) self.w = b self.w.channel.set_text(self.channel.name) col = Widgets.VBox() row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(w, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) col.add_widget(row, stretch=0) col.add_widget(Widgets.Label(''), stretch=1) sw2 = Widgets.ScrollArea() sw2.set_widget(col) vbox.add_widget(sw2, stretch=1) channel = self.channel self.w.channel.set_text(channel.name) captions = (('spacer_1', 'spacer', 'X', 'llabel', 'Y', 'llabel'), ('Col:', 'label', 'x_col', 'combobox', 'y_col', 'combobox'), ('spacer_3', 'spacer', 'spacer_4', 'spacer', 'Make Plot', 'button'), ) w, b = Widgets.build_info(captions) self.w.update(b) # Controls for X-axis column listing combobox = b.x_col combobox.set_enabled(False) for name in self.cols: combobox.append_text(name) if self.x_col in self.cols: combobox.set_text(self.x_col) combobox.add_callback('activated', self.x_select_cb) combobox.set_tooltip('Select a column to plot on X-axis') # Controls for Y-axis column listing combobox = b.y_col combobox.set_enabled(False) for name in self.cols: combobox.append_text(name) if self.y_col in self.cols: combobox.set_text(self.y_col) combobox.add_callback('activated', self.y_select_cb) combobox.set_tooltip('Select a column to plot on Y-axis') # Button to save plot b.make_plot.set_tooltip("Plot selected columns") b.make_plot.add_callback('activated', self.make_plot_cb) b.make_plot.set_enabled(False) fr = Widgets.Frame("Plot Maker") fr.set_widget(w) row = Widgets.HBox() row.set_spacing(0) row.set_border_width(0) row.add_widget(fr, stretch=0) row.add_widget(Widgets.Label(''), stretch=1) vbox.add_widget(row, stretch=0) container.add_widget(sw, stretch=1) self.gui_up = True
[docs] def start(self): self.redo()
[docs] def stop(self): self.tab = None self.gui_up = False
[docs] def close(self): # NOTE: this shouldn't be called under normal usage self.fv.stop_local_plugin(self.chname, str(self))
def _set_combobox(self, combobox, vals, default=0): """Populate combobox with given list.""" combobox.clear() for val in vals: combobox.append_text(val) if default > len(vals): default = 0 val = vals[default] combobox.show_text(val) return val
[docs] def setup_table(self, dataobj): # Generate column indices self.w.x_col.set_enabled(True) self.w.y_col.set_enabled(True) self.w.make_plot.set_enabled(True) # Generate column indices self.tab = dataobj.get_data() self._idx = np.arange(len(self.tab)) # Populate combobox with table column names cols = [self._idxname] + self.tab.colnames if cols != self.cols: self.cols = cols self.x_col = self._set_combobox(self.w.x_col, self.cols, default=1) self.y_col = self._set_combobox(self.w.y_col, self.cols, default=2)
[docs] def clear(self): self.tab = None self.cols = [] self._idx = [] self._idxname = '_idx' self.x_col = '' self.y_col = ''
[docs] def redo(self): """This is called when a new image arrives or the data in the existing image changes. """ if not self.gui_up: return dataobj = self.table_viewer.get_dataobj() if isinstance(dataobj, AstroTable): self.setup_table(dataobj) else: self.logger.info("not able to process this object") name = self.trunc(dataobj.get('name', 'Noname')) self.w.name.set_text(name)
# LOGIC def _get_label(self, axis): """Return plot label for column for the given axis.""" if axis == 'x': colname = self.x_col else: # y colname = self.y_col if colname == self._idxname: label = 'Index' else: col = self.tab[colname] if col.unit: label = '{0} ({1})'.format(col.name, col.unit) else: label = col.name return label
[docs] def x_select_cb(self, w, index): """Callback to set X-axis column.""" try: self.x_col = self.cols[index] except IndexError as e: self.logger.error(str(e))
[docs] def y_select_cb(self, w, index): """Callback to set Y-axis column.""" try: self.y_col = self.cols[index] except IndexError as e: self.logger.error(str(e))
[docs] def make_plot_cb(self, w): if self.x_col == self._idxname: x_data = self._idx else: x_data = self.tab[self.x_col].data if self.y_col == self._idxname: y_data = self._idx else: y_data = self.tab[self.y_col].data x_label = self._get_label('x') y_label = self._get_label('y') dataobj = self.table_viewer.get_dataobj() name = "plot_{}".format(time.time()) #title = self.trunc(dataobj.get('name', 'NoName')) title = name if self.tab.masked: if self.x_col == self._idxname: x_mask = np.ones_like(self._idx, dtype=bool) else: x_mask = ~self.tab[self.x_col].mask if self.y_col == self._idxname: y_mask = np.ones_like(self._idx, dtype=bool) else: y_mask = ~self.tab[self.y_col].mask mask = x_mask & y_mask x_data = x_data[mask] y_data = y_data[mask] if len(x_data) > 1: i = np.argsort(x_data) # Sort X-axis to avoid messy line plot x_data = x_data[i] y_data = y_data[i] plot = Plotable(logger=self.logger) plot.set_titles(x_axis=x_label, y_axis=y_label, title=title) plot.set_grid(True) plot.set(name=name, path=None, nothumb=False) canvas = plot.get_canvas() dc = get_canvas_types() points = np.array((x_data, y_data)).T canvas.add(dc.Path(points, color='black', linewidth=1, alpha=1.0), redraw=False) self.channel.add_image(plot)
def __str__(self): return 'info_ginga_table'