Exemplo n.º 1
0
class PlotHandler(gui.handlers.BaseHandlers):
    def canvas_click(self, event):
        builder = self.state.builder
        toggle = builder.get_object('pick_location_toggle')
        if toggle.get_active():
            print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
                  ('double' if event.dblclick else 'single', event.button,
                   event.x, event.y, event.xdata, event.ydata))
            lat = event.ydata
            lon = event.xdata
            lat_box = builder.get_object('lat_entry')
            lon_box = builder.get_object('lon_entry')
            lat_box.set_text(str(lat))
            lon_box.set_text(str(lon))
            toggle.set_active(False)

    def __init__(self, *args, **kwargs):
        super(PlotHandler, self).__init__(*args, **kwargs)

        with open('osmnx_pune_1km', 'rb') as f:
            self.graph = pickle.load(f)
            self.graph = osmnx.core.add_edge_lengths(self.graph)

        builder = self.state.builder
        sw = builder.get_object('GraphArea')
        nav_box_holder = builder.get_object('plot_box')

        self.fig, self.ax = osmnx.plot_graph(self.graph, show=False)

        self.ax.plot()

        self.canvas = FigureCanvas(self.fig)
        self.canvas.mpl_connect('button_release_event', self.canvas_click)
        window = builder.get_object('AppWin')
        toolbar = NavigationToolbar(self.canvas, window)
        nav_box_holder.pack_start(toolbar, False, True, 1)
        sw.add_with_viewport(self.canvas)
Exemplo n.º 2
0
class GraphBase(object):
	"""
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
	name = 'Unknown'
	"""The name of the graph provider."""
	name_human = 'Unknown'
	"""The human readable name of the graph provider used for UI identification."""
	graph_title = 'Unknown'
	"""The title that will be given to the graph."""
	is_available = True
	def __init__(self, application, size_request=None, style_context=None):
		"""
		:param tuple size_request: The size to set for the canvas.
		"""
		self.application = application
		self.style_context = style_context
		self.config = application.config
		"""A reference to the King Phisher client configuration."""
		self.figure, _ = pyplot.subplots()
		self.figure.set_facecolor(self.get_color('bg', ColorHexCode.WHITE))
		self.axes = self.figure.get_axes()
		self.canvas = FigureCanvas(self.figure)
		self.manager = None
		self.minimum_size = (380, 200)
		"""An absolute minimum size for the canvas."""
		if size_request is not None:
			self.resize(*size_request)
		self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed)
		self.canvas.show()
		self.navigation_toolbar = NavigationToolbar(self.canvas, self.application.get_active_window())

		self.popup_menu = managers.MenuManager()
		self.popup_menu.append('Export', self.signal_activate_popup_menu_export)
		self.popup_menu.append('Refresh', self.signal_activate_popup_refresh)

		menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
		menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar)
		self._menu_item_show_toolbar = menu_item
		self.popup_menu.append_item(menu_item)

		self.navigation_toolbar.hide()
		self._legend = None

	@property
	def rpc(self):
		return self.application.rpc

	@staticmethod
	def _ax_hide_ticks(ax):
		for tick in ax.yaxis.get_major_ticks():
			tick.tick1On = False
			tick.tick2On = False

	@staticmethod
	def _ax_set_spine_color(ax, spine_color):
		for pos in ('top', 'right', 'bottom', 'left'):
			ax.spines[pos].set_color(spine_color)

	def add_legend_patch(self, legend_rows, fontsize=None):
		if self._legend is not None:
			self._legend.remove()
			self._legend = None
		fontsize = fontsize or self.fontsize_scale
		legend_bbox = self.figure.legend(
			tuple(patches.Patch(color=patch_color) for patch_color, _ in legend_rows),
			tuple(label for _, label in legend_rows),
			borderaxespad=1.25,
			fontsize=fontsize,
			frameon=True,
			handlelength=1.5,
			handletextpad=0.75,
			labelspacing=0.3,
			loc='lower right'
		)
		legend_bbox.legendPatch.set_linewidth(0)
		self._legend = legend_bbox

	def get_color(self, color_name, default):
		"""
		Get a color by its style name such as 'fg' for foreground. If the
		specified color does not exist, default will be returned. The underlying
		logic for this function is provided by
		:py:func:`~.gui_utilities.gtk_style_context_get_color`.

		:param str color_name: The style name of the color.
		:param default: The default color to return if the specified one was not found.
		:return: The desired color if it was found.
		:rtype: tuple
		"""
		color_name = 'theme_color_graph_' + color_name
		sc_color = gui_utilities.gtk_style_context_get_color(self.style_context, color_name, default)
		return (sc_color.red, sc_color.green, sc_color.blue)

	def make_window(self):
		"""
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
		if self.manager is None:
			self.manager = FigureManager(self.canvas, 0)
		self.navigation_toolbar.destroy()
		self.navigation_toolbar = self.manager.toolbar
		self._menu_item_show_toolbar.set_active(True)
		window = self.manager.window
		window.set_transient_for(self.application.get_active_window())
		window.set_title(self.graph_title)
		return window

	@property
	def fontsize_scale(self):
		scale = self.markersize_scale
		if scale < 5:
			fontsize = 'xx-small'
		elif scale < 7:
			fontsize = 'x-small'
		elif scale < 9:
			fontsize = 'small'
		else:
			fontsize = 'medium'
		return fontsize

	@property
	def markersize_scale(self):
		bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
		return bbox.width * self.figure.dpi * 0.01

	def mpl_signal_canvas_button_pressed(self, event):
		if event.button != 3:
			return
		self.popup_menu.menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time())
		return True

	def signal_activate_popup_menu_export(self, action):
		dialog = extras.FileChooserDialog('Export Graph', self.application.get_active_window())
		file_name = self.config['campaign_name'] + '.png'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		self.figure.savefig(destination_file, dpi=200, facecolor=self.figure.get_facecolor(), format='png')

	def signal_activate_popup_refresh(self, event):
		self.refresh()

	def signal_toggled_popup_menu_show_toolbar(self, widget):
		if widget.get_property('active'):
			self.navigation_toolbar.show()
		else:
			self.navigation_toolbar.hide()

	def resize(self, width=0, height=0):
		"""
		Attempt to resize the canvas. Regardless of the parameters the canvas
		will never be resized to be smaller than :py:attr:`.minimum_size`.

		:param int width: The desired width of the canvas.
		:param int height: The desired height of the canvas.
		"""
		min_width, min_height = self.minimum_size
		width = max(width, min_width)
		height = max(height, min_height)
		self.canvas.set_size_request(width, height)
Exemplo n.º 3
0
class Profile(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Profile")
        #f = Figure(figsize=(5,4), dpi=100)
        f = Figure()
        self.axes = f.add_subplot(111)
        self.axes.set_xlabel('Time (sec.)')
        self.axes.set_ylabel('Temperature (C)')
        self.axes.set_xlim(0, 5*60)
        self.axes.set_ylim(20, 300)
        self.axes.grid()
        self.axes.xaxis.set_major_formatter(FuncFormatter(self.format_xaxis))
        self.x = []
        self.y = []
        self.plot, = self.axes.plot(self.x, self.y, 'o-', picker=5)
        self.minutes = False
        self.file_name = None

        self.canvas = FigureCanvas(f)
        self.canvas.set_size_request(800,600)
        self.canvas.mpl_connect('button_press_event', self.onclick)
        self.canvas.mpl_connect('button_release_event', self.onrelease)
        self.canvas.mpl_connect('pick_event', self.onpick)
        self.canvas.mpl_connect('motion_notify_event', self.onmotion)
        self.picking = None

        self.store = Gtk.ListStore(str, str)
        self.tree = Gtk.TreeView(self.store)

        renderer = Gtk.CellRendererText()
        renderer.set_property("editable", True)
        renderer.connect('edited', self.on_time_edited)
        column = Gtk.TreeViewColumn("Time", renderer, text=0)
        self.tree.append_column(column)

        renderer = Gtk.CellRendererText()
        renderer.set_property("editable", True)
        renderer.connect('edited', self.on_temp_edited)
        column = Gtk.TreeViewColumn("Temperature", renderer, text=1)
        self.tree.append_column(column)

        self.box = Gtk.Box()
        self.box.pack_start(self.canvas, False, False, 0)
        self.box.pack_start(self.tree, True, True, 0)

        action_group = Gtk.ActionGroup("profile_actions")
        action_group.add_actions([
            ("FileMenu", None, "File", None, None, None),
            ("FileNew", Gtk.STOCK_NEW, "_New", "<control>N", None, self.on_file_new),
            ("FileOpen", Gtk.STOCK_OPEN, "_Open", "<control>O", None, self.on_file_open),
            ("FileSave", Gtk.STOCK_SAVE, "_Save", "<control>S", None, self.on_file_save),
            ("FileSaveAs", Gtk.STOCK_SAVE_AS, "Save _As…", "<shift><control>S", None, self.on_file_save_as),
            ("FileQuit", Gtk.STOCK_QUIT, "_Quit", "<control>Q", None, Gtk.main_quit)
        ])

        uimanager = Gtk.UIManager()
        uimanager.add_ui_from_string(UI_INFO)
        accelgroup = uimanager.get_accel_group()
        self.add_accel_group(accelgroup)
        uimanager.insert_action_group(action_group)

        menubar = uimanager.get_widget("/MenuBar")
        self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.vbox.pack_start(menubar, False, False, 0)
        self.vbox.pack_start(self.box, True, True, 0)
        self.statusbar = Gtk.Statusbar()
        self.status_pos = self.statusbar.get_context_id("position")
        self.vbox.pack_start(self.statusbar, False, False, 0)

        self.add(self.vbox)

    def open_file(self, name):
        reader = csv.reader(open(name, 'rb'))
        x, y = zip(*reader)
        x = [ float(i) for i in x]
        y = [ float(i) for i in y]
        self.x, self.y = x, y
        self.file_name = name
        self.set_title('Profile - ' + name)
        self.update_data()
        self.update_scale()
        self.canvas.draw()

    def save_file(self, name):
        writer = csv.writer(open(name, 'wd'))
        writer.writerows(zip(self.x, self.y))
        self.file_name = name
        self.set_title('Profile - ' + name)

    def on_file_new(self, widget):
        self.file_name = None
        self.set_title('Profile')
        self.x = []
        self.y = []
        self.store.clear()
        self.update_data()
        self.update_scale()
        self.canvas.draw()

    def on_file_open(self, widget):
        dialog = Gtk.FileChooserDialog("", self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
        dialog.set_current_folder_uri(Gio.file_new_for_path(os.curdir).get_uri())
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            try:
                self.open_file(dialog.get_filename())
            except:
                pass       
        dialog.destroy()

    def on_file_save(self, widget):
        if self.file_name is None:
            self.on_file_save_as(widget)
        else:
            self.save_file(self.file_name)

    def on_file_save_as(self, widget):
        dialog = Gtk.FileChooserDialog("", self,
            Gtk.FileChooserAction.SAVE,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_SAVE_AS, Gtk.ResponseType.OK))
        dialog.set_current_folder_uri(Gio.file_new_for_path(os.curdir).get_uri())
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            self.save_file(dialog.get_filename())
        dialog.destroy()

    def format_xaxis(self, x, pos):
        return self.format_x(x)

    def format_x(self, val):
        m, s = 0, val
        if self.minutes:
            m = int(val / 60)
            s = val % 60
        if m:
            return '%dm%.1fs'%(m,s)
        else:
            return '%.1fs'%s

    def format_y(self, val):
        return '%.1f°C'%val

    def on_time_edited(self, widget, path, text):
        treeiter = self.store.get_iter(path)
        try:
            m, s = "0", "0"
            if 'm' in text:
                m, text = text.split('m')
            if 's' in text:
                s, text = text.split('s')
            else:
                text = ""
            assert len(text) == 0
            val = int(m) * 60.0 + round(float(s), 1)
        except:
            return

        at = max(0, int(path))
        if at != 0:
            val = max(val, self.x[at-1])
        if at != len(self.x)-1:
            val = min(val, self.x[at+1])
        self.store.set(treeiter, 0, self.format_x(val))
        self.x[int(path)] = val
        self.update_data()
        self.update_scale()
        self.canvas.draw()

    def on_temp_edited(self, widget, path, text):
        treeiter = self.store.get_iter(path)
        try:
            if '°' in text:
                text, _ = text.split('°')
            if 'c' in text.lower():
                text, _ = text.lower().split('c')
            val = round(float(text), 1)
        except:
            return
        val = min(300.0, val)
        val = max(20.0, val)
        self.store.set(treeiter, 1, self.format_y(val))
        self.y[int(path)] = val
        self.update_data()
        self.canvas.draw()

    def update_data(self):
        self.plot.set_data(self.x, self.y)

    def update_scale(self):
        if len(self.x) and self.x[-1] + 30 > 5 * 60:
            minutes = int((self.x[-1] + 90) / 60)
        else:
            minutes = 5
        self.axes.set_xlim(0, minutes*60)

    def onclick(self, event):
        if self.picking is not None or event.button != 1:
            return
        xdata = round(event.xdata, 1)
        ydata = round(event.ydata, 1)
        at = bisect.bisect(self.x, xdata)
        self.x.insert(at, xdata)
        self.y.insert(at, ydata)
        self.store.insert(at, [self.format_x(xdata), self.format_y(ydata)])
        self.update_data()
        self.update_scale()
        self.canvas.draw()

    def onrelease(self, event):
        if self.picking is None:
            return
        self.update_scale()
        self.canvas.draw()
        self.picking = None

    def onpick(self, event):
        on = event.artist
        ind = event.ind[0]
        if event.mouseevent.button == 1:
            self.picking = ind
        elif event.mouseevent.button == 3:
            self.x.pop(ind)
            self.y.pop(ind)
            self.store.remove(self.store.get_iter(Gtk.TreePath(ind)))
            self.update_data()
            self.update_scale()
            self.canvas.draw()

    def onmotion(self, event):
        self.statusbar.remove_all(self.status_pos)
        if event.xdata is None or event.ydata is None:
            return
        self.statusbar.push(self.status_pos,
            self.format_x(event.xdata) + ', ' + self.format_y(event.ydata))
        if self.picking is None:
            return
        xdata = max(0, round(event.xdata, 1))
        ydata = round(event.ydata, 1)
        ydata = min(300.0, ydata)
        ydata = max(20.0, ydata)
        if self.picking != 0:
            xdata = max(xdata, self.x[self.picking-1])
        if self.picking != len(self.x)-1:
            xdata = min(xdata, self.x[self.picking+1])
        self.x[self.picking] = xdata
        self.y[self.picking] = ydata
        treeiter = self.store.get_iter(Gtk.TreePath(self.picking))
        self.store.set(treeiter, 0, self.format_x(xdata), 1, self.format_y(ydata))
        self.update_data()
        self.canvas.draw()
Exemplo n.º 4
0
class GraphBase(object):
    """
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
    name = 'Unknown'
    """The name of the graph provider."""
    name_human = 'Unknown'
    """The human readable name of the graph provider used for UI identification."""
    graph_title = 'Unknown'
    """The title that will be given to the graph."""
    table_subscriptions = []
    """A list of tables from which information is needed to produce the graph."""
    is_available = True

    def __init__(self, application, size_request=None, style_context=None):
        """
		:param tuple size_request: The size to set for the canvas.
		"""
        self.application = application
        self.style_context = style_context
        self.config = application.config
        """A reference to the King Phisher client configuration."""
        self.figure, _ = pyplot.subplots()
        self.figure.set_facecolor(self.get_color('bg', ColorHexCode.WHITE))
        self.axes = self.figure.get_axes()
        self.canvas = FigureCanvas(self.figure)
        self.manager = None
        self.minimum_size = (380, 200)
        """An absolute minimum size for the canvas."""
        if size_request is not None:
            self.resize(*size_request)
        self.canvas.mpl_connect('button_press_event',
                                self.mpl_signal_canvas_button_pressed)
        self.canvas.show()
        self.navigation_toolbar = NavigationToolbar(
            self.canvas, self.application.get_active_window())
        self.popup_menu = Gtk.Menu.new()

        menu_item = Gtk.MenuItem.new_with_label('Export')
        menu_item.connect('activate', self.signal_activate_popup_menu_export)
        self.popup_menu.append(menu_item)

        menu_item = Gtk.MenuItem.new_with_label('Refresh')
        menu_item.connect('activate', self.signal_activate_popup_refresh)
        self.popup_menu.append(menu_item)

        menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
        menu_item.connect('toggled',
                          self.signal_toggled_popup_menu_show_toolbar)
        self._menu_item_show_toolbar = menu_item
        self.popup_menu.append(menu_item)
        self.popup_menu.show_all()
        self.navigation_toolbar.hide()
        self._legend = None

    @property
    def rpc(self):
        return self.application.rpc

    @staticmethod
    def _ax_hide_ticks(ax):
        for tick in ax.yaxis.get_major_ticks():
            tick.tick1On = False
            tick.tick2On = False

    @staticmethod
    def _ax_set_spine_color(ax, spine_color):
        for pos in ('top', 'right', 'bottom', 'left'):
            ax.spines[pos].set_color(spine_color)

    def add_legend_patch(self, legend_rows, fontsize=None):
        if self._legend is not None:
            self._legend.remove()
            self._legend = None
        fontsize = fontsize or self.fontsize_scale
        legend_bbox = self.figure.legend(tuple(
            patches.Patch(color=patch_color)
            for patch_color, _ in legend_rows),
                                         tuple(label
                                               for _, label in legend_rows),
                                         borderaxespad=1.25,
                                         fontsize=fontsize,
                                         frameon=True,
                                         handlelength=1.5,
                                         handletextpad=0.75,
                                         labelspacing=0.3,
                                         loc='lower right')
        legend_bbox.legendPatch.set_linewidth(0)
        self._legend = legend_bbox

    def get_color(self, color_name, default):
        """
		Get a color by its style name such as 'fg' for foreground. If the
		specified color does not exist, default will be returned. The underlying
		logic for this function is provided by
		:py:func:`~.gui_utilities.gtk_style_context_get_color`.

		:param str color_name: The style name of the color.
		:param default: The default color to return if the specified one was not found.
		:return: The desired color if it was found.
		:rtype: tuple
		"""
        color_name = 'theme_color_graph_' + color_name
        sc_color = gui_utilities.gtk_style_context_get_color(
            self.style_context, color_name, default)
        return (sc_color.red, sc_color.green, sc_color.blue)

    def make_window(self):
        """
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
        if self.manager is None:
            self.manager = FigureManager(self.canvas, 0)
        self.navigation_toolbar.destroy()
        self.navigation_toolbar = self.manager.toolbar
        self._menu_item_show_toolbar.set_active(True)
        window = self.manager.window
        window.set_transient_for(self.application.get_active_window())
        window.set_title(self.graph_title)
        return window

    @property
    def fontsize_scale(self):
        scale = self.markersize_scale
        if scale < 5:
            fontsize = 'xx-small'
        elif scale < 7:
            fontsize = 'x-small'
        elif scale < 9:
            fontsize = 'small'
        else:
            fontsize = 'medium'
        return fontsize

    @property
    def markersize_scale(self):
        bbox = self.axes[0].get_window_extent().transformed(
            self.figure.dpi_scale_trans.inverted())
        return bbox.width * self.figure.dpi * 0.01

    def mpl_signal_canvas_button_pressed(self, event):
        if event.button != 3:
            return
        self.popup_menu.popup(None, None, None, None, event.button,
                              Gtk.get_current_event_time())
        return True

    def signal_activate_popup_menu_export(self, action):
        dialog = extras.FileChooserDialog('Export Graph',
                                          self.application.get_active_window())
        file_name = self.config['campaign_name'] + '.png'
        response = dialog.run_quick_save(file_name)
        dialog.destroy()
        if not response:
            return
        destination_file = response['target_path']
        self.figure.savefig(destination_file,
                            dpi=200,
                            facecolor=self.figure.get_facecolor(),
                            format='png')

    def signal_activate_popup_refresh(self, event):
        self.refresh()

    def signal_toggled_popup_menu_show_toolbar(self, widget):
        if widget.get_property('active'):
            self.navigation_toolbar.show()
        else:
            self.navigation_toolbar.hide()

    def resize(self, width=0, height=0):
        """
		Attempt to resize the canvas. Regardless of the parameters the canvas
		will never be resized to be smaller than :py:attr:`.minimum_size`.

		:param int width: The desired width of the canvas.
		:param int height: The desired height of the canvas.
		"""
        min_width, min_height = self.minimum_size
        width = max(width, min_width)
        height = max(height, min_height)
        self.canvas.set_size_request(width, height)
Exemplo n.º 5
0
class CampaignGraph(object):
	"""
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
	name = 'Unknown'
	"""The name of the graph provider."""
	name_human = 'Unknown'
	"""The human readable name of the graph provider used for UI identification."""
	graph_title = 'Unknown'
	"""The title that will be given to the graph."""
	table_subscriptions = []
	"""A list of tables from which information is needed to produce the graph."""
	is_available = True
	def __init__(self, config, parent, size_request=None):
		"""
		:param dict config: The King Phisher client configuration.
		:param parent: The parent window for this object.
		:type parent: :py:class:`Gtk.Window`
		:param tuple size_request: The size to set for the canvas.
		"""
		self.config = config
		"""A reference to the King Phisher client configuration."""
		self.parent = parent
		"""The parent :py:class:`Gtk.Window` instance."""
		self.figure, _ = pyplot.subplots()
		self.axes = self.figure.get_axes()
		self.canvas = FigureCanvas(self.figure)
		self.manager = None
		if size_request:
			self.canvas.set_size_request(*size_request)
		self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed)
		self.canvas.show()
		self.navigation_toolbar = NavigationToolbar(self.canvas, self.parent)
		self.popup_menu = Gtk.Menu.new()

		menu_item = Gtk.MenuItem.new_with_label('Export')
		menu_item.connect('activate', self.signal_activate_popup_menu_export)
		self.popup_menu.append(menu_item)

		menu_item = Gtk.MenuItem.new_with_label('Refresh')
		menu_item.connect('activate', lambda action: self.refresh())
		self.popup_menu.append(menu_item)

		menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
		menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar)
		self._menu_item_show_toolbar = menu_item
		self.popup_menu.append(menu_item)
		self.popup_menu.show_all()
		self.navigation_toolbar.hide()

	def _load_graph(self, info_cache):
		raise NotImplementedError()

	def _graph_bar_set_yparams(self, top_lim):
		min_value = top_lim + (top_lim * 0.075)
		if min_value <= 25:
			scale = 5
		else:
			scale = scale = 10 ** (len(str(int(min_value))) - 1)
		inc_scale = scale
		while scale <= min_value:
			scale += inc_scale
		top_lim = scale

		ax = self.axes[0]
		yticks = set((round(top_lim * 0.5), top_lim))
		ax.set_yticks(tuple(yticks))
		ax.set_ylim(top=top_lim)
		return

	def _graph_null_pie(self, title):
		ax = self.axes[0]
		ax.pie((100,), labels=(title,), colors=(MPL_COLOR_NULL,), autopct='%1.0f%%', shadow=True, startangle=90)
		ax.axis('equal')
		return

	def add_legend_patch(self, legend_rows, fontsize=None):
		handles = []
		if not fontsize:
			scale = self.markersize_scale
			if scale < 5:
				fontsize = 'xx-small'
			elif scale < 7:
				fontsize = 'x-small'
			elif scale < 9:
				fontsize = 'small'
			else:
				fontsize = 'medium'
		for row in legend_rows:
			handles.append(patches.Patch(color=row[0], label=row[1]))
		self.axes[0].legend(handles=handles, fontsize=fontsize, loc='lower right')

	def graph_bar(self, bars, color=None, xticklabels=None, ylabel=None):
		"""
		Create a standard bar graph with better defaults for the standard use
		cases.

		:param list bars: The values of the bars to graph.
		:param color: The color of the bars on the graph.
		:type color: list, str
		:param list xticklabels: The labels to use on the x-axis.
		:param str ylabel: The label to give to the y-axis.
		:return: The bars created using :py:mod:`matplotlib`
		:rtype: `matplotlib.container.BarContainer`
		"""
		color = color or MPL_COLOR_NULL
		width = 0.25
		ax = self.axes[0]
		self._graph_bar_set_yparams(max(bars) if bars else 0)
		bars = ax.bar(range(len(bars)), bars, width, color=color)
		ax.set_xticks([float(x) + (width / 2) for x in range(len(bars))])
		if xticklabels:
			ax.set_xticklabels(xticklabels, rotation=30)
			for col in bars:
				height = col.get_height()
				ax.text(col.get_x() + col.get_width() / 2.0, height, "{0:,}".format(height), ha='center', va='bottom')
		if ylabel:
			ax.set_ylabel(ylabel)
		self.figure.subplots_adjust(bottom=0.25)
		return bars

	def make_window(self):
		"""
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
		if self.manager == None:
			self.manager = FigureManager(self.canvas, 0)
		self.navigation_toolbar.destroy()
		self.navigation_toolbar = self.manager.toolbar
		self._menu_item_show_toolbar.set_active(True)
		window = self.manager.window
		window.set_transient_for(self.parent)
		window.set_title(self.graph_title)
		return window

	@property
	def markersize_scale(self):
		bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
		return max(bbox.width, bbox.width) * self.figure.dpi * 0.01

	def mpl_signal_canvas_button_pressed(self, event):
		if event.button != 3:
			return
		self.popup_menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time())
		return True

	def signal_activate_popup_menu_export(self, action):
		dialog = gui_utilities.UtilityFileChooser('Export Graph', self.parent)
		file_name = self.config['campaign_name'] + '.png'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		self.figure.savefig(destination_file, format='png')

	def signal_toggled_popup_menu_show_toolbar(self, widget):
		if widget.get_property('active'):
			self.navigation_toolbar.show()
		else:
			self.navigation_toolbar.hide()

	def load_graph(self):
		"""Load the graph information via :py:meth:`.refresh`."""
		self.refresh()

	def refresh(self, info_cache=None, stop_event=None):
		"""
		Refresh the graph data by retrieving the information from the
		remote server.

		:param dict info_cache: An optional cache of data tables.
		:param stop_event: An optional object indicating that the operation should stop.
		:type stop_event: :py:class:`threading.Event`
		:return: A dictionary of cached tables from the server.
		:rtype: dict
		"""
		info_cache = (info_cache or {})
		if not self.parent.rpc:
			return info_cache
		for table in self.table_subscriptions:
			if stop_event and stop_event.is_set():
				return info_cache
			if not table in info_cache:
				info_cache[table] = tuple(self.parent.rpc.remote_table('campaign/' + table, self.config['campaign_id']))
		for ax in self.axes:
			ax.clear()
		self._load_graph(info_cache)
		self.axes[0].set_title(self.graph_title, y=1.03)
		self.canvas.draw()
		return info_cache
Exemplo n.º 6
0
class CampaignGraph(object):
	"""
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
	name = 'Unknown'
	"""The name of the graph provider."""
	name_human = 'Unknown'
	"""The human readable name of the graph provider used for UI identification."""
	graph_title = 'Unknown'
	"""The title that will be given to the graph."""
	table_subscriptions = []
	"""A list of tables from which information is needed to produce the graph."""
	is_available = True
	def __init__(self, application, size_request=None, style_context=None):
		"""
		:param tuple size_request: The size to set for the canvas.
		"""
		self.application = application
		self.style_context = style_context
		self.config = application.config
		"""A reference to the King Phisher client configuration."""
		self.figure, _ = pyplot.subplots()
		self.figure.set_facecolor(self.get_color('bg', ColorHexCode.WHITE))
		self.axes = self.figure.get_axes()
		self.canvas = FigureCanvas(self.figure)
		self.manager = None
		if size_request:
			self.canvas.set_size_request(*size_request)
		self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed)
		self.canvas.show()
		self.navigation_toolbar = NavigationToolbar(self.canvas, self.application.get_active_window())
		self.popup_menu = Gtk.Menu.new()

		menu_item = Gtk.MenuItem.new_with_label('Export')
		menu_item.connect('activate', self.signal_activate_popup_menu_export)
		self.popup_menu.append(menu_item)

		menu_item = Gtk.MenuItem.new_with_label('Refresh')
		menu_item.connect('activate', lambda action: self.refresh())
		self.popup_menu.append(menu_item)

		menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
		menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar)
		self._menu_item_show_toolbar = menu_item
		self.popup_menu.append(menu_item)
		self.popup_menu.show_all()
		self.navigation_toolbar.hide()
		self._legend = None

	@property
	def rpc(self):
		return self.application.rpc

	@staticmethod
	def _ax_hide_ticks(ax):
		for tick in ax.yaxis.get_major_ticks():
			tick.tick1On = False
			tick.tick2On = False

	@staticmethod
	def _ax_set_spine_color(ax, spine_color):
		for pos in ('top', 'right', 'bottom', 'left'):
			ax.spines[pos].set_color(spine_color)

	def _load_graph(self, info_cache):
		raise NotImplementedError()

	def add_legend_patch(self, legend_rows, fontsize=None):
		if self._legend is not None:
			self._legend.remove()
			self._legend = None
		if fontsize is None:
			scale = self.markersize_scale
			if scale < 5:
				fontsize = 'xx-small'
			elif scale < 7:
				fontsize = 'x-small'
			elif scale < 9:
				fontsize = 'small'
			else:
				fontsize = 'medium'
		legend_bbox = self.figure.legend(
			tuple(patches.Patch(color=patch_color) for patch_color, _ in legend_rows),
			tuple(label for _, label in legend_rows),
			borderaxespad=1.25,
			fontsize=fontsize,
			frameon=True,
			handlelength=1.5,
			handletextpad=0.75,
			labelspacing=0.3,
			loc='lower right'
		)
		legend_bbox.legendPatch.set_linewidth(0)
		self._legend = legend_bbox

	def get_color(self, color_name, default):
		"""
		Get a color by its style name such as 'fg' for foreground. If the
		specified color does not exist, default will be returned. The underlying
		logic for this function is provided by
		:py:func:`~.gui_utilities.gtk_style_context_get_color`.

		:param str color_name: The style name of the color.
		:param default: The default color to return if the specified one was not found.
		:return: The desired color if it was found.
		:rtype: tuple
		"""
		color_name = 'theme_color_graph_' + color_name
		sc_color = gui_utilities.gtk_style_context_get_color(self.style_context, color_name, default)
		return (sc_color.red, sc_color.green, sc_color.blue)

	def make_window(self):
		"""
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
		if self.manager is None:
			self.manager = FigureManager(self.canvas, 0)
		self.navigation_toolbar.destroy()
		self.navigation_toolbar = self.manager.toolbar
		self._menu_item_show_toolbar.set_active(True)
		window = self.manager.window
		window.set_transient_for(self.application.get_active_window())
		window.set_title(self.graph_title)
		return window

	@property
	def markersize_scale(self):
		bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
		return bbox.width * self.figure.dpi * 0.01

	def mpl_signal_canvas_button_pressed(self, event):
		if event.button != 3:
			return
		self.popup_menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time())
		return True

	def signal_activate_popup_menu_export(self, action):
		dialog = gui_utilities.FileChooser('Export Graph', self.application.get_active_window())
		file_name = self.config['campaign_name'] + '.png'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		self.figure.savefig(destination_file, format='png')

	def signal_toggled_popup_menu_show_toolbar(self, widget):
		if widget.get_property('active'):
			self.navigation_toolbar.show()
		else:
			self.navigation_toolbar.hide()

	def load_graph(self):
		"""Load the graph information via :py:meth:`.refresh`."""
		self.refresh()

	def refresh(self, info_cache=None, stop_event=None):
		"""
		Refresh the graph data by retrieving the information from the
		remote server.

		:param dict info_cache: An optional cache of data tables.
		:param stop_event: An optional object indicating that the operation should stop.
		:type stop_event: :py:class:`threading.Event`
		:return: A dictionary of cached tables from the server.
		:rtype: dict
		"""
		info_cache = (info_cache or {})
		if not self.rpc:
			return info_cache
		for table in self.table_subscriptions:
			if stop_event and stop_event.is_set():
				return info_cache
			if not table in info_cache:
				info_cache[table] = tuple(self.rpc.remote_table(table, query_filter={'campaign_id': self.config['campaign_id']}))
		for ax in self.axes:
			ax.clear()
		if self._legend is not None:
			self._legend.remove()
			self._legend = None
		self._load_graph(info_cache)
		self.figure.suptitle(
			self.graph_title,
			color=self.get_color('fg', ColorHexCode.BLACK),
			size=14,
			weight='bold',
			y=0.97
		)
		self.canvas.draw()
		return info_cache
Exemplo n.º 7
0
class CampaignGraph(object):
	"""
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
	name = 'Unknown'
	"""The name of the graph provider."""
	name_human = 'Unknown'
	"""The human readable name of the graph provider used for UI identification."""
	graph_title = 'Unknown'
	"""The title that will be given to the graph."""
	table_subscriptions = []
	"""A list of tables from which information is needed to produce the graph."""
	is_available = True
	def __init__(self, config, parent, size_request=None):
		"""
		:param dict config: The King Phisher client configuration.
		:param parent: The parent window for this object.
		:type parent: :py:class:`Gtk.Window`
		:param tuple size_request: The size to set for the canvas.
		"""
		self.config = config
		"""A reference to the King Phisher client configuration."""
		self.parent = parent
		"""The parent :py:class:`Gtk.Window` instance."""
		self.figure, _ = pyplot.subplots()
		self.axes = self.figure.get_axes()
		self.canvas = FigureCanvas(self.figure)
		self.manager = None
		if size_request:
			self.canvas.set_size_request(*size_request)
		self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed)
		self.canvas.show()
		self.navigation_toolbar = NavigationToolbar(self.canvas, self.parent)
		self.popup_menu = Gtk.Menu.new()

		menu_item = Gtk.MenuItem.new_with_label('Export')
		menu_item.connect('activate', self.signal_activate_popup_menu_export)
		self.popup_menu.append(menu_item)

		menu_item = Gtk.MenuItem.new_with_label('Refresh')
		menu_item.connect('activate', lambda action: self.refresh())
		self.popup_menu.append(menu_item)

		menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
		menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar)
		self._menu_item_show_toolbar = menu_item
		self.popup_menu.append(menu_item)
		self.popup_menu.show_all()
		self.navigation_toolbar.hide()

	def _load_graph(self, info_cache):
		raise NotImplementedError()

	def _graph_bar_set_yparams(self, top_lim):
		min_value = top_lim + (top_lim * 0.075)
		if min_value <= 25:
			scale = 5
		else:
			scale = scale = 10 ** (len(str(int(min_value))) - 1)
		inc_scale = scale
		while scale <= min_value:
			scale += inc_scale
		top_lim = scale

		ax = self.axes[0]
		yticks = set((round(top_lim * 0.5), top_lim))
		ax.set_yticks(tuple(yticks))
		ax.set_ylim(top=top_lim)
		return

	def _graph_null_pie(self, title):
		ax = self.axes[0]
		ax.pie((100,), labels=(title,), colors=(MPL_COLOR_NULL,), autopct='%1.0f%%', shadow=True, startangle=90)
		ax.axis('equal')
		return

	def add_legend_patch(self, legend_rows, fontsize=None):
		handles = []
		if not fontsize:
			scale = self.markersize_scale
			if scale < 5:
				fontsize = 'xx-small'
			elif scale < 7:
				fontsize = 'x-small'
			elif scale < 9:
				fontsize = 'small'
			else:
				fontsize = 'medium'
		for row in legend_rows:
			handles.append(patches.Patch(color=row[0], label=row[1]))
		self.axes[0].legend(handles=handles, fontsize=fontsize, loc='lower right')

	def graph_bar(self, bars, color=None, xticklabels=None, ylabel=None):
		"""
		Create a standard bar graph with better defaults for the standard use
		cases.

		:param list bars: The values of the bars to graph.
		:param color: The color of the bars on the graph.
		:type color: list, str
		:param list xticklabels: The labels to use on the x-axis.
		:param str ylabel: The label to give to the y-axis.
		:return: The bars created using :py:mod:`matplotlib`
		:rtype: `matplotlib.container.BarContainer`
		"""
		color = color or MPL_COLOR_NULL
		width = 0.25
		ax = self.axes[0]
		self._graph_bar_set_yparams(max(bars) if bars else 0)
		bars = ax.bar(range(len(bars)), bars, width, color=color)
		ax.set_xticks([float(x) + (width / 2) for x in range(len(bars))])
		if xticklabels:
			ax.set_xticklabels(xticklabels, rotation=30)
			for col in bars:
				height = col.get_height()
				ax.text(col.get_x() + col.get_width() / 2.0, height, "{0:,}".format(height), ha='center', va='bottom')
		if ylabel:
			ax.set_ylabel(ylabel)
		self.figure.subplots_adjust(bottom=0.25)
		return bars

	def make_window(self):
		"""
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
		if self.manager == None:
			self.manager = FigureManager(self.canvas, 0)
		self.navigation_toolbar.destroy()
		self.navigation_toolbar = self.manager.toolbar
		self._menu_item_show_toolbar.set_active(True)
		window = self.manager.window
		window.set_transient_for(self.parent)
		window.set_title(self.graph_title)
		return window

	@property
	def markersize_scale(self):
		bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
		return max(bbox.width, bbox.width) * self.figure.dpi * 0.01

	def mpl_signal_canvas_button_pressed(self, event):
		if event.button != 3:
			return
		self.popup_menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time())
		return True

	def signal_activate_popup_menu_export(self, action):
		dialog = gui_utilities.FileChooser('Export Graph', self.parent)
		file_name = self.config['campaign_name'] + '.png'
		response = dialog.run_quick_save(file_name)
		dialog.destroy()
		if not response:
			return
		destination_file = response['target_path']
		self.figure.savefig(destination_file, format='png')

	def signal_toggled_popup_menu_show_toolbar(self, widget):
		if widget.get_property('active'):
			self.navigation_toolbar.show()
		else:
			self.navigation_toolbar.hide()

	def load_graph(self):
		"""Load the graph information via :py:meth:`.refresh`."""
		self.refresh()

	def refresh(self, info_cache=None, stop_event=None):
		"""
		Refresh the graph data by retrieving the information from the
		remote server.

		:param dict info_cache: An optional cache of data tables.
		:param stop_event: An optional object indicating that the operation should stop.
		:type stop_event: :py:class:`threading.Event`
		:return: A dictionary of cached tables from the server.
		:rtype: dict
		"""
		info_cache = (info_cache or {})
		if not self.parent.rpc:
			return info_cache
		for table in self.table_subscriptions:
			if stop_event and stop_event.is_set():
				return info_cache
			if not table in info_cache:
				info_cache[table] = tuple(self.parent.rpc.remote_table('campaign/' + table, self.config['campaign_id']))
		for ax in self.axes:
			ax.clear()
		self._load_graph(info_cache)
		self.axes[0].set_title(self.graph_title, y=1.03)
		self.canvas.draw()
		return info_cache
Exemplo n.º 8
0
class Plotter(Gtk.Alignment):
    def __init__(self, loop=False, buffer_size=2500, xformat='%g', dpi=80):
        super().__init__()
        self.set(0.5, 0.5, 1, 1)

        self.format_x = FormatStrFormatter(xformat)
        self.ring_buffer = loop
        self.buffer_size = buffer_size
        self.colormap = cm.get_cmap('Dark2')
        self.axis_space = 0.92
        self.cursor_line = None
        self.cursor_points = {}
        self.plot_scales = {}
        self.lines = {}

        self.axis = {}
        self.data_type = {}
        self.values = None

        self.grid_mode = False
        self.grid_specs = {}
        self.grid_image = None
        self.grid_norm = Normalize()
        self.grid_snake = False

        self.fig = Figure(figsize=(10, 6), dpi=dpi)
        self.clear()

        self.canvas = FigureCanvas(self.fig)  # a Gtk.DrawingArea
        self.canvas.mpl_connect('motion_notify_event', self.on_mouse_motion)
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.toolbar = PlotterToolbar(self.canvas, dialogs.MAIN_WINDOW)

        box.pack_start(self.canvas, True, True, 0)
        box.pack_start(self.toolbar, False, False, 0)
        self.add(box)
        self.show_all()

    def clear(self, specs=None):
        """
        Clear the plot and configure it for the given specifications.

        :param specs: dictionary containing configuration parameters
        """
        self.fig.clear()
        self.fig.subplots_adjust(bottom=0.1,
                                 left=0.05,
                                 top=0.90,
                                 right=self.axis_space)
        specs = {} if specs is None else specs
        self.grid_mode = 'grid' in specs.get('scan_type', '')
        self.data_type = specs.get('data_type')
        self.values = misc.RecordArray(self.data_type,
                                       size=self.buffer_size,
                                       loop=self.ring_buffer)
        self.cursor_line = None
        self.lines = {}
        self.grid_snake = specs.get('grid_snake', False)
        self.grid_specs = {}
        self.grid_image = None
        self.grid_norm = Normalize()

        ax = self.fig.add_subplot(111)
        ax.yaxis.tick_right()
        ax.yaxis.set_major_formatter(ScalarFormatter())
        self.axis = {'default': ax}

        if specs:
            names = self.data_type['names'][1:]
            scales = specs.get('data_scale')
            if scales:
                self.plot_scales = {
                    ('default' if i == 0 else 'axis-{}'.format(i)): scale
                    for i, scale in enumerate(scales)
                }
            else:
                self.plot_scales = {
                    ('default' if i == 0 else 'axis-{}'.format(i)): (name, )
                    for i, name in enumerate(names)
                }

    def get_axis_for(self, name):
        """
        Return the axis for the named line

        :param name: line name
        :return: an axis object
        """
        return self.lines[name].axes

    def add_axis(self, name=None, label=""):
        """
        Add a named axis to the plot with the

        :param name: axis name
        :param label: axis label
        :return: matplotlib axis object
        """
        name = 'axis-{}'.format(len(self.axis)) if not name else name
        default = self.axis.get('default')
        index = len(self.axis) + 1
        axis_position = 1 / (self.axis_space**(index - 1))
        self.fig.subplots_adjust(right=self.axis_space**index)
        ax = self.fig.add_axes(default.get_position(),
                               sharex=default,
                               frameon=False)
        ax.spines['right'].set_position(('axes', axis_position))
        ax.yaxis.set_major_formatter(ScalarFormatter())
        ax.set_frame_on(True)
        ax.patch.set_visible(False)

        ax.yaxis.tick_right()
        ax.yaxis.set_label_position('right')
        ax.set_ylabel(label)
        for label in ax.get_xticklabels():
            label.set_visible(False)
        self.axis[name] = ax
        self.plot_scales[name] = ()
        return ax

    def add_line(self,
                 xpoints,
                 ypoints,
                 style='-',
                 name='',
                 lw=1,
                 axis="default",
                 alpha=1.0,
                 color=None,
                 redraw=True,
                 markevery=[]):
        """
        Add a named line to the plot

        :param xpoints: initial x axis values
        :param ypoints: initial y axis values
        :param style: matplotlib line style string
        :param name: line name, optional
        :param lw: line width
        :param axis: optional name of axis of add line to
        :param alpha: line transparency
        :param color: line color
        :param redraw: whether to redraw the line or note
        :param markevery: matplotlit 'markevery' parameter, set to None to show markers at every point
        """
        assert (len(xpoints) == len(ypoints))

        if axis not in self.axis:
            self.add_axis(axis)

        name = 'line-{}'.format(len(self.lines)) if not name else name
        color = self.colormap(len(self.lines)) if not color else color
        self.axis[axis].autoscale(False)
        xmin_current, xmax_current = self.axis[axis].get_xlim()
        ymin_current, ymax_current = self.axis[axis].get_ylim()
        line, = self.axis[axis].plot(xpoints,
                                     ypoints,
                                     '.',
                                     ls=style,
                                     lw=lw,
                                     markersize=8,
                                     label=name,
                                     alpha=alpha,
                                     markevery=markevery,
                                     color=color)

        # adjust axes limits as necessary
        xmin, xmax = misc.get_min_max(xpoints, ldev=0, rdev=0)
        ymin, ymax = misc.get_min_max(ypoints, ldev=1, rdev=1)

        xmin, xmax = min(xmin, xmin_current), max(xmax, xmax_current)
        ymin, ymax = min(ymin, ymin_current), max(ymax, ymax_current)
        line.axes.set_xlim(xmin, xmax)
        line.axes.set_ylim(ymin, ymax)
        self.lines[name] = line
        if name not in self.plot_scales[axis]:
            self.plot_scales[axis] += (name, )

        if len(xpoints) > 1:
            self.values.add_func(name, xpoints, ypoints)

        if redraw:
            self.redraw()

    def add_point(self, row, redraw=True):
        """
        Add a row of scan points to the data table

        :param row: sequence of values to add
        :param redraw: Whether to redraw the plot
        """

        if numpy.nan in row:
            return

        self.values.append(row)
        x_name = self.data_type['names'][0]
        if self.grid_mode:
            # no lines for grid mode
            self.update_grid_data()
        elif not self.lines:
            count = 0
            for axis, lines in self.plot_scales.items():
                if axis != 'default':
                    self.add_axis(name=axis)
                for line in lines:
                    self.add_line(self.values.data[x_name],
                                  self.values.data[line],
                                  color=self.colormap(count),
                                  name=line,
                                  axis=axis,
                                  markevery=[-1])
                    count += 1
        else:
            xmin, xmax = misc.get_min_max(self.values.data[x_name],
                                          ldev=0,
                                          rdev=0)
            for axis, lines in self.plot_scales.items():
                ymin = ymax = None
                ax = None
                for name in lines:
                    line = self.lines[name]
                    line.set_data(self.values.data[x_name],
                                  self.values.data[name])
                    ax = line.axes
                    ylo, yhi = misc.get_min_max(self.values.data[name],
                                                ldev=0.5,
                                                rdev=0.5)
                    if ymin is None:
                        ymin, ymax = ylo, yhi
                    else:
                        ymin, ymax = min(ymin, ylo), max(ymax, yhi)
                        ymin, ymax = ymin, ymax

                # adjust axes limits as necessary
                if ax is not None:
                    offset = (ymax - ymin) * .1
                    ax.set_ylim(ymin - offset, ymax + offset)
                    ax.set_xlim(xmin, xmax)

            if len(self.lines) > 1:
                default = self.axis.get('default')
                xmin_current, xmax_current = default.get_xlim()
                default.set_xlim(min(xmin, xmin_current),
                                 max(xmax, xmax_current))

        if redraw:
            self.redraw()

    def new_row(self, index):
        """
        Prepare for A new row of data
        :param index: row index for next row
        """

        if self.grid_mode and index > 1:
            # for slew grid scans, data needs to be padded/truncated
            y_name = self.data_type['names'][1]
            yo = self.values.data[y_name]

            x_size = (yo == yo[0]).sum()
            y_size = index
            pad = x_size * y_size - yo.shape[0]
            if pad == 0:
                return
            elif pad > 0:
                for i in range(pad):
                    self.values.append(self.values.data[-1])  # padding
            elif pad < 0:
                self.values.length = x_size * y_size
            self.update_grid_data()

    def update_grid_data(self):
        """
        Update the grid image values
        """
        x_name, y_name, counts_name = self.data_type['names'][:3]
        xo = self.values.data[x_name]
        yo = self.values.data[y_name]
        counts = self.values.data[counts_name]

        x_min, x_max = xo.min(), xo.max()
        y_min, y_max = yo.min(), yo.max()

        self.grid_norm.autoscale(counts)

        xsize = (yo == yo[0]).sum()
        ysize = int(numpy.ceil(yo.shape[0] / xsize))

        # pad unfilled values with nan
        blanks = xsize * ysize - counts.shape[0]
        if blanks:
            counts = numpy.pad(counts, (0, blanks),
                               'constant',
                               constant_values=(numpy.nan, numpy.nan))

        count_data = numpy.resize(counts, (ysize, xsize))

        # flip alternate rows
        if self.grid_snake:
            count_data[1::2, :] = count_data[1::2, ::-1]

        self.grid_specs.update({
            'x': xo,
            'y': yo,
            'counts': count_data,
        })
        extent = [
            x_min,
            x_max,
            y_min,
            y_max,
        ]
        if self.grid_image is None:
            default = self.axis.get('default')
            self.grid_image = default.imshow(
                self.grid_specs['counts'],
                cmap=cm.get_cmap(GRID_COLORMAP),
                origin='lower',
                norm=self.grid_norm,
                extent=extent,
                aspect='auto',
                interpolation=GRID_INTERPOLATION,
            )
        else:
            self.grid_image.set_data(self.grid_specs['counts'])
            self.grid_image.set_extent(extent)

        # set axis limits
        self.grid_image.axes.set_xlim(extent[:2])
        self.grid_image.axes.set_ylim(extent[-2:])
        self.redraw()

    def get_records(self):
        """
        Return the data array manager for the plot
        """
        return self.values

    def set_labels(self, title="", x_label="", y1_label=""):
        default = self.axis.get('default')
        default.set_xlabel(x_label, ha='right', va='top')
        default.set_ylabel(y1_label)
        default.xaxis.set_label_coords(1.0, -0.075)

    def set_time_labels(self, labels, fmt, maj_int, min_int):
        default = self.axis.get('default')
        default.xaxis.set_major_locator(MinuteLocator(interval=maj_int))
        default.xaxis.set_minor_locator(SecondLocator(interval=min_int))
        if len(default.xaxis.get_major_ticks()) < len(labels):
            labels.pop(0)
        default.set_xticklabels(
            [d != ' ' and d.strftime(fmt) or '' for d in labels])

    def redraw(self):
        if not self.grid_mode:
            lines = list(self.lines.values())
            labels = list(self.lines.keys())
            self.axis['default'].legend(lines,
                                        labels,
                                        loc='upper left',
                                        bbox_to_anchor=(0, 1.075),
                                        ncol=8,
                                        fancybox=False,
                                        framealpha=0.0,
                                        edgecolor='inherit',
                                        borderaxespad=0,
                                        fontsize=9)
        self.canvas.draw_idle()

    def on_mouse_motion(self, event):
        default = self.axis.get('default')

        if event.inaxes and self.lines and not self.grid_mode:
            x, y = event.xdata, event.ydata

            if self.cursor_line is None:
                self.cursor_line = default.axvline(x,
                                                   lw=1,
                                                   color='#3a7ca8',
                                                   antialiased=None)
                for axis, lines in self.plot_scales.items():
                    for name in lines:
                        y_value = self.values(name, x)
                        ax = self.axis[axis]
                        if name in self.lines:
                            line = self.lines[name]
                            trans = transforms.blended_transform_factory(
                                ax.get_yticklabels()[0].get_transform(),
                                ax.transData)
                            self.cursor_points[name] = ax.text(
                                1,
                                y_value,
                                "< {}".format(name),
                                color=line.get_color(),
                                transform=trans,
                                ha="left",
                                va="center")
            else:
                self.cursor_line.set_xdata(x)
                for axis, lines in self.plot_scales.items():
                    for name in lines:
                        if name in self.lines:
                            y_value = self.values(name, x)
                            if name in self.cursor_points:
                                self.cursor_points[name].set_position(
                                    (1, y_value))
            self.canvas.draw_idle()
        else:
            if self.cursor_line:
                self.cursor_line.remove()
                self.cursor_line = None
                for name in list(self.cursor_points.keys()):
                    mark = self.cursor_points.pop(name)
                    mark.remove()
                self.canvas.draw_idle()
Exemplo n.º 9
0
class App(Gtk.Application):

    def __init__(self):
        '''
        Build GUI
        '''
        # build GUI from glade file
        self.builder = Gtk.Builder()
        self.glade_file = os.path.join(curr_dir, 'kfit.glade')
        self.builder.add_from_file(self.glade_file)
        # get the necessary ui objects
        self.main_window = self.builder.get_object('main_window')
        self.graph_box = self.builder.get_object('graph_box')
        self.gau_sw = self.builder.get_object('param_scroll_gau')
        self.lor_sw = self.builder.get_object('param_scroll_lor')
        self.voi_sw = self.builder.get_object('param_scroll_voi')
        self.lin_sw = self.builder.get_object('param_scroll_lin')
        self.param_viewport_gau = self.builder.get_object('param_viewport_gau')
        self.param_viewport_lor = self.builder.get_object('param_viewport_lor')
        self.param_viewport_voi = self.builder.get_object('param_viewport_voi')
        self.param_viewport_lin = self.builder.get_object('param_viewport_lin')
        self.statusbar_viewport = self.builder.get_object('statusbar_viewport')
        self.data_treeview = self.builder.get_object('data_treeview')
        self.column_entry_x = self.builder.get_object('column_entry_x')
        self.column_entry_y = self.builder.get_object('column_entry_y')
        self.graph_box = self.builder.get_object('graph_box')
        self.fname_textview = self.builder.get_object('fname_textview')
        self.fit_button = self.builder.get_object('fit_button')
        self.reset_button = self.builder.get_object('reset_button')
        self.settings_button = self.builder.get_object('settings_button')
        self.import_button = self.builder.get_object('import_button')
        self.export_button = self.builder.get_object('export_button')
        self.help_button = self.builder.get_object('help_button')
        self.output_textview = self.builder.get_object('output_textview')
        self.settings_dialog = self.builder.get_object('settings_dialog')
        self.sep_entry = self.builder.get_object('sep_entry')
        self.header_entry = self.builder.get_object('header_entry')
        self.skiprows_entry = self.builder.get_object('skiprows_entry')
        self.dtype_entry = self.builder.get_object('dtype_entry')
        self.encoding_entry = self.builder.get_object('encoding_entry')
        self.fit_method_entry = self.builder.get_object('fit_method_entry')
        self.add_gau = self.builder.get_object('add_gau')
        self.rem_gau = self.builder.get_object('rem_gau')
        self.add_lor = self.builder.get_object('add_lor')
        self.rem_lor = self.builder.get_object('rem_lor')
        self.add_voi = self.builder.get_object('add_voi')
        self.rem_voi = self.builder.get_object('rem_voi')
        self.add_lin = self.builder.get_object('add_lin')
        self.rem_lin = self.builder.get_object('rem_lin')

        # define other class attributes
        self.title = 'kfit'
        self.file_name = ''
        self.xcol_idx = 0
        self.ycol_idx = 1
        self.cmode_state = 0
        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        # configure help button
        self.help_button.set_label(' Help')
        help_image = Gtk.Image()
        help_image.set_from_file(
            os.path.join(curr_dir, '../images/dialog-question-symbolic.svg')
        )
        self.help_button.set_image(help_image)
        self.help_button.set_always_show_image(True)

        # for graph...
        x = np.linspace(0, 10, 500)
        y = models.gauss(x, 0.5, 4, 0.4) + \
            models.gauss(x, 0.8, 5, 0.2) + \
            models.gauss(x, 0.4, 6, 0.3) + 0.2
        self.data = pd.DataFrame([x, y]).T
        self.data.columns = ['x', 'y']
        self.x = self.data['x'].values
        self.y = self.data['y'].values
        self.xmin = self.data['x'].min()
        self.xmax = self.data['x'].max()
        plt.style.use(os.path.join(curr_dir, 'kfit.mplstyle'))
        self.figure = Figure(figsize=(10, 4), dpi=60)
        self.canvas = FigureCanvas(self.figure)
        self.canvas.set_size_request(900, 400)
        self.toolbar = NavigationToolbar(self.canvas, self.main_window)
        self.graph_box.pack_start(self.toolbar, True, True, 0)
        self.graph_box.pack_start(self.canvas, True, True, 0)
        self.cmode_toolitem = Gtk.ToolItem()
        self.cmode_box = Gtk.Box()
        self.cmode_box.set_margin_start(24)
        self.cmode_box.set_margin_end(36)
        self.cmode_radio_off = Gtk.RadioButton.new_with_label_from_widget(
            None, label='off'
        )
        self.cmode_radio_off.connect('toggled', self.toggle_copy_mode)
        self.cmode_radio_x = Gtk.RadioButton.new_from_widget(
            self.cmode_radio_off
        )
        self.cmode_radio_x.set_label('x')
        self.cmode_radio_x.connect('toggled', self.toggle_copy_mode)
        self.cmode_radio_y = Gtk.RadioButton.new_from_widget(
            self.cmode_radio_off
        )
        self.cmode_radio_y.set_label('y')
        self.cmode_radio_y.connect('toggled', self.toggle_copy_mode)
        self.cmode_box.pack_start(
            Gtk.Label(label='Copy mode:'), False, False, 0
        )
        self.cmode_box.pack_start(self.cmode_radio_off, False, False, 0)
        self.cmode_box.pack_start(self.cmode_radio_x, False, False, 0)
        self.cmode_box.pack_start(self.cmode_radio_y, False, False, 0)
        self.cmode_toolitem.add(self.cmode_box)
        self.toolbar.insert(self.cmode_toolitem, -1)

        # for fit...
        self.fit_method = 'least_squares'
        self.model = None
        self.result = None
        self.yfit = None
        self.ngau = 0
        self.nlor = 0
        self.nvoi = 0
        self.nlin = 1
        self.curves_df = None
        self.params_df = None
        self.params = Parameters()
        self.guesses = {
            'value': {},
            'min': {},
            'max': {}
        }
        self.usr_vals = {
            'value': {},
            'min': {},
            'max': {}
        }
        self.usr_entry_widgets = {}
        self.cid = None

        # for data view...
        self.fname_buffer = Gtk.TextBuffer()
        self.display_data()

        # for output...
        self.output_buffer = Gtk.TextBuffer()
        self.output_textview.set_buffer(self.output_buffer)

        # file import settings
        self.sep = ','
        self.header = 'infer'
        self.index_col = None
        self.skiprows = None
        self.dtype = None
        self.encoding = None

        # show initial plot
        self.plot()

        # add statusbar
        self.statusbar = Gtk.Statusbar()
        self.statusbar.set_margin_top(0)
        self.statusbar.set_margin_bottom(0)
        self.statusbar.set_margin_start(0)
        self.statusbar.set_margin_end(0)
        self.statusbar_viewport.add(self.statusbar)

        # connect signals
        events = {
            'on_fit_button_clicked': self.fit,
            'on_import_button_clicked': self.get_data,
            'on_settings_button_clicked': self.run_settings_dialog,
            'on_reset_button_clicked': self.hard_reset,
            'on_add_gau_clicked': self.on_add_gau_clicked,
            'on_rem_gau_clicked': self.on_rem_gau_clicked,
            'on_add_lor_clicked': self.on_add_lor_clicked,
            'on_rem_lor_clicked': self.on_rem_lor_clicked,
            'on_add_voi_clicked': self.on_add_voi_clicked,
            'on_rem_voi_clicked': self.on_rem_voi_clicked,
            'on_add_lin_clicked': self.on_add_lin_clicked,
            'on_rem_lin_clicked': self.on_rem_lin_clicked,
            'on_column_entry_changed': self.get_column_index,
            'on_export_button_clicked': self.export_data,
        }
        self.builder.connect_signals(events)

        # add accelerators / keyboard shortcuts
        self.accelerators = Gtk.AccelGroup()
        self.main_window.add_accel_group(self.accelerators)
        self.add_accelerator(self.fit_button, '<Control>f')
        self.add_accelerator(self.reset_button, '<Control>r')
        self.add_accelerator(self.settings_button, '<Control>p')
        self.add_accelerator(self.import_button, '<Control>o')
        self.add_accelerator(self.export_button, '<Control>s')
        self.add_accelerator(self.add_gau, 'g')
        self.add_accelerator(self.rem_gau, '<Shift>g')
        self.add_accelerator(self.add_lor, 'l')
        self.add_accelerator(self.rem_lor, '<Shift>l')
        self.add_accelerator(self.add_voi, 'v')
        self.add_accelerator(self.rem_voi, '<Shift>v')
        self.add_accelerator(self.add_lin, 'n')
        self.add_accelerator(self.rem_lin, '<Shift>n')

        # configure interface
        self.main_window.connect('destroy', Gtk.main_quit)

        self.init_param_widgets()
        # show the app window
        self.main_window.show_all()

    def plot(self):
        self.figure.clear()
        self.axis = self.figure.add_subplot(111)
        self.set_xlims()
        if len(self.x) >= 1000:
            self.axis.plot(
                self.x, self.y, c='#af87ff',
                linewidth=12, label='data'
            )
        else:
            self.axis.scatter(
                self.x, self.y, s=200, c='#af87ff',
                edgecolors='black', linewidth=1,
                label='data'
            )
        if self.result is not None:
            self.yfit = self.result.best_fit
            self.axis.plot(self.x, self.yfit, c='r', linewidth=2.5)
            cmap = cm.get_cmap('gnuplot')
            components = self.result.eval_components()
            for i, comp in enumerate(components):
                self.axis.plot(
                    self.x, components[comp],
                    linewidth=2.5, linestyle='--',
                    c=cmap(i/len(components)),
                    label=comp[:comp.find('_')]
                )
        self.axis.set_xlabel(self.data.columns[self.xcol_idx])
        self.axis.set_ylabel(self.data.columns[self.ycol_idx])
        self.axis.legend(loc='best')
        self.axis.set_xlim([self.xmin, self.xmax])
        self.canvas.draw()

    def fit(self, source=None, event=None):
        self.cmode_radio_off.set_active(True)
        self.toggle_copy_mode(self.cmode_radio_off)
        self.set_xrange_to_zoom()
        self.filter_nan()
        self.set_params()
        self.result = self.model.fit(
            data=self.y, params=self.params, x=self.x,
            method=self.fit_method
        )
        self.output_buffer.set_text(self.result.fit_report())
        self.plot()
        # overwrite widgets to clear input (not ideal method..)
        self.init_param_widgets()

    def init_model(self):
        # note: increment() ensures nlin >= 1
        self.model = models.line_mod(self.nlin)
        if self.ngau != 0:
            self.model += models.gauss_mod(self.ngau)
        if self.nlor != 0:
            self.model += models.lor_mod(self.nlor)
        if self.nvoi != 0:
            self.model += models.voigt_mod(self.nvoi)
        self.statusbar.push(
                self.statusbar.get_context_id('info'),
                "Model updated: " +
                str([self.ngau, self.nlor, self.nvoi, self.nlin])
        )

    def init_param_widgets(self):
        self.init_model()
        self.usr_entry_widgets = {
            'value': {},
            'min': {},
            'max': {}
        }
        labels = {}
        rnd = 3  # decimals to round to in placeholder text
        self.clear_param_viewports()

        # main boxes to hold user entry widgets
        self.vbox_gau = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.vbox_lor = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.vbox_voi = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.vbox_lin = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        for param_name in self.model.param_names:
            # set param label text
            labels[param_name] = Gtk.Label()
            labels[param_name].set_text(param_name)

            # make user entry widgets
            for key in self.usr_entry_widgets:
                self.usr_entry_widgets[key][param_name] = Gtk.Entry()
                if param_name in self.usr_vals[key]:
                    self.usr_entry_widgets[key][param_name]\
                        .set_placeholder_text(
                            str(round(self.usr_vals[key][param_name], rnd))
                        )
                else:
                    self.usr_entry_widgets[key][param_name]\
                        .set_placeholder_text(key)
                # set up connections
                self.usr_entry_widgets[key][param_name].connect(
                    'changed', self.update_usr_vals, self.usr_entry_widgets
                )

            # add widgets to respective layouts
            vbox_sub = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
            for key in self.usr_entry_widgets:
                vbox_sub.pack_start(
                    self.usr_entry_widgets[key][param_name],
                    False, False, pad
                )
            if param_name.find('gau') != -1:
                self.vbox_gau.pack_start(labels[param_name], False, False, pad)
                self.vbox_gau.pack_start(vbox_sub, False, False, pad)
                self.vbox_gau.set_halign(Gtk.Align.CENTER)
            if param_name.find('lor') != -1:
                self.vbox_lor.pack_start(labels[param_name], False, False, pad)
                self.vbox_lor.pack_start(vbox_sub, False, False, pad)
                self.vbox_lor.set_halign(Gtk.Align.CENTER)
            if param_name.find('voi') != -1:
                self.vbox_voi.pack_start(labels[param_name], False, False, pad)
                self.vbox_voi.pack_start(vbox_sub, False, False, pad)
                self.vbox_voi.set_halign(Gtk.Align.CENTER)
            if param_name.find('lin') != -1:
                self.vbox_lin.pack_start(labels[param_name], False, False, pad)
                self.vbox_lin.pack_start(vbox_sub, False, False, pad)
                self.vbox_lin.set_halign(Gtk.Align.CENTER)

        # Resize all of the entry widgets
        for key in self.usr_entry_widgets:
            for param, widget in self.usr_entry_widgets[key].items():
                widget.set_width_chars(7)

        # add/replace box in viewport
        self.param_viewport_gau.add(self.vbox_gau)
        self.param_viewport_lor.add(self.vbox_lor)
        self.param_viewport_voi.add(self.vbox_voi)
        self.param_viewport_lin.add(self.vbox_lin)
        for viewport in [self.param_viewport_gau, self.param_viewport_lor,
                         self.param_viewport_voi, self.param_viewport_lin]:
            viewport.show_all()

        if self.result is not None:
            self.set_params()
            self.update_param_widgets()

    def update_usr_vals(self, widget, entry_widget_dict):
        # get text input from each usr_entry_widget
        for val_type, param_dict in entry_widget_dict.items():
            for param, param_widget in param_dict.items():
                try:
                    self.usr_vals[val_type][param] = \
                        float(param_widget.get_text())
                except Exception:
                    pass

    def update_param_widgets(self):
        rnd = 3
        # the 'value' placeholder text is the result for that param
        # taken from self.result
        # the 'min' and 'max' text is from either the self.guesses
        # or from self.usr_vals
        for param in self.params:
            if param in self.result.best_values:
                self.usr_entry_widgets['value'][param].set_placeholder_text(
                    str(round(self.result.best_values[param], rnd))
                )
                self.usr_entry_widgets['min'][param].set_placeholder_text(
                    str(round(self.params[param].min, rnd))
                )
                self.usr_entry_widgets['max'][param].set_placeholder_text(
                    str(round(self.params[param].max, rnd))
                )

    def guess_params(self, source=None, event=None):
        for comp in self.model.components:
            if comp.prefix.find('gau') != -1 or \
                    comp.prefix.find('lor') != -1 or \
                    comp.prefix.find('voi') != -1:

                # need to define explicitly to make proper guesses
                c = comp.prefix + 'center'
                a = comp.prefix + 'amplitude'
                s = comp.prefix + 'sigma'
                f = comp.prefix + 'fraction'

                self.guesses['value'][c] = \
                    self.data.iloc[:, self.xcol_idx].mean()
                self.guesses['value'][a] = \
                    self.data.iloc[:, self.ycol_idx].mean()
                self.guesses['value'][s] = \
                    self.data.iloc[:, self.xcol_idx].std()
                self.guesses['min'][c] = None
                self.guesses['min'][a] = 0
                self.guesses['min'][s] = 0
                self.guesses['max'][c] = None
                self.guesses['max'][a] = None
                self.guesses['max'][s] = None

                if comp.prefix.find('voi') != -1:
                    self.guesses['value'][f] = 0.5
                    self.guesses['min'][f] = 0
                    self.guesses['max'][f] = 1
            else:
                slope = comp.prefix + 'slope'
                intc = comp.prefix + 'intercept'
                for p in [slope, intc]:
                    self.guesses['value'][p] = \
                        self.data.iloc[:, self.ycol_idx].mean()
                    self.guesses['min'][p] = None
                    self.guesses['max'][p] = None

    def set_params(self, source=None, event=None):
        self.params = Parameters()
        self.guess_params()
        self.update_usr_vals(None, self.usr_entry_widgets)
        vals = {}

        # fill params with any user-entered values
        # fill in blanks with guesses
        for param_name in self.model.param_names:
            for val_type in ['value', 'min', 'max']:
                if param_name in self.usr_vals[val_type]:
                    vals[val_type] = self.usr_vals[val_type][param_name]
                else:
                    vals[val_type] = self.guesses[val_type][param_name]
            self.params.add(
                name=param_name, value=vals['value'], vary=True,
                min=vals['min'], max=vals['max']
            )

    def set_xlims(self, source=None, event=None):
        self.xmin = np.min(self.x) - 0.02*(np.max(self.x) - np.min(self.x))
        self.xmax = np.max(self.x) + 0.02*(np.max(self.x) - np.min(self.x))

    def set_xrange_to_zoom(self):
        self.xmin, self.xmax = self.axis.get_xlim()
        range_bool = (self.x >= self.xmin) & (self.x <= self.xmax)
        self.x = self.x[range_bool]
        self.y = self.y[range_bool]

    def filter_nan(self):
        if True in np.isnan(self.x) or True in np.isnan(self.y):
            nanbool = (~np.isnan(self.x) & ~np.isnan(self.y))
            self.x = self.x[nanbool]
            self.y = self.y[nanbool]

    def increment(self, val, add):
        if add:
            if val == 'gau':
                self.ngau += 1
            if val == 'lor':
                self.nlor += 1
            if val == 'voi':
                self.nvoi += 1
            if val == 'lin':
                self.nlin += 1
        if not add:
            if val == 'gau':
                self.ngau -= 1
            if val == 'lor':
                self.nlor -= 1
            if val == 'voi':
                self.nvoi -= 1
            if val == 'lin':
                self.nlin -= 1

        # make sure value doesn't go below zero
        if self.ngau < 0:
            self.ngau = 0
        if self.nlor < 0:
            self.nlor = 0
        if self.nvoi < 0:
            self.nvoi = 0
        if self.nlin < 1:
            self.nlin = 1

    def clear_param_viewports(self):
        # clear any existing widgets from viewports
        for viewport in [self.param_viewport_gau, self.param_viewport_lin,
                         self.param_viewport_lor, self.param_viewport_voi]:
            if viewport.get_child():
                viewport.remove(viewport.get_child())

    def hard_reset(self, source=None, event=None):
        self.clear_param_viewports()
        self.ngau = 0
        self.nlor = 0
        self.nvoi = 0
        self.nlin = 1
        self.init_model()
        self.params = Parameters()
        self.result = None
        self.params_df = None
        self.curves_df = None
        self.guesses = {
            'value': {},
            'min': {},
            'max': {}
        }
        self.usr_vals = {
            'value': {},
            'min': {},
            'max': {}
        }
        self.output_buffer.set_text('')
        self.init_param_widgets()
        self.get_column_index()
        self.plot()
        self.cmode_radio_off.set_active(True)
        self.toggle_copy_mode(self.cmode_radio_off)

    def on_add_gau_clicked(self, source=None, event=None):
        self.increment('gau', True)
        self.init_param_widgets()

    def on_rem_gau_clicked(self, source=None, event=None):
        self.increment('gau', False)
        self.init_param_widgets()

    def on_add_lor_clicked(self, source=None, event=None):
        self.increment('lor', True)
        self.init_param_widgets()

    def on_rem_lor_clicked(self, source=None, event=None):
        self.increment('lor', False)
        self.init_param_widgets()

    def on_add_voi_clicked(self, source=None, event=None):
        self.increment('voi', True)
        self.init_param_widgets()

    def on_rem_voi_clicked(self, source=None, event=None):
        self.increment('voi', False)
        self.init_param_widgets()

    def on_add_lin_clicked(self, source=None, event=None):
        self.increment('lin', True)
        self.init_param_widgets()

    def on_rem_lin_clicked(self, source=None, event=None):
        self.increment('lin', False)
        self.init_param_widgets()

    def get_data(self, source=None, event=None):
        self.cmode_radio_off.set_active(True)
        self.toggle_copy_mode(self.cmode_radio_off)
        # reset column indices
        self.xcol_idx = 0
        self.column_entry_x.set_text(str(self.xcol_idx))
        self.ycol_idx = 1
        self.column_entry_y.set_text(str(self.ycol_idx))
        # open file dialog
        self.dialog = Gtk.FileChooserDialog(
            title='Import data file...', parent=self.main_window,
            action=Gtk.FileChooserAction.OPEN,
        )
        self.dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OPEN, Gtk.ResponseType.OK
        )
        filter_csv = Gtk.FileFilter()
        filter_csv.set_name('.csv files')
        filter_csv.add_mime_type('text/csv')
        self.dialog.add_filter(filter_csv)

        filter_any = Gtk.FileFilter()
        filter_any.set_name('All files')
        filter_any.add_pattern("*")
        self.dialog.add_filter(filter_any)
        self.dialog.set_default_size(800, 400)

        response = self.dialog.run()
        if response == Gtk.ResponseType.OK:
            self.file_name = self.dialog.get_filename()
            try:
                df = tools.to_df(
                    self.file_name, sep=self.sep, header=self.header,
                    index_col=self.index_col, skiprows=self.skiprows,
                    dtype=self.dtype, encoding=self.encoding
                )
                df.iloc[:, self.xcol_idx]
                df.iloc[:, self.ycol_idx]
            except Exception:
                self.statusbar.push(
                    self.statusbar.get_context_id('import_error'),
                    file_import_error_msg
                )
                self.dialog.destroy()
                return
        else:
            self.file_name = None
            self.statusbar.push(
                self.statusbar.get_context_id('import_canceled'),
                'Import canceled.'
            )
            self.dialog.destroy()
            return

        self.dialog.destroy()

        self.data = df
        self.display_data()
        self.result = None
        # reset x, y, and xlim
        self.x = self.data.iloc[:, self.xcol_idx].values
        self.y = self.data.iloc[:, self.ycol_idx].values
        self.filter_nan()
        self.set_xlims()
        self.plot()
        self.statusbar.push(
            self.statusbar.get_context_id('import_finished'),
            'Imported {}'.format(self.file_name)
        )

    def display_data(self):
        # remove any pre-existing columns from treeview
        for col in self.data_treeview.get_columns():
            self.data_treeview.remove_column(col)
        # create model
        # TODO: allow for other types, and infer from data
        col_types = [float for col in self.data.columns]
        list_store = Gtk.ListStore(*col_types)

        # fill model with data
        for row in self.data.itertuples():
            list_store.append(
                [row[i+1] for i, col in enumerate(self.data.columns)]
            )
        # set it as TreeView model
        self.data_treeview.set_model(list_store)
        # Create and append columns
        for i, col in enumerate(self.data.columns):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(col, renderer, text=i)
            self.data_treeview.append_column(column)
        self.fname_buffer.set_text('Source:  {}'.format(self.file_name))
        self.fname_textview.set_buffer(self.fname_buffer)

    def export_data(self, source=None, event=None):
        '''
        Export fit data and parameters to .csv
        '''
        self.file_export_dialog = Gtk.FileChooserDialog(
            title='Export results file...', parent=self.main_window,
            action=Gtk.FileChooserAction.SAVE,
        )
        self.file_export_dialog.add_buttons(
            Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
            Gtk.STOCK_OK, Gtk.ResponseType.OK
        )
        filter_csv = Gtk.FileFilter()
        filter_csv.set_name('.csv files')
        filter_csv.add_mime_type('text/csv')
        self.file_export_dialog.add_filter(filter_csv)
        response = self.file_export_dialog.run()

        if response == Gtk.ResponseType.OK:
            export_filename = self.file_export_dialog.get_filename()
            if export_filename.find('.csv') == -1:
                export_filename += '.csv'
            self.process_results()
            self.curves_df.to_csv(export_filename)
            self.params_df.to_csv(
                '{}.params.csv'.format(
                    export_filename[:export_filename.find('.csv')]
                )
            )
            self.statusbar.push(
                self.statusbar.get_context_id('export_results'),
                'Exported: {}'.format(export_filename)
            )
        else:
            self.statusbar.push(
                self.statusbar.get_context_id('export_canceled'),
                'Export canceled.'
            )
        self.file_export_dialog.hide()

    def process_results(self):
        if self.result is not None:
            self.params_df = pd.DataFrame.from_dict(
                self.result.best_values, orient='index'
            )
            self.params_df.index.name = 'parameter'
            self.params_df.columns = ['value']
            curves_dict = {
                'data': self.y,
                'total_fit': self.result.best_fit,
            }
            components = self.result.eval_components()
            for i, comp in enumerate(components):
                curves_dict[comp[:comp.find('_')]] = components[comp]
            self.curves_df = pd.DataFrame.from_dict(curves_dict)
            self.curves_df.index = self.x
            self.curves_df.index.name = self.data.columns[self.xcol_idx]
        else:
            self.statusbar.push(
                self.statusbar.get_context_id('no_fit_results'),
                'No fit results to export!'
            )
            self.file_export_dialog.hide()

    def get_column_index(self, source=None, event=None):
        # make sure user enters index that can be converted to int
        try:
            idx_x = int(self.column_entry_x.get_text())
        except ValueError:
            self.statusbar.push(
                self.statusbar.get_context_id('idx_type_error'),
                idx_type_error_msg
            )
            self.column_entry_x.set_text('')
            return
        try:
            idx_y = int(self.column_entry_y.get_text())
        except ValueError:
            self.statusbar.push(
                self.statusbar.get_context_id('idx_type_error'),
                idx_type_error_msg
                )
            self.column_entry_y.set_text('')
            return
        self.xcol_idx = idx_x
        self.ycol_idx = idx_y
        self.result = None
        # make sure user enters an index that's in the data range
        try:
            self.x = self.data.iloc[:, self.xcol_idx]
        except IndexError:
            self.statusbar.push(
                self.statusbar.get_context_id('idx_range_error'),
                idx_range_error_msg
            )
            self.column_entry_x.set_text(None)
            return
        try:
            self.y = self.data.iloc[:, self.ycol_idx]
        except IndexError:
            self.statusbar.push(
                self.statusbar.get_context_id('idx_range_error'),
                idx_range_error_msg
            )
            self.column_entry_y.set_text(None)
            return
        self.xmin = np.min(self.x)
        self.xmax = np.max(self.x)
        self.statusbar.push(
            self.statusbar.get_context_id('new_idx_success'),
            'Column Index (X) = ' + str(self.xcol_idx) + ', ' +
            'Column Index (Y) = ' + str(self.ycol_idx)
        )
        self.plot()

    def run_settings_dialog(self, source=None, event=None):
        '''
        Opens the settings dialog window and controls its behavior.
        '''
        # set label text for help button
        # couldn't do this in glade for some reason...
        import_help_button = self.builder.get_object('import_help_button')
        import_help_button.set_label('help')
        fit_help_button = self.builder.get_object('fit_help_button')
        fit_help_button.set_label('help')
        # run the dialog
        response = self.settings_dialog.run()

        if response == Gtk.ResponseType.APPLY:
            self.sep = self.sep_entry.get_text()
            if self.header_entry.get_text() != 'infer':
                self.header = int(self.header_entry.get_text())
            else:
                self.header = 'infer'
            if self.skiprows_entry.get_text() == 'None':
                self.skiprows = None
            else:
                self.skiprows = int(self.skiprows_entry.get_text())
            if self.dtype_entry.get_text() == 'None':
                self.dtype = None
            else:
                self.dtype = self.dtype_entry.get_text()
            if self.encoding_entry.get_text() == 'None':
                self.encoding = None
            else:
                self.encoding = self.encoding_entry.get_text()
            if self.fit_method_entry.get_text() != 'least_squares':
                self.fit_method = self.fit_method_entry.get_text()
            else:
                self.fit_method = 'least_squares'
        else:
            self.settings_dialog.hide()

        self.settings_dialog.hide()

    def toggle_copy_mode(self, button):
        state_messages = {
            0: 'Copy mode off',
            1: 'Copy mode on | x-value',
            2: 'Copy mode on | y-value',
        }
        if button.get_active():
            if button.get_label() == 'x':
                self.cmode_state = 1
                self.mpl_cursor = Cursor(
                    self.axis, lw=1, c='red', linestyle='--'
                )
                self.cid = self.canvas.mpl_connect(
                    'button_press_event', self.get_coord_click
                )
            elif button.get_label() == 'y':
                self.cmode_state = 2
                self.mpl_cursor = Cursor(
                    self.axis, lw=1, c='red', linestyle='--'
                )
                self.cid = self.canvas.mpl_connect(
                    'button_press_event', self.get_coord_click
                )
            else:
                # copy mode off
                self.cmode_state = 0
                self.mpl_cursor = None
                self.canvas.mpl_disconnect(self.cid)

            self.statusbar.push(
                self.statusbar.get_context_id('cmode_state'),
                state_messages[self.cmode_state]
            )

    def get_coord_click(self, event):
        x_copy, y_copy = str(round(event.xdata, 3)), str(round(event.ydata, 3))
        if self.cmode_state == 1:
            self.clipboard.set_text(x_copy, -1)
            self.statusbar.push(
                    self.statusbar.get_context_id('copied_x'),
                    'Copied X=' + str(x_copy) + ' to clipboard!'
            )
        if self.cmode_state == 2:
            self.clipboard.set_text(y_copy, -1)
            self.statusbar.push(
                    self.statusbar.get_context_id('copied_y'),
                    'Copied Y=' + str(y_copy) + ' to clipboard!'
            )

    def add_accelerator(self, widget, accelerator, signal="activate"):
        '''
        Adds keyboard shortcuts
        '''
        if accelerator is not None:
            key, mod = Gtk.accelerator_parse(accelerator)
            widget.add_accelerator(
                signal, self.accelerators, key, mod, Gtk.AccelFlags.VISIBLE
            )
Exemplo n.º 10
0
class AppCanvas():
    def __init__(self):
        self.curvesCounter = 0
        self.activeCurve = None
        self.pointOfRotation = None
        self.getScaleWidget = None
        self.getAngleWidget = None

        fig = plt.figure(
            figsize=[9, 6],
            dpi=100,
        )
        self.ax = fig.add_subplot()
        #plt.axis([0,300,0,200],'scaled')
        plt.axis([0, 300, 0, 200])

        self.canvas = FigureCanvas(fig)

    def get_canvas(self):
        return self.canvas

    def get_ax(self):
        return self.ax

    def set_activeCurve(self, activeCurve):
        self.activeCurve = activeCurve

    def set_getScaleWidget(self, getScaleWidget):
        self.getScaleWidget = getScaleWidget

    def set_getAngleWidget(self, getAngleWidget):
        self.getAngleWidget = getAngleWidget

    def pick_curve(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.linePlot.get_label():
            self.drag_curve_active = self.canvas.mpl_connect(
                'motion_notify_event', self.drag_curve)
            self.drop_curve_active = self.canvas.mpl_connect(
                'button_release_event', self.drop_curve)
            self.mouseX = event.mouseevent.xdata
            self.mouseY = event.mouseevent.ydata
            #self.activeCurve.set_working_accurancy()

    def drop_curve(self, event):
        if self.drag_curve_active != None:
            if event.inaxes != None:
                self.activeCurve.move_curve(event.xdata - self.mouseX,
                                            event.ydata - self.mouseY)
            self.canvas.mpl_disconnect(self.drag_curve_active)
            self.canvas.mpl_disconnect(self.drop_curve_active)
            #self.activeCurve.set_normal_accurancy()
            self.canvas.draw_idle()
            self.drag_curve_active = None
            self.drop_curve_active = None

    def drag_curve(self, event):
        if event.inaxes != None:
            self.activeCurve.move_curve(event.xdata - self.mouseX,
                                        event.ydata - self.mouseY)
            self.mouseX = event.xdata
            self.mouseY = event.ydata
            self.canvas.draw_idle()

    def resize_curve(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.resize_curve(scale / 100.0)
        self.canvas.draw_idle()

    def change_line_width(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.change_line_width(scale / 100.0)
        self.canvas.draw_idle()

    def change_curve(self, event):
        scale = self.getScaleWidget.get_scale_value()
        self.getScaleWidget.reset_scale_value()
        if self.activeCurve != None:
            self.activeCurve.resize_curve(scale / 100.0)
        self.canvas.draw_idle()

    def rotate_curve(self, event):
        angle = self.getAngleWidget.get_entry_text()
        if self.pointOfRotation != None:
            s = self.pointOfRotation[0].get_xdata()[0]
            t = self.pointOfRotation[0].get_ydata()[0]
        else:
            s, t = 0, 0
        try:
            if self.activeCurve != None and int(angle) < 360 and int(
                    angle) > -360:
                angle = int(angle)
                if angle < 0:
                    angle += 360
            self.activeCurve.rotate_curve(angle, s, t)
            self.canvas.draw_idle()
        except:
            pass

    def add_point_of_rotation(self, event):
        self.delete_point_of_rotation()
        self.pointOfRotation = self.ax.plot([event.xdata], [event.ydata], 'ko')
        self.canvas.draw_idle()

    def delete_point_of_rotation(self):
        if self.pointOfRotation != None:
            self.pointOfRotation[0].remove()
            del self.pointOfRotation
            self.pointOfRotation = None
            self.canvas.draw_idle()

    def select_point(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.pointsPlot.get_label():
            self.activeCurve.activate_point(event.mouseevent.xdata,
                                            event.mouseevent.ydata)
            self.canvas.draw_idle()

    def delete_point(self, event):
        lineName = event.artist.get_label()
        if lineName == self.activeCurve.pointsPlot.get_label():
            self.activeCurve.delete_point(event.mouseevent.xdata,
                                          event.mouseevent.ydata)
            self.canvas.draw_idle()

    def add_point(self, event):
        if self.activeCurve != None and event.inaxes != None:
            self.activeCurve.add_point(event.xdata, event.ydata)
            self.canvas.draw_idle()

    def pick_point(self, event):
        self.drag_point_active = self.canvas.mpl_connect(
            'motion_notify_event', self.drag_point)
        self.activeCurve.set_working_accurancy()

    def drop_point(self, event):
        self.canvas.mpl_disconnect(self.drag_point_active)
        if self.activeCurve != None:
            self.activeCurve.disactivate_point()
            self.activeCurve.set_normal_accurancy()
            self.canvas.draw_idle()

    def drag_point(self, event):
        if event.inaxes != None:
            self.activeCurve.move_point(event.xdata, event.ydata)
            self.canvas.draw_idle()
Exemplo n.º 11
0
class MyWindow(Gtk.Window):

	@timing
	def __init__(self):
		Gtk.Window.__init__(self, title="Echelle Reduction GUI")

		self.set_default_size(1000, 800)
		self.figure = Figure(figsize=(5,7), dpi=100)
		self.plot_1D = self.figure.add_subplot(212)
		self.plot_2D = self.figure.add_subplot(232)
		self.plot_PHD = self.figure.add_subplot(231)
		self.plot_orders = self.figure.add_subplot(233)
		self.plot_orders.tick_params(axis='both', labelsize=6)
		self.plot_orders.set_title("Orders")
		self.plot_2D.tick_params(axis='both', labelsize=7)
		self.plot_2D.set_title("2D Raw Data")
		self.plot_1D.set_title("1D Extracted Data")
		self.plot_1D.set_xlabel('pixels')
		self.plot_1D.set_ylabel('intensity')
		self.plot_1D.tick_params(axis='both', labelsize=7)
		self.plot_PHD.set_title('Pulse Height Data')
		self.plot_PHD.tick_params(axis='both', labelsize=7)

		self.canvas = FigureCanvas(self.figure)

		menubar = Gtk.MenuBar()

		menubar_file = Gtk.MenuItem("File")
		filemenu = Gtk.Menu()
		menubar_file.set_submenu(filemenu)
		menubar.append(menubar_file)

		filemenu_open = Gtk.MenuItem("Open")
		filemenu_open.connect('activate', self.open_file_dialog)
		filemenu.append(filemenu_open)

		filemenu.append(Gtk.SeparatorMenuItem())

		filemenu_save = Gtk.MenuItem("Save Orders")
		filemenu_save.connect('activate', self.on_filemenu_save_clicked)
		filemenu.append(filemenu_save)

		filemenu_load = Gtk.MenuItem("Load Orders")
		filemenu_load.connect('activate', self.on_filemenu_load_clicked)
		filemenu.append(filemenu_load)

		filemenu.append(Gtk.SeparatorMenuItem())

		filemenu_exit = Gtk.MenuItem('Exit')
		filemenu_exit.connect('activate', Gtk.main_quit)
		filemenu.append(filemenu_exit)

		menubox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		menubox.pack_start(menubar, False, False, 0)

		toolbar = NavigationToolbar(self.canvas, self)
		main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.add(main_box)

		self.statusbar = Gtk.Statusbar()
		self.context_id = self.statusbar.get_context_id("stat bar example")
		self.statusbar.push(0, 'Please Open 2D Fits Data File')

		hbutton_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		self.count_rate_button = Gtk.ToggleButton(label='Raw count rate')
		self.count_rate_button.connect("toggled", self.on_count_rate_button_clicked, self.context_id)
		self.filter_phd_button = Gtk.Button('Filter PHD')
		self.filter_phd_button.connect("clicked", self.on_filter_phd_button_clicked, self.context_id)
		self.gauss_fit_button = Gtk.ToggleButton(label='Fit 1D Gauss')
		self.gauss_fit_button.set_active(False)
		self.gauss_fit_button.connect("toggled", self.on_gauss_fit_button_clicked, self.context_id)
		self.remove_orders_button = Gtk.ToggleButton(label='Remove Orders')
		self.remove_orders_button.connect("toggled", self.on_remove_orders_button_clicked, self.context_id)
		self.add_orders_button = Gtk.ToggleButton(label='Add Orders')
		self.add_orders_button.connect("toggled", self.on_add_orders_button_clicked, self.context_id)
		self.buttonbg = Gtk.Button('Airglow')
		self.buttonbg.connect('clicked', self.buttonbg_clicked, self.context_id)
		self.recalculate_button = Gtk.Button('Recalculate')
		self.recalculate_button.connect('clicked', self.on_recalculate_button_clicked, self.context_id)

		hbutton_box.pack_start(self.count_rate_button, True, True, 0)
		hbutton_box.pack_start(self.filter_phd_button, True, True, 0)
		hbutton_box.pack_start(self.gauss_fit_button, True, True, 0)
		hbutton_box.pack_start(self.remove_orders_button, True, True, 0)
		hbutton_box.pack_start(self.add_orders_button, True, True, 0)
		hbutton_box.pack_start(self.buttonbg, True, True, 0)
		hbutton_box.pack_start(self.recalculate_button, True, True, 0)

		main_box.pack_start(menubox, False, False, 0)
		main_box.pack_start(toolbar, False, False, 0)
		main_box.pack_start(self.canvas, True, True, 0)
		main_box.pack_start(hbutton_box, False, False, 0)
		main_box.pack_start(self.statusbar, False, False, 0)

		#~ self.filename = './2014-03-14-185937.fits'
		#~ self.open_file(self.filename)

	@timing
	def open_file_dialog(self, widget):
		dialog = Gtk.FileChooserDialog(
			"Please Choose a File",
			self,
			Gtk.FileChooserAction.OPEN,
			(	Gtk.STOCK_CANCEL,
				Gtk.ResponseType.CANCEL,
				Gtk.STOCK_OPEN,
				Gtk.ResponseType.OK ) )
		dialog.set_default_response(Gtk.ResponseType.OK)
		filter = Gtk.FileFilter()
		filter.set_name('fits Files')
		filter.add_mime_type('fits')
		filter.add_pattern('*.fits')
		dialog.add_filter(filter)

		response = dialog.run()

		if response == Gtk.ResponseType.OK :
			self.filename = dialog.get_filename()
			self.statusbar.push(0, 'Opened File: ' + self.filename)
			dialog.destroy()
			del dialog
			self.open_file(self.filename)

		elif response == Gtk.ResponseType.CANCEL:
			dialog.destroy()
			del dialog


	@timing
	def open_file(self, filename):
		hdulist = fits.open(filename)
		self.photon_list = hdulist[1].data
		hdulist.close()

		 # Init for new file
		self.orders = []
		self.science(self.photon_list)


	@timing
	def science(self, photon_list, min_phd=0, max_phd=255, orders=[]):
		self.recalculate_button.set_sensitive(False)
		self.min_phd = min_phd
		self.max_phd = max_phd
		PHD = np.array(photon_list['PHD'])
		photon_list_filtered = photon_list[(PHD >= min_phd) & (PHD <= max_phd)]
		image, xedges, yedges = np.histogram2d(photon_list_filtered['X'], photon_list_filtered['Y'], bins=2048, range=[[0,8192],[0,8192]])
		self.update_2D_plot(image)

	# Collapse 2D data into 1D to view orders as peaks
		peaks = []
		for i in range(0, len(image[0])):
			# sums ith row
			peaks.append(np.sum(image[i,:]))

	# Smooth peaks by convolving with a boxcar
		boxcar = np.zeros(300)
		boxcar[125:175] = 1.0
		smooth = np.convolve(peaks, boxcar, mode='same')
		#~ self.peaks_smoothed = peaks / smooth
		self.peaks_smoothed = (peaks/max(peaks)) - (smooth/max(smooth)/2)

	# Only find orders if there are no orders yet, eg. first run
		if not self.orders and not orders:
			print 'Finding peaks...'
			# Requires scipy version .11.0 or greater
			self.orders = signal.find_peaks_cwt(self.peaks_smoothed, np.arange(10, 20))
		elif orders:
			print 'Using premade peaks list...'
			self.orders = orders

	# Plot 1D data with lines through the centers of the
	# orders to double check how the peak finder did
		self.update_orders_plot(self.peaks_smoothed, self.orders)

	#
		self.update_PHD_plot(PHD, min_phd, max_phd)

		self.dragbox = []

	### extraction of orders ###
	# find the widths of the orders (difference between peaks)
		peak_widths = []
		for i in range(1, len(self.orders)):
			peak_widths.append(self.orders[i] - self.orders[i - 1])

	# Double last entry to make peak_widths the right length
		peak_widths.append(peak_widths[-1])


	# Add up spectrum lines from 2D plot where y coord is an order +/-FWHM
	# Remember, peak_widths[0] is orders[1]-orders[0].
		FWHM = 0.63 # full width half max
		spectrum = []
		for i in range(len(self.orders)):
			peak = int(self.orders[i])
			#~ width = int(peak_widths[i]*FWHM)
			width = 1
			for j in range(0, width):
				# Find chord length of circular image
				r = len(image[0])/2
				start = int(r - (peak * (2*r - peak))**0.5)
				end = int(2 * r - start)
				for k in range(start, end):
					spectrum.append(image[peak-width/2+j][k])

		self.update_1D_plot(spectrum)

		self.statusbar.push(0, 'Done! ' + self.filename)

	@timing
	def update_2D_plot(self, image):
		self.plot_2D.cla()
		self.plot_2D.tick_params(axis='both', labelsize=7)
		self.plot_2D.set_title("2D Raw Data")
		max = np.amax(image)

		self.plot_2D.imshow(image, norm=LogNorm(vmin=0.1, vmax=max/2), origin='lower')
		self.canvas.draw()

	@timing
	def update_orders_plot(self, peaks, orders):
		self.plot_orders.cla()
		self.plot_orders_line, = self.plot_orders.plot([],[])
		self.plot_orders.tick_params(axis='both', labelsize=6)
		self.plot_orders.set_title("Orders")

		self.plot_orders_line.set_xdata(peaks)
		self.plot_orders_line.set_ydata(np.arange(len(peaks)))
		self.plot_orders.hlines(orders, min(peaks), max(peaks), color='purple')

		self.plot_orders.relim()
		self.plot_orders.autoscale_view(True, True, True)

		self.canvas.draw()


	# Seems a max of 18980 x values are supported by Cairo.  Since
	# we have more than the max we have to reduce the spectrum
	# resolution to fit.
	# Maybe do this dynamically based on xlim()?
	# xmin, xmax = xlim()   # return the current xlim
	@timing
	def update_1D_plot(self, spectrum):
		self.plot_1D.cla()
		self.plot_1D_line, = self.plot_1D.plot([],[])
		self.plot_1D.set_title("1D Extracted Data")
		self.plot_1D.set_xlabel('pixels')
		self.plot_1D.set_ylabel('intensity')
		self.plot_1D.tick_params(axis='both', labelsize=7)

		MAX = 18980 #16384 # 2^14
		scale = int(len(spectrum)/MAX) + 1

		print len(spectrum), scale, len(spectrum)/scale

		# When we get wavelength calibration, change this line to
		#~ x = np.linspace(minWL, maxWL, num = len(spectrum)/scale)
		x = np.arange(0, len(spectrum), scale)
		spectrum = [np.sum(spectrum[i:i+scale]) for i in x]

		self.plot_1D_line.set_xdata(x)
		self.plot_1D_line.set_ydata(spectrum)
		self.plot_1D.relim()
		self.plot_1D.autoscale_view(True, True, True)
		self.canvas.draw()


	@timing
	def update_PHD_plot(self, PHD, min_phd, max_phd):
		self.plot_PHD.cla()
		self.plot_PHD.set_title('Pulse Height Data')
		self.plot_PHD.tick_params(axis='both', labelsize=7)
		self.plot_PHD.axvline(x=min_phd, color='purple')
		self.plot_PHD.axvline(x=max_phd, color='purple')
		self.plot_PHD.hist(PHD, bins=256, range=[0,255], histtype='stepfilled')
		self.plot_PHD.relim()
		self.plot_PHD.autoscale_view(True, True, True)
		self.canvas.draw()


## airglow button ##
	def buttonbg_clicked(self, widget, data):
		dialog = Gtk.FileChooserDialog("Please Choose Airglow File", self,
			Gtk.FileChooserAction.OPEN,
			(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
			 Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
		dialog.set_default_response(Gtk.ResponseType.OK)
		filter = Gtk.FileFilter()
		filter.set_name('fits Files')
		filter.add_mime_type('fits')
		filter.add_pattern('*.fits')
		dialog.add_filter(filter)

		response = dialog.run()

		if response == Gtk.ResponseType.OK:
			self.airglow_filename = dialog.get_filename()
			#~ global _AirglowFile
			#~ _AirglowFile = fname
			#print "open file" + fname
			#self.statusbar.push(0,'Opened File:' + fname)
			dialog.destroy()
			self.open_airglow_file(self.airglow_filename)
		elif response == Gtk.ResponseType.CANCEL:
			dialog.destroy()

# opening a‏iglow fits file
	def open_airglow_file(self, airglow_filename):
		#this opens up the fits file
		hdulist = fits.open(airglow_filename)

	## this line will need to be used for real data
		targname = hdulist[0].header['targname']
		#targname = self.targname

	## but for now
		#~ targname = 'HD128627J'

	#  this picks out the actual data from fits file, and turns it into numpy array
		self.glowdata = hdulist[0].data
		hdulist.close()
	# simple subtraction of aurglow image from science image
		scidata = self.scidata - self.glowdata
		self.science(scidata, targname)

## gauss fitting button
	def on_gauss_fit_button_clicked(self, widget, data):

		self.statusbar.push(data,'Ready to fit.  Click on both sides of the emission feature you wish to fit')
		self.xdata = []
		def onclick(event):
			if self.gauss_fit_button.get_active():
				self.xdata.append(event.xdata)
				self.statusbar.push(data, 'one more click...')
				if len(self.xdata) == 2:
					self.statusbar.push(data, 'Ready to fit.  Click on both sides of the emission feature you wish to fit')
					xdata = self.xdata
					self.gauss_fit(xdata)

		#  mouse click event on 1d
		cid = self.canvas.mpl_connect('button_press_event', onclick)
		if [self.gauss_fit_button.get_active()] == [False]:
			self.statusbar.push(0, 'Opened File:' + File)


### gauss fitting ###
	def gauss_fit(self, xdata):

		x = list(self.x)
		xg = []
		xg.append(int(xdata[0]))
		xg.append(int(xdata[1]))
		xg1 = min(xg)
		xg2 = max(xg)
		xgauss = x[xg1:xg2]
		ygauss = self.odo[xg1:xg2]
		right = ygauss[len(xgauss)-4:len(xgauss)]
		left = ygauss[0:4]

	# background subtraction
		averight = sum(right) / len(right)
		aveleft = sum(left) / len(left)
		bg_y = [averight, aveleft]
		rightx = xgauss[len(xgauss)-4:len(xgauss)]
		leftx = xgauss[0:4]
		averightx = sum(rightx) / len(rightx)
		aveleftx = sum(leftx) / len(leftx)
		bg_x = [averightx, aveleftx]

		m,b = np.polyfit(bg_x, bg_y, 1)
		slopex = [i * m for i in xgauss]
		bg_fit = slopex + b



		# making a model gauss
		def gauss(xgauss, MAX, mu, sigma):
			return MAX * np.exp(-(xgauss - mu)**2 / (2.0 * sigma**2))

		avex = sum(xgauss) / len(xgauss)
		guess = [1.0, avex, 1.0]

		# plugging in model to matplotlibs curve_fit()
		coeff, var_matrix = curve_fit(gauss, xgauss, ygauss-bg_fit, p0=guess)
		fit = gauss(xgauss, *coeff)
		sigma = coeff[2]
		FWHM = sigma * 2 * np.sqrt(2 * np.log(2))
		FWHM = round(FWHM, 2)
		fitplot = plt.plot(xgauss, ygauss, color='k')
		plt.plot(xgauss, fit + bg_fit, color='b', linewidth=1.5)
		xpos = xgauss[0] + 0.01 * (coeff[1] - xgauss[0])
		strFWHM = str(FWHM)
		plt.text(xpos, 0.9 * max(ygauss), 'FWHM = ' + strFWHM + '', color='purple', fontweight='bold')

		center = str(round(coeff[1], 2))
		plt.text(xpos, 0.95 * max(ygauss), 'Center = ' + center + '', color='green', fontweight='bold')
		plt.plot(xgauss, bg_fit, 'r--')

		plt.show()
		self.xdata = []


	### count rate button
	def on_count_rate_button_clicked(self, widget, data):
		if self.count_rate_button.get_active():
		   self.statusbar.push(data,'Use zoom feature in navigation bar to select count rate region')
		else:
		   self.statusbar.push(0, 'Opened File:' + self.filename)

		dragbox = []

		def onclick(event):
			if self.count_rate_button.get_active():
				dragbox.append(event.xdata)
				dragbox.append(event.ydata)


		def offclick(event):
			if self.count_rate_button.get_active():
				dragbox.append(event.xdata)
				dragbox.append(event.ydata)
				self.count_rate(dragbox, data)


		self.canvas.mpl_connect('button_press_event', onclick)
		self.canvas.mpl_connect('button_release_event', offclick)


	def count_rate(self, dragbox, data):
		# fake exposure time in seconds
		datafake = './chesstest.fits'
		hdu = fits.open(datafake)
		exptime = hdu[0].header['EXPOSURE']
		dragbox = [int(x) for x in dragbox]
		cntbox = self.scidata[ dragbox[0]:dragbox[2], 1024-dragbox[1]:1024-dragbox[3] ]
		totpix = np.size(cntbox)
		cntrate = np.sum(cntbox) / exptime
		totpix = str(totpix)
		cntrate = str(cntrate)
		self.statusbar.push(data, 'count rate in box = ' + cntrate + ' cnt/sec,    pixels in box = ' + totpix + '')
		return cntrate


	@timing
	def on_filter_phd_button_clicked(self, widget, data):
		self.phd_window = Gtk.MessageDialog(image = None)
		self.phd_window.set_size_request(500, 100)
		self.phd_window.move(400, 300)
		#self.phd_window.connect("delet_event",lambda w,e:)
		#~ self.phd_window.connect("delete-event", self.phd_window.destroy)

		mainbox = self.phd_window.get_content_area()
		thebox = Gtk.HBox(False, 0)

		label = Gtk.Label("Keep PHD between")
		label2 = Gtk.Label('and')

		label.show()
		label2.show()

		self.ok_button = Gtk.Button('Okay')
		self.ok_button.connect('clicked', self.phd_entry_button_clicked)
		self.min_phd_entry = Gtk.Entry()
		self.min_phd_entry.set_activates_default(True)
		self.max_phd_entry = Gtk.Entry()
		#~ self.max_phd_entry.set_activates_default(True)

		self.min_phd_entry.show()
		self.max_phd_entry.show()
		self.ok_button.show()

		thebox.pack_start(label,False,False,0)
		thebox.pack_start(self.min_phd_entry,False,False,0)

		thebox.pack_start(label2,False,False,0)
		thebox.pack_start(self.max_phd_entry,False,False,0)
		mainbox.pack_start(thebox,True,True,0)
		mainbox.pack_start(self.ok_button,True,False,0)

		mainbox.show()
		thebox.show()
		self.phd_window.show()

	@timing
	def phd_entry_button_clicked(self, widget):
		min_phd = int(self.min_phd_entry.get_text())
		max_phd = int(self.max_phd_entry.get_text())
		self.phd_window.destroy()
		self.statusbar.push(0, 'Filtering by: ' + str(min_phd) + ' < PHD < ' + str(max_phd))
		self.orders = []
		self.science(self.photon_list, min_phd, max_phd)


	@timing
	def on_add_orders_button_clicked(self, widget, data):
		if [self.add_orders_button.get_active()] == [True]:
			self.remove_orders_button.set_sensitive(False)
			self.statusbar.push(data, 'Click where to add an order.')

			def onclick_peak(event):
				if self.add_orders_button.get_active():
					self.add_order(event.ydata)

			self.cid_add = self.canvas.mpl_connect('button_press_event', onclick_peak)

		else:
			self.canvas.mpl_disconnect(self.cid_add)
			del self.cid_add

			self.remove_orders_button.set_sensitive(True)
			self.statusbar.push(0, 'Opened File:' + self.filename)


	@timing
	def add_order(self, ydata):
		order = ydata

		print 'Number of original orders', len(self.orders)

		self.orders.append(order)
		self.orders.sort()
		self.update_orders_plot(self.peaks_smoothed, self.orders)
		self.recalculate_button.set_sensitive(True)

		print 'Corrected number of orders', len(self.orders)


	@timing
	def on_remove_orders_button_clicked(self, widget, data):
		if [self.remove_orders_button.get_active()] == [True]:
			self.add_orders_button.set_sensitive(False)
			self.statusbar.push(data, 'Click on the order to remove.')

			def onclick_order(event):
				if self.remove_orders_button.get_active():
					self.remove_order(event.ydata)

			self.cid_remove = self.canvas.mpl_connect('button_press_event', onclick_order)

		else:
			self.canvas.mpl_disconnect(self.cid_remove)
			del self.cid_remove

			self.add_orders_button.set_sensitive(True)
			self.statusbar.push(0, 'Opened File:' + self.filename)


	@timing
	def remove_order(self, ydata):
		order = min(self.orders, key=lambda x:abs(x - ydata))

		print 'Number of original orders', len(self.orders)

		self.orders.remove(order)
		self.update_orders_plot(self.peaks_smoothed, self.orders)
		self.recalculate_button.set_sensitive(True)

		print 'Corrected number of orders', len(self.orders)


	def on_recalculate_button_clicked(self, widget, data):
		self.science(self.photon_list, self.min_phd, self.max_phd)


	def on_filemenu_save_clicked(self, widget):
		now = datetime.datetime.now()
		date = now.strftime("%Y_%m_%d_%H_%M_%S")
		temp = self.orders, self.min_phd, self.max_phd
		pickle.dump(temp, open(self.filename[:-5] + '_1D_' + date + '.orders', 'wb'))
		self.statusbar.push(0, 'Saved Orders: ' + self.filename[:-5] + '_1D_' + date + '.orders')


	def on_filemenu_load_clicked(self, widget):
		dialog = Gtk.FileChooserDialog(
			"Please Choose a File",
			self,
			Gtk.FileChooserAction.OPEN,
			(	Gtk.STOCK_CANCEL,
				Gtk.ResponseType.CANCEL,
				Gtk.STOCK_OPEN,
				Gtk.ResponseType.OK ) )
		dialog.set_default_response(Gtk.ResponseType.OK)
		filter = Gtk.FileFilter()
		filter.set_name('orders	Files')
		filter.add_mime_type('orders')
		filter.add_pattern('*.orders')
		dialog.add_filter(filter)

		response = dialog.run()

		if response == Gtk.ResponseType.OK :
			filename = dialog.get_filename()
			self.statusbar.push(0, 'Loaded Orders: ' + filename)
			dialog.destroy()
			del dialog
			orders, min_phd, max_phd = pickle.load(open(filename, 'rb'))
			self.science(self.photon_list, min_phd, max_phd, orders)

		elif response == Gtk.ResponseType.CANCEL:
			dialog.destroy()
			del dialog
Exemplo n.º 12
0
    def __init__(self, sigs={}, fig=None, title='', uidir=''):
        oscopy.Figure.__init__(self, None, fig)
        self.to_figure = [Gtk.TargetEntry.new("text/plain",
                                              Gtk.TargetFlags.SAME_APP,
                                              self.TARGET_TYPE_SIGNAL)]
        self.hadjpreval = None
        self.vadjpreval = None
        self.current_axes = None

        # The canvas for the Figure
        canvas = FigureCanvas(self)
        canvas.supports_blit = False
        canvas.mpl_connect('button_press_event', self.button_press)
        canvas.mpl_connect('scroll_event', self.mouse_scroll)
        canvas.mpl_connect('axes_enter_event', self.axes_enter)
        canvas.mpl_connect('axes_leave_event', self.axes_leave)
        canvas.mpl_connect('figure_enter_event', self.figure_enter)
        canvas.mpl_connect('figure_leave_event', self.figure_leave)
        canvas.mpl_connect('key_press_event', self.key_press)
        canvas.mpl_connect('motion_notify_event', self.show_coords)
        self.canvas = canvas
        self.draw_hid = canvas.mpl_connect('draw_event', self.update_scrollbars)

        # The GtkBuilder
        builder = Gtk.Builder()
        builder.add_from_file('/'.join((uidir, IOSCOPY_GTK_FIGURE_UI)))
        self.builder = builder
        self.uidir = uidir

        # The window
        w = builder.get_object('w')
        w.set_title(title)
        w.drag_dest_set(Gtk.DestDefaults.ALL, self.to_figure, Gdk.DragAction.COPY)

        # Init the store for the combo box
        store = builder.get_object('store')
        iter = store.append([_('All Graphs'), False, True, False, Gtk.Adjustment(), Gtk.Adjustment()])
        for i in range(4):
            iter = store.append([_('Graph %d') % (i + 1), False, True if i < len(self.graphs) else False, False, Gtk.Adjustment(), Gtk.Adjustment()])
        self.cbx_store = store

        # The Graph Combobox
        graphs_cbx = builder.get_object('graphs_cbx')
        graphs_cbx.set_active(0)

        # Add remaining widgets
        builder.get_object('box').pack_start(canvas, True, True, 0)

        # Expose widgets needed elsewhere
        self.window = w
        self.hbar = builder.get_object('hbar')
        self.vbar = builder.get_object('vbar')
        self.coords_lbl1 = builder.get_object('coord_lbl1')
        self.coords_lbl2 = builder.get_object('coord_lbl2')
        self.graphs_cbx = graphs_cbx
        self.store = store
        self.mpsel_get_act = [builder.get_object('rb%d' % (b + 1)).get_active for b in range(4)]
        self.mpsel_set_act = [builder.get_object('rb%d' % (b + 1)).set_active for b in range(4)]
        self.mpsel_set_sens = [builder.get_object('rb%d' % (b + 1)).set_sensitive for b in range(4)]
        self.window.show_all()

        # Actions
        a = Gio.SimpleAction.new('set_range', GLib.VariantType.new('t'))
        a.connect('activate', self.set_range_activated)
        self.window.add_action(a)

        a = Gio.SimpleAction.new('set_units', GLib.VariantType.new('t'))
        a.connect('activate', self.set_units_activated)
        self.window.add_action(a)

        a = Gio.SimpleAction.new_stateful('set_scale', GLib.VariantType.new('(ts)'), GLib.Variant.new_string('lin'))
        a.connect('activate', self.set_scale_activated)
        self.window.add_action(a)

        a = Gio.SimpleAction.new('remove_signal', GLib.VariantType.new('(ts)'))
        a.connect('activate', self.remove_signal_activated)
        self.window.add_action(a)

        # Connect additional GTK signals
        cbmap = {'span_toggle_btn_toggled': self.span_toggle_btn_toggled,
                 'x10_toggle_btn_toggled': self.x10_toggle_btn_toggled,
                 'hadj_pressed': self.hadj_pressed,
                 'hadj_released': self.hadj_released,
                 'hscroll_change_value': self.hscroll_change_value,
                 'vadj_pressed': self.vadj_pressed,
                 'vadj_released': self.vadj_released,
                 'vscroll_change_value': self.vscroll_change_value,
                 'disable_adj_update_on_draw': self.disable_adj_update_on_draw,
                 'enable_adj_update_on_draw': self.enable_adj_update_on_draw,
                 'update_scrollbars': self.update_scrollbars,
                 'save_fig_btn_clicked': self.save_fig_btn_clicked,
                 'delete_event_cb': lambda w, e: w.hide() or True,
                 }
        builder.connect_signals(cbmap)
        graphs_cbx.connect('changed', self.graphs_cbx_changed,
                           (builder.get_object('x10_toggle_btn'),
                            builder.get_object('span_toggle_btn'),
                            store))

        # Add signals
        if sigs:
            self.add(sigs)
#        # Update canvas for SpanSelector of Graphs
        for gr in self.graphs:
            if hasattr(gr, 'span'):
                gr.span.new_axes(gr)
Exemplo n.º 13
0
class CampaignGraph(object):
    """
	A basic graph provider for using :py:mod:`matplotlib` to create graph
	representations of campaign data. This class is meant to be subclassed
	by real providers.
	"""
    title = 'Unknown'
    """The title of the graph."""
    _graph_id = None
    table_subscriptions = []
    """A list of tables from which information is needed to produce the graph."""
    def __init__(self, config, parent, size_request=None):
        """
		:param dict config: The King Phisher client configuration.
		:param parent: The parent window for this object.
		:type parent: :py:class:`Gtk.Window`
		:param tuple size_request: The size to set for the canvas.
		"""
        self.config = config
        """A reference to the King Phisher client configuration."""
        self.parent = parent
        """The parent :py:class:`Gtk.Window` instance."""
        self.figure, ax = pyplot.subplots()
        self.axes = self.figure.get_axes()
        self.canvas = FigureCanvas(self.figure)
        self.manager = None
        if size_request:
            self.canvas.set_size_request(*size_request)
        self.canvas.mpl_connect('button_press_event',
                                self.mpl_signal_canvas_button_pressed)
        self.canvas.show()
        self.navigation_toolbar = NavigationToolbar(self.canvas, self.parent)
        self.navigation_toolbar.hide()
        self.popup_menu = Gtk.Menu.new()

        menu_item = Gtk.MenuItem.new_with_label('Export')
        menu_item.connect('activate', self.signal_activate_popup_menu_export)
        self.popup_menu.append(menu_item)

        menu_item = Gtk.MenuItem.new_with_label('Refresh')
        menu_item.connect('activate', lambda action: self.refresh())
        self.popup_menu.append(menu_item)

        menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar')
        menu_item.connect('toggled',
                          self.signal_toggled_popup_menu_show_toolbar)
        self.popup_menu.append(menu_item)
        self.popup_menu.show_all()

    @classmethod
    def get_graph_id(klass):
        """
		The graph id of an exported :py:class:`.CampaignGraph`.

		:param klass: The class to return the graph id of.
		:type klass: :py:class:`.CampaignGraph`
		:return: The id of the graph.
		:rtype: int
		"""
        return klass._graph_id

    def make_window(self):
        """
		Create a window from the figure manager.

		:return: The graph in a new, dedicated window.
		:rtype: :py:class:`Gtk.Window`
		"""
        if self.manager == None:
            self.manager = FigureManager(self.canvas, 0)
        window = self.manager.window
        window.set_transient_for(self.parent)
        window.set_title(self.title)
        return window

    def mpl_signal_canvas_button_pressed(self, event):
        if event.button != 3:
            return
        self.popup_menu.popup(None, None, None, None, event.button,
                              Gtk.get_current_event_time())
        return True

    def signal_activate_popup_menu_export(self, action):
        dialog = gui_utilities.UtilityFileChooser('Export Graph', self.parent)
        file_name = self.config['campaign_name'] + '.png'
        response = dialog.run_quick_save(file_name)
        dialog.destroy()
        if not response:
            return
        destination_file = response['target_path']
        self.figure.savefig(destination_file, format='png')

    def signal_toggled_popup_menu_show_toolbar(self, widget):
        if widget.get_property('active'):
            self.navigation_toolbar.show()
        else:
            self.navigation_toolbar.hide()

    def load_graph(self):
        """Load the graph information via :py:meth:`.refresh`."""
        self.refresh()

    def refresh(self, info_cache=None, stop_event=None):
        """
		Refresh the graph data by retrieving the information from the
		remote server.

		:param dict info_cache: An optional cache of data tables.
		:param stop_event: An optional object indicating that the operation should stop.
		:type stop_event: :py:class:`threading.Event`
		:return: A dictionary of cached tables from the server.
		:rtype: dict
		"""
        info_cache = (info_cache or {})
        if not self.parent.rpc:
            return info_cache
        for table in self.table_subscriptions:
            if stop_event and stop_event.is_set():
                return info_cache
            if not table in info_cache:
                info_cache[table] = list(
                    self.parent.rpc.remote_table('campaign/' + table,
                                                 self.config['campaign_id']))
        map(lambda ax: ax.clear(), self.axes)
        self._load_graph(info_cache)
        self.canvas.draw()
        return info_cache
Exemplo n.º 14
0
class MyWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self,title="Echelle Reduction GUI")
      
        
   ## setting up canvase for plotting ### 
   
        #self.box = Gtk.EventBox()
        
        
        self.set_default_size(1000,700)
    	self.f = Figure(figsize=(5,7), dpi=100)
        self.b = self.f.add_subplot(212) # 1D
        self.c = self.f.add_subplot(231) # PHD
        self.e = self.f.add_subplot(233) # orders 
        self.a = self.f.add_subplot(232) # raw
        self.e.tick_params(axis='both',labelsize=6)
        self.e.set_title("orders")
        self.a.tick_params(axis='both', labelsize=7)
        self.a.set_title("2D raw data")
        self.b.set_title("1D extracted data")
        self.b.set_xlabel('pixels')
        self.b.set_ylabel('intensity')
        self.b.tick_params(axis='both', labelsize=7)
        self.c.set_title('PHD')
        self.c.tick_params(axis='both', labelsize=7)
        
        self.canvas = FigureCanvas(self.f) 
        
   
    # menue bar 
        menubar = Gtk.MenuBar()
        
        filem = Gtk.MenuItem("File")
        filemenu = Gtk.Menu()
        filem.set_submenu(filemenu)
        
        open = Gtk.MenuItem("Open")
        open.connect('activate',self.on_file_clicked)
        filemenu.append(open)
        menubar.append(filem)
        #savesub = Gtk.Menu()
        #savesub.append()
        exit = Gtk.MenuItem('Exit')
        exit.connect('activate',Gtk.main_quit)
        filemenu.append(exit)
       
        
        menubox = Gtk.Box( orientation = Gtk.Orientation.VERTICAL)
        menubox.pack_start(menubar,False,False,0)
        #open.connect("open", self.on_file_clicked)
     
    
    # Navigtion toolbar stuff     
        
        toolbar = NavigationToolbar(self.canvas, self)
        main_box = Gtk.Box( orientation = Gtk.Orientation.VERTICAL)  
        self.add(main_box)
     
    # status bar
        self.statusbar = Gtk.Statusbar()
        context_id=self.statusbar.get_context_id("stat bar example")  
        self.statusbar.push(0,'Please Open 2D Fits Data File')  
        
    # button box
        vbutton_box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)
        self.button1 = Gtk.ToggleButton(label='Raw count rate')
        self.button1.connect("toggled", self.on_button1_clicked, context_id)
        self.button2 = Gtk.Button('Filter PHD')
        self.button2.connect("clicked",self.on_button2_clicked,context_id)
        self.button3 = Gtk.ToggleButton(label='Fit 1D Gauss')
        self.button3.set_active(False)
        self.button3.connect("toggled", self.on_button3_clicked,  context_id)
        self.orderbutton = Gtk.ToggleButton(label='Remove Orders')
        self.orderbutton.connect("toggled",self.orderbutton_clicked,context_id)
        self.buttonbg = Gtk.Button('Airglow')
        self.buttonbg.connect('clicked', self.buttonbg_clicked, context_id)
        
        
        vbutton_box.pack_start(self.button1,True,True, 0)
        vbutton_box.pack_start(self.button2,True, True, 0)
        vbutton_box.pack_start(self.button3,True, True, 0)
        vbutton_box.pack_start(self.orderbutton,True,True,0)
        vbutton_box.pack_start(self.buttonbg,True,True,0)
        
     
        
    # packing in main_box
        main_box.pack_start(self.statusbar, False,False,0)
        main_box.pack_start(self.canvas, True, True, 0)
        main_box.pack_start(vbutton_box,False,False,0)
        main_box.pack_start(toolbar, False, False, 0)
        main_box.pack_start(menubox,False,False,0)


# ### file selector window ###
    def on_file_clicked(self, widget):
        dialog = Gtk.FileChooserDialog("Please Choose a File", self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
        dialog.set_default_response(Gtk.ResponseType.OK)
        filter = Gtk.FileFilter()
        filter.set_name('fits Files')
        filter.add_mime_type('fits')
        filter.add_pattern('*.fits')
        dialog.add_filter(filter)
   
        response = dialog.run()
        
        if response == Gtk.ResponseType.OK :
        	fname = dialog.get_filename()
        	global _File
        	_File = fname
        	#print "open file" + fname
        	self.statusbar.push(0,'Opened File:' + fname)
        	dialog.destroy()
        	self.open_file(_File)
        elif response == Gtk.ResponseType.CANCEL:
        	dialog.destroy()
   
   # opening fits file
    def open_file(self,_File):
    	#this opens up the fits file
    	hdulist = fits.open(_File)
        self.targname = hdulist[0].header['targname']    
    
        #this picks out the actual data from fits file, and turns it into numpy array
    	#self.scidata = hdulist['sci',2].data 
    	self.scidata = hdulist['sci',1].data 
    	hdulist.close()
    	
    	scidata = self.scidata
    	
    	targname = self.targname
    	self.science(scidata,targname)
    	
    
    def science(self,scidata,targname):
    	# sends 2d data to my gui plotting funtion
	
    	self.update_plot(scidata)
        #self.scidata = scidata
        
        
    ### smashing 2D data into 1D to view orders as peaks ####
    	
    # filling in y with the sums of the rows of scidata
        y=[]
   	for i in range(0,len(scidata[0])):
        	t = np.sum(scidata[i,:])
        	y.append(t)
        
    # making an x axis with same dementions as y
    	x = np.linspace(0,len(scidata[0]), num = len(scidata[0]))
    	self.x = x

    #reversing x for the sake of the visualization
    	xrev = x[::-1]

    # the orders seem to blend around lyman alpha so i have to select
    # the biggest chunk I could use to get a delta y between the
    # orders. i wil have to make this selectable on the GUI some how
    	chunk = [0,500]
	minv = chunk[0]
	maxv = chunk[1]
 
	
    #drawing box around my chunk to check makesure I have a good portion
	#plt.hself.lines(chunk,[-1000],[5000],color='r')
        #plt.vself.lines([-1000,5000],minv,maxv,color='r')
	#plt.show()
    
    #cutting out the chunk of data that i selected
	xchunk = x[max(x)-chunk[1]:max(x)]
	index1 = int(max(x)-chunk[1])
	index2 = int(max(x))
	ychunk = y[index1:index2]
    #reversing x for the sake of the plot
    	xrevchunk = xchunk[::-1]
    	
    	
    
    	
    	#plt.figure(figsize=(7.5,8.4))
    #
    # using scipy.signal.find_peaks_cwt() to find centers of orders.  this required scipy version .11.0 or greater
    	peakind=signal.find_peaks_cwt(ychunk,np.arange(3,15))
    
    # plotting chunk of 1D data with self.lines through the centers of the orders to double check how the peak finder did

    	self.lines=xchunk[peakind]
    	revlines=self.lines[::-1]
    	lines = self.lines
    	self.xchunk = xchunk
    	self.ychunk = ychunk
    	self.update_ordersplot(ychunk,xchunk,self.lines)
   
      
    	
    ### fake PDH stuff ### (fake data for now)
	PHDfake = '/home/rachel/codes/chesstest.fits'
	hdu = fits.open(PHDfake)
	PHD = hdu[1].data['PHD']
	self.update_PHDplot(PHD)
	hdu.close() 
	
	self.dragbox=[]
   
      ### extraction of orders ###   
     # find w, the widths of the orders (difference between peaks)
    	w=[]
    	for i in range(1,len(peakind)):
    		t=peakind[i]-peakind[i-1]
    		w.append(t)
    	
    # i have to add an extra w at the end of the array to make it the right size i (hopefully this is kosher)
    	maxw=max(w)-4
    	w.append(maxw)
        self.w = w
    
    ### making arrays of 1s ans 0s and extracting the 1d orders by matrix multiplication 
    #def extraction(self, peakind,x,scidata):
   	zeros=np.zeros((len(x),1))
    	index = range(0,len(self.w))
    	reindex = index[::-1]
        global oneDorders
    	oneDorders = {}
    	for i in reindex:
    		zeros1=np.copy(zeros)
    		zeros1[ len(scidata[0]) - (np.sum(w[(i):18])) : len(scidata[0]) - np.sum(w[(i+1):18]) ] = 1
    		twoD = scidata*zeros1
    			   		
            # making 2d orders in to 1d orders
                Y=[]
    		for j in range(0,len(scidata[0])):
        		t = np.sum(twoD[:,j])
        		Y.append(t)
            # placing 1d orders in dictionary called oneDorders
        	oneDorders[str(i)]=Y
        
        	
     # sending plotting info to update_1dplot for gui (for now using just on order until cross coralation is added to script
    	self.x = np.linspace(0,len(scidata[0]), num = len(scidata[0]))
        self.odo=oneDorders['16']
        odo = self.odo[:]
    	self.update_1dplot(odo,x)
    	self.save_pickle(oneDorders)
    		
        
    def update_plot(self, scidata):
        
        #self.a.cla()
        #cbar.self.a.cla()
        
        self.plt= self.a.imshow(scidata, vmin = 0, vmax = 255,origin = 'lower')
        
        cbar=self.f.colorbar(self.plt,shrink=.84,pad=0.01) 
 
        self.canvas.draw()

    def update_ordersplot(self,ychunk,xchunk,lines):
        ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line
        self.e.cla()
        self.e.set_title("orders")
        self.plt=self.e.plot(ychunk,xchunk)
        self.e.hlines(lines,0,2000,color='purple',label='centers')
        self.canvas.draw()

    def update_1dplot(self,odo,x):
        ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line
        self.b.cla()
    	self.plt=self.b.plot(x,self.odo)
    	self.canvas.draw()
    	
    def update_PHDplot(self,PHD):
        ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line
        self.c.cla()
        self.c.set_title('PHD')
        self.plt=self.c.hist(PHD,bins=80,histtype='stepfilled')
        self.canvas.draw()
        
        
 ## airglow button ##
    def buttonbg_clicked(self, widget, data):
    	dialog = Gtk.FileChooserDialog("Please Choose Airglow File", self,
            Gtk.FileChooserAction.OPEN,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
        dialog.set_default_response(Gtk.ResponseType.OK)
        filter = Gtk.FileFilter()
        filter.set_name('fits Files')
        filter.add_mime_type('fits')
        filter.add_pattern('*.fits')
        dialog.add_filter(filter)
   
        response = dialog.run()
        
        if response == Gtk.ResponseType.OK :
        	fname = dialog.get_filename()
        	global _AirglowFile
        	_AirglowFile = fname
        	#print "open file" + fname
        	#self.statusbar.push(0,'Opened File:' + fname)
        	dialog.destroy()
        	self.open_Airglowfile(_AirglowFile)
        elif response == Gtk.ResponseType.CANCEL:
        	dialog.destroy()
        	
# opening a‏iglow fits file
    def open_Airglowfile(self,_AirglowFile):
    	#this opens up the fits file
    	hdulist = fits.open(_AirglowFile)
      
      ## this line will need to be used for real data
        #self.targname = hdulist[0].header['targname']    
        #targname = self.targname
        
      ## but for now
        targname = 'HD128627J'
        
      #  this picks out the actual data from fits file, and turns it into numpy array
    	self.glowdata = hdulist[0].data 
    	hdulist.close()
      # simple subtraction of aurglow image from science image
    	scidata = self.scidata - self.glowdata
    	self.science(scidata,targname)
        
  ## gauss fitting button 
    
    def on_button3_clicked(self, widget, data):

         	self.statusbar.push(data,'Ready to fit.  Click on both sides of the emission feature you wish to fit')
         	self.xdata = []
                def onclick(event):
                    if self.button3.get_active():
    	        	 self.xdata.append(event.xdata)
    	        	 self.statusbar.push(data,'one more click...')
        		 if len(self.xdata) == 2:
			        self.statusbar.push(data,'Ready to fit.  Click on both sides of the emission feature you wish to fit')
			 	xdata=self.xdata
	    	                self.gauss_fit(xdata)
	    	    
	    	    	
        #  mouse click event on 1d	
    		cid = self.canvas.mpl_connect('button_press_event', onclick)
    		if [self.button3.get_active()] == [False]:
    			self.statusbar.push(0,'Opened File:' + _File)
    	 
    
    		
 ### guass fitting ###
    def gauss_fit(self,xdata):
        
        x = list(self.x)
        xg=[]     
        xg.append(int(xdata[0]))
        xg.append(int(xdata[1])) 
        xg1 = min(xg)
        xg2 = max(xg)       
        xgauss = x[xg1:xg2]
        ygauss = self.odo[xg1:xg2]
        right = ygauss[len(xgauss)-4:len(xgauss)]
        left = ygauss[0:4]
       
       # background subtraction
        averight = sum(right)/len(right)
        aveleft = sum(left)/len(left)
        bg_y = [averight,aveleft]
        rightx = xgauss[len(xgauss)-4:len(xgauss)]
        leftx = xgauss[0:4]
        averightx = sum(rightx)/len(rightx)
        aveleftx = sum(leftx)/len(leftx)
        bg_x = [averightx,aveleftx]
       
        m,b = np.polyfit(bg_x,bg_y,1)
        slopex = [i * m for i in xgauss]
        bg_fit = slopex+b
        
        
        
       # makeing a model gauss
        def gauss(xgauss,MAX,mu,sigma):
        	return MAX*np.exp(-(xgauss-mu)**2/(2.*sigma**2))
        avex = sum(xgauss)/len(xgauss) 
        guess = [1.,avex,1.]
       # plugging in model to matplotlibs curve_fit()
       
        coeff, var_matrix = curve_fit(gauss,xgauss,ygauss-bg_fit,p0=guess) 
        fit = gauss(xgauss, *coeff)
        sigma = coeff[2]
        FWHM = sigma*2*np.sqrt(2*np.log(2))
        FWHM = round(FWHM,2)
 	fitplot = plt.plot(xgauss,ygauss,color='k')
 	plt.plot(xgauss,fit+bg_fit,color = 'b',linewidth = 1.5)
 	xpos = xgauss[0]+.01*(coeff[1]-xgauss[0])
 	strFWHM = str(FWHM)
 	plt.text(xpos,.9*max(ygauss),'FWHM = '+strFWHM+'',color = 'purple',fontweight = 'bold')
 	 
 	center = str(round(coeff[1],2))
 	plt.text(xpos,.95*max(ygauss),'Center = '+center+'',color = 'green',fontweight = 'bold')
 	plt.plot(xgauss,bg_fit,'r--')
 	
 	plt.show()
 	self.xdata = []
 	
 	
    ### count rate button  
    def on_button1_clicked(self, widget,data):
        if self.button1.get_active():
    	   self.statusbar.push(data,'Use zoom feature in navigation bar to select count rate region')
    	else: 
    	   self.statusbar.push(0,'Opened File:' + _File)
    	
    	def onclick2(event):
    	       if self.button1.get_active():
    	          self.dragbox = []
                  #print event.xdata, event.ydata
                  self.dragbox.append(event.xdata)
                  self.dragbox.append(event.ydata)
                  
                  
        def offclick2(event):
                 # print event.xdata, event.ydata
               if self.button1.get_active():
                  self.dragbox.append(event.xdata)
                  self.dragbox.append(event.ydata)
                  dragbox = self.dragbox
                  self.cnt_rate(dragbox,data)
                 
                  
        cid2 = self.canvas.mpl_connect('button_press_event',onclick2 )
        cid3 = self.canvas.mpl_connect('button_release_event',offclick2 )
       

 	
 ### count rate #####
        
    def cnt_rate(self,dragbox,data):
        # fake exposure time in seconds
        datafake = '/home/rachel/codes/chesstest.fits'
	hdu = fits.open(datafake)
	exptime = hdu[0].header['EXPOSURE']
    	dragbox = [int(x) for x in dragbox]
    	cntbox = self.scidata[dragbox[0]:dragbox[2],1024-dragbox[1]:1024-dragbox[3]]
    	totpix = np.size(cntbox)
    	cntrate = np.sum(cntbox)/exptime
    	totpix = str(totpix)
    	cntrate = str(cntrate)
    	self.statusbar.push(data,'count rate in box = '+cntrate+' cnt/sec,    pixels in box = '+totpix+'')
    	return cntrate
  
  #### phd filter button ##
    
    def on_button2_clicked(self,widget,data):
    	self.phd_window = Gtk.MessageDialog(image = None)
    	self.phd_window.set_size_request(500,100)
    	self.phd_window.move(400, 300)
    	#self.phd_window.connect("delet_event",lambda w,e:)    
       
        mainbox = self.phd_window.get_content_area()
        self.phd_window.add(mainbox)
        thebox = Gtk.HBox(False, 0)
        
        label = Gtk.Label("Discard PHD between")
        label2 = Gtk.Label('and')
        
        label.show()
        label2.show()
        
        self.okbutton = Gtk.Button('Okay')
        self.okbutton.connect('clicked',self.phd_entry_button)
        
        self.entry = Gtk.Entry()
        self.entry.set_activates_default(True)
        
        self.entry2 = Gtk.Entry()
        self.entry2.set_activates_default(True)
        self.entry.show()
        self.entry2.show()
        self.okbutton.show()
        
        thebox.pack_start(label,False,False,0)
        thebox.pack_start(self.entry,False,False,0)
        
        thebox.pack_start(label2,False,False,0)
        thebox.pack_start(self.entry2,False,False,0)
        mainbox.pack_start(thebox,True,True,0)
        mainbox.pack_start(self.okbutton,True,False,0)
       
        
        
        
        mainbox.show()
        thebox.show()
        self.phd_window.show()
        
    def phd_entry_button(self,widget):
        minphd = self.entry.get_text()
        maxphd = self.entry2.get_text()
        phdfilt = [minphd,maxphd]
        self.phd_window.destroy()
        self.filter_phd(phdfilt)
#    	
    ### phd filter function ##
    def filter_phd(self, phdfilt):
        phdfilt = [int(x) for x in phdfilt]
        
    	
    	fakedata = '/home/rachel/codes/chesstest.fits'
	hdu = fits.open(fakedata)
	PHD = hdu[1].data['PHD']
	PHD = np.array(PHD)
	data  = hdu[1].data
	newdata = data[(PHD > phdfilt[0]) & (PHD < phdfilt[1])]
	
	
	plt.subplot(221)
	oldplot = plt.plot(data['X'],data['Y'],linestyle = '',marker = '.')
	
	plt.subplot(222)
	newplot = plt.plot(newdata['X'],newdata['Y'],linestyle = '',marker = '.')
	
	plt.show()
    	

    	 
   ### mouse click on remove orders ###
    def orderbutton_clicked(self, widget, data):
    	self.statusbar.push(data,'click on the order that you want to exclude.')
   	
   	self.remove = []
   	lines = self.lines
   	def onclick_order(event):
   		if self.orderbutton.get_active():
	   		self.remove.append(event.ydata)
	   		remove = self.remove
	   		self.remove_orders(remove,lines)
   		
   	cid4 = self.canvas.mpl_connect('button_press_event',onclick_order)
   	if [self.orderbutton.get_active()] == [False]:
    			self.statusbar.push(0,'Opened File:' + _File)
   ### removing orders
    def remove_orders(self,remove,lines):
    	bad_orders = []
    	bad_orders_index = []
    	
    	for i in range(0,len(remove)):
    		bad = min(lines, key=lambda x:abs(x-remove[i]))
    	        bad_orders.append(bad)
    	        bad_orders_index.append( np.where(lines == bad) )

    	      
    	newlines = filter(lambda lines: lines not in bad_orders,lines)
    	xchunk = self.xchunk
    	ychunk = self.ychunk
    	self.e.cla()
    	lines = newlines
    	self.update_ordersplot(ychunk,xchunk,lines)
    	
    	#for key in oneDorders.keys(): print key
    	print 'number or original orders',len(oneDorders)
        self.new_dictionary(bad_orders_index)
        

    def new_dictionary(self,bad_orders_index):
                
        maxint = len(oneDorders)	
        for i in range(0,len(bad_orders_index)):
        	num = int(bad_orders_index[i][0])
	
        	if oneDorders.get(str(num),None):
        		oneDorders.pop(str(num))
        	for key in oneDorders.keys():
        	        k = int(key)
        		if k > num:
        		        oneDorders[str(k-1)] = oneDorders.get(key)
        		if k == maxint-1:
        		        
        			oneDorders.pop(key)
#        		
        	
        print 'corrected number of orders',len(oneDorders)
        	
        self.save_pickle(oneDorders)
        #for key in oneDorders.keys(): print key
    def save_pickle(self, ondDorders):
    	order_dict = oneDorders
        now = datetime.datetime.now()
        date = now.strftime("%m_%d_%Y")	
        targname = str(self.targname)
    	pickle.dump(order_dict,open(''+targname+'_1D_'+date+'.p','wb'))