def __init__(self, text_color): # Init prarent and set up container Gtk.ScrolledWindow.__init__(self, border_width=10) self.text_color = text_color self.set_hexpand(True) self.set_vexpand(True) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) but = Gtk.Box() # Convert string to tuple self.text_color = tuple( map(float, self.text_color.strip('rgba()').split(','))) # Normalize between 0 and 1 for matplotlib self.text_color = tuple(x / 255 for x in self.text_color) self.f = Figure(figsize=(600, 400), dpi=100) # Set figure to be transparent self.f.patch.set_visible(False) self.a = self.f.add_subplot(111, xscale="log", yscale="log", xlabel="t", ylabel="G(t)") self.a.xaxis.label.set_color(self.text_color) self.a.yaxis.label.set_color(self.text_color) # Set the background to be transparent self.a.set_facecolor('None') # Set the colors of the axes to the font colors of the theme self.a.tick_params(labelcolor=self.text_color, color=self.text_color, which='both') for child in self.a.axes.get_children(): if isinstance(child, matplotlib.spines.Spine): child.set_color(self.text_color) # Finalize canvas self.canvas = FigureCanvas(self.f) # Set up toolbar toolbar = NavigationToolbar(self.canvas, self) toolbar.set_orientation(Gtk.Orientation.HORIZONTAL) box.pack_start(toolbar, False, False, 1) box.pack_start(self.canvas, True, True, 0) self.add(box)
def __init__(self, application, size_request=None): """ :param tuple size_request: The size to set for the canvas. """ self.application = application self.config = application.config """A reference to the King Phisher client configuration.""" 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.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()
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
def __init__(self, config, parent, size_request=None): self.config = config self.parent = parent 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()
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
def build_imagePanel(self): self.fig = Figure(dpi=100, tight_layout=True) canvas = FigureCanvas(self.fig) self.fig.canvas.mpl_connect('draw_event', self.ondraw) toolbar = NavigationToolbar(canvas, self) children = toolbar.get_children() for i in range(len(children) - 3): children[i].destroy() self.main_ax = None self.x_hist = None self.y_hist = None self.zoom_ax = None box = Gtk.VBox() box.pack_start(canvas, True, True, 0) box.pack_end(toolbar, False, False, 0) return box
def reinit(self, canvas, messagelabel, coordlabel): """Call the real super __init__ after the canvas is known and give it to this function.""" self.message = messagelabel self.mode_label = messagelabel self.xy_label = coordlabel NavigationToolbar.__init__(self, canvas, None) self.span_selector = SpanSelector(self.canvas.figure.ax, lambda *args: None, "horizontal", minspan=0.2, span_stays=False, useblit=True) self.span_selector.active = False self.peak_selector = PeakSelector(self.canvas.figure.ax, lambda *args: None, peak_stays=False, useblit=True) self.peak_selector.active = False
def __init__(self, statfile): Gtk.Window.__init__(self) self.connect('key-release-event', self.KeyPressed) self.set_border_width(8) self.set_default_size(1600, 900) self.set_position(Gtk.WindowPosition.CENTER) self.set_title(statfile[-1]) self.statfile = statfile vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) vbox.set_homogeneous(False) self.add(vbox) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) hbox.set_homogeneous(True) vbox.pack_end(hbox, False, False, 0) self.entries, self.values = self.ReadData() store = Gtk.ListStore.new([str]) for entry in self.entries: store.append([entry]) self.xCombo = Gtk.ComboBox.new_with_model_and_entry(store) self.yCombo = Gtk.ComboBox.new_with_model_and_entry(store) self.ComboParams(self.xCombo) self.ComboParams(self.yCombo) self.InitCombo('load') self.InitCompletion(self.xCombo) self.InitCompletion(self.yCombo) self.xCon = self.xCombo.connect('changed', self.ComboChanged) self.xCombo.connect('key-release-event', self.ReleaseFocus) self.yCon = self.yCombo.connect('changed', self.ComboChanged) self.yCombo.connect('key-release-event', self.ReleaseFocus) hbox.pack_start(self.xCombo, True, True, 0) hbox.pack_start(self.yCombo, True, True, 0) self.fig = Figure(figsize=(14, 7)) self.ax = self.fig.gca() self.canvas = FigureCanvas(self.fig) self.canvas.set_can_focus(True) vbox.pack_start(self.canvas, True, True, 0) self.PlotType = 'line' self.PlotData('create', self.PlotType, 'linear', 'linear') self.toolbar = NavigationToolbar(self.canvas, self) vbox.pack_start(self.toolbar, False, False, 0)
def __init__(self, application, size_request=None, style_context=None): """ :param tuple size_request: The size to set for the canvas. """ self.logger = logging.getLogger('KingPhisher.Client.Graph.' + self.__class__.__name__[13:]) 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
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()
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)
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
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
def __init__(self): Gtk.Window.__init__(self, title="Numeric methods") self.set_border_width(10) self.set_size_request(872, 657) #self.set_resizable(resizable=False) self.pl = PlotGraph() # Layout grid self.grid = Gtk.Grid(column_homogeneous=True, column_spacing=10, row_spacing=10) self.add(self.grid) # All entries (equation, x0, y0, step, bound) # equation self.equation = Gtk.Entry(placeholder_text="write your equation") self.equation.set_size_request(357, 47) # x0 self.x_entry = Gtk.Entry(placeholder_text="initial point x") self.x_entry.set_size_request(134, 37) # y0 self.y_entry = Gtk.Entry(placeholder_text="initial point y") self.y_entry.set_size_request(134, 37) # grid self.step = Gtk.Entry(placeholder_text="step") self.step.set_size_request(134, 37) # grid bound self.grid_bound = Gtk.Entry(placeholder_text="step bound") self.grid_bound.set_size_request(134, 37) # bound self.bound = Gtk.Entry(placeholder_text="x bound") self.bound.set_size_request(134, 37) # flag to check which error should be plotted(local or global) self.error_flag = False # when False it means local, when True it means global # creating button self.button = Gtk.Button(label="PLOT GRAPH") self.button.connect("clicked", self.draw) self.button.set_size_request(115, 47) self.canvas = FigureCanvas(self.pl.fig) self.canvas.set_size_request(800, 600) # check buttons (for choice of methods that would be plotted) ex_box = Gtk.Box() label_ex = Gtk.Label("Exact") self.exact_check = Gtk.CheckButton() self.exact_check.connect("toggled", self.toggled) ex_box.pack_start(label_ex, True, True, 0) ex_box.pack_start(self.exact_check, True, True, 0) label_eul = Gtk.Label("Euler") eul_box = Gtk.Box() self.eul_check = Gtk.CheckButton() self.eul_check.connect("toggled", self.toggled) eul_box.pack_start(label_eul, True, True, 0) eul_box.pack_start(self.eul_check, True, True, 0) label_imp = Gtk.Label("Improved Eul") imp_box = Gtk.Box() self.imp_check = Gtk.CheckButton() self.imp_check.connect("toggled", self.toggled) imp_box.pack_start(label_imp, True, True, 0) imp_box.pack_start(self.imp_check, True, True, 0) label_kutta = Gtk.Label("Runge-Kutta ") kutta_box = Gtk.Box() self.kutta_check = Gtk.CheckButton() self.kutta_check.connect("toggled", self.toggled) kutta_box.pack_start(label_kutta, True, True, 0) kutta_box.pack_start(self.kutta_check, True, True, 0) # global error canvas self.gl_canvas = FigureCanvas(self.pl.fig_gl_err) self.gl_canvas.set_size_request(800, 600) self.toolbar = NavigationToolbar(self.canvas, Gtk) self.toolbox = Gtk.Box() self.toolbox.add(self.toolbar) radio_box = Gtk.Box() loc_button = Gtk.RadioButton.new_with_label_from_widget(None, "Local") loc_button.connect("toggled", self.radio_toggled, "local", self) radio_box.pack_start(loc_button, False, False, 10) glob_button = Gtk.RadioButton.new_from_widget(loc_button) glob_button.set_label("Global") glob_button.connect("toggled", self.radio_toggled, "global", self) radio_box.pack_start(glob_button, False, False, 10) # Adding entries and buttons to grid self.grid.attach(self.equation, 2, 0, 3, 1) self.grid.attach(self.button, 4, 2, 1, 1) self.grid.attach(self.x_entry, 2, 1, 1, 1) self.grid.attach(self.y_entry, 3, 1, 1, 1) self.grid.attach(self.step, 2, 2, 1, 1) self.grid.attach(self.bound, 4, 1, 1, 1) self.grid.attach(self.toolbox, 2, 3, 1, 1) self.grid.attach(self.canvas, 2, 4, 3, 4) self.grid.attach(self.gl_canvas, 5, 4, 3, 4) self.grid.attach(ex_box, 5, 0, 1, 1) self.grid.attach(eul_box, 5, 1, 1, 1) self.grid.attach(imp_box, 7, 0, 1, 1) self.grid.attach(kutta_box, 7, 1, 1, 1) self.grid.attach(self.grid_bound, 3, 2, 1, 1) self.grid.attach(radio_box, 6, 2, 1, 1)
class MainWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Numeric methods") self.set_border_width(10) self.set_size_request(872, 657) #self.set_resizable(resizable=False) self.pl = PlotGraph() # Layout grid self.grid = Gtk.Grid(column_homogeneous=True, column_spacing=10, row_spacing=10) self.add(self.grid) # All entries (equation, x0, y0, step, bound) # equation self.equation = Gtk.Entry(placeholder_text="write your equation") self.equation.set_size_request(357, 47) # x0 self.x_entry = Gtk.Entry(placeholder_text="initial point x") self.x_entry.set_size_request(134, 37) # y0 self.y_entry = Gtk.Entry(placeholder_text="initial point y") self.y_entry.set_size_request(134, 37) # grid self.step = Gtk.Entry(placeholder_text="step") self.step.set_size_request(134, 37) # grid bound self.grid_bound = Gtk.Entry(placeholder_text="step bound") self.grid_bound.set_size_request(134, 37) # bound self.bound = Gtk.Entry(placeholder_text="x bound") self.bound.set_size_request(134, 37) # flag to check which error should be plotted(local or global) self.error_flag = False # when False it means local, when True it means global # creating button self.button = Gtk.Button(label="PLOT GRAPH") self.button.connect("clicked", self.draw) self.button.set_size_request(115, 47) self.canvas = FigureCanvas(self.pl.fig) self.canvas.set_size_request(800, 600) # check buttons (for choice of methods that would be plotted) ex_box = Gtk.Box() label_ex = Gtk.Label("Exact") self.exact_check = Gtk.CheckButton() self.exact_check.connect("toggled", self.toggled) ex_box.pack_start(label_ex, True, True, 0) ex_box.pack_start(self.exact_check, True, True, 0) label_eul = Gtk.Label("Euler") eul_box = Gtk.Box() self.eul_check = Gtk.CheckButton() self.eul_check.connect("toggled", self.toggled) eul_box.pack_start(label_eul, True, True, 0) eul_box.pack_start(self.eul_check, True, True, 0) label_imp = Gtk.Label("Improved Eul") imp_box = Gtk.Box() self.imp_check = Gtk.CheckButton() self.imp_check.connect("toggled", self.toggled) imp_box.pack_start(label_imp, True, True, 0) imp_box.pack_start(self.imp_check, True, True, 0) label_kutta = Gtk.Label("Runge-Kutta ") kutta_box = Gtk.Box() self.kutta_check = Gtk.CheckButton() self.kutta_check.connect("toggled", self.toggled) kutta_box.pack_start(label_kutta, True, True, 0) kutta_box.pack_start(self.kutta_check, True, True, 0) # global error canvas self.gl_canvas = FigureCanvas(self.pl.fig_gl_err) self.gl_canvas.set_size_request(800, 600) self.toolbar = NavigationToolbar(self.canvas, Gtk) self.toolbox = Gtk.Box() self.toolbox.add(self.toolbar) radio_box = Gtk.Box() loc_button = Gtk.RadioButton.new_with_label_from_widget(None, "Local") loc_button.connect("toggled", self.radio_toggled, "local", self) radio_box.pack_start(loc_button, False, False, 10) glob_button = Gtk.RadioButton.new_from_widget(loc_button) glob_button.set_label("Global") glob_button.connect("toggled", self.radio_toggled, "global", self) radio_box.pack_start(glob_button, False, False, 10) # Adding entries and buttons to grid self.grid.attach(self.equation, 2, 0, 3, 1) self.grid.attach(self.button, 4, 2, 1, 1) self.grid.attach(self.x_entry, 2, 1, 1, 1) self.grid.attach(self.y_entry, 3, 1, 1, 1) self.grid.attach(self.step, 2, 2, 1, 1) self.grid.attach(self.bound, 4, 1, 1, 1) self.grid.attach(self.toolbox, 2, 3, 1, 1) self.grid.attach(self.canvas, 2, 4, 3, 4) self.grid.attach(self.gl_canvas, 5, 4, 3, 4) self.grid.attach(ex_box, 5, 0, 1, 1) self.grid.attach(eul_box, 5, 1, 1, 1) self.grid.attach(imp_box, 7, 0, 1, 1) self.grid.attach(kutta_box, 7, 1, 1, 1) self.grid.attach(self.grid_bound, 3, 2, 1, 1) self.grid.attach(radio_box, 6, 2, 1, 1) @staticmethod def toggled(check_butt): if check_butt.get_active(): print("Active") @staticmethod def radio_toggled(button, name, activity): if button.get_active() and name == "global": activity.error_flag = True else: activity.error_flag = False def draw(self, wid): # draw the graph on ui, click listener on button 'plot graph' self.grid.remove_row(5) if self.exact_check.get_active(): self.pl.ex_flag = True else: self.pl.ex_flag = False if self.kutta_check.get_active(): self.pl.kutta_flag = True else: self.pl.kutta_flag = False if self.eul_check.get_active(): self.pl.eul_flag = True else: self.pl.eul_flag = False if self.imp_check.get_active(): self.pl.imp_flag = True else: self.pl.imp_flag = False # check if method is chosen if not self.pl.imp_flag and not self.pl.kutta_flag and not self.pl.eul_flag: dialog = Gtk.MessageDialog( self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "You should choose at least one method") dialog.format_secondary_text("No methods are chosen") dialog.run() dialog.destroy() return # user input (some bad code style, need to create function for checking) try: x0 = float(self.x_entry.get_text()) except ValueError: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Wrong initial x") dialog.format_secondary_text("Your initial x is not a number") dialog.run() dialog.destroy() return try: y0 = float(self.y_entry.get_text()) except ValueError: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Wrong initial y") dialog.format_secondary_text("Your initial y is not a number") dialog.run() dialog.destroy() return try: bound = float(self.bound.get_text()) except ValueError: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Wrong x bound") dialog.format_secondary_text("Your x bound is not a number") dialog.run() dialog.destroy() return try: step = float(self.step.get_text()) except ValueError: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Wrong step") dialog.format_secondary_text("Your step is not a number") dialog.run() dialog.destroy() return try: step_bound = float(self.grid_bound.get_text()) except ValueError: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Wrong step bound") dialog.format_secondary_text("Your step bound is not a number") dialog.run() dialog.destroy() return equation = str(self.equation.get_text()) self.pl.init_methods(x0, y0, bound, step, equation) self.pl.plot_graph() self.canvas = FigureCanvas(self.pl.fig) self.canvas.show() self.toolbar.canvas = self.canvas self.toolbar.show() if self.error_flag: self.pl.init_global(x0, y0, bound, step, step_bound, equation) self.pl.plot_global() self.gl_canvas = FigureCanvas(self.pl.fig_global) self.gl_canvas.show() else: self.pl.init_gl_error() self.pl.plot_gll_err() self.gl_canvas = FigureCanvas(self.pl.fig_gl_err) self.gl_canvas.show() self.grid.attach(self.canvas, 2, 4, 3, 4) self.grid.attach(self.gl_canvas, 5, 4, 3, 4) self.show_all()
def home(self,*args): """This version of 'home' resets the history, instead of just clipping it to this view""" _NavigationToolbar.home(self,*args) self.set_max_ranges() self.update() # reset the history...
class CampaignGraph(object): title = 'Unknown' _graph_id = None table_subscriptions = [] def __init__(self, config, parent, size_request=None): self.config = config self.parent = parent 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): return klass._graph_id def make_window(self): 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 pos_func = lambda m, d: (event.x, event.y, True) 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_filename'] 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): self.refresh() def refresh(self, info_cache=None): info_cache = (info_cache or {}) if not self.parent.rpc: return info_cache for table in self.table_subscriptions: 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
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
def pan(self,*args): self.disable_all_span_controls() _NavigationToolbar.pan(self,*args)
class StatplotWindow(Gtk.Window): def __init__(self, statfile): Gtk.Window.__init__(self) self.connect('key-release-event', self.KeyPressed) self.set_border_width(8) self.set_default_size(1600, 900) self.set_position(Gtk.WindowPosition.CENTER) self.set_title(statfile[-1]) self.statfile = statfile vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) vbox.set_homogeneous(False) self.add(vbox) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) hbox.set_homogeneous(True) vbox.pack_end(hbox, False, False, 0) self.entries, self.values = self.ReadData(self.statfile) store = Gtk.ListStore.new([str]) for entry in self.entries: store.append([entry]) self.xCombo = self.CreateCombo(store) self.yCombo = self.CreateCombo(store) self.InitCombo() self.InitCompletion(self.xCombo) self.InitCompletion(self.yCombo) self.xCombo.connect('changed', self.ComboChanged) self.xCombo.connect('key-release-event', self.ReleaseFocus) self.yCombo.connect('changed', self.ComboChanged) self.yCombo.connect('key-release-event', self.ReleaseFocus) hbox.pack_start(self.xCombo, True, True, 0) hbox.pack_start(self.yCombo, True, True, 0) self.fig = Figure(figsize=(14, 7)) self.ax = self.fig.gca() self.canvas = FigureCanvas(self.fig) self.canvas.set_can_focus(True) vbox.pack_start(self.canvas, True, True, 0) self.PlotType = 'marker' if self.values.ndim == 1 else 'line' self.PlotData('create', self.PlotType, 'linear', 'linear') self.toolbar = NavigationToolbar(self.canvas, self) vbox.pack_start(self.toolbar, False, False, 0) def ComboChanged(self, comboBox): activeText = comboBox.get_child().get_text() if activeText in self.entries: comboBox.set_active(self.entries.index(activeText)) if comboBox is self.xCombo: self.PlotData('update', self.PlotType, 'linear', self.ax.get_yscale()) elif comboBox is self.yCombo: self.PlotData('update', self.PlotType, self.ax.get_xscale(), 'linear') self.canvas.grab_focus() @staticmethod def CreateCombo(store): comboBox = Gtk.ComboBox.new_with_model_and_entry(store) comboBox.set_entry_text_column(0) comboBox.set_margin_left(100) comboBox.set_margin_right(100) comboBox.set_wrap_width(3) return comboBox def FormatAxis(self, ax2fmt, scale): if ax2fmt == 'x': setScale = self.ax.set_xscale curAxis = self.ax.xaxis curData = self.xData elif ax2fmt == 'y': setScale = self.ax.set_yscale curAxis = self.ax.yaxis curData = self.yData setScale(scale) if scale == 'linear': self.ax.ticklabel_format(style='sci', axis=ax2fmt, scilimits=(0, 0), useMathText=True) curAxis.set_minor_locator(tck.AutoMinorLocator()) self.ax.relim() self.ax.autoscale(True, ax2fmt, None) elif scale == 'log': curAxis.set_minor_locator(tck.LogLocator(subs=numpy.arange(2, 10))) logFmt = tck.LogFormatterSciNotation(base=10, labelOnlyBase=False, minor_thresholds=(4, 1)) curAxis.set_minor_formatter(logFmt) self.ax.relim() self.ax.autoscale(True, ax2fmt, None) elif scale == 'symlog': axMin = min(abs(curData[curData != 0])) axMax = max(abs(curData)) axRange = numpy.log10(axMax / axMin) if ax2fmt == 'x': setScale('symlog', basex=10, subsx=numpy.arange(2, 10), linthreshx=axMin * 10**(axRange / 2)) elif ax2fmt == 'y': setScale('symlog', basey=10, subsy=numpy.arange(2, 10), linthreshy=axMin * 10**(axRange / 2)) # Thomas Duvernay, 06/01/19 # There seems to be a bug with the labelling of the 0 tick # when a 'symlog' is used as an axis scale. It looks like # it is considered as a minor tick. symLogLoc = tck.SymmetricalLogLocator(subs=numpy.arange(2, 10), linthresh=axMin * 10**(axRange / 2), base=10) curAxis.set_minor_locator(symLogLoc) logFmt = tck.LogFormatterSciNotation(base=10, labelOnlyBase=False, minor_thresholds=(4, 1), linthresh=axMin * 10**(axRange / 2)) curAxis.set_minor_formatter(logFmt) self.ax.set_xlabel(self.xCombo.get_child().get_text(), fontweight='bold', fontsize=20) self.ax.set_ylabel(self.yCombo.get_child().get_text(), fontweight='bold', fontsize=20) self.ax.tick_params(which='major', length=7, labelsize=16, width=2) self.ax.tick_params(which='minor', length=4, labelsize=10, width=2, colors='xkcd:scarlet', labelrotation=45) self.ax.xaxis.get_offset_text().set(fontsize=13, fontweight='bold', color='xkcd:black') self.ax.yaxis.get_offset_text().set(fontsize=13, fontweight='bold', color='xkcd:black') self.fig.set_tight_layout(True) def InitCombo(self): if 'ElapsedTime' in self.entries: iterX = self.xCombo.get_model().get_iter( self.entries.index('ElapsedTime')) self.xCombo.set_active_iter(iterX) self.yCombo.set_active(0) else: self.xCombo.set_active(0) self.yCombo.set_active(1) @staticmethod def InitCompletion(comboBox): completion = Gtk.EntryCompletion.new() completion.set_text_column(0) completion.set_inline_completion(True) completion.set_inline_selection(False) completion.set_model(comboBox.get_model()) comboBox.get_child().set_completion(completion) def KeyPressed(self, widget, event): key = event.string if (self.xCombo.get_child().has_focus() or self.yCombo.get_child().has_focus()): pass elif key == 'r': self.entries, self.values = self.ReadData(self.statfile) self.PlotData('update', self.PlotType, self.ax.get_xscale(), self.ax.get_yscale()) elif key == 'q': self.destroy() elif key == 'x' or key == 'y': if self.values.ndim == 1: warnings.warn( 'Insufficient data available to turn on ' 'logarithmic scale', stacklevel=2) return if key == 'x': self.get_scale = self.ax.get_xscale curData = self.xData else: self.get_scale = self.ax.get_yscale curData = self.yData if self.get_scale() == 'linear' and (curData == 0).all(): warnings.warn( 'Change to logarithmic scale denied: the ' f'selected variable for the {key} axis is null.', stacklevel=2) return elif (self.get_scale() == 'linear' and max(abs(curData)) / min(abs(curData)) < 10): warnings.warn( 'Change to logarithmic scale denied: the ' f'selected variable for the {key} axis has a ' 'range of variation smaller than one order of ' 'magnitude.', stacklevel=2) return elif self.get_scale() == 'linear': axMin = min(abs(curData[curData != 0])) axMax = max(abs(curData)) if axMin != axMax and min(curData) < 0: scale = 'symlog' elif axMin != axMax: scale = 'log' else: warnings.warn( 'Change to logarithmic scale denied: the ' f'selected variable for the {key} axis is a ' 'constant.', stacklevel=2) return elif self.get_scale() in ['log', 'symlog']: scale = 'linear' self.FormatAxis(key, scale) self.fig.canvas.draw_idle() self.fig.canvas.draw() self.fig.canvas.flush_events() elif key == 'l': if self.values.ndim == 1: warnings.warn('Insufficient data available to turn on line ' + 'display', stacklevel=2) return self.PlotType = 'marker' if self.PlotType == 'line' else 'line' self.PlotData('update', self.PlotType, self.ax.get_xscale(), self.ax.get_yscale()) elif key == 'a': self.xCombo.grab_focus() elif key == 'o': self.yCombo.grab_focus() def PlotData(self, action, type, xscale, yscale): self.xData = self.values[..., self.xCombo.get_active()] self.yData = self.values[..., self.yCombo.get_active()] if action == 'create': self.statplot, = self.ax.plot(self.xData, self.yData) elif action == 'update': self.statplot.set_xdata(self.xData) self.statplot.set_ydata(self.yData) self.toolbar.update() if type == 'line': self.statplot.set_color('xkcd:light purple') self.statplot.set_linestyle('solid') self.statplot.set_linewidth(2) self.statplot.set_marker('None') elif type == 'marker': self.statplot.set_linestyle('None') self.statplot.set_marker('d') self.statplot.set_markeredgecolor('xkcd:black') self.statplot.set_markerfacecolor('xkcd:light purple') self.statplot.set_markersize(7) self.statplot.set_markeredgewidth(0.3) self.FormatAxis('x', xscale) self.FormatAxis('y', yscale) if action == 'update': self.fig.canvas.draw_idle() self.fig.canvas.draw() self.fig.canvas.flush_events() @staticmethod def ReadData(statfile): def GatherEntries(stat2read): print('Reading ' + stat2read) entries = [] with open(stat2read, 'r') as fid: for num, line in enumerate(fid): if line.startswith('<field'): info = etree.fromstring(line) if info.attrib['statistic'] == 'value': entries.append(info.attrib['name']) elif all([ x in info.attrib for x in ['components', 'material_phase'] ]): for i in range(int(info.attrib['components'])): entries.append('{e[material_phase]}%{e[name]}' '%{e[statistic]}%{i}'.format( e=info.attrib, i=i)) elif 'components' in info.attrib: for i in range(int(info.get('components'))): entries.append( '{e[name]}%{e[statistic]}%{i}'.format( e=info.attrib, i=i)) elif 'material_phase' in info.attrib: entries.append( '{e[material_phase]}%{e[name]}' '%{e[statistic]}'.format(e=info.attrib)) else: entries.append('{e[name]}%{e[statistic]}'.format( e=info.attrib)) elif line.startswith('</header>'): break return numpy.asarray(entries), num if statfile[0].endswith('stat'): entries, num = GatherEntries(statfile[0]) values = numpy.genfromtxt(statfile[0], skip_header=num + 1) values = values[..., numpy.argsort(entries)] entries = entries[numpy.argsort(entries)] if len(statfile) > 1: for item in statfile[1:]: entriesTemp, num = GatherEntries(item) if numpy.array_equal( entries, entriesTemp[numpy.argsort(entriesTemp)]): valuesTemp = numpy.genfromtxt(item, skip_header=num + 1) valuesTemp = valuesTemp.T[numpy.argsort(entriesTemp)].T values = numpy.vstack((values, valuesTemp)) else: sys.exit('Statfiles entries do not match') elif statfile[0].endswith('detectors'): entries = GatherEntries(statfile[0])[0] values = numpy.fromfile(statfile[0] + '.dat', dtype=numpy.float64) ncols = entries.size nrows = values.size // ncols values = values[:nrows * ncols].reshape(nrows, ncols) return list(entries), values def ReleaseFocus(self, widget, event): if event.keyval == Gdk.KEY_Escape: self.canvas.grab_focus()
def zoom(self, *args, **kwargs): self.pan_button.set_active(False) return NavigationToolbar2GTK3.zoom(self, *args, **kwargs)
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)
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
class StatplotWindow(Gtk.Window): def __init__(self, statfile): Gtk.Window.__init__(self) self.connect('key-press-event', self.KeyPressed) self.set_border_width(8) self.set_default_size(1600, 900) self.set_position(Gtk.WindowPosition.CENTER) self.set_title(statfile[-1]) self.statfile = statfile self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) self.vbox.set_homogeneous(False) self.add(self.vbox) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) hbox.set_homogeneous(True) self.vbox.pack_end(hbox, False, False, 0) self.entries = self.ReadData(statfile) self.xCombo = Gtk.ComboBoxText.new_with_entry() self.xCombo.set_wrap_width(3) self.yCombo = Gtk.ComboBoxText.new_with_entry() self.yCombo.set_wrap_width(3) self.PopulateCombo('load') self.InitCompletion(self.xCombo) self.InitCompletion(self.yCombo) self.xCon = self.xCombo.connect('changed', self.ComboChangedX) self.xCombo.connect('key-release-event', self.ReleaseFocus) self.yCon = self.yCombo.connect('changed', self.ComboChangedY) self.yCombo.connect('key-release-event', self.ReleaseFocus) hbox.pack_start(self.xCombo, True, True, 0) hbox.pack_start(self.yCombo, True, True, 0) self.fig = Figure(figsize=(14, 7)) self.ax = self.fig.gca() self.canvas = FigureCanvas(self.fig) self.canvas.set_can_focus(True) self.vbox.pack_start(self.canvas, True, True, 0) self.PlotType = 'line' self.PlotData('create', self.PlotType, 'linear', 'linear') self.toolbar = NavigationToolbar(self.canvas, self) self.vbox.pack_start(self.toolbar, False, False, 0) def ComboChangedX(self, widget): if self.xCombo.get_active_text() in sorted(self.entries.Paths()): self.PlotData('update', self.PlotType, 'linear', self.ax.get_yscale()) self.canvas.grab_focus() def ComboChangedY(self, widget): if self.yCombo.get_active_text() in sorted(self.entries.Paths()): self.PlotData('update', self.PlotType, self.ax.get_xscale(), 'linear') self.canvas.grab_focus() def FormatAxis(self, ax2fmt, scale): if ax2fmt == 'x': self.set_scale = self.ax.set_xscale self.axis = self.ax.xaxis self.Data = self.xData elif ax2fmt == 'y': self.set_scale = self.ax.set_yscale self.axis = self.ax.yaxis self.Data = self.yData self.set_scale(scale) if scale == 'linear': self.ax.ticklabel_format(style='sci', axis=ax2fmt, scilimits=(0, 0), useMathText=True) self.axis.set_minor_locator(tck.AutoMinorLocator()) self.ax.relim() self.ax.autoscale(True, ax2fmt, None) elif scale == 'log': self.axis.set_minor_locator( tck.LogLocator(subs=numpy.arange(2, 10))) logFmt = tck.LogFormatterSciNotation(base=10, labelOnlyBase=False, minor_thresholds=(4, 1)) self.axis.set_minor_formatter(logFmt) self.ax.relim() self.ax.autoscale(True, ax2fmt, None) elif scale == 'symlog': axMin = min(abs(self.Data[self.Data != 0])) axMax = max(abs(self.Data)) axRange = numpy.log10(axMax / axMin) if ax2fmt == 'x': self.set_scale('symlog', basex=10, subsx=numpy.arange(2, 10), linthreshx=axMin * 10**(axRange / 2)) elif ax2fmt == 'y': self.set_scale('symlog', basey=10, subsy=numpy.arange(2, 10), linthreshy=axMin * 10**(axRange / 2)) # Thomas Duvernay, 06/01/19 # There seems to be a bug with the labelling of the 0 tick # when a 'symlog' is used as an axis scale. It looks like # it is considered as a minor tick. symLogLoc = tck.SymmetricalLogLocator(subs=numpy.arange(2, 10), linthresh=axMin * 10**(axRange / 2), base=10) self.axis.set_minor_locator(symLogLoc) logFmt = tck.LogFormatterSciNotation(base=10, labelOnlyBase=False, minor_thresholds=(4, 1), linthresh=axMin * 10**(axRange / 2)) self.axis.set_minor_formatter(logFmt) self.ax.set_xlabel(self.xField, fontweight='bold', fontsize=20) self.ax.set_ylabel(self.yField, fontweight='bold', fontsize=20) self.ax.tick_params(which='major', length=7, labelsize=16, width=2) self.ax.tick_params(which='minor', length=4, labelsize=10, width=2, colors='xkcd:scarlet', labelrotation=45) self.ax.xaxis.get_offset_text().set(fontsize=13, fontweight='bold', color='xkcd:black') self.ax.yaxis.get_offset_text().set(fontsize=13, fontweight='bold', color='xkcd:black') self.fig.set_tight_layout(True) def InitCompletion(self, comboBox): completion = Gtk.EntryCompletion.new() completion.set_text_column(0) completion.set_inline_completion(True) completion.set_inline_selection(True) completion.set_model(comboBox.get_model()) comboBox.get_child().set_completion(completion) def KeyPressed(self, widget, event): key = event.string if key == 'r': self.RefreshData(self.statfile) self.PlotData('update', self.PlotType, self.ax.get_xscale(), self.ax.get_yscale()) elif key == 'q': self.destroy() elif key == 'x' or key == 'y': if key == 'x': self.get_scale = self.ax.get_xscale self.Data = self.xData elif key == 'y': self.get_scale = self.ax.get_yscale self.Data = self.yData if self.get_scale() == 'linear' \ and self.Data[self.Data != 0].size == 0: warnings.warn('Change to logarithmic scale denied: the ' + 'selected variable for the ' + key + ' axis ' + 'is null.', stacklevel=2) scale = 'linear' elif self.get_scale() == 'linear' \ and max(abs(self.Data)) / min(abs(self.Data)) < 100: warnings.warn('Change to logarithmic scale denied: the ' + 'selected variable for the ' + key + ' axis ' + 'has a range of variation smaller than two ' + 'orders of magnitude.', stacklevel=2) scale = 'linear' elif self.get_scale() == 'linear': axMin = min(abs(self.Data[self.Data != 0])) axMax = max(abs(self.Data)) if axMin != axMax and min(self.Data) < 0: scale = 'symlog' elif axMin != axMax: scale = 'log' else: warnings.warn('Change to logarithmic scale denied: the ' + 'selected variable for the ' + key + ' ' + 'axis is a constant.', stacklevel=2) scale = 'linear' elif self.get_scale() in ['log', 'symlog']: scale = 'linear' self.FormatAxis(key, scale) self.fig.canvas.draw() elif key == 'l': self.PlotType = 'line' if self.PlotType == 'marker' else 'marker' self.PlotData('update', self.PlotType, self.ax.get_xscale(), self.ax.get_yscale()) def PlotData(self, action, type, xscale, yscale): self.xField = self.xCombo.get_active_text() self.yField = self.yCombo.get_active_text() self.xData = self.entries[self.xField] self.yData = self.entries[self.yField] if numpy.unique(self.xData).size == 1 \ and numpy.unique(self.yData).size == 1: type = 'marker' if action == 'create': self.statplot, = self.ax.plot(self.xData, self.yData, linewidth=2, color='xkcd:light purple') elif action == 'update': self.statplot.set_xdata(self.xData) self.statplot.set_ydata(self.yData) if type == 'line': self.statplot.set_color('xkcd:light purple') self.statplot.set_linestyle('solid') self.statplot.set_linewidth(2) self.statplot.set_marker('None') elif type == 'marker': self.statplot.set_linestyle('None') self.statplot.set_marker('d') self.statplot.set_markeredgecolor('xkcd:black') self.statplot.set_markerfacecolor('xkcd:light purple') self.statplot.set_markersize(7) self.statplot.set_markeredgewidth(0.3) self.toolbar.update() self.FormatAxis('x', xscale) self.FormatAxis('y', yscale) self.fig.canvas.draw() self.fig.canvas.flush_events() def PopulateCombo(self, action, prevIterX=None, prevIterY=None): for entry in sorted(self.entries.Paths()): self.xCombo.append_text(entry) self.yCombo.append_text(entry) if action == 'load': if 'ElapsedTime' in self.entries.Paths(): iterX = self.xCombo.get_model(). \ get_iter(sorted(self.entries.Paths()).index('ElapsedTime')) self.xCombo.set_active_iter(iterX) self.yCombo.set_active(0) else: self.xCombo.set_active(0) self.yCombo.set_active(1) elif action == 'reload': self.xCombo.set_active_iter(prevIterX) self.yCombo.set_active_iter(prevIterY) def ReadData(self, statfile): stats = [] for i, filename in enumerate(statfile): failcount = 0 while failcount < 4: try: stats.append(fluidity_tools.Stat(filename)) break except (TypeError, ValueError): time.sleep(0.2) failcount += 1 if failcount == 4: stats.append(fluidity_tools.Stat(filename)) if len(stats) == 1: return stats[0] else: return fluidity_tools.JoinStat(*stats) def RefreshData(self, statfile): with self.xCombo.handler_block(self.xCon), \ self.yCombo.handler_block(self.yCon): self.xCombo.remove_all() self.yCombo.remove_all() self.entries = self.ReadData(statfile) self.PopulateCombo('reload', prevIterX=self.xCombo.get_active_iter(), prevIterY=self.yCombo.get_active_iter()) def ReleaseFocus(self, widget, event): if event.keyval == Gdk.KEY_Escape: self.canvas.grab_focus()