def __init__(self, application): """ Create a summary page containing various statistics such as the number of logs in the logbook, the logbook's modification date, etc. :arg application: The PyQSO application containing the main Gtk window, etc. """ self.application = application self.logbook = self.application.logbook self.builder = self.application.builder glade_file_path = join(realpath(dirname(__file__)), "res", "pyqso.glade") self.builder.add_objects_from_file(glade_file_path, ("summary_page",)) self.summary_page = self.builder.get_object("summary_page") self.items = {} # Database name in large font at the top of the summary page self.builder.get_object("database_name").set_markup("<span size=\"x-large\">%s</span>" % basename(self.logbook.path)) self.items["LOG_COUNT"] = self.builder.get_object("log_count") self.items["QSO_COUNT"] = self.builder.get_object("qso_count") self.items["DATE_MODIFIED"] = self.builder.get_object("date_modified") # Yearly statistics config = configparser.ConfigParser() have_config = (config.read(expanduser('~/.config/pyqso/preferences.ini')) != []) (section, option) = ("general", "show_yearly_statistics") if(have_config and config.has_option(section, option)): if(config.getboolean("general", "show_yearly_statistics") and have_matplotlib): hbox = Gtk.HBox() label = Gtk.Label(label="Display statistics for year: ", halign=Gtk.Align.START) hbox.pack_start(label, False, False, 6) year_select = Gtk.ComboBoxText() min_year, max_year = self.get_year_bounds() if min_year and max_year: for year in range(max_year, min_year-1, -1): year_select.append_text(str(year)) year_select.append_text("") year_select.connect("changed", self.on_year_changed) hbox.pack_start(year_select, False, False, 6) self.summary_page.pack_start(hbox, False, False, 4) self.items["YEARLY_STATISTICS"] = Figure() canvas = FigureCanvas(self.items["YEARLY_STATISTICS"]) canvas.set_size_request(800, 175) canvas.show() self.summary_page.pack_start(canvas, True, True, 0) # Summary tab label and icon. tab = Gtk.HBox(homogeneous=False, spacing=0) label = Gtk.Label(label="Summary ") icon = Gtk.Image.new_from_icon_name(Gtk.STOCK_INDEX, Gtk.IconSize.MENU) tab.pack_start(label, False, False, 0) tab.pack_start(icon, False, False, 0) tab.show_all() self.logbook.notebook.insert_page(self.summary_page, tab, 0) # Append as a new tab self.logbook.notebook.show_all() return
def run_inner(self, endog, exog, **kwargs ): signs = ['x', '+'] colours = ['r', 'b'] f = plt.figure() var_index = 0 for var in exog.columns: sign = signs[var_index % len( signs )] colour = colours[var_index % len( colours )] plt.plot( endog, exog[var], colour+sign ) var_index += 1 canvas = FigureCanvas( f ) self.ui.output_vbox.pack_start( canvas, True, True, 0 ) canvas.show() self.ui.output_dialog.run() self.ui.output_dialog.hide()
class Pie(ChartBase): def setup(self): fig = Figure(figsize=(10, 10), dpi=100, facecolor="white", edgecolor="white") self.ax = fig.add_subplot(111) fig.subplots_adjust(left=0.1, right=0.90, top=0.95, bottom=0.05) # ensure the pie is round self.ax.set_aspect('equal') matplotlib.rc('font', family="sans", weight="normal", size=9) self.plot = FigureCanvas(fig) self.pack_start(self.plot, True, True, 0) def draw_plot(self): self.ax.clear() self.ax.pie(self.controller.values.values(), labels=self.controller.values.keys(), autopct='%1.1f%%', colors=self.colors ) # show pie self.spinner.hide() self.plot.show()
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 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, 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 SenSysDemo2(): def __init__(self, lc, ylim_correct=[-42,20], ylim_wrong=[-42,2], cmds=[]): self.lc = lc self.commands = cmds; self.create_queues() self.window = Gtk.Window(title="SenSys'17 - Packetized-LTE PHY") self.window.set_default_size(1200, 900) self.superbox = Gtk.Box() self.window.add(self.superbox) self.boxplot = Gtk.Box() self.superbox.pack_start(self.boxplot, True, True, 0) self.grid = Gtk.Grid() self.superbox.pack_start(self.grid, False, False, 0) self.boxbuttons = Gtk.Box() self.grid.add(self.boxbuttons) self.boxstats = Gtk.Box() self.grid.attach_next_to(self.boxstats, self.boxbuttons, Gtk.PositionType.BOTTOM, 1, 2) button = Gtk.Button.new_with_label("Single Link") button.connect("clicked", self.on_single_radio) self.boxbuttons.pack_start(button, True, True, 0) button = Gtk.Button.new_with_label("LBT Disabled") button.connect("clicked", self.on_lbt_disabled) self.boxbuttons.pack_start(button, True, True, 0) button = Gtk.Button.new_with_label("LBT Enabled") button.connect("clicked", self.on_lbt_enabled) self.boxbuttons.pack_start(button, True, True, 0) self.box = Gtk.Box() self.boxplot.pack_start(self.box, True, True, 0) scale = 10 self.fig = Figure(figsize=(4*scale,3*scale)) self.ax1 = self.fig.add_subplot(2, 1, 1) plt.grid(True) self.ax2 = self.fig.add_subplot(2, 1, 2) plt.grid(True) # draw and show it self.ax1.set_title("Successful Statistics") self.line1_correct, = self.ax1.plot(np.zeros(measure_lbt_performance.CORRECT_ARRAY_SIZE),'b',visible=True,label='RSSI [dBm]') self.line2_correct, = self.ax1.plot(np.zeros(measure_lbt_performance.CORRECT_ARRAY_SIZE),'k',visible=True,label='CQI') self.line3_correct, = self.ax1.plot(np.zeros(measure_lbt_performance.CORRECT_ARRAY_SIZE),'g',visible=True,label='Noise [dBm]') self.ax1.legend() self.ax1.grid(True) self.ax1.set_ylim(ylim_correct) self.ax2.set_title("Unsuccessful Statistics") self.line1_wrong, = self.ax2.plot(np.zeros(measure_lbt_performance.ERROR_ARRAY_SIZE),'b',visible=True,label='RSSI [dBm]') self.line2_wrong, = self.ax2.plot(np.zeros(measure_lbt_performance.ERROR_ARRAY_SIZE),'g',visible=True,label='Noise [dBm]') self.ax2.legend() self.ax2.grid(True) self.ax2.set_ylim(ylim_wrong) self.canvas = FigureCanvas(self.fig) self.canvas.show() self.box.pack_start(self.canvas, True, True, 0) plt.ion() self.liststore_correct_rx = Gtk.ListStore(str, float) self.liststore_correct_rx.append(["In sequence %", 0.0]) self.liststore_correct_rx.append(["Out of sequence %", 0.0]) self.liststore_correct_rx.append(["Correct %", 0.0]) self.liststore_correct_rx.append(["Error %", 0.0]) self.liststore_correct_rx.append(["Channel free %", 0.0]) self.liststore_correct_rx.append(["Channel busy %", 0.0]) self.liststore_correct_rx.append(["Channel free pwr", 0.0]) self.liststore_correct_rx.append(["Channel busy pwr", 0.0]) self.liststore_correct_rx.append(["Avg. coding time", 0.0]) self.liststore_correct_rx.append(["Avg. # TX slots", 0.0]) treeview = Gtk.TreeView(model=self.liststore_correct_rx) renderer_text = Gtk.CellRendererText() column_text = Gtk.TreeViewColumn("Statistics", renderer_text, text=0) treeview.append_column(column_text) renderer_spin = Gtk.CellRendererSpin() renderer_spin.set_property("editable", False) column_spin = Gtk.TreeViewColumn("Value", renderer_spin, text=1) treeview.append_column(column_spin) self.boxstats.pack_start(treeview, True, True, 0) gobject.idle_add(self.update_graph) def create_queues(self): self.rx_success_error_queue = queue.Queue() self.rx_success_plot_queue = queue.Queue() self.rx_error_plot_queue = queue.Queue() self.tx_stats_queue = queue.Queue() self.decoded_stats_queue = queue.Queue() def clear_queues(self): while(self.rx_success_error_queue.qsize() > 0): dumb = self.rx_success_error_queue.get() while(self.rx_success_plot_queue.qsize() > 0): dumb = self.rx_success_plot_queue.get() while(self.rx_error_plot_queue.qsize() > 0): dumb = self.rx_error_plot_queue.get() while(self.tx_stats_queue.qsize() > 0): dumb = self.tx_stats_queue.get() while(self.decoded_stats_queue.qsize() > 0): dumb = self.decoded_stats_queue.get() def close_all(self, widget=None, *data): # Do some other clean up before killing the loop. radio.stop_radio() Gtk.main_quit() os.system("sudo killall -9 python3") def init(self): self.window.connect("delete-event", self.close_all) self.window.show_all() def set_commands(self, cmds): self.commands = cmds def on_single_radio(self, button): print("Single Link") radio.stop_radio() print(self.commands[0]) self.clear_queues() radio.start_radio(self.commands[0],self.lc,self) def on_lbt_disabled(self, button): print("Two Links: LBT disabled") radio.stop_radio() print(self.commands[1]) self.clear_queues() radio.start_radio(self.commands[1],self.lc,self) def on_lbt_enabled(self, button): print("Two Links: LBT enabled") radio.stop_radio() print(self.commands[2]) self.clear_queues() radio.start_radio(self.commands[2],self.lc,self) def update_graph(self): if(self.decoded_stats_queue.empty() == False): try: decoded = self.decoded_stats_queue.get_nowait() self.liststore_correct_rx[0][1] = decoded.in_sequence_percentage # In sequence self.liststore_correct_rx[1][1] = decoded.out_of_sequence_percentage # Out of sequence except queue.Empty: pass if(self.rx_success_error_queue.empty() == False): try: internal = self.rx_success_error_queue.get_nowait() self.liststore_correct_rx[2][1] = internal.correct # Correct self.liststore_correct_rx[3][1] = internal.error # Error except queue.Empty: pass if(self.tx_stats_queue.empty() == False): try: internal = self.tx_stats_queue.get_nowait() self.liststore_correct_rx[8][1] = internal.average_coding_time # average coding Time self.liststore_correct_rx[9][1] = internal.average_num_of_tx_slots # average number of tx slots self.liststore_correct_rx[4][1] = internal.channel_free_percentage # LBT Free self.liststore_correct_rx[5][1] = internal.channel_busy_percentage # LBT Busy self.liststore_correct_rx[6][1] = internal.channel_free_avg_power # Channle Free Power self.liststore_correct_rx[7][1] = internal.channel_busy_avg_power # Channle Busy Power except queue.Empty: pass if(self.rx_success_plot_queue.empty() == False): try: internal = self.rx_success_plot_queue.get_nowait() self.plot_correct_rx_stats(internal) except queue.Empty: pass if(self.rx_error_plot_queue.empty() == False): try: internal = self.rx_error_plot_queue.get_nowait() self.plot_wrong_rx_stats(internal) except queue.Empty: pass return True # Plot figure with correct decoding statistics. def plot_correct_rx_stats(self, internal): # set the new data. self.line1_correct.set_ydata(internal.correct_rssi_vector) self.line2_correct.set_ydata(internal.correct_cqi_vector) self.line3_correct.set_ydata(internal.correct_noise_vector) # Draw stats. self.fig.canvas.draw() # Plot figure with wrong decoding/dectection statistics. def plot_wrong_rx_stats(self, internal): # set the new data. self.line1_wrong.set_ydata(internal.error_rssi_vector) self.line2_wrong.set_ydata(internal.error_noise_vector) # Draw stats. self.fig.canvas.draw()
class MainWindow(Gtk.ApplicationWindow): def __init__(self, file_path=None): Gtk.Window.__init__(self) self.set_default_size(900, 600) self.set_icon_name(config.PROGRAM_NAME_LOWER) self.settings = Gio.Settings.new(config.GSETTINGS_BASE_KEY) self.circuit = None self.netlist_file_path = None self.file_monitor = None self.raw_data_window = console_gui.ConsoleOutputWindow( _("Simulation output")) self.execution_log_window = console_gui.ConsoleOutputWindow( _("Execution log")) self._create_menu_models() ########## #headerbar self.hb = Gtk.HeaderBar() self.hb.props.show_close_button = True if config.csd_are_supported() == True: self.set_titlebar(self.hb) else: #disable headerbar as titlebar if not supported self.no_csd_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.no_csd_box.pack_start(self.hb, False, False, 0) self.hb.props.show_close_button = False self.add(self.no_csd_box) ## Right side of headerbar self.hb_rbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) self._add_insert_button() self._add_simulate_button() self._add_gear_button() self.hb.pack_end(self.hb_rbox) ## Left side of headerbar self._add_arrow_buttons() self._add_load_button() ######## #Content self.stack = Gtk.Stack() self.stack.set_transition_type( Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.stack.set_transition_duration(500) ## Overview stack self.overview_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.source_view = None self._open_state("new") self.infobar = None self.stack.add_titled(self.overview_box, "overview", _("Circuit")) ## Simulation stack self.simulation_box = Gtk.Box() self.canvas = Gtk.DrawingArea() self.simulation_box.pack_start(self.canvas, True, True, 0) self.stack.add_titled(self.simulation_box, "simulation", _("Simulation")) if config.csd_are_supported() == True: self.add(self.stack) else: #disable headerbar as titlebar if not supported self.no_csd_box.pack_end(self.stack, True, True, 0) self.overview_view() self.forward_button.props.sensitive = False # HACK: self._open_state("new") sets it to # False but self.overview_view() sets it to # True. This line fixes the incongruence. self.connect_after('destroy', self._on_destroy) if file_path is not None: self.load_file(file_path) def _open_state(self, state="opened"): """ show sourceview state="opened" or suggest opening a file state="new" """ if state == "opened": self.overview_view() self.forward_button.props.sensitive = False # Don go forward until having some simulations for child in self.overview_box.get_children(): self.overview_box.remove(child) self.source_scrolled = Gtk.ScrolledWindow(None, None) self.source_scrolled.set_hexpand(True) self.source_scrolled.set_vexpand(True) self.source_buffer = GtkSource.Buffer() self.source_buffer.connect("modified-changed", self.on_source_buffer_modified_changed) self.source_buffer.set_highlight_syntax(True) self.source_buffer.set_language( GtkSource.LanguageManager.get_default().get_language( "spice-netlist")) self.sourceview = GtkSource.View() self.settings.bind('show-line-numbers', self.sourceview, 'show-line-numbers', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('highlight-current-line', self.sourceview, 'highlight-current-line', Gio.SettingsBindFlags.DEFAULT) font_desc = Pango.FontDescription('monospace') if font_desc: self.sourceview.modify_font(font_desc) self.sourceview.set_buffer(self.source_buffer) self.sourceview.set_show_line_numbers(True) self.source_scrolled.add(self.sourceview) self.overview_box.pack_end(self.source_scrolled, True, True, 0) self.overview_box.show_all() self.insert_button.props.sensitive = True self.simulate_button.props.sensitive = True self.lookup_action("save").props.enabled = True elif state == "new": if self.source_view is not None: self.overview_box.remove(self.source_scrolled) self.source_view = None else: self.emptyGrid = Gtk.Grid( orientation=Gtk.Orientation.HORIZONTAL, hexpand=True, vexpand=True, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, column_spacing=12, margin=30) self.overview_box.add(self.emptyGrid) emptyPageImage = Gtk.Image(icon_name='document-open-symbolic', icon_size=Gtk.IconSize.DIALOG) emptyPageImage.get_style_context().add_class('dim-label') self.emptyGrid.add(emptyPageImage) emptyPageDirections = Gtk.Label( label=_("Use the <b>Open</b> button to load a circuit"), use_markup=True, max_width_chars=30, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER) emptyPageDirections.get_style_context().add_class('dim-label') self.emptyGrid.add(emptyPageDirections) self.emptyGrid.show_all() self.overview_box.pack_end(self.emptyGrid, True, True, 0) self.insert_button.props.sensitive = False self.simulate_button.props.sensitive = False self.forward_button.props.sensitive = False # TODO: let see why this is not effective... self.lookup_action("save").props.enabled = False def _create_menu_models(self): # gear_menu overview xml # ## Create menu model builder = Gtk.Builder() builder.set_translation_domain(config.DOMAIN) builder.add_from_file( os.path.join(os.path.dirname(__file__), "data", "menu.ui")) self.gearmenu_overview = builder.get_object('gearmenu-overview') ## Bind actions save_action = Gio.SimpleAction.new("save", None) save_action.connect("activate", self.save_cb) self.add_action(save_action) close_action = Gio.SimpleAction.new("close", None) close_action.connect("activate", self.close_cb) self.add_action(close_action) # gear_menu simulation xml # ## Create menu model self.gearmenu_simulation = builder.get_object('gearmenu-simulation') ## Bind actions save_plot_action = Gio.SimpleAction.new("save-plot", None) save_plot_action.connect("activate", self.save_plot_cb) self.add_action(save_plot_action) save_data_action = Gio.SimpleAction.new("save-data", None) save_data_action.connect("activate", self.save_data_cb) self.add_action(save_data_action) simulation_log_action = Gio.SimpleAction.new("simulation-output", None) simulation_log_action.connect("activate", self.simulation_output_action_cb) self.add_action(simulation_log_action) # insert_menu_xml # ## Create menu model self.insertmenu = builder.get_object('insertmenu') ## Bind actions insert_simulation_action = Gio.SimpleAction.new( "insert-simulation", None) insert_simulation_action.connect("activate", self.insert_simulation_action) self.add_action(insert_simulation_action) insert_print_action = Gio.SimpleAction.new("insert-print", None) insert_print_action.connect("activate", self.insert_print_cb) self.add_action(insert_print_action) insert_model_action = Gio.SimpleAction.new("insert-model", None) insert_model_action.connect("activate", self.insert_model_cb) self.add_action(insert_model_action) insert_lib_action = Gio.SimpleAction.new("insert-lib", None) insert_lib_action.connect("activate", self.insert_lib_cb) self.add_action(insert_lib_action) insert_include_action = Gio.SimpleAction.new("insert-include", None) insert_include_action.connect("activate", self.insert_include_cb) self.add_action(insert_include_action) def save_cb(self, action, parameters): self.save_netlist_file() def save_plot_cb(self, action, parameters): dialog = Gtk.FileChooserDialog( _("Save plot"), self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) png_filter = Gtk.FileFilter() png_filter.set_name(_("Portable Network Graphics")) png_filter.add_mime_type("image/png") dialog.add_filter(png_filter) svg_filter = Gtk.FileFilter() svg_filter.set_name(_("Scalable Vector Graphics")) svg_filter.add_mime_type("image/svg+xml") dialog.add_filter(svg_filter) dialog.set_current_name(self.circuit_title + " - " + self.simulation_output.analysis) response = dialog.run() dialog.set_filter(png_filter) if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() dialog.destroy() if file_name.split(".")[-1] == "png": self.figure.savefig(file_name, transparent=True, dpi=None, format="png") elif file_name.split(".")[-1] == "svg": self.figure.savefig(file_name, transparent=True, dpi=None, format="svg") else: self.figure.savefig(file_name + ".png", transparent=True, dpi=None, format="png") #TODO: Fix this! # selected_filter = dialog.get_filter() # if selected_filter is png_filter: # self.figure.savefig(file_name+".png", transparent=True, dpi=None, format="png") # elif selected_filter is png_filter: # self.figure.savefig(file_name+".png", transparent=True, dpi=None, format="png") else: dialog.destroy() def save_data_cb(self, action, parameters): dialog = Gtk.FileChooserDialog( _("Save simulation data"), self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) csv_filter = Gtk.FileFilter() csv_filter.set_name(_("Comma-separated values")) csv_filter.add_mime_type("text/csv") dialog.add_filter(csv_filter) dialog.set_current_name(self.circuit_title + " - " + self.simulation_output.analysis) response = dialog.run() if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() dialog.destroy() if file_name.split(".")[-1] != "csv": file_name += ".csv" self.simulation_output.save_csv(file_name) else: dialog.destroy() def simulation_output_action_cb(self, action, parameters): if self.raw_data_window is None: self.raw_data_window = console_gui.ConsoleOutputWindow( _("Simulation output")) self.raw_data_window.show_all() def close_cb(self, action, parameters): self.destroy() def insert_simulation_action(self, action, parameters): dialog = add_simulation_gui.AddSimulation(self, []) response = dialog.run() if response == Gtk.ResponseType.OK: self.source_buffer.insert_at_cursor(dialog.statement) dialog.destroy() dialog.destroy() def insert_print_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".print ") def insert_model_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".model mname type ( )") def insert_lib_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".lib filename libname") def insert_include_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".include filename") def _add_back_button(self): self.back_button = Gtk.Button() self.back_button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE)) self.back_button.connect("clicked", self.on_back_button_clicked) self.hb.pack_start(self.back_button) self.back_button.props.visible = False def _add_gear_button(self): self.gear_button = Gtk.MenuButton() #Set content icon = Gio.ThemedIcon(name="emblem-system-symbolic") # Use open-menu-symbolic on Gtk+>=3.14 if Gtk.check_version(3, 14, 0) is None: icon = Gio.ThemedIcon(name="open-menu-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.gear_button.add(image) # Use popover on Gtk+>=3.12 if Gtk.check_version(3, 12, 0) is None: self.gear_button.set_use_popover(True) #Pack self.hb_rbox.pack_start(self.gear_button, False, False, 0) def _add_insert_button(self): self.insert_button = Gtk.MenuButton() #Set content icon = Gio.ThemedIcon(name="insert-text-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.insert_button.add(image) # Set menu model self.insert_button.props.menu_model = self.insertmenu # Use popover on Gtk+>=3.12 if Gtk.check_version(3, 12, 0) is None: self.insert_button.set_use_popover(True) #Pack self.hb_rbox.pack_start(self.insert_button, False, False, 0) def _add_arrow_buttons(self): self.arrow_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) Gtk.StyleContext.add_class(self.arrow_box.get_style_context(), "linked") self.back_button = Gtk.Button() self.back_button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE)) self.arrow_box.add(self.back_button) self.back_button.connect("clicked", self.on_back_button_clicked) self.forward_button = Gtk.Button() self.forward_button.add( Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE)) self.arrow_box.add(self.forward_button) self.forward_button.connect("clicked", self.on_forward_button_clicked) self.hb.pack_start(self.arrow_box) def _add_load_button(self): self.load_button = Gtk.Button() self.load_button.set_label(_("Open")) self.load_button.connect("clicked", self.on_button_open_clicked) self.hb.pack_start(self.load_button) def _add_simulate_button(self): self.simulate_button = Gtk.Button() icon = Gio.ThemedIcon(name="media-playback-start-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.simulate_button.add(image) self.simulate_button.connect("clicked", self.on_simulate_button_clicked) sim_accel = Gtk.AccelGroup() self.add_accel_group(sim_accel) self.simulate_button.add_accelerator("clicked", sim_accel, Gdk.KEY_F5, 0, Gtk.AccelFlags.VISIBLE) self.simulate_button.props.sensitive = False self.hb_rbox.pack_start(self.simulate_button, False, False, 0) def _update_canvas(self, figure): self.simulation_box.remove(self.canvas) self.canvas = FigureCanvas(figure) # a Gtk.DrawingArea self.simulation_box.pack_start(self.canvas, True, True, 0) self.canvas.show() def set_error(self, title=None, message=None, message_type=Gtk.MessageType.ERROR, actions=None): '''set_error(self, title=None, message=None, message_type=Gtk.MessageType.ERROR, actions=None) -> None Sets and shows an information bar with actions as an option. params: actions -> (button_text, response_id, callback_function) ''' if self.infobar is not None: self.infobar.close() self.infobar = InfoMessageBar() self.infobar.set_message_type(message_type) self.overview_box.pack_start(self.infobar, False, True, 0) if title is not None: self.infobar.message_title = title else: self.infobar.messsage_title = "" if message is not None: self.infobar.message_subtitle = message else: self.infobar.message_subtitle = "" if actions is not None: for action in actions: self.infobar.add_button(action[0], action[1]) self.infobar.user_responses[action[1]] = action[2] self.infobar.show_all() def dismiss_error(self): if self.infobar is not None: self.infobar.props.visible = False self.infobar = None def simulation_view(self): self.stack.set_visible_child(self.simulation_box) self.back_button.props.sensitive = True self.forward_button.props.sensitive = False self.load_button.props.visible = False self.simulate_button.props.visible = False self.insert_button.props.visible = False self.gear_button.props.menu_model = self.gearmenu_simulation def overview_view(self): self.stack.set_visible_child(self.overview_box) self.back_button.props.sensitive = False self.forward_button.props.sensitive = True self.load_button.props.visible = True self.simulate_button.props.visible = True self.insert_button.props.visible = True self.gear_button.props.menu_model = self.gearmenu_overview def _on_destroy(self, data): self.destroy() def on_back_button_clicked(self, button): self.overview_view() def on_forward_button_clicked(self, button): self.simulation_view() def on_simulate_button_clicked(self, button): # Dismiss infobar messages (if they exists) self.dismiss_error() simulator = ngspice_simulation.NgspiceAsync() dialog = running_dialog.RunningDialog(self, simulator.end_event) try: # First, save changes on disk self.save_netlist_file() # Start simulation simulator.simulatefile(self.netlist_file_path) # Show dialog if dialog.run() == 1: # Not cancelled by the user if not simulator.errors: self.simulation_output = ngspice_simulation.NgspiceOutput.parse_file( self.netlist_file_path + ".out") self.figure = self.simulation_output.get_figure() self._update_canvas(self.figure) self.simulation_view() else: errors_str = [str(x) for x in simulator.errors] self.set_execution_log(self.netlist_file_path, "\n".join(errors_str)) self.set_error(title=_("Simulation failed."), actions=[(_("Execution log"), 1000, self.on_execution_log_clicked)]) else: simulator.terminate() self.set_output_file_content(self.netlist_file_path + ".out") except Exception as e: self.set_error(title=_("Simulation failed."), message=str(e)) finally: dialog.destroy() def set_output_file_content(self, output_file): self.raw_data_window.clear_buffer() with open(output_file, 'r') as f: lines = f.readlines() for line in lines: self.raw_data_window.insert_text(line) self.raw_data_window.set_subtitle(output_file) def on_execution_log_clicked(self, button, response_id): if self.execution_log_window is None: self.execution_log_window = console_gui.ConsoleOutputWindow( _("Execution log")) self.execution_log_window.show_all() def set_execution_log(self, file_name, content): self.execution_log_window.clear_buffer() self.execution_log_window.insert_text(content) self.execution_log_window.set_subtitle(file_name) def start_file_monitor(self): if self.schematic_file_path is not None: path = self.schematic_file_path elif self.netlist_file_path is not None: path = self.netlist_file_path else: return target_file = Gio.File.new_for_path(path) self.file_monitor = target_file.monitor_file(Gio.FileMonitorFlags.NONE, None) self.file_monitor.connect("changed", self.on_file_changed) def stop_file_monitor(self): if self.file_monitor is not None: self.file_monitor.cancel() def on_file_changed(self, file_monitor, _file, other_file, event_type): ''' on_file_changed(file_monitor,_file, other_file, event_type) -> None Callback function for file monitor on netlist file ''' if event_type == Gio.FileMonitorEvent.CHANGED or event_type == Gio.FileMonitorEvent.CREATED: self.set_error(title=_("Opened file changed on disk."), message=None, message_type=Gtk.MessageType.WARNING, actions=[(_("Reload"), 1000, self.on_infobar_reload_clicked)]) def on_infobar_reload_clicked(self, button, response_id): if self.schematic_file_path is not None: self.load_file(self.schematic_file_path) elif self.netlist_file_path is not None: self.load_file(self.netlist_file_path) else: raise Exception( "self.schematic_file_path and self.netlist_file_path are None") def on_source_buffer_modified_changed(self, data): if self.source_buffer.get_modified(): self.hb.set_title("* " + self.circuit_title) else: self.hb.set_title(self.circuit_title) def load_file(self, path): ''' load a file, converts it to netlist if needed and updates program state ''' self.netlist_file_path = None self.schematic_file_path = None file_content = None #schematic to netlist conversion if os.path.splitext(path)[1] == ".sch": # Try convert schematic to netlist try: ngspice_simulation.Gnetlist.create_netlist_file( path, path + ".net") self.netlist_file_path = path + ".net" self.schematic_file_path = path except Exception as e: self.set_error( title=_("Schematic could not be converted to netlist."), message=str(e)) self.netlist_file_path = None self.schematic_file_path = None return else: self.netlist_file_path = path # Read netlist file if self.netlist_file_path is not None: with open(self.netlist_file_path) as f: file_content = f.read() # Set a file monitor self.start_file_monitor() if file_content is not None and self.netlist_file_path is not None: #Set window title netlist = ngspice_simulation.Netlist(file_content) self.circuit_title = netlist.get_title() if self.circuit_title is not None: self.hb.set_title(self.circuit_title) else: self.hb.set_title("") self.hb.set_subtitle(self.netlist_file_path) # Dismiss older errors self.dismiss_error() # Set content on source view self._open_state("opened") self.source_buffer.props.text = file_content self.source_buffer.set_modified(False) self.simulate_button.props.sensitive = True self.canvas.show() def on_button_open_clicked(self, button): #Filechooserdialog initialization dialog = Gtk.FileChooserDialog( _("Please choose a file"), self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) netlist_filter = Gtk.FileFilter() netlist_filter.set_name(_("Netlist")) netlist_filter.add_pattern("*.net") netlist_filter.add_pattern("*.cir") netlist_filter.add_pattern("*.ckt") dialog.add_filter(netlist_filter) gschem_filter = Gtk.FileFilter() gschem_filter.set_name(_("GEDA schematic")) gschem_filter.add_mime_type("application/x-geda-schematic") dialog.add_filter(gschem_filter) all_filter = Gtk.FileFilter() all_filter.set_name(_("Supported files")) all_filter.add_pattern("*.net") all_filter.add_pattern("*.cir") all_filter.add_pattern("*.ckt") all_filter.add_mime_type("application/x-geda-schematic") dialog.add_filter(all_filter) dialog.set_filter(all_filter) response = dialog.run() if response == Gtk.ResponseType.OK: try: path = dialog.get_filename() dialog.destroy() self.load_file(path) except Exception as e: self.set_error(title=_("File could not be loaded."), message=str(e.message)) else: dialog.destroy() def save_netlist_file(self): """Save file on self.netlist_file_path path.""" self.stop_file_monitor() with open(self.netlist_file_path, "w") as f: f.write(self.source_buffer.props.text) self.start_file_monitor() self.source_buffer.set_modified(False)
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 MainGUI(): def __init__(self): builder = Gtk.Builder() builder.add_from_file("./gui/xradio_viewer.glade") self.mainwin = builder.get_object("mainWindow") self.plotwin = builder.get_object("plotWindow") self.mainwin.connect("delete-event", Gtk.main_quit) #connect_switch = builder.get_object("connect_switch") #connect_switch.connect("state-set", self.on_connect_switch) play_button = builder.get_object("playbtn1") play_button.connect("clicked", self.on_play_button) pause_button = builder.get_object("pausebtn1") pause_button.connect("clicked", self.on_pause_button) record_button = builder.get_object("record_btn1") record_button.connect("clicked", self.on_record_button) screenshot_button = builder.get_object("screenshot_btn1") screenshot_button.connect("clicked", self.on_screenshot_button) save_as_button = builder.get_object("save_btn1") save_as_button.connect("clicked", self.on_save_button) #load matrix mat = scio.loadmat('./gui/interfaces.mat', verify_compressed_data_integrity=False) self.simData = np.array(mat['simulation_data']) # Normalize data by factor 1/0.2 resolution = 5 # Get data resolution x, y, z = self.simData.nonzero() # Scale by factor self.X, self.Y, self.Z = x / resolution, y / resolution, z / resolution self.fig = Figure(figsize=(9, 6), dpi=80, facecolor='w', edgecolor='k') self.ax = self.fig.add_subplot(111, projection='3d') self.ax.scatter(self.X, self.Y, self.Z, zdir='z', c='red', marker='s') self.ax.set_zlim(0, 3) self.ax.set_ylim(0, 3) self.ax.set_xlim(0, 4) self.canvas = FigureCanvas(self.fig) self.canvas.show() self.plotwin.add(self.canvas) self.fig.canvas.draw() def update_graph(self): t1 = time.time() self.ax.cla() step = 0.5 self.X, self.Y, self.Z = self.X, self.Y + step, self.Z self.ax.scatter(self.X, self.Y, self.Z, zdir='z', c='blue', marker='s') self.ax.set_zlim(0, 3) self.ax.set_ylim(0, 20) self.ax.set_xlim(0, 4) self.canvas.show() self.fig.canvas.draw() t2 = time.time() print(t2 - t1) return True def startUpdateTimer(self): GObject.timeout_add(100, self.update_graph) def on_play_button(self, button): print("Play Button Pressed") return True def on_pause_button(self, button): print("Pause Button Pressed") return True def on_screenshot_button(self, button): print("Screenshot Button Pressed") return True def on_save_button(self, button): print("Save Button Pressed") return True def on_record_button(self, button): print("Record Button Pressed") return True def on_connect_switch(self, switch, state): print("Connect Switch Pressed") return True
class MainWindow(Gtk.ApplicationWindow): def __init__(self, file_path=None): Gtk.Window.__init__(self) self.set_default_size(900, 600) self.set_icon_name(config.PROGRAM_NAME_LOWER) self.settings = Gio.Settings.new(config.GSETTINGS_BASE_KEY) self.circuit = None self.netlist_file_path = None self.file_monitor = None self.raw_data_window = console_gui.ConsoleOutputWindow(_("Simulation output")) self.execution_log_window = console_gui.ConsoleOutputWindow(_("Execution log")) self._create_menu_models() ########## #headerbar self.hb = Gtk.HeaderBar() self.hb.props.show_close_button = True if config.csd_are_supported() == True: self.set_titlebar(self.hb) else: #disable headerbar as titlebar if not supported self.no_csd_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.no_csd_box.pack_start(self.hb, False, False, 0) self.hb.props.show_close_button = False self.add(self.no_csd_box) ## Right side of headerbar self.hb_rbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) self._add_insert_button() self._add_simulate_button() self._add_gear_button() self.hb.pack_end(self.hb_rbox) ## Left side of headerbar self._add_arrow_buttons() self._add_load_button() ######## #Content self.stack = Gtk.Stack() self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) self.stack.set_transition_duration(500) ## Overview stack self.overview_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.source_view = None self._open_state("new") self.infobar = None self.stack.add_titled(self.overview_box, "overview", _("Circuit")) ## Simulation stack self.simulation_box = Gtk.Box() self.canvas = Gtk.DrawingArea() self.simulation_box.pack_start(self.canvas, True, True, 0) self.stack.add_titled(self.simulation_box, "simulation", _("Simulation")) if config.csd_are_supported() == True: self.add(self.stack) else: #disable headerbar as titlebar if not supported self.no_csd_box.pack_end(self.stack, True, True, 0) self.overview_view() self.forward_button.props.sensitive = False # HACK: self._open_state("new") sets it to # False but self.overview_view() sets it to # True. This line fixes the incongruence. self.connect_after('destroy', self._on_destroy) if file_path is not None: self.load_file(file_path) def _open_state(self, state="opened"): """ show sourceview state="opened" or suggest opening a file state="new" """ if state == "opened": self.overview_view() self.forward_button.props.sensitive = False # Don go forward until having some simulations for child in self.overview_box.get_children(): self.overview_box.remove(child) self.source_scrolled = Gtk.ScrolledWindow(None, None) self.source_scrolled.set_hexpand(True) self.source_scrolled.set_vexpand(True) self.source_buffer = GtkSource.Buffer() self.source_buffer.connect("modified-changed", self.on_source_buffer_modified_changed) self.source_buffer.set_highlight_syntax(True) self.source_buffer.set_language(GtkSource.LanguageManager.get_default().get_language("spice-netlist")) self.sourceview = GtkSource.View() self.settings.bind('show-line-numbers', self.sourceview, 'show-line-numbers', Gio.SettingsBindFlags.DEFAULT) self.settings.bind('highlight-current-line', self.sourceview, 'highlight-current-line', Gio.SettingsBindFlags.DEFAULT) font_desc = Pango.FontDescription('monospace') if font_desc: self.sourceview.modify_font(font_desc) self.sourceview.set_buffer(self.source_buffer) self.sourceview.set_show_line_numbers(True) self.source_scrolled.add(self.sourceview) self.overview_box.pack_end(self.source_scrolled, True, True, 0) self.overview_box.show_all() self.insert_button.props.sensitive = True self.simulate_button.props.sensitive = True self.lookup_action("save").props.enabled = True elif state == "new": if self.source_view is not None: self.overview_box.remove(self.source_scrolled) self.source_view = None else: self.emptyGrid = Gtk.Grid(orientation=Gtk.Orientation.HORIZONTAL, hexpand=True, vexpand=True, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, column_spacing=12, margin=30) self.overview_box.add(self.emptyGrid); emptyPageImage = Gtk.Image(icon_name='document-open-symbolic', icon_size=Gtk.IconSize.DIALOG) emptyPageImage.get_style_context().add_class('dim-label') self.emptyGrid.add(emptyPageImage) emptyPageDirections = Gtk.Label(label=_("Use the <b>Open</b> button to load a circuit"), use_markup=True, max_width_chars=30, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER ) emptyPageDirections.get_style_context().add_class('dim-label'); self.emptyGrid.add(emptyPageDirections); self.emptyGrid.show_all(); self.overview_box.pack_end(self.emptyGrid, True, True, 0) self.insert_button.props.sensitive = False self.simulate_button.props.sensitive = False self.forward_button.props.sensitive = False # TODO: let see why this is not effective... self.lookup_action("save").props.enabled = False def _create_menu_models(self): # gear_menu overview xml # ## Create menu model builder = Gtk.Builder() builder.set_translation_domain(config.DOMAIN) builder.add_from_file(os.path.join(os.path.dirname(__file__), "data", "menu.ui")) self.gearmenu_overview = builder.get_object('gearmenu-overview') ## Bind actions save_action = Gio.SimpleAction.new("save", None) save_action.connect("activate", self.save_cb) self.add_action(save_action) close_action = Gio.SimpleAction.new("close", None) close_action.connect("activate", self.close_cb) self.add_action(close_action) # gear_menu simulation xml # ## Create menu model self.gearmenu_simulation = builder.get_object('gearmenu-simulation') ## Bind actions save_plot_action = Gio.SimpleAction.new("save-plot", None) save_plot_action.connect("activate", self.save_plot_cb) self.add_action(save_plot_action) save_data_action = Gio.SimpleAction.new("save-data", None) save_data_action.connect("activate", self.save_data_cb) self.add_action(save_data_action) simulation_log_action = Gio.SimpleAction.new("simulation-output", None) simulation_log_action.connect("activate", self.simulation_output_action_cb) self.add_action(simulation_log_action) # insert_menu_xml # ## Create menu model self.insertmenu = builder.get_object('insertmenu') ## Bind actions insert_simulation_action = Gio.SimpleAction.new("insert-simulation", None) insert_simulation_action.connect("activate", self.insert_simulation_action) self.add_action(insert_simulation_action) insert_print_action = Gio.SimpleAction.new("insert-print", None) insert_print_action.connect("activate", self.insert_print_cb) self.add_action(insert_print_action) insert_model_action = Gio.SimpleAction.new("insert-model", None) insert_model_action.connect("activate", self.insert_model_cb) self.add_action(insert_model_action) insert_lib_action = Gio.SimpleAction.new("insert-lib", None) insert_lib_action.connect("activate", self.insert_lib_cb) self.add_action(insert_lib_action) insert_include_action = Gio.SimpleAction.new("insert-include", None) insert_include_action.connect("activate", self.insert_include_cb) self.add_action(insert_include_action) def save_cb(self, action, parameters): self.save_netlist_file() def save_plot_cb(self, action, parameters): dialog = Gtk.FileChooserDialog(_("Save plot"), self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) png_filter = Gtk.FileFilter() png_filter.set_name(_("Portable Network Graphics")) png_filter.add_mime_type("image/png") dialog.add_filter(png_filter) svg_filter = Gtk.FileFilter() svg_filter.set_name(_("Scalable Vector Graphics")) svg_filter.add_mime_type("image/svg+xml") dialog.add_filter(svg_filter) dialog.set_current_name(self.circuit_title + " - " + self.simulation_output.analysis) response = dialog.run() dialog.set_filter(png_filter) if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() dialog.destroy() if file_name.split(".")[-1] == "png": self.figure.savefig(file_name, transparent=True, dpi=None, format="png") elif file_name.split(".")[-1] == "svg": self.figure.savefig(file_name, transparent=True, dpi=None, format="svg") else: self.figure.savefig(file_name+".png", transparent=True, dpi=None, format="png") #TODO: Fix this! # selected_filter = dialog.get_filter() # if selected_filter is png_filter: # self.figure.savefig(file_name+".png", transparent=True, dpi=None, format="png") # elif selected_filter is png_filter: # self.figure.savefig(file_name+".png", transparent=True, dpi=None, format="png") else: dialog.destroy() def save_data_cb(self, action, parameters): dialog = Gtk.FileChooserDialog(_("Save simulation data"), self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) csv_filter = Gtk.FileFilter() csv_filter.set_name(_("Comma-separated values")) csv_filter.add_mime_type("text/csv") dialog.add_filter(csv_filter) dialog.set_current_name(self.circuit_title + " - " + self.simulation_output.analysis) response = dialog.run() if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() dialog.destroy() if file_name.split(".")[-1] != "csv": file_name += ".csv" self.simulation_output.save_csv(file_name) else: dialog.destroy() def simulation_output_action_cb(self, action, parameters): if self.raw_data_window is None: self.raw_data_window = console_gui.ConsoleOutputWindow(_("Simulation output")) self.raw_data_window.show_all() def close_cb(self, action, parameters): self.destroy() def insert_simulation_action(self, action, parameters): dialog = add_simulation_gui.AddSimulation(self,[]) response = dialog.run() if response == Gtk.ResponseType.OK: self.source_buffer.insert_at_cursor(dialog.statement) dialog.destroy() dialog.destroy() def insert_print_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".print ") def insert_model_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".model mname type ( )") def insert_lib_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".lib filename libname") def insert_include_cb(self, action, parameters): self.source_buffer.insert_at_cursor(".include filename") def _add_back_button(self): self.back_button = Gtk.Button() self.back_button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE)) self.back_button.connect("clicked", self.on_back_button_clicked) self.hb.pack_start(self.back_button) self.back_button.props.visible = False def _add_gear_button(self): self.gear_button = Gtk.MenuButton() #Set content icon = Gio.ThemedIcon(name="emblem-system-symbolic") # Use open-menu-symbolic on Gtk+>=3.14 if Gtk.check_version(3, 14, 0) is None: icon = Gio.ThemedIcon(name="open-menu-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.gear_button.add(image) # Use popover on Gtk+>=3.12 if Gtk.check_version(3, 12, 0) is None: self.gear_button.set_use_popover(True) #Pack self.hb_rbox.pack_start(self.gear_button, False, False, 0) def _add_insert_button(self): self.insert_button = Gtk.MenuButton() #Set content icon = Gio.ThemedIcon(name="insert-text-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.insert_button.add(image) # Set menu model self.insert_button.props.menu_model = self.insertmenu # Use popover on Gtk+>=3.12 if Gtk.check_version(3, 12, 0) is None: self.insert_button.set_use_popover(True) #Pack self.hb_rbox.pack_start(self.insert_button, False, False, 0) def _add_arrow_buttons(self): self.arrow_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) Gtk.StyleContext.add_class(self.arrow_box.get_style_context(), "linked") self.back_button = Gtk.Button() self.back_button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE)) self.arrow_box.add(self.back_button) self.back_button.connect("clicked", self.on_back_button_clicked) self.forward_button = Gtk.Button() self.forward_button.add(Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE)) self.arrow_box.add(self.forward_button) self.forward_button.connect("clicked", self.on_forward_button_clicked) self.hb.pack_start(self.arrow_box) def _add_load_button(self): self.load_button = Gtk.Button() self.load_button.set_label(_("Open")) self.load_button.connect("clicked", self.on_button_open_clicked) self.hb.pack_start(self.load_button) def _add_simulate_button(self): self.simulate_button = Gtk.Button() icon = Gio.ThemedIcon(name="media-playback-start-symbolic") image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.MENU) self.simulate_button.add(image) self.simulate_button.connect("clicked", self.on_simulate_button_clicked) sim_accel = Gtk.AccelGroup() self.add_accel_group(sim_accel) self.simulate_button.add_accelerator("clicked", sim_accel, Gdk.KEY_F5, 0, Gtk.AccelFlags.VISIBLE); self.simulate_button.props.sensitive = False self.hb_rbox.pack_start(self.simulate_button, False, False, 0) def _update_canvas(self, figure): self.simulation_box.remove(self.canvas) self.canvas = FigureCanvas(figure) # a Gtk.DrawingArea self.simulation_box.pack_start(self.canvas, True, True, 0) self.canvas.show() def set_error(self, title=None, message=None, message_type=Gtk.MessageType.ERROR, actions=None): '''set_error(self, title=None, message=None, message_type=Gtk.MessageType.ERROR, actions=None) -> None Sets and shows an information bar with actions as an option. params: actions -> (button_text, response_id, callback_function) ''' if self.infobar is not None: self.infobar.close() self.infobar = InfoMessageBar() self.infobar.set_message_type(message_type) self.overview_box.pack_start(self.infobar, False, True, 0) if title is not None: self.infobar.message_title = title else: self.infobar.messsage_title = "" if message is not None: self.infobar.message_subtitle = message else: self.infobar.message_subtitle = "" if actions is not None: for action in actions: self.infobar.add_button(action[0], action[1]) self.infobar.user_responses[action[1]] = action[2] self.infobar.show_all() def dismiss_error(self): if self.infobar is not None: self.infobar.props.visible = False self.infobar = None def simulation_view(self): self.stack.set_visible_child(self.simulation_box) self.back_button.props.sensitive = True self.forward_button.props.sensitive = False self.load_button.props.visible = False self.simulate_button.props.visible = False self.insert_button.props.visible = False self.gear_button.props.menu_model = self.gearmenu_simulation def overview_view(self): self.stack.set_visible_child(self.overview_box) self.back_button.props.sensitive = False self.forward_button.props.sensitive = True self.load_button.props.visible = True self.simulate_button.props.visible = True self.insert_button.props.visible = True self.gear_button.props.menu_model = self.gearmenu_overview def _on_destroy(self, data): self.destroy() def on_back_button_clicked(self, button): self.overview_view() def on_forward_button_clicked(self, button): self.simulation_view() def on_simulate_button_clicked(self, button): # Dismiss infobar messages (if they exists) self.dismiss_error() simulator = ngspice_simulation.NgspiceAsync() dialog = running_dialog.RunningDialog(self,simulator.end_event) try: # First, save changes on disk self.save_netlist_file() # Start simulation simulator.simulatefile(self.netlist_file_path) # Show dialog if dialog.run() == 1: # Not cancelled by the user if not simulator.errors: self.simulation_output = ngspice_simulation.NgspiceOutput.parse_file(self.netlist_file_path + ".out") self.figure = self.simulation_output.get_figure() self._update_canvas(self.figure) self.simulation_view() else: errors_str = [str(x) for x in simulator.errors] self.set_execution_log(self.netlist_file_path,"\n".join(errors_str)) self.set_error(title=_("Simulation failed."), actions=[(_("Execution log"), 1000, self.on_execution_log_clicked)]) else: simulator.terminate() self.set_output_file_content(self.netlist_file_path + ".out") except Exception as e: self.set_error(title=_("Simulation failed."), message=str(e)) finally: dialog.destroy() def set_output_file_content(self, output_file): self.raw_data_window.clear_buffer() with open(output_file, 'r') as f: lines = f.readlines() for line in lines: self.raw_data_window.insert_text(line) self.raw_data_window.set_subtitle(output_file) def on_execution_log_clicked(self, button, response_id): if self.execution_log_window is None: self.execution_log_window = console_gui.ConsoleOutputWindow(_("Execution log")) self.execution_log_window.show_all() def set_execution_log(self, file_name, content): self.execution_log_window.clear_buffer() self.execution_log_window.insert_text(content) self.execution_log_window.set_subtitle(file_name) def start_file_monitor(self): if self.schematic_file_path is not None: path = self.schematic_file_path elif self.netlist_file_path is not None: path = self.netlist_file_path else: return target_file = Gio.File.new_for_path(path) self.file_monitor = target_file.monitor_file(Gio.FileMonitorFlags.NONE, None) self.file_monitor.connect("changed", self.on_file_changed) def stop_file_monitor(self): if self.file_monitor is not None: self.file_monitor.cancel() def on_file_changed(self, file_monitor, _file, other_file, event_type): ''' on_file_changed(file_monitor,_file, other_file, event_type) -> None Callback function for file monitor on netlist file ''' if event_type == Gio.FileMonitorEvent.CHANGED or event_type == Gio.FileMonitorEvent.CREATED: self.set_error(title=_("Opened file changed on disk."), message=None, message_type=Gtk.MessageType.WARNING, actions=[(_("Reload"), 1000, self.on_infobar_reload_clicked)]) def on_infobar_reload_clicked(self, button, response_id): if self.schematic_file_path is not None: self.load_file(self.schematic_file_path) elif self.netlist_file_path is not None: self.load_file(self.netlist_file_path) else: raise Exception("self.schematic_file_path and self.netlist_file_path are None") def on_source_buffer_modified_changed(self, data): if self.source_buffer.get_modified(): self.hb.set_title("* " + self.circuit_title) else: self.hb.set_title(self.circuit_title) def load_file(self, path): ''' load a file, converts it to netlist if needed and updates program state ''' self.netlist_file_path = None self.schematic_file_path = None file_content = None #schematic to netlist conversion if os.path.splitext(path)[1] == ".sch": # Try convert schematic to netlist try: ngspice_simulation.Gnetlist.create_netlist_file(path, path + ".net") self.netlist_file_path = path + ".net" self.schematic_file_path = path except Exception as e: self.set_error(title=_("Schematic could not be converted to netlist."), message=str(e)) self.netlist_file_path = None self.schematic_file_path = None return else: self.netlist_file_path = path # Read netlist file if self.netlist_file_path is not None: with open(self.netlist_file_path) as f: file_content = f.read() # Set a file monitor self.start_file_monitor() if file_content is not None and self.netlist_file_path is not None: #Set window title netlist = ngspice_simulation.Netlist(file_content) self.circuit_title = netlist.get_title() if self.circuit_title is not None: self.hb.set_title(self.circuit_title) else: self.hb.set_title("") self.hb.set_subtitle(self.netlist_file_path) # Dismiss older errors self.dismiss_error() # Set content on source view self._open_state("opened") self.source_buffer.props.text = file_content self.source_buffer.set_modified(False) self.simulate_button.props.sensitive = True self.canvas.show() def on_button_open_clicked(self, button): #Filechooserdialog initialization dialog = Gtk.FileChooserDialog(_("Please choose a file"), self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) netlist_filter = Gtk.FileFilter() netlist_filter.set_name(_("Netlist")) netlist_filter.add_pattern("*.net") netlist_filter.add_pattern("*.cir") netlist_filter.add_pattern("*.ckt") dialog.add_filter(netlist_filter) gschem_filter = Gtk.FileFilter() gschem_filter.set_name(_("GEDA schematic")) gschem_filter.add_mime_type("application/x-geda-schematic") dialog.add_filter(gschem_filter) all_filter = Gtk.FileFilter() all_filter.set_name(_("Supported files")) all_filter.add_pattern("*.net") all_filter.add_pattern("*.cir") all_filter.add_pattern("*.ckt") all_filter.add_mime_type("application/x-geda-schematic") dialog.add_filter(all_filter) dialog.set_filter(all_filter) response = dialog.run() if response == Gtk.ResponseType.OK: try: path = dialog.get_filename() dialog.destroy() self.load_file(path) except Exception as e: self.set_error(title=_("File could not be loaded."), message=str(e.message)) else: dialog.destroy() def save_netlist_file(self): """Save file on self.netlist_file_path path.""" self.stop_file_monitor() with open(self.netlist_file_path, "w") as f: f.write(self.source_buffer.props.text) self.start_file_monitor() self.source_buffer.set_modified(False)
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 SimpleLineChart(ChartBase): def setup(self): fig = Figure(dpi=100, facecolor="white", edgecolor="white") self.ax = ax = fig.add_subplot(111) fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.1) # gridlines ax.yaxis.grid(color='gray') ax.xaxis.grid(color='gray') # background # ax.patch.set_alpha(1) # font matplotlib.rc('font', family="sans", weight="normal", size=9) # frame ax.set_frame_on(False) # formatter formatter = FuncFormatter(gui_utils.get_currency_format_from_float) ax.yaxis.set_major_formatter(formatter) formatter = FuncFormatter(self.x_formatter) ax.xaxis.set_major_formatter(formatter) self.ax.autoscale(enable=True, axis='both', tight=True) # annotation self.annotation = ax.annotate("foo", xy=(0.0, 0.0), xytext=(-20, 20), textcoords='offset points', ha='right', va='bottom', color='white', bbox=dict(boxstyle='round,pad=0.5', color=self.colors[0]), # arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0') ) self.annotation.set_visible(False) # pack self.plot = FigureCanvas(fig) self.pack_start(self.plot, True, True, 0) # connect events fig.canvas.mpl_connect('motion_notify_event', self.on_move) def draw_plot(self): # remove lines self.ax.lines = [] # vertical line self.line = self.ax.axvline(color='gray') self.line.set_visible(False) c = 0 x_values = range(len(self.controller.x_values)) for key, val in self.controller.y_values: while len(val) < len(x_values): val.append(0.0) self.ax.plot(x_values, val, 'o-', label=key, color=self.colors[c]) c += 1 # legend legend = self.ax.legend(loc="best") # legend.draw_frame(False) legend.get_frame().set_edgecolor("gray") # show figure self.spinner.hide() self.plot.show() def on_move(self, event): if not event.inaxes: self.line.set_visible(False) self.annotation.set_visible(False) event.canvas.draw() return x_val = min(int(round(event.xdata, 0)), len(self.controller.x_values) - 1) self.line.set_xdata(x_val) self.line.set_visible(True) self.annotation.set_visible(True) self.annotation.xy = x_val, event.ydata text = self.controller.x_values[x_val] + "\n" for name, vals in self.controller.y_values: text += name + ": " + gui_utils.get_currency_format_from_float(vals[x_val]) + "\n" self.annotation.set_text(text) event.canvas.draw()
class BarChart(ChartBase): bar_width = 0.6 def setup(self): fig = Figure(figsize=(5, 5), dpi=100, facecolor="white", edgecolor="white") self.ax = fig.add_subplot(111) fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.2) # font matplotlib.rc('font', family="sans", weight="normal", size=9) # frame self.ax.set_frame_on(False) # pack fig self.plot = FigureCanvas(fig) self.pack_start(self.plot, True, True, 0) # connect events fig.canvas.mpl_connect('motion_notify_event', self.on_move) def draw_plot(self): self.ax.clear() # gridlines self.ax.yaxis.grid(color='gray') self.ax.xaxis.grid(color='gray') # annotation self.annotation = self.ax.annotate("", xy=(0.0, 0.0), xytext=(-20, 20), textcoords='offset points', ha='right', va='bottom', color='white', bbox=dict(boxstyle='round,pad=0.5', color=self.colors[0]), # arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0') ) self.annotation.set_visible(False) self.spinner.hide() pos = range(len(self.controller.x_values)) if len(self.controller.y_values) == 0: self.plot.show() return c = 0 for name, vals in self.controller.y_values: self.ax.bar(pos, vals, width=self.bar_width, align="center", facecolor=self.colors[c], linewidth=0, label=name) c += 1 # self.ax.set_xticklabels(self.controller.x_values) # formatter formatter = FuncFormatter(gui_utils.get_currency_format_from_float) self.ax.yaxis.set_major_formatter(formatter) formatter = FuncFormatter(self.x_formatter) self.ax.xaxis.set_major_formatter(formatter) # ensure plots with less than 5 bars look ok if len(pos) < 5: self.ax.xaxis.set_ticks(pos) # legend legend = self.ax.legend(loc="best") # legend.draw_frame(False) legend.get_frame().set_edgecolor("gray") # recalc limits self.ax.relim() self.ax.autoscale_view(True, True, True) # vertical line # line needs to be drawn after recalc limits self.line = self.ax.axvline(color='gray') self.line.set_visible(False) # show plot self.plot.show() def on_move(self, event): if not event.inaxes: self.annotation.set_visible(False) self.line.set_visible(False) event.canvas.draw() return x_val = min(int(round(event.xdata, 0)), len(self.controller.x_values) - 1) self.annotation.set_visible(True) self.line.set_visible(True) self.line.set_xdata(x_val) self.annotation.xy = x_val, event.ydata text = self.controller.x_values[x_val] + "\n" for name, vals in self.controller.y_values: text += name + ": " + gui_utils.get_currency_format_from_float(vals[x_val]) + "\n" self.annotation.set_text(text) event.canvas.draw()
class UI: # ====== Variables de la classe UI ======== xn, yn = 0, 0 # ========================================= def __init__(self): # ### Initialise les datas### self.array_size = 10 self.nbr_dots = 1 # ########################### self.builder = Gtk.Builder() # voir commentaires lignes 21-25 # self.builder = gtk.Builder() self.builder.add_from_file(os.path.join(os.getcwd(), 'TBench_GUI_gl3.ui')) self.window = self.builder.get_object('dialog1') self.aboutdialog = self.builder.get_object('aboutdialog1') self.assistant = self.builder.get_object('assistant1') self.textview = self.builder.get_object('textview1') self.textbuffer = self.builder.get_object('textbuffer1') self.bt_exit = self.builder.get_object('bt_exit') self.tbt_state0 = self.builder.get_object('tbt_state0') self.tbt_state1 = self.builder.get_object('tbt_state1') self.tbt_state2 = self.builder.get_object('tbt_state2') self.imagemenuitem5 = self.builder.get_object('imagemenuitem5') self.imagemenuitem10 = self.builder.get_object('imagemenuitem10') self.builder.connect_signals(self) self.bufsize = 10 # ajout 20.02 self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize) # ajout 20.02 self.x = [1 * i for i in range(-self.bufsize + 1, 1)] # ajout 20.02(-self.bufsize+1,1) # Matplotlib trucs self.figure = Figure(figsize=(100, 100), dpi=100) self.ax = self.figure.add_subplot(111) self.canvas = FigureCanvas(self.figure) # une gtk.DrawingArea self.line, = self.ax.plot(self.x, self.databuffer) # Gtk trucs self.canvas.show() self.graphview = self.builder.get_object("plot") self.graphview.pack_start(self.canvas, True, True, True) self.arrow = self.builder.get_object('arrow1') self.window.connect('delete-event', self.quit) self.tbt_state0.connect('toggled', self.on_button_toggled0) self.tbt_state1.connect('toggled', self.on_button_toggled1) self.tbt_state2.connect('toggled', self.on_button_toggled2) self.bt_exit.connect('clicked', self.quit) self.imagemenuitem5.connect('activate', self.quit) self.imagemenuitem10.connect('activate', self.show_aboutdialog) self.window.show() # ================= Recherche du port de l'arduino ==================== self.sonde = arduino.search() if self.sonde == "impossible d'ouvrir un port série": info = (self.sonde + "!" + '\n' + "quitter cette session, vérifier la connexion avec le PLC, puis relancer le programme") self.ajout_log_term("TB", info) else: self.ajout_log_term("PLC", self.sonde) self.init_arduino() # initialise l'arduino # ===================================================================== def updateplot(self): self.databuffer.append(UI.yn) self.line.set_ydata(self.databuffer) self.ax.relim() self.ax.autoscale_view(False, False, True) self.canvas.draw() def on_button_toggled0(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) self.updateplot() # pour marquer un temps... UI.xn = UI.xn + 1 UI.yn = 0.8 else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) self.updateplot() # pour marquer un temps... UI.xn = UI.xn + 1 UI.yn = 0 # print(UI.xn, UI.yn) self.updateplot() # self.updateplot() # print 'Button0 was turned: ', state[1] def on_button_toggled1(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) def on_button_toggled2(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) def show_aboutdialog(self, *args): self.aboutdialog.run() self.aboutdialog.hide() def show_assistant(self, *args): self.assistant.show() self.assistant.hide() def quit(self, *args): if self.sonde != "impossible d'ouvrir un port série": self.quit_arduino() # réinitialise l'arduino # Gtk.main_quit() # voir commentaires lignes 21-25 Gtk.main_quit() def init_arduino(self): arduino.open() time.sleep(4) arduino.write('0') # éteint la led si elle était allumée arduino.flush() def quit_arduino(self): arduino.write('0') # éteint la led si elle était allumée arduino.flush() arduino.close() def send_command(self, val): arduino.write(val) self.rec = arduino.reading() info = ("Bascule l'état de la led PLC à " + val) self.ajout_log_term("TB", info) info = self.rec.decode('utf-8').rstrip('\n\r') self.ajout_log_term("PLC", info) def ajout_log_term(self, src, msg): #self.textbuffer.insert_at_cursor(src + ": " + msg + '\n') # text_buffer is a gtk.TextBuffer start_iter = self.textbuffer.get_start_iter() self.textbuffer.insert(start_iter, (src + ": " + msg + '\n'))
class Calculator(): def __init__(self): self.history_ex = ["","","","","","","","","",""] self.hisotry_page = [0,0,0,0,0,0,0,0,0,0] self.x = [] self.y = [] self.init_gui() def init_gui(self): self.builder = Gtk.Builder() self.builder.add_from_file("finally.glade") self.builder.connect_signals( { "window_destroy" : self.window_destroy, "press_button" : self.press_button, "clear_text" : self.clear_text, "remove_last_char": self.remove_last_char, "calculate" : self.calculate, "switch_page" : self.switch_page, "num_base_change" : self.num_base_change, "fix_cursor" : self.fix_cursor, "enable_textview" : self.enable_textview, "disable_textview": self.disable_textview, "prog_calc" : self.prog_calc, "back_to_future" : self.back_to_future, "plot" : self.plot } ) self.window = self.builder.get_object("main_window") self.window.set_size_request(250,305) self.window.set_title("The Calculator") self.window.set_icon_from_file("/usr/share/pixmaps/thecalculator-icon.png") self.text_buff = self.builder.get_object("class_17").get_buffer() # Access buffer from TextView self.builder.get_object("class_17").grab_focus() self.builder.get_object("radiobutton3").set_active(True) self.num_base_change(self.builder.get_object("radiobutton3")) ############### PLOT FUNCTION ############## sw = self.builder.get_object("scrolledwindow1") sw2 = self.builder.get_object("scrolledwindow2") fig = Figure(figsize=(5,5),dpi=120) self.ax = fig.add_subplot(111) self.x = arange(-3.1415,3.1415,0.01) self.y = sin(self.x) self.ax.plot(self.x,self.y,label="sin(x)") self.ax.set_xlim(-3.2,3.2) self.ax.set_ylim(-1.2,1.2) self.ax.grid(True) fig.set_size_inches(9.5, 5.5, forward=True) fig.tight_layout() self.canvas = FigureCanvas(fig) sw.add_with_viewport(self.canvas) self.canvas.show() toolbar = NavigationToolbar(self.canvas, self.window) sw2.add_with_viewport(toolbar) def plot(self,w,data=None,from_x=-10,to_x=10): text = self.builder.get_object("plot_1").get_text() y_lim = [] # ([+-]?\d*\.\d+|[+-]?\d+) # ([+-]?\d{1,})\) c = search("x_lim=\(([+-]?\d*\.\d+|[+-]?\d+),([+-]?\d*\.\d+|[+-]?\d+)",text) if (c): num1 = float(c.group(1)) num2 = float(c.group(2)) print(num1),print(type(num1)) print(num2),print(type(num2)) if ( num1 < num2 ): print("fees") from_x=num1 to_x=num2 else: from_x=num2 to_x=num1 c = search("y_lim=\(([+-]?\d*\.\d+|[+-]?\d+),([+-]?\d*\.\d+|[+-]?\d+)",text) if (c): num1 = float(c.group(1)) num2 = float(c.group(2)) if ( num1 < num2 ): y_lim.append(num1) y_lim.append(num2) else: y_lim.append(num2) y_lim.append(num1) self.x = [] self.y = [] self.x = arange(from_x,to_x,0.01) for x in self.x: try: string = text.split(",")[0].replace("x",str("(%f)"%(x))) tmp_number = solve_expresion(string) if ( type(tmp_number) != str ): self.y.append(tmp_number) else: self.y.append(None) continue except TypeError: self.y.append(None) except Exception as e: print(e) self.builder.get_object("plot_1").set_text("Invalid synatax, use variable x") self.y = array(self.y) print(len(self.x)) print(len(self.y)) self.ax.plot(self.x,self.y) self.ax.set_xlim(from_x,to_x) if ( len(y_lim) == 2 ): print("ase") self.ax.set_ylim(y_lim[0],y_lim[1]) self.ax.grid(True) self.canvas.draw_idle() # c == None podminka # c = re.match("(from=[-+]?(\d{1,}) to=[-+]?(\d{1,}))|(to=[-+]?(\d{1,}) from=[-+]?(\d{1,}))" #group(0) = founded string #group(1) = fouund first variante #group(2) = number from #group(3) = number to #group(4) = found second variante #group(5) = number to #group(6) = number from def back_to_future(self,w,data=None): if ( w.get_label() != "" ): self.text_buff.set_text(w.get_label()) self.builder.get_object("notebook1").set_current_page(self.hisotry_page[self.history_ex.index(w.get_label())]) def add_to_history(self,expresion,page): if ( expresion in self.history_ex): return if ( len(self.history_ex) == 10 ): del(self.history_ex[0]) del(self.hisotry_page[0]) self.history_ex.append(expresion) self.hisotry_page.append(page) else: self.history_ex.append(expresion) self.hisotry_page.append(page) o = 9 for i in self.history_ex: self.builder.get_object("history_"+str(o)).set_text(i) o-=1 def solve_prog(self,base_string): try: base_dictionary = {"BIN":2,"OCT":8,"DEC":10,"HEX":16} base_string=base_string.replace("xor","^") for i in base_string: if i not in ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","~","^","&","|","+","-","/","*"," ",]: return "Invalid input syntax" insert_base = base_dictionary[self.get_radio_state("in")] output_base = base_dictionary[self.get_radio_state("out")] base_string = prog_transfer(base_string,insert_base) output = eval(base_string, {"__builtins__":None}) if output_base == insert_base: output = output elif output_base == 2: output = bin(int(str(output)))[2:] elif output_base == 8: output = oct(int(str(output)))[2:] elif output_base == 10: output = int(str(output)) elif output_base == 16: output = hex(int(str(output)))[2:] return str(output).upper() except SyntaxError: return "Invalid input syntax" except ValueError: return "Invalid base" except: return "Some Error" def prog_calc(self,w,d=None): text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False) if ( text == "" ): return text = text.splitlines() self.text_buff.set_text( text[0] + '\n' + str( self.solve_prog(text[0]) ) ) self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])) ) def enable_textview(self,w,e): page = self.builder.get_object("notebook1").get_current_page() textview = ("class_17", "science_30", "prog_28") if ( page in [0,1,2] ): if ( e.keyval == Gdk.KEY_Return or e.keyval == Gdk.KEY_KP_Enter): self.builder.get_object( textview[page] ).set_editable(True) if ( page == 2 ): self.prog_calc(w) else: self.calculate(w) def disable_textview(self,w,e): # tuple with name of all textview page = self.builder.get_object("notebook1").get_current_page() textview = ("class_17", "science_30", "prog_28") if ( page in [0,1,2] ): if ( e.keyval == Gdk.KEY_Return or e.keyval == Gdk.KEY_KP_Enter): self.builder.get_object(textview[page]).set_editable(False) def fix_cursor(self,widget,data=None): text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False) if ('\n' in text): if ( len(text.splitlines()) > 2): text = text.replace("\n","",1) self.text_buff.set_text(text) text = text.splitlines()[0] if ( self.text_buff.props.cursor_position > len(text) ): self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text)) ) def calculate(self,widget,data=None): # except for plot text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False) if ( text == "" ): return text = text.splitlines() self.text_buff.set_text( text[0] + '\n' + str( solve_expresion(text[0]) ) ) self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])) ) self.add_to_history(text[0],self.builder.get_object("notebook1").get_current_page()) def num_base_change(self,widget,data=None): if ( widget.get_active() == True): if ( widget.get_label() == "BIN" ): base=2 elif ( widget.get_label() == "OCT" ): base=8 elif ( widget.get_label() == "DEC" ): base=10 elif (widget.get_label() == "HEX" ): base=16 for i in range(16): if ( i < base ): self.builder.get_object("prog_"+str(i)).set_sensitive(True) else: self.builder.get_object("prog_"+str(i)).set_sensitive(False) def get_radio_state(self,group): if ( group == "in" ): g = self.builder.get_object("radiobutton1").get_group() elif ( group == "out" ): g = self.builder.get_object("radiobutton5").get_group() for i in g: if ( i.get_active() ): return i.get_label() def clear_text(self,widget,data=None): self.text_buff.set_text("") def window_destroy(self,widget,data=None): Gtk.main_quit() def remove_last_char(self,widget,data=None): text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False) l_text = list(text) cursor_pos = self.text_buff.props.cursor_position-1 del(l_text[cursor_pos]) s = "".join(l_text) self.text_buff.set_text(s) self.text_buff.place_cursor(self.text_buff.get_iter_at_line_offset(0,cursor_pos)) def show_tab(self,name,number_of_item): for i in range(number_of_item+1): self.builder.get_object(name+"_"+str(i)).show() def hide_tab(self,name,number_of_item): for i in range(number_of_item+1): self.builder.get_object(name+"_"+str(i)).hide() # @TODO focus switch textview def switch_page(self,widget,p1,p2): if ( widget.get_current_page() != 4): self.text_buff.set_text("") modes = { "class" : 0, "science" : 1, "prog" : 2, "plot" : 3, "history" : 4, "authors" : 5 } number_of_item = (19,30,29,2,19,0) size = ( (300,330),(450,320),(450,320),(800,600),(480,330),(302,330) ) for i in modes: if ( p2 != modes[i] and i!="authors"): self.hide_tab(i,number_of_item[modes[i]]) else: self.show_tab(i,number_of_item[modes[i]]) self.window.set_size_request(size[p2][0],size[p2][1]) self.window.resize(size[p2][0],size[p2][1]) def press_button(self,widget,data=None): char = widget.get_label() functions = {"√":"sqrt()","x!":"!","ln":"ln()","abs":"||","sin":"sin()","cos":"cos()","tg":"tan()","cotg":"cotg()"} if char in ["0","1","2","3","4","5","6","7","8","9","+","-","/","*",",","e","π","%","^","&","|","xor","~","A","B","C","D","E","F"]: self.text_buff.insert_at_cursor(char) elif char in functions: self.text_buff.insert_at_cursor(functions[char]) if ( char != "x! "): text = self.text_buff.get_text(self.text_buff.get_start_iter(),self.text_buff.get_end_iter(),False) text = text.splitlines() self.text_buff.place_cursor( self.text_buff.get_iter_at_line_offset(0,len(text[0])-1) ) def main(self): self.window.show() Gtk.main()
def __init__(self, application): """ Create a summary page containing various statistics such as the number of logs in the logbook, the logbook's modification date, etc. :arg application: The PyQSO application containing the main Gtk window, etc. """ self.application = application self.logbook = self.application.logbook self.builder = self.application.builder glade_file_path = join(realpath(dirname(__file__)), "res", "pyqso.glade") self.builder.add_objects_from_file(glade_file_path, ("summary_page", )) self.summary_page = self.builder.get_object("summary_page") self.items = {} # Database name in large font at the top of the summary page self.builder.get_object("database_name").set_markup( "<span size=\"x-large\">%s</span>" % basename(self.logbook.path)) self.items["LOG_COUNT"] = self.builder.get_object("log_count") self.items["QSO_COUNT"] = self.builder.get_object("qso_count") self.items["DATE_MODIFIED"] = self.builder.get_object("date_modified") # Yearly statistics config = configparser.ConfigParser() have_config = (config.read( expanduser('~/.config/pyqso/preferences.ini')) != []) (section, option) = ("general", "show_yearly_statistics") if (have_config and config.has_option(section, option)): if (config.getboolean("general", "show_yearly_statistics") and have_matplotlib): hbox = Gtk.HBox() label = Gtk.Label(label="Display statistics for year: ", halign=Gtk.Align.START) hbox.pack_start(label, False, False, 6) year_select = Gtk.ComboBoxText() min_year, max_year = self.get_year_bounds() if min_year and max_year: for year in range(max_year, min_year - 1, -1): year_select.append_text(str(year)) year_select.append_text("") year_select.connect("changed", self.on_year_changed) hbox.pack_start(year_select, False, False, 6) self.summary_page.pack_start(hbox, False, False, 4) self.items["YEARLY_STATISTICS"] = Figure() canvas = FigureCanvas(self.items["YEARLY_STATISTICS"]) canvas.set_size_request(800, 175) canvas.show() self.summary_page.pack_start(canvas, True, True, 0) # Summary tab label and icon. tab = Gtk.HBox(homogeneous=False, spacing=0) label = Gtk.Label(label="Summary ") icon = Gtk.Image.new_from_icon_name(Gtk.STOCK_INDEX, Gtk.IconSize.MENU) tab.pack_start(label, False, False, 0) tab.pack_start(icon, False, False, 0) tab.show_all() self.logbook.notebook.insert_page(self.summary_page, tab, 0) # Append as a new tab self.logbook.notebook.show_all() return
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. """ 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
class StatPerspective(Gtk.Box, Ide.Perspective): """ Sets up the stats perspective and handles the signals. This class sets up the containers of the perspective and the matplotlib figure and canvas. An asynchronous method iterates over the project and gathers the data for the plot. """ def __init__(self, workdir, *args, **kwargs): super().__init__(*args, **kwargs) self.workdir = workdir # main containers scr_win = Gtk.ScrolledWindow(visible=True) pad_box = Gtk.Box(visible=True) pad_box.pack_start(scr_win, True, True, 20) main_box = Gtk.Box(visible=True, orientation=Gtk.Orientation.VERTICAL) scr_win.add_with_viewport(main_box) # content lbl_head = '<span font="36.0"><b>Project Stats</b></span>' heading = Gtk.Label(label=lbl_head, expand=True, visible=True) heading.set_use_markup(True) main_box.pack_start(heading, False, False, 0) line = Gtk.Separator(visible=True) main_box.pack_start(line, False, False, 0) self.fig = Figure(facecolor='none') self.ax = self.fig.add_subplot(111, axisbg='#ffffff') self.canvas = FigCanvas(self.fig) self.canvas.set_size_request(800, 600) self.canvas.draw() self.canvas.show() main_box.pack_start(self.canvas, True, True, 0) self.add(pad_box) self.titlebar = Ide.WorkbenchHeaderBar(visible=True) # Gather stats thread = threading.Thread(target=self.gather_stats) thread.daemon = True thread.start() def gather_stats(self): file_types = {} for root, subfolders, files in os.walk(self.workdir): for file in files: try: with open(root + "/" + file) as fl: line_count = 0 for line in fl: line_count += 1 splt_str = file.split(".") if len(splt_str) > 1: file_ext = splt_str[-1] else: continue if file_ext in file_types: # key exists, add line count file_types[ file_ext] = file_types[file_ext] + line_count else: # key doesn't exist, create new key file_types[file_ext] = line_count except: continue keys = [] values = [] for key, value in file_types.items(): keys.append(key) values.append(value) key_ar = np.array(keys) val_ar = np.array(values).astype(int) ar = np.vstack((key_ar, val_ar)).T ar = ar[ar[:, 1].astype(int).argsort()] rows = ar.shape[0] val_pos = np.arange(1, ar.shape[0] + 1) self.ax.barh(val_pos, ar[:, 1].astype(int), 0.8, align="center") # facecolor='yellow' self.ax.set_yticks(val_pos) self.ax.tick_params(axis="both", which="major", pad=15) self.ax.set_yticklabels(ar[:, 0], fontsize="16", weight="bold") self.ax.tick_params(axis="x", which="major", labelsize="16") self.canvas.set_size_request(800, (rows * 20 + 50)) self.canvas.draw() def do_get_id(self): return 'hello-world2' def do_get_title(self): return 'Hello' def do_get_priority(self): return 10000 def do_get_icon_name(self): return "utilities-system-monitor-symbolic" def do_get_titlebar(self): return self.titlebar
class Handler: def __init__(self): global ppp self.window1 = builder.get_object("window1") self.window1.set_title("SPCinspector - No file loaded") self.vbox1 = builder.get_object("vbox1") self.label0 = builder.get_object("label0") self.label1 = builder.get_object("label1") self.label2 = builder.get_object("label2") self.label3 = builder.get_object("label3") self.label4 = builder.get_object("label4") self.label5 = builder.get_object("label5") self.label6 = builder.get_object("label6") self.label7 = builder.get_object("label7") self.label8 = builder.get_object("label8") # self.label9 = builder.get_object("label9") # self.label10 = builder.get_object("label10") # self.label11 = builder.get_object("label11") self.label12 = builder.get_object("label12") self.label13 = builder.get_object("label13") self.label14 = builder.get_object("label14") self.label15 = builder.get_object("label15") self.label16 = builder.get_object("label16") self.label17 = builder.get_object("label17") self.label18 = builder.get_object("label18") self.label19 = builder.get_object("label19") self.label20 = builder.get_object("label20") self.label21 = builder.get_object("label21") self.label22 = builder.get_object("label22") self.label23 = builder.get_object("label23") self.label24 = builder.get_object("label24") self.label25 = builder.get_object("label25") self.label26 = builder.get_object("label26") self.spin1 = builder.get_object("spin1") # depth self.spin2 = builder.get_object("spin2") # species self.spin3 = builder.get_object("spin3") # E-bins self.spin1.set_sensitive(False) self.spin2.set_sensitive(False) self.spin3.set_sensitive(False) self.button1 = builder.get_object("button1") self.button2 = builder.get_object("button2") self.button1.set_sensitive(False) self.cur_depth = 0 self.cur_species = 0 self.cur_ebin = 0 self.spc = [] self.path = "" fig = Figure(figsize=(5,5), dpi=100) #ax = fig.add_subplot(111, projection='polar') self.ax = fig.add_subplot(111) #x = [1, 2, 3, 4, 5] #y = [1, 4, 9, 16, 25] #self.ax.plot(x,y) self.ax.set_xlabel('Energy [MeV/u]') self.ax.set_ylabel('Histories') #sw = Gtk.ScrolledWindow() #myfirstwindow.add(sw) self.canvas = FigureCanvas(fig) self.canvas.set_size_request(600,600) self.vbox1.pack_end(self.canvas,True,True,True) self.zlut = { 1:'H',2:'He', 3:'Li',4:'Be',5:'B',6:'C',7:'N', 8:'O',9:'F',10:'Ne', 11:'Na',12:'Mg',13:'Al',14:'Si',15:'P',16:'S',17:'Cl',18:'Ar', 19:'K',20:'Ca' } print(ppp) if os.path.isfile(ppp): self.load_spc(ppp) def update_plot(self): d = self.spc.data[self.cur_depth] ds = d.species[self.cur_species] dse = ds.ebindata dsh = ds.histdata dsc = ds.rcumdata #print("NB:",len(dse),len(dsh),len(dsc)) self.ax.cla() self.ax.set_xlabel('Energy [MeV/u]') self.ax.set_ylabel('Histories') # plot legent in upper right corner text = r'$\mathregular{^{'+str(ds.la)+'}}$'+self.zlut.get(ds.lz,'?') self.ax.text(0.90, 0.94, text, fontsize=15,transform=self.ax.transAxes) #self.ax.plot(dse[1:],dsc[1:]) # cumulative sum self.ax.bar(dse[1:],dsh) self.canvas.draw() self.canvas.show() def onDeleteWindow(self, *args): Gtk.main_quit(*args) def onButtonPressed(self, button1): print("Hello World!") def main_quit(self, *args): print("Call Gtk.main_quit") Gtk.main_quit(*args) def on_spin1_value_changed(self,*args): self.cur_depth = self.spin1.get_value_as_int()-1 self.set_labels(self.spc) self.update_plot() def on_spin2_value_changed(self,*args): self.cur_species = self.spin2.get_value_as_int()-1 self.set_labels(self.spc) self.update_plot() def on_spin3_value_changed(self,*args): self.cur_ebin = self.spin3.get_value_as_int()-1 self.set_labels(self.spc) #self.update_plot() # no need to, as the energy marker is missing. def on_menuopen_activate(self,*args): dialog = Gtk.FileChooserDialog("Please choose a file", None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) #self.add_filters(dialog) response = dialog.run() if response == Gtk.ResponseType.OK: print("Open clicked") print("File selected: " + dialog.get_filename()) path = dialog.get_filename() dialog.destroy() self.load_spc(path) elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def load_spc(self,path): spc = SPC(path) spc.read_spc() self.filename = os.path.split(path)[1] self.window1.set_title("SPCinspector - "+ self.filename) self.spin1.set_range(1,spc.ndsteps) self.spin2.set_range(1,spc.data[self.cur_depth].nparts) self.spin3.set_range(1,spc.data[self.cur_depth].species[self.cur_species].ne) self.spin1.set_sensitive(True) self.spin2.set_sensitive(True) self.spin3.set_sensitive(True) self.button1.set_sensitive(False) # Todo: currently disabled self.set_labels(spc) self.spc = spc self.update_plot() def set_labels(self,spc): self.label0.set_text(self.filename) self.label1.set_text(spc.filetype) self.label2.set_text(spc.fileversion) self.label3.set_text(spc.filedate) self.label4.set_text(spc.targname) self.label5.set_text(spc.projname) self.label6.set_text('{:f}'.format(spc.energy)) self.label7.set_text('{:f}'.format(spc.peakpos)) self.label8.set_text('{:f}'.format(spc.norm)) # - depth self.label12.set_text('{:d}'.format(spc.ndsteps)) d = spc.data[self.cur_depth] self.label13.set_text('{:f}'.format(d.depth)) self.label14.set_text('{:f}'.format(d.dsnorm)) # - species self.label15.set_text('{:d}'.format(d.nparts)) ds = d.species[self.cur_species] self.label16.set_text('{:f} {:f}'.format(ds.z,ds.a)) self.label17.set_text('{:d} {:d}'.format(ds.lz,ds.la)) self.label18.set_text('{:f}'.format(ds.dscum)) # running cum.sum over species self.label19.set_text('{:d}'.format(ds.nc)) # - energy self.label20.set_text('{:d}'.format(ds.ne)) dse = ds.ebindata[self.cur_ebin] if self.cur_ebin == 0: dsel = dse else: dsel = ds.ebindata[self.cur_ebin-1] dsh = ds.histdata[self.cur_ebin] dsc = ds.rcumdata[self.cur_ebin] self.label21.set_text('{:.4f} {:.4f} {:.4f}'.format(dsel,(dsel+dse)*0.5,dse)) self.label22.set_text('{:f}'.format(dse - dsel)) self.label23.set_text('{:f}'.format(dsh)) self.label24.set_text('{:f}'.format(dsh*(dse-dsel))) self.label25.set_text('{:f}'.format(dsc)) #cummulative sum self.label26.set_text('{:.2f} {:.2f}'.format(ds.ebindata.min(),ds.ebindata.max())) def _do_expose(self, widget, event): print("_do_expose") self.canvas.draw() self.canvas.show()
class StatPerspective(Gtk.Box, Ide.Perspective): """ Sets up the stats perspective and handles the signals. This class sets up the containers of the perspective and the matplotlib figure and canvas. An asynchronous method iterates over the project and gathers the data for the plot. """ def __init__(self, workdir, *args, **kwargs): super().__init__(*args, **kwargs) self.workdir = workdir # main containers scr_win = Gtk.ScrolledWindow(visible=True) pad_box = Gtk.Box(visible=True) pad_box.pack_start(scr_win, True, True, 20) main_box = Gtk.Box(visible=True, orientation=Gtk.Orientation.VERTICAL) scr_win.add_with_viewport(main_box) # content lbl_head = '<span font="36.0"><b>Project Stats</b></span>' heading = Gtk.Label(label=lbl_head, expand=True, visible=True) heading.set_use_markup(True) main_box.pack_start(heading, False, False, 0) line = Gtk.Separator(visible=True) main_box.pack_start(line, False, False, 0) self.fig = Figure(facecolor='none') self.ax = self.fig.add_subplot(111, axisbg='#ffffff') self.canvas = FigCanvas(self.fig) self.canvas.set_size_request(800, 600) self.canvas.draw() self.canvas.show() main_box.pack_start(self.canvas, True, True, 0) self.add(pad_box) self.titlebar = Ide.WorkbenchHeaderBar(visible=True) # Gather stats thread = threading.Thread(target=self.gather_stats) thread.daemon = True thread.start() def gather_stats(self): file_types = {} for root, subfolders, files in os.walk(self.workdir): for file in files: try: with open(root + "/" + file) as fl: line_count = 0 for line in fl: line_count += 1 splt_str = file.split(".") if len(splt_str) > 1: file_ext = splt_str[-1] else: continue if file_ext in file_types: # key exists, add line count file_types[file_ext] = file_types[file_ext] + line_count else: # key doesn't exist, create new key file_types[file_ext] = line_count except: continue keys = [] values = [] for key, value in file_types.items(): keys.append(key) values.append(value) key_ar = np.array(keys) val_ar = np.array(values).astype(int) ar = np.vstack((key_ar, val_ar)).T ar = ar[ar[:,1].astype(int).argsort()] rows = ar.shape[0] val_pos = np.arange(1, ar.shape[0]+1) self.ax.barh(val_pos, ar[:,1].astype(int), 0.8, align="center") # facecolor='yellow' self.ax.set_yticks(val_pos) self.ax.tick_params(axis="both", which="major", pad=15) self.ax.set_yticklabels(ar[:,0], fontsize="16", weight="bold") self.ax.tick_params(axis="x", which="major", labelsize="16") self.canvas.set_size_request(800, (rows * 20 + 50)) self.canvas.draw() def do_get_id(self): return 'hello-world2' def do_get_title(self): return 'Hello' def do_get_priority(self): return 10000 def do_get_icon_name(self): return "utilities-system-monitor-symbolic" def do_get_titlebar(self): return self.titlebar
class UI: # ====== Variables de la classe UI ======== xn, yn = 0, 0 # ========================================= def __init__(self): # ### Initialise les datas### self.array_size = 10 self.nbr_dots = 1 # ########################### self.builder = Gtk.Builder() # voir commentaires lignes 21-25 # self.builder = gtk.Builder() self.builder.add_from_file( os.path.join(os.getcwd(), 'TBench_GUI_gl3.ui')) self.window = self.builder.get_object('dialog1') self.aboutdialog = self.builder.get_object('aboutdialog1') self.assistant = self.builder.get_object('assistant1') self.textview = self.builder.get_object('textview1') self.textbuffer = self.builder.get_object('textbuffer1') self.bt_exit = self.builder.get_object('bt_exit') self.tbt_state0 = self.builder.get_object('tbt_state0') self.tbt_state1 = self.builder.get_object('tbt_state1') self.tbt_state2 = self.builder.get_object('tbt_state2') self.imagemenuitem5 = self.builder.get_object('imagemenuitem5') self.imagemenuitem10 = self.builder.get_object('imagemenuitem10') self.builder.connect_signals(self) self.bufsize = 10 # ajout 20.02 self.databuffer = collections.deque([0.0] * self.bufsize, self.bufsize) # ajout 20.02 self.x = [1 * i for i in range(-self.bufsize + 1, 1) ] # ajout 20.02(-self.bufsize+1,1) # Matplotlib trucs self.figure = Figure(figsize=(100, 100), dpi=100) self.ax = self.figure.add_subplot(111) self.canvas = FigureCanvas(self.figure) # une gtk.DrawingArea self.line, = self.ax.plot(self.x, self.databuffer) # Gtk trucs self.canvas.show() self.graphview = self.builder.get_object("plot") self.graphview.pack_start(self.canvas, True, True, True) self.arrow = self.builder.get_object('arrow1') self.window.connect('delete-event', self.quit) self.tbt_state0.connect('toggled', self.on_button_toggled0) self.tbt_state1.connect('toggled', self.on_button_toggled1) self.tbt_state2.connect('toggled', self.on_button_toggled2) self.bt_exit.connect('clicked', self.quit) self.imagemenuitem5.connect('activate', self.quit) self.imagemenuitem10.connect('activate', self.show_aboutdialog) self.window.show() # ================= Recherche du port de l'arduino ==================== self.sonde = arduino.search() if self.sonde == "impossible d'ouvrir un port série": info = ( self.sonde + "!" + '\n' + "quitter cette session, vérifier la connexion avec le PLC, puis relancer le programme" ) self.ajout_log_term("TB", info) else: self.ajout_log_term("PLC", self.sonde) self.init_arduino() # initialise l'arduino # ===================================================================== def updateplot(self): self.databuffer.append(UI.yn) self.line.set_ydata(self.databuffer) self.ax.relim() self.ax.autoscale_view(False, False, True) self.canvas.draw() def on_button_toggled0(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) self.updateplot() # pour marquer un temps... UI.xn = UI.xn + 1 UI.yn = 0.8 else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) self.updateplot() # pour marquer un temps... UI.xn = UI.xn + 1 UI.yn = 0 # print(UI.xn, UI.yn) self.updateplot() # self.updateplot() # print 'Button0 was turned: ', state[1] def on_button_toggled1(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) def on_button_toggled2(self, button): if button.get_active(): state = ['1', 'on'] button.set_label(state[1].upper()) self.send_command(state[0]) else: state = ['0', 'off'] button.set_label(state[1].upper()) self.send_command(state[0]) def show_aboutdialog(self, *args): self.aboutdialog.run() self.aboutdialog.hide() def show_assistant(self, *args): self.assistant.show() self.assistant.hide() def quit(self, *args): if self.sonde != "impossible d'ouvrir un port série": self.quit_arduino() # réinitialise l'arduino # Gtk.main_quit() # voir commentaires lignes 21-25 Gtk.main_quit() def init_arduino(self): arduino.open() time.sleep(4) arduino.write('0') # éteint la led si elle était allumée arduino.flush() def quit_arduino(self): arduino.write('0') # éteint la led si elle était allumée arduino.flush() arduino.close() def send_command(self, val): arduino.write(val) self.rec = arduino.reading() info = ("Bascule l'état de la led PLC à " + val) self.ajout_log_term("TB", info) info = self.rec.decode('utf-8').rstrip('\n\r') self.ajout_log_term("PLC", info) def ajout_log_term(self, src, msg): #self.textbuffer.insert_at_cursor(src + ": " + msg + '\n') # text_buffer is a gtk.TextBuffer start_iter = self.textbuffer.get_start_iter() self.textbuffer.insert(start_iter, (src + ": " + msg + '\n'))