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.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 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, 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
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()
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. """ 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 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