class MyProgram(): def __init__(self): #some_gtk_stuff self.builder = Gtk.Builder() self.builder.add_from_file("u.glade") self.window = self.builder.get_object("window1") # sw = builder.get_object("sw") # canvas = FigureCanvas(fig) # sw.add_with_viewport(canvas) self.signals = { 'cos_clicked': self.create_plot, 'sin_clicked': self.create_plot, 'sq_clicked': self.create_plot, 'on_window1_destroy': self.on_window1_destroy, } self.builder.connect_signals(self.signals) self.vbox = self.builder.get_object('vbox') self.figure = plt.figure(figsize=(1, 1), dpi=100) # self.axis = self.figure.add_subplot(111) # self.figure = Figure(figsize=(1,1), dpi=100) self.ax = self.figure.add_subplot(111) self.canvas = None self.window.set_size_request(700, 500) self.window.show_all() def create_plot(self, button): self.ani = animation.FuncAnimation(self.figure, self.update_plot, interval=1000) def on_window1_destroy(self, *args): Gtk.main_quit(*args) def update_plot(self, i): #read SampleData from txt File x = [] y = [] with open('s.dat') as f: x_raw, y_raw = f.readline().strip().split(',') x.append(int(x_raw)) y.append(int(y_raw)) self.axis.plot(x, y) if not self.canvas: self.canvas = FigureCanvas(self.figure) self.vbox.pack_start(self.canvas) self.canvas.show() self.canvas.draw()
class EditAtomTypeView(BaseView): builder = resource_filename(__name__, "glade/atoms.glade") top = "edit_atom_type" widget_format = "atom_%s" # ------------------------------------------------------------ # Initialisation and other internals # ------------------------------------------------------------ def __init__(self, *args, **kwargs): BaseView.__init__(self, *args, **kwargs) self.graph_parent = self["view_graph"] self.setup_matplotlib_widget() def setup_matplotlib_widget(self): self.figure = Figure(dpi=72) self.plot = self.figure.add_subplot(111) self.figure.subplots_adjust(bottom=0.20) self.matlib_canvas = FigureCanvasGTK(self.figure) self.plot.autoscale_view() self.graph_parent.add(self.matlib_canvas) self.graph_parent.show_all() # ------------------------------------------------------------ # Methods & Functions # ------------------------------------------------------------ def update_figure(self, x, y): self.plot.cla() self.plot.plot(x, y, 'k-', aa=True) self.plot.set_ylabel('Scattering factor', size=14, weight="heavy") self.plot.set_xlabel('2θ', size=14, weight="heavy") self.plot.autoscale_view() if self.matlib_canvas is not None: self.matlib_canvas.draw()
class MonitorWindow(Gtk.Window): def __init__(self, pid): Gtk.Window.__init__(self, title="Monitor") self.pid = pid self.restart = False main_hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) f = matplotlib.figure.Figure() self.axes = f.add_subplot(111) self.axes.set_xlabel('Time (sec.)') self.axes.set_ylabel('Temperature (C)') self.axes.autoscale() self.out_axes = self.axes.twinx() self.out_axes.set_ylabel('OUT (%)') self.out_axes.autoscale() self.axes.grid() self.pv_x = [] self.pv_y = [] self.sv_x = [] self.sv_y = [] self.out_x = [] self.out_y = [] self.pv_plot, = self.axes.plot(self.pv_x, self.pv_y, 'b--') #b self.sv_plot, = self.axes.plot(self.sv_x, self.sv_y, 'k-') #k self.out_plot, = self.out_axes.plot(self.out_x, self.out_y, 'r:') #r self.canvas = FigureCanvas(f) self.canvas.set_size_request(800,600) vbox.add(self.canvas) hbox = Gtk.Box() # Start/restart program button = Gtk.Button('First Step') button.connect('clicked', self.on_restart) hbox.pack_start(button, False, False, 0) button = Gtk.Button('Continue') button.connect('clicked', lambda x: self.pid.coil('next')) hbox.pack_start(button, False, False, 0) button = Gtk.Button('Pause') button.connect('clicked', lambda x: self.pid.coil('pause')) hbox.pack_start(button, False, False, 0) button = Gtk.Button('Last Step') button.connect('clicked', lambda x: self.pid.coil('end')) hbox.pack_start(button, False, False, 0) hbox.set_sensitive(False) vbox.add(hbox) main_hbox.add(vbox) # Extra information required # Manual/automatic # Program step # Program status (running/pause) # Time/time remaining in step # PV/SV/dSV display? # AL1/AL2 self.add(main_hbox) table = Gtk.Table() row = 0 combo = widgets.PIDComboBoxText(pid, 'ModL') pid.connect('changed', lambda pid, n, val, mult, w=combo: w.set_sensitive(not val['A/M']) if n == 'flags' else None) label = Gtk.Label('Mode') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) table.attach(combo, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) row += 1 # Allow changing SV in manual mode # FIXME: disable if not SV? spin = widgets.PIDSpinButton(pid, 'SV') spin.set_sensitive(False) #combo.connect('changed', lambda combo, s=spin, c=combo: s.set_sensitive(c.get_active_text() == 'SV')) pid.connect('changed', lambda pid, n, val, mult, w=spin, c=combo: w.set_sensitive(not val['A/M'] and c.get_active_text() == 'SV') if n == 'flags' else None) pid.connect('changed', lambda pid, n, val, mult, w=spin: w.set_sensitive(val == 'SV') if n == 'ModL' else None) pid.connect('changed', lambda pid, n, val, mult, w=hbox: w.set_sensitive(val != 'SV') if n == 'ModL' else None) label = Gtk.Label('Set value') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) table.attach(spin, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) row += 1 label = Gtk.Label('Dynamic SV') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) pid.connect('changed', lambda pid, n, val, mult, w=label: w.set_text('%0.1f'%val if mult == .1 else '%d'%val) if n == 'dSV' else None) row += 1 label = Gtk.Label('PV') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) pid.connect('changed', lambda pid, n, val, mult, w=label: w.set_text('%0.1f'%val if mult == .1 else '%d'%val) if n == 'PV' else None) row += 1 check = widgets.ActionCheckButton(lambda val: pid.coil('manual' if val else 'auto', val), read=lambda p=pid: p.flag('A/M')) pid.connect('changed', lambda pid, n, val, mult, w=check: w.set_active(val['A/M'], user=False) if n == 'flags' else None) label = Gtk.Label('Manual mode') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) table.attach(check, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) row += 1 reg = pld.registers['OUT'] spin = widgets.PIDSpinButton(pid, 'OUT') spin.set_sensitive(False) pid.connect('changed', lambda pid, n, val, mult, w=spin: w.set_sensitive(val['A/M']) if n == 'flags' else None) label = Gtk.Label('Output level') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) table.attach(spin, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) row += 1 label = Gtk.Label('Alarm 1') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) pid.connect('changed', lambda pid, n, val, mult, w=label: w.set_text('On' if val['AL1'] else 'Off') if n == 'flags' else None) row += 1 label = Gtk.Label('Alarm 2') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) pid.connect('changed', lambda pid, n, val, mult, w=label: w.set_text('On' if val['AL2'] else 'Off') if n == 'flags' else None) row += 1 label = Gtk.Label('Current Step') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) self.current_step = label row += 1 label = Gtk.Label('Time elapsed/total') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) self.step_time = label row += 1 label = Gtk.Label('Step action') label.set_alignment(0, 0.5) table.attach(label, 0, 1, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) label = Gtk.Label() label.set_alignment(0, 0.5) table.attach(label, 1, 2, row, row+1, yoptions=Gtk.AttachOptions.SHRINK) self.status = label row += 1 main_hbox.add(table) self.run = True self.d = self.loop() self.d.addErrback(lambda x: None) self.connect('delete-event', self.on_delete) def on_restart(self, widget): self.restart = True def on_delete(self, widget, event): self.run = False @twisted.internet.defer.inlineCallbacks def loop(self): start = time.time() while self.run: if self.restart: start = time.time() self.pv_x = [] self.pv_y = [] self.sv_x = [] self.sv_y = [] self.out_x = [] self.out_y = [] self.axes.relim() self.axes.autoscale() self.canvas.draw() self.restart = False yield self.pid.coil('start') yield self.pid.coil('auto') try: val, mult = yield self.pid.holding_read('Pr+t') step, step_t = val val, mult = yield self.pid.holding_read('t-' + ('%02d' % step)) step_total = 0 if type(val) is tuple: val, idx = val if val == 'Run': step_total = idx elif val == 'Jump': val += ' to ' + str(abs(idx)) self.current_step.set_text(str(step)) self.step_time.set_text(str(step_t) + '/' + str(step_total) if val == 'Run' else 'NA') self.status.set_text(val) pv, mult = yield self.pid.holding_read('PV') now = time.time() - start if len(self.pv_y) > 1 and self.pv_y[-2] == self.pv_y[-1] == pv: self.pv_x[-1] = now else: self.pv_x.append(now) self.pv_y.append(pv) self.pv_plot.set_data(self.pv_x, self.pv_y) self.axes.relim() self.axes.autoscale() self.canvas.draw() sv, mult = yield self.pid.holding_read('dSV') now = time.time() - start if len(self.sv_y) > 1 and self.sv_y[-2] == self.sv_y[-1] == sv: self.sv_x[-1] = now else: self.sv_x.append(now) self.sv_y.append(sv) self.sv_plot.set_data(self.sv_x, self.sv_y) self.axes.relim() self.axes.autoscale() self.canvas.draw() out, mult = yield self.pid.holding_read('OUT') now = time.time() - start if len(self.out_y) > 1 and self.out_y[-2] == self.out_y[-1] == out: self.out_x[-1] = now else: self.out_x.append(now) self.out_y.append(out) self.out_plot.set_data(self.out_x, self.out_y) self.out_axes.relim() self.out_axes.autoscale() self.canvas.draw() except Exception, e: print 'monitor error', e traceback.print_exc(file=sys.stdout) d = twisted.internet.defer.Deferred() twisted.internet.reactor.callLater(0.300 , d.callback, None) yield d
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 HP8903BWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="HP 8903B Control") # Serial connection! self.ser = None self.gpib_dev = None self.devices = list_ports.comports() # Menu Bar junk! action_group = Gtk.ActionGroup("my_actions") action_filemenu = Gtk.Action("FileMenu", "File", None, None) action_group.add_action(action_filemenu) self.action_filesave = Gtk.Action("FileSave", "Save Data", None, None) action_filequit = Gtk.Action("FileQuit", None, None, Gtk.STOCK_QUIT) action_filequit.connect("activate", self.on_menu_file_quit) action_group.add_action(self.action_filesave) action_group.add_action(action_filequit) self.action_filesave.set_sensitive(False) self.action_filesave.connect('activate', self.save_data) uimanager = self.create_ui_manager() uimanager.insert_action_group(action_group) menubar = uimanager.get_widget("/MenuBar") self.status_bar = Gtk.Statusbar() self.status_bar.push(0, "HP 8903 Audio Analyzer Control") self.master_vbox = Gtk.Box(False, spacing = 2, orientation = 'vertical') self.master_vbox.pack_start(menubar, False, False, 0) master_hsep = Gtk.HSeparator() self.master_vbox.pack_start(master_hsep, False, False, 0) self.add(self.master_vbox) self.hbox = Gtk.Box(spacing = 2) self.master_vbox.pack_start(self.hbox, True, True, 0) self.master_vbox.pack_start(self.status_bar, False, False, 0) # Begin controls bframe = Gtk.Frame(label = "Control") left_vbox = Gtk.Box(spacing = 2, orientation = 'vertical') self.box = Gtk.Box(spacing = 2) bframe.add(left_vbox) # GPIB device selector gpib_frame = Gtk.Frame(label = "GPIB Communication Device") self.gpib_big_box = Gtk.Box(spacing = 2) gpib_frame.add(self.gpib_big_box) self.gpib_box = Gtk.Box(spacing = 2) self.gpib_vbox = Gtk.Box(spacing = 2, orientation = 'vertical') gpib_label = Gtk.Label("GPIB Device: ") self.gpib_box.pack_start(gpib_label, False, False, 0) gpib_store = Gtk.ListStore(int, str) for n, g_dev in enumerate(HP8903_GPIB_devices): gpib_store.append([n, g_dev[1]]) self.gpib_combo = Gtk.ComboBox.new_with_model_and_entry(gpib_store) self.gpib_combo.set_entry_text_column(1) self.gpib_combo.set_active(0) self.gpib_box.pack_start(self.gpib_combo, False, False, 0) gpib_addr_box = Gtk.Box(spacing = 2) self.gpib_addr = Gtk.SpinButton() self.gpib_addr.set_range(0, 30) self.gpib_addr.set_digits(0) self.gpib_addr.set_value(0) self.gpib_addr.set_increments(1.0, 1.0) gpib_addr_label = Gtk.Label("GPIB Address: ") gpib_addr_box.pack_start(gpib_addr_label, False, False, 0) gpib_addr_box.pack_start(self.gpib_addr, False, False, 0) self.gpib_vbox.pack_start(self.gpib_box, False, False, 0) self.gpib_vbox.pack_start(gpib_addr_box, False, False, 0) self.gpib_big_box.pack_start(self.gpib_vbox, False, False, 0) left_vbox.pack_start(gpib_frame, False, False, 0) # Device items left_vbox.pack_start(self.box, False, False, 0) self.hbox.pack_start(bframe, False, False, 0) con_hbox = Gtk.Box(spacing = 2) self.con_button = Gtk.Button(label = "Connect") self.dcon_button = Gtk.Button(label = "Disconnect") self.con_button.connect("clicked", self.setup_gpib) self.dcon_button.connect("clicked", self.close_gpib) con_hbox.pack_start(self.con_button, False, False, 0) con_hbox.pack_start(self.dcon_button, False, False, 0) left_vbox.pack_start(con_hbox, False, False, 0) device_store = Gtk.ListStore(int, str) for i, dev in enumerate(self.devices): device_store.append([i, dev[0]]) self.device_combo = Gtk.ComboBox.new_with_model_and_entry(device_store) self.device_combo.set_entry_text_column(1) self.device_combo.set_active(0) device_label = Gtk.Label("Device: ") self.box.pack_start(device_label, False, False, 0) self.box.pack_start(self.device_combo, False, False, 0) hsep0 = Gtk.HSeparator() left_vbox.pack_start(hsep0, False, False, 2) # Measurement Selection mframe = Gtk.Frame(label = "Measurement Selection") meas_box = Gtk.Box(spacing = 2) meas_vbox = Gtk.Box(spacing = 2) mframe.add(meas_box) meas_box.pack_start(meas_vbox, False, False, 0) meas_store = Gtk.ListStore(int, str) meas_dict = {0: "THD+n", 1:"Frequency Response", 2: "THD+n (Ratio)", 3: "Frequency Response (Ratio)", 4: "Ouput Level"} for k, v in meas_dict.iteritems(): meas_store.append([k, v]) self.meas_combo = Gtk.ComboBox.new_with_model_and_entry(meas_store) self.meas_combo.set_entry_text_column(1) self.meas_combo.set_active(0) self.meas_combo.connect("changed", self.meas_changed) meas_vbox.pack_start(self.meas_combo, False, False, 0) left_vbox.pack_start(mframe, False, False, 0) units_frame = Gtk.Frame(label = "Units") units_box = Gtk.Box(spacing = 2) units_vbox = Gtk.Box(spacing = 2) units_frame.add(units_box) units_box.pack_start(units_vbox, False, False, 0) self.thd_units_store = Gtk.ListStore(int, str) self.ampl_units_store = Gtk.ListStore(int, str) self.thdr_units_store = Gtk.ListStore(int, str) self.amplr_units_store = Gtk.ListStore(int, str) self.optlvl_units_store = Gtk.ListStore(int, str) thd_units_dict = {0: "%", 1: "dB"} ampl_units_dict = {0: "V", 1: "dBm"} thdr_units_dict = {0: "%", 1: "dB"} amplr_units_dict = {0: "%", 1:"dB"} optlvl_units_dict = {0: "V"} for k, v in thd_units_dict.iteritems(): self.thd_units_store.append([k, v]) for k, v in ampl_units_dict.iteritems(): self.ampl_units_store.append([k, v]) for k, v in thdr_units_dict.iteritems(): self.thdr_units_store.append([k, v]) for k, v in amplr_units_dict.iteritems(): self.amplr_units_store.append([k, v]) for k, v in optlvl_units_dict.iteritems(): self.optlvl_units_store.append([k, v]) self.units_combo = Gtk.ComboBox.new_with_model_and_entry(self.thd_units_store) self.units_combo.set_entry_text_column(1) self.units_combo.set_active(0) self.units_combo.connect("changed", self.units_changed) units_vbox.pack_start(self.units_combo, False, False, 0) left_vbox.pack_start(units_frame, False, False, 0) # units_combo.set_model(ampl_units_store) # units_combo.set_active(0) #left_vbox.pack_start(units_combo, False, False, 0) hsep1 = Gtk.HSeparator() left_vbox.pack_start(hsep1, False, False, 2) # Frequency Sweep Control #side_filler = Gtk.Box(spacing = 2, orientation = 'vertical') swconf = Gtk.Frame(label = "Frequency Sweep Control") swhbox = Gtk.Box(spacing = 2) swbox = Gtk.Box(spacing = 2, orientation = 'vertical') swconf.add(swhbox) swhbox.pack_start(swbox, False, False, 0) left_vbox.pack_start(swconf, False, False, 0) startf = Gtk.Frame(label = "Start Frequency (Hz)") self.start_freq = Gtk.SpinButton() self.start_freq.set_range(20.0, 100000.0) self.start_freq.set_digits(5) self.start_freq.set_value(20.0) self.start_freq.set_increments(100.0, 1000.0) startf.add(self.start_freq) #left_vbox.pack_start(startf, False, False, 0) swbox.pack_start(startf, False, False, 0) self.start_freq.connect("value_changed", self.freq_callback) stopf = Gtk.Frame(label = "Stop Frequency (Hz)") self.stop_freq = Gtk.SpinButton() self.stop_freq.set_range(20.0, 100000.0) self.stop_freq.set_digits(5) self.stop_freq.set_value(30000.0) self.stop_freq.set_increments(100.0, 1000.0) stopf.add(self.stop_freq) #left_vbox.pack_start(stopf, False, False, 0) swbox.pack_start(stopf, False, False, 0) self.stop_freq.connect("value_changed", self.freq_callback) stepsf = Gtk.Frame(label = "Steps per Decade") self.steps = Gtk.SpinButton() self.steps.set_range(1.0, 1000.0) self.steps.set_digits(1) self.steps.set_value(10.0) self.steps.set_increments(1.0, 10.0) stepsf.add(self.steps) swbox.pack_start(stepsf, False, False, 0) #left_vbox.pack_start(stepsf, False, False, 0) hsep2 = Gtk.HSeparator() left_vbox.pack_start(hsep2, False, False, 2) # Freq Control freqf = Gtk.Frame(label = "Frequency") freqbox = Gtk.Box(spacing = 2) freqhbox = Gtk.Box(spacing = 2, orientation = 'vertical') freqf.add(freqhbox) freqhbox.pack_start(freqbox, False, False, 0) self.freq = Gtk.SpinButton() self.freq.set_range(20.0, 100000.0) self.freq.set_digits(5) self.freq.set_value(1000.0) self.freq.set_increments(100.0, 1000.0) self.freq.set_sensitive(False) freqbox.pack_start(self.freq, False, False, 0) left_vbox.pack_start(freqf, False, False, 0) freqhsep = Gtk.HSeparator() left_vbox.pack_start(freqhsep, False, False, 2) # Source Control sourcef = Gtk.Frame(label = "Source Control (V RMS)") source_box = Gtk.Box(spacing = 2) sourcef.add(source_box) self.source = Gtk.SpinButton() self.source.set_range(0.0006, 6.0) self.source.set_digits(4) self.source.set_value(0.5) self.source.set_increments(0.5, 1.0) source_box.pack_start(self.source, False, False, 0) left_vbox.pack_start(sourcef, False, False, 0) hsep3 = Gtk.HSeparator() left_vbox.pack_start(hsep3, False, False, 2) vswconf = Gtk.Frame(label = "Voltage Sweep Control") vswhbox = Gtk.Box(spacing = 2) vswbox = Gtk.Box(spacing = 2, orientation = 'vertical') vswconf.add(vswhbox) vswhbox.pack_start(vswbox, False, False, 0) left_vbox.pack_start(vswconf, False, False, 0) startv = Gtk.Frame(label = "Start Voltage (V)") self.start_v = Gtk.SpinButton() self.start_v.set_range(0.0006, 6.0) self.start_v.set_digits(5) self.start_v.set_value(0.1) self.start_v.set_increments(0.1, 1) startv.add(self.start_v) #left_vbox.pack_start(startf, False, False, 0) vswbox.pack_start(startv, False, False, 0) self.start_v.connect("value_changed", self.volt_callback) stopv = Gtk.Frame(label = "Stop Voltage (V)") self.stop_v = Gtk.SpinButton() self.stop_v.set_range(0.0006, 6.0) self.stop_v.set_digits(5) self.stop_v.set_value(1.0) self.stop_v.set_increments(0.1, 1.0) stopv.add(self.stop_v) #left_vbox.pack_start(stopf, False, False, 0) vswbox.pack_start(stopv, False, False, 0) self.stop_v.connect("value_changed", self.volt_callback) stepsv = Gtk.Frame(label = "Total Samples") self.stepsv = Gtk.SpinButton() self.stepsv.set_range(1.0, 1000.0) self.stepsv.set_digits(1) self.stepsv.set_value(10.0) self.stepsv.set_increments(1.0, 10.0) stepsv.add(self.stepsv) vswbox.pack_start(stepsv, False, False, 0) #left_vbox.pack_start(stepsf, False, False, 0) hsepsv = Gtk.HSeparator() left_vbox.pack_start(hsepsv, False, False, 2) filterf = Gtk.Frame(label = "Filters") filterb = Gtk.Box(spacing = 2) filtervb = Gtk.Box(spacing = 2, orientation = 'vertical') filterf.add(filterb) filterb.pack_start(filtervb, False, False, 0) self.f30k = Gtk.CheckButton("30 kHz LP") self.f80k = Gtk.CheckButton("80 kHz LP") self.lpi = Gtk.CheckButton("Left Plug-in filter") self.rpi = Gtk.CheckButton("Right Plug-in filter") self.f30k.connect("toggled", self.filter1_callback) self.f80k.connect("toggled", self.filter1_callback) self.lpi.connect("toggled", self.filter2_callback) self.rpi.connect("toggled", self.filter2_callback) filtervb.pack_start(self.f30k, False, False, 0) filtervb.pack_start(self.f80k, False, False, 0) filtervb.pack_start(self.lpi, False, False, 0) filtervb.pack_start(self.rpi, False, False, 0) left_vbox.pack_start(filterf, False, False, 0) hsep = Gtk.HSeparator() left_vbox.pack_start(hsep, False, False, 2) self.run_button = Gtk.Button(label = "Start Sequence") self.run_button.set_sensitive(False) left_vbox.pack_start(self.run_button, False, False, 0) self.run_button.connect("clicked", self.run_test) self.f = Figure(figsize=(5,4), dpi=100) self.a = self.f.add_subplot(111) #self.plt = self.a.plot(20,-90, marker = 'x') self.plt = self.a.plot(marker = 'x') self.a.grid(True) self.a.set_xscale('log') self.a.set_xlim((10.0, 30000.0)) self.a.set_ylim((0.0005, 0.01)) self.a.set_xlabel("Frequency (Hz)") self.a.set_ylabel("THD+n (%)") self.canvas = FigureCanvas(self.f) toolbar = NavigationToolbar(self.canvas, self) plot_vbox = Gtk.Box(spacing = 2, orientation = 'vertical') plot_vbox.pack_start(self.canvas, True, True, 0) plot_vbox.pack_start(toolbar, False, False, 0) #self.hbox.pack_start(self.canvas, True, True, 0) self.hbox.pack_start(plot_vbox, True, True, 0) # Groups of widgets self.measurement_widgets = [self.meas_combo, self.units_combo] self.freq_sweep_widgets = [self.start_freq, self.stop_freq, self.steps] self.source_widgets = [self.source] self.filter_widgets = [self.f30k, self.f80k, self.lpi, self.rpi] self.vsweep_widgets = [self.start_v, self.stop_v, self.stepsv] for w in self.measurement_widgets: w.set_sensitive(False) for w in self.freq_sweep_widgets: w.set_sensitive(False) for w in self.source_widgets: w.set_sensitive(False) for w in self.filter_widgets: w.set_sensitive(False) for w in self.vsweep_widgets: w.set_sensitive(False) self.meas_string = "THD+n (%)" self.units_string = "%" self.measurements = None def setup_gpib(self, button): # Get GPIB info gpib_model = self.gpib_combo.get_model() gpib_tree_iter = self.gpib_combo.get_active_iter() # Get address gpib_addr = self.gpib_addr.get_value_as_int() # Instantiate GPIB Device class self.gpib_dev = HP8903_GPIB_devices[gpib_model[gpib_tree_iter][0]][0](gpib_addr = gpib_addr) print("Using GPIB Device: %s" % self.gpib_dev.name()) print("Using GPIB Address: %s" % str(gpib_addr)) if (not self.gpib_dev.implements_addr()): print("Warning: this GPIB communication device does not implement") print(" address setting, check your hardware's settings!") # Get device info model = self.device_combo.get_model() tree_iter = self.device_combo.get_active_iter() print("Device: %s" % model[tree_iter][1]) dev_name = model[tree_iter][1] # Disable gpib and devices buttons self.con_button.set_sensitive(False) self.device_combo.set_sensitive(False) self.gpib_combo.set_sensitive(False) self.gpib_addr.set_sensitive(False) if(not self.gpib_dev.open(dev_name)): # Make into warning window? print("Failed to open GPIB Device: %s at %s" % (self.gpib_dev.name(), dev_name)) print("Verify hardware setup and try to connect again") self.con_button.set_sensitive(True) self.device_combo.set_sensitive(True) self.gpib_combo.set_sensitive(True) self.gpib_addr.set_sensitive(True) return(False) # Do test? if (not self.gpib_dev.test()): print("GPIB device failed self test: %s at %s" % (self.gpib_dev.name(), dev_name)) print("Verify hardware setup and try to connect again") self.con_button.set_sensitive(True) self.device_combo.set_sensitive(True) self.gpib_combo.set_sensitive(True) self.gpib_addr.set_sensitive(True) return(False) if (self.gpib_dev.is_open()): self.gpib_dev.flush_input() # Initialize the HP 8903 status = self.init_hp8903() if (not status): print("Failed to initialize HP 8903") print("Verify hardware setup and try to connect again") self.gpib_dev.close() self.con_button.set_sensitive(True) self.device_combo.set_sensitive(True) self.gpib_combo.set_sensitive(True) self.gpib_addr.set_sensitive(True) return(False) else: print("Failed to use GPIB device") print("Verify hardware setup and try to connect again") self.gpib_dev.close() self.con_button.set_sensitive(True) self.device_combo.set_sensitive(True) self.gpib_combo.set_sensitive(True) self.gpib_addr.set_sensitive(True) return(False) # Enable measurement controls self.run_button.set_sensitive(True) for w in self.measurement_widgets: w.set_sensitive(True) for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.source_widgets: w.set_sensitive(True) for w in self.filter_widgets: w.set_sensitive(True) for w in self.vsweep_widgets: w.set_sensitive(False) self.status_bar.push(0, "Connected to HP 8903, ready for measurements") def close_gpib(self, button): if (self.gpib_dev): self.gpib_dev.close() # Activate device/connection buttons self.con_button.set_sensitive(True) self.device_combo.set_sensitive(True) self.gpib_combo.set_sensitive(True) self.gpib_addr.set_sensitive(True) # Disable measurement controls for w in self.measurement_widgets: w.set_sensitive(False) for w in self.freq_sweep_widgets: w.set_sensitive(False) for w in self.source_widgets: w.set_sensitive(False) for w in self.filter_widgets: w.set_sensitive(False) for w in self.vsweep_widgets: w.set_sensitive(False) self.freq.set_sensitive(False) self.run_button.set_sensitive(False) def run_test(self, button): # Disable all control widgets during sweep self.run_button.set_sensitive(False) self.action_filesave.set_sensitive(False) for w in self.measurement_widgets: w.set_sensitive(False) for w in self.freq_sweep_widgets: w.set_sensitive(False) for w in self.source_widgets: w.set_sensitive(False) for w in self.filter_widgets: w.set_sensitive(False) for w in self.vsweep_widgets: w.set_sensitive(False) self.freq.set_sensitive(False) self.x = [] self.y = [] # 30, 80, LPI, RPI filters = [False, False, False, False] filters[0] = self.f30k.get_active() filters[1] = self.f80k.get_active() filters[2] = self.lpi.get_active() filters[3] = self.rpi.get_active() #print(filters) amp = self.source.get_value() strtf = self.start_freq.get_value() stopf = self.stop_freq.get_value() num_steps = self.steps.get_value_as_int() step_size = 10**(1.0/num_steps) strt_dec = math.floor(math.log10(strtf)) stop_dec = math.floor(math.log10(stopf)) meas = self.meas_combo.get_active() units = self.units_combo.get_active() lsteps = [] vsteps = [] if ((meas < 4) and (meas >= 0)): decs = math.log10(stopf/strtf) npoints = int(decs*num_steps) for n in range(npoints + 1): lsteps.append(strtf*10.0**(float(n)/float(num_steps))) self.a.set_xlim((lsteps[0]*10**(-2.0/10.0), lsteps[-1]*10**(2.0/10.0))) self.a.set_xscale('log') elif (meas == 4): start_amp = self.start_v.get_value() stop_amp = self.stop_v.get_value() num_vsteps = self.stepsv.get_value() vsteps = np.linspace(start_amp, stop_amp, num_vsteps) amp_buf = ((stop_amp - start_amp)*0.1)/2.0 print(amp_buf) self.a.set_xlim(((start_amp - amp_buf), (stop_amp + amp_buf))) self.a.set_xscale('linear') # print(start_amp) # print(stop_amp) # print(num_vsteps) center_freq = self.freq.get_value() # center freq... self.measurements = [amp, filters, meas, units, self.meas_string, self.units_string] if ((meas == 0) or (meas == 1)): #pass pt = self.send_measurement(meas, units, center_freq, amp, filters, ratio = 2) elif ((meas == 2) or (meas == 3)): pt = self.send_measurement(meas, units, center_freq, amp, filters) #print(pt) pt = self.send_measurement(meas, units, center_freq, amp, filters, ratio = 1) #print("PT: %s" % pt) elif (meas == 4): pt = self.send_measurement(meas, units, center_freq, start_amp, filters, ratio = 2) if ((meas < 4) and (meas >= 0)): for i in lsteps: meas_point = self.send_measurement(meas, units, i, amp, filters) self.x.append(float(i)) self.y.append(float(meas_point)) print(float(meas_point)) self.update_plot(self.x, self.y) # plot new measures #print(meas_point) elif (meas == 4): #pass for v in vsteps: meas_point = self.send_measurement(meas, units, center_freq, v, filters) self.x.append(v) self.y.append(float(meas_point)) print("in: %f, out %f" % (v, float(meas_point))) self.update_plot(self.x, self.y) for w in self.measurement_widgets: w.set_sensitive(True) for w in self.filter_widgets: w.set_sensitive(True) if ((meas < 4) and (meas >= 0)): for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.source_widgets: w.set_sensitive(True) if (meas == 4): for w in self.vsweep_widgets: w.set_sensitive(True) if (meas > 1): self.freq.set_sensitive(True) self.run_button.set_sensitive(True) self.action_filesave.set_sensitive(True) def update_plot(self, x, y): if (len(self.plt) < 1): self.plt = self.a.plot(x, y, marker = 'x') self.plt[0].set_data(x, y) ymin = min(y) ymax = max(y) # if (ymin == 0.0): # ymin = -0.01 # if (ymax == 0.0): # ymax = 0.01 sep = abs(ymax - ymin) sep = sep/10.0 if (sep == 0.0): sep = 0.01 #self.a.set_ylim((ymin - abs(ymin*0.10), ymax + abs(ymax*0.10))) self.a.set_ylim((ymin - abs(sep), ymax + abs(sep))) self.canvas.draw() def init_hp8903(self): self.gpib_dev.flush_input() # Arbitrary but simple measurement to check device self.gpib_dev.write("FR1000.0HZAP0.100E+00VLM1LNL0LNT3") status, meas = self.gpib_dev.read(msg_len = 12, timeout = 5000) if (status): print(meas) else: print("Failed to initialize HP8903!") print(status, meas) return(False) return(True) def send_measurement(self, meas, unit, freq, amp, filters, ratio = 0): # Store parameters for saving after any measure #self.measurements = [amp, filters, meas, unit] measurement = "" meas_unit = "" if (filters[0]): fs1 = "L1" elif (filters[1]): fs1 = "L2" else: fs1 = "L0" if (filters[2]): fs2 = "H1" elif (filters[3]): fs2 = "H2" else: fs2 = "H0" if ((meas == 0) or (meas == 2)): measurement = "M3" elif ((meas == 1) or (meas == 3) or (meas == 4)): measurement = "M1" if (unit == 0): meas_unit = "LN" elif (unit == 1): meas_unit = "LG" source_freq = ("FR%.4EHZ" % freq) source_ampl = ("AP%.4EVL" % amp) filter_s = fs1 + fs2 rat = "" if (ratio == 1): rat = "R1" elif (ratio == 2): rat = "R0" #payload = source_freq + source_ampl + "M3LN" + filter_s + "LNT3" payload = source_freq + source_ampl + measurement + filter_s + meas_unit + rat + "T3" #print(payload) #print("FR%.4EHZAP1VLM1LNL0LNT3" % freq) #print("FR%.4EHZAP%.4EVLM3LNL0LNT3" % (freq, amp)) #self.ser.write(("FR%.4EHZAP%.4EVLM3LNL2LNT3" % (freq, amp))) # Send and read measurement via GPIB controller self.gpib_dev.write(payload) status, samp = self.gpib_dev.read(timeout = 2500) if (status): sampf = float(samp) else: sampf = np.NAN print("Failed to get sample") if (sampf > 4.0e9): print(("Error: %s" % samp[4:6]) + " " + HP8903_errors[int(samp[4:6])]) samp = np.NAN self.status_bar.push(0, "Freq: %f, Amp: %f, Return: %f, GPIB: %s" % (freq, amp, sampf, payload)) return(samp) def save_data(self, button): fname = datetime.now().strftime("%Y-%m-%d-%H%M%S") fid = open(fname + '.txt', 'w') # Write source voltage info source_v = str(self.measurements[0]) fid.write("# Measurement: " + self.measurements[4] + "\n") fid.write("# Source Voltage: " + source_v + " V RMS\n") # write filter info for n, f in enumerate(self.measurements[1]): if f: fid.write("# " + HP8903_filters[n] + " active\n") fid.write("# Frequency (Hz) " + self.measurements[5] + "\n") n = np.array([np.array(self.x), np.array(self.y)]) np.savetxt(fid, n.transpose(), fmt = ["%f", "%f"]) fid.close() def freq_callback(self, spinb): if (self.start_freq.get_value() > self.stop_freq.get_value()): self.start_freq.set_value(self.stop_freq.get_value()) def volt_callback(self, spinb): if (self.start_v.get_value() > self.stop_v.get_value()): self.start_v.set_value(self.stop_v.get_value()) # 30k/80k toggle def filter1_callback(self, cb): if (cb.get_active()): if (cb.get_label() == "30 kHz LP"): self.f80k.set_active(False) elif (cb.get_label() == "80 kHz LP"): self.f30k.set_active(False) # left plugin/right plugin toggle def filter2_callback(self, cb): if (cb.get_active()): if (cb.get_label() == "Left Plug-in filter"): self.rpi.set_active(False) elif (cb.get_label() == "Right Plug-in filter"): self.lpi.set_active(False) def on_menu_file_quit(self, widget): if (self.gpib_dev): self.gpib_dev.close() Gtk.main_quit() def meas_changed(self, widget): meas_ind = self.meas_combo.get_active() if (meas_ind == 0): self.units_combo.set_model(self.thd_units_store) self.units_combo.set_active(0) self.a.set_ylabel("THD+n (%)") self.a.set_xlabel("Frequency (Hz)") self.canvas.draw() self.freq.set_sensitive(False) self.source.set_sensitive(True) for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.vsweep_widgets: w.set_sensitive(False) elif (meas_ind == 1): self.units_combo.set_model(self.ampl_units_store) self.units_combo.set_active(0) self.a.set_ylabel("AC Level (V RMS)") self.a.set_xlabel("Frequency (Hz)") self.canvas.draw() self.freq.set_sensitive(False) self.source.set_sensitive(True) for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.vsweep_widgets: w.set_sensitive(False) elif (meas_ind == 2): self.units_combo.set_model(self.thdr_units_store) self.units_combo.set_active(0) self.a.set_ylabel("THD+n Ratio (%)") self.a.set_xlabel("Frequency (Hz)") self.canvas.draw() self.freq.set_sensitive(True) self.source.set_sensitive(True) for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.vsweep_widgets: w.set_sensitive(False) elif (meas_ind == 3): self.units_combo.set_model(self.amplr_units_store) self.units_combo.set_active(0) self.a.set_ylabel("AC Level Ratio (%)") self.a.set_xlabel("Frequency (Hz)") self.canvas.draw() self.freq.set_sensitive(True) self.source.set_sensitive(True) for w in self.freq_sweep_widgets: w.set_sensitive(True) for w in self.vsweep_widgets: w.set_sensitive(False) elif (meas_ind == 4): self.units_combo.set_model(self.optlvl_units_store) self.units_combo.set_active(0) self.a.set_ylabel("Output Level (V)") self.a.set_xlabel("Input Level (V)") self.canvas.draw() self.freq.set_sensitive(True) self.source.set_sensitive(False) for w in self.freq_sweep_widgets: w.set_sensitive(False) for w in self.vsweep_widgets: w.set_sensitive(True) def units_changed(self, widget): meas_ind = self.meas_combo.get_active() units_ind = self.units_combo.get_active() #print("meas ind: %d units ind: %d" % (meas_ind, units_ind)) # Set units on plot meas = "" if (meas_ind == 0): meas = "THD+n " if (units_ind == 0): meas += "(%)" self.units_string = "%" elif (units_ind == 1): meas += "(dB)" self.units_string = "dB" elif (meas_ind == 1): meas = "AC Level " if (units_ind == 0): meas += "(V RMS)" self.units_string = "V RMS" elif (units_ind == 1): meas += "(dB V)" self.units_string = "dB V" elif (meas_ind == 2): meas = "THD+n (Ratio) " if (units_ind == 0): meas += "(%)" self.units_string = "%" elif (units_ind == 1): meas += "(dB)" self.units_string = "dB" elif (meas_ind == 3): meas = "AC Level (Ratio) " if (units_ind == 0): meas += "(%)" self.units_string = "%" elif (units_ind == 1): meas += "(dB)" self.units_string = "dB" # Save text info about units self.meas_string = meas # Updated plot self.a.set_ylabel(meas) self.canvas.draw() # menu bar junk def create_ui_manager(self): uimanager = Gtk.UIManager() # Throws exception if something went wrong uimanager.add_ui_from_string(UI_INFO) # Add the accelerator group to the toplevel window accelgroup = uimanager.get_accel_group() self.add_accel_group(accelgroup) return uimanager
class MyWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self,title="Echelle Reduction GUI") ## setting up canvase for plotting ### #self.box = Gtk.EventBox() self.set_default_size(1000,700) self.f = Figure(figsize=(5,7), dpi=100) self.b = self.f.add_subplot(212) # 1D self.c = self.f.add_subplot(231) # PHD self.e = self.f.add_subplot(233) # orders self.a = self.f.add_subplot(232) # raw self.e.tick_params(axis='both',labelsize=6) self.e.set_title("orders") self.a.tick_params(axis='both', labelsize=7) self.a.set_title("2D raw data") self.b.set_title("1D extracted data") self.b.set_xlabel('pixels') self.b.set_ylabel('intensity') self.b.tick_params(axis='both', labelsize=7) self.c.set_title('PHD') self.c.tick_params(axis='both', labelsize=7) self.canvas = FigureCanvas(self.f) # menue bar menubar = Gtk.MenuBar() filem = Gtk.MenuItem("File") filemenu = Gtk.Menu() filem.set_submenu(filemenu) open = Gtk.MenuItem("Open") open.connect('activate',self.on_file_clicked) filemenu.append(open) menubar.append(filem) #savesub = Gtk.Menu() #savesub.append() exit = Gtk.MenuItem('Exit') exit.connect('activate',Gtk.main_quit) filemenu.append(exit) menubox = Gtk.Box( orientation = Gtk.Orientation.VERTICAL) menubox.pack_start(menubar,False,False,0) #open.connect("open", self.on_file_clicked) # Navigtion toolbar stuff toolbar = NavigationToolbar(self.canvas, self) main_box = Gtk.Box( orientation = Gtk.Orientation.VERTICAL) self.add(main_box) # status bar self.statusbar = Gtk.Statusbar() context_id=self.statusbar.get_context_id("stat bar example") self.statusbar.push(0,'Please Open 2D Fits Data File') # button box vbutton_box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL) self.button1 = Gtk.ToggleButton(label='Raw count rate') self.button1.connect("toggled", self.on_button1_clicked, context_id) self.button2 = Gtk.Button('Filter PHD') self.button2.connect("clicked",self.on_button2_clicked,context_id) self.button3 = Gtk.ToggleButton(label='Fit 1D Gauss') self.button3.set_active(False) self.button3.connect("toggled", self.on_button3_clicked, context_id) self.orderbutton = Gtk.ToggleButton(label='Remove Orders') self.orderbutton.connect("toggled",self.orderbutton_clicked,context_id) self.buttonbg = Gtk.Button('Airglow') self.buttonbg.connect('clicked', self.buttonbg_clicked, context_id) vbutton_box.pack_start(self.button1,True,True, 0) vbutton_box.pack_start(self.button2,True, True, 0) vbutton_box.pack_start(self.button3,True, True, 0) vbutton_box.pack_start(self.orderbutton,True,True,0) vbutton_box.pack_start(self.buttonbg,True,True,0) # packing in main_box main_box.pack_start(self.statusbar, False,False,0) main_box.pack_start(self.canvas, True, True, 0) main_box.pack_start(vbutton_box,False,False,0) main_box.pack_start(toolbar, False, False, 0) main_box.pack_start(menubox,False,False,0) # ### file selector window ### def on_file_clicked(self, widget): dialog = Gtk.FileChooserDialog("Please Choose a File", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name('fits Files') filter.add_mime_type('fits') filter.add_pattern('*.fits') dialog.add_filter(filter) response = dialog.run() if response == Gtk.ResponseType.OK : fname = dialog.get_filename() global _File _File = fname #print "open file" + fname self.statusbar.push(0,'Opened File:' + fname) dialog.destroy() self.open_file(_File) elif response == Gtk.ResponseType.CANCEL: dialog.destroy() # opening fits file def open_file(self,_File): #this opens up the fits file hdulist = fits.open(_File) self.targname = hdulist[0].header['targname'] #this picks out the actual data from fits file, and turns it into numpy array #self.scidata = hdulist['sci',2].data self.scidata = hdulist['sci',1].data hdulist.close() scidata = self.scidata targname = self.targname self.science(scidata,targname) def science(self,scidata,targname): # sends 2d data to my gui plotting funtion self.update_plot(scidata) #self.scidata = scidata ### smashing 2D data into 1D to view orders as peaks #### # filling in y with the sums of the rows of scidata y=[] for i in range(0,len(scidata[0])): t = np.sum(scidata[i,:]) y.append(t) # making an x axis with same dementions as y x = np.linspace(0,len(scidata[0]), num = len(scidata[0])) self.x = x #reversing x for the sake of the visualization xrev = x[::-1] # the orders seem to blend around lyman alpha so i have to select # the biggest chunk I could use to get a delta y between the # orders. i wil have to make this selectable on the GUI some how chunk = [0,500] minv = chunk[0] maxv = chunk[1] #drawing box around my chunk to check makesure I have a good portion #plt.hself.lines(chunk,[-1000],[5000],color='r') #plt.vself.lines([-1000,5000],minv,maxv,color='r') #plt.show() #cutting out the chunk of data that i selected xchunk = x[max(x)-chunk[1]:max(x)] index1 = int(max(x)-chunk[1]) index2 = int(max(x)) ychunk = y[index1:index2] #reversing x for the sake of the plot xrevchunk = xchunk[::-1] #plt.figure(figsize=(7.5,8.4)) # # using scipy.signal.find_peaks_cwt() to find centers of orders. this required scipy version .11.0 or greater peakind=signal.find_peaks_cwt(ychunk,np.arange(3,15)) # plotting chunk of 1D data with self.lines through the centers of the orders to double check how the peak finder did self.lines=xchunk[peakind] revlines=self.lines[::-1] lines = self.lines self.xchunk = xchunk self.ychunk = ychunk self.update_ordersplot(ychunk,xchunk,self.lines) ### fake PDH stuff ### (fake data for now) PHDfake = '/home/rachel/codes/chesstest.fits' hdu = fits.open(PHDfake) PHD = hdu[1].data['PHD'] self.update_PHDplot(PHD) hdu.close() self.dragbox=[] ### extraction of orders ### # find w, the widths of the orders (difference between peaks) w=[] for i in range(1,len(peakind)): t=peakind[i]-peakind[i-1] w.append(t) # i have to add an extra w at the end of the array to make it the right size i (hopefully this is kosher) maxw=max(w)-4 w.append(maxw) self.w = w ### making arrays of 1s ans 0s and extracting the 1d orders by matrix multiplication #def extraction(self, peakind,x,scidata): zeros=np.zeros((len(x),1)) index = range(0,len(self.w)) reindex = index[::-1] global oneDorders oneDorders = {} for i in reindex: zeros1=np.copy(zeros) zeros1[ len(scidata[0]) - (np.sum(w[(i):18])) : len(scidata[0]) - np.sum(w[(i+1):18]) ] = 1 twoD = scidata*zeros1 # making 2d orders in to 1d orders Y=[] for j in range(0,len(scidata[0])): t = np.sum(twoD[:,j]) Y.append(t) # placing 1d orders in dictionary called oneDorders oneDorders[str(i)]=Y # sending plotting info to update_1dplot for gui (for now using just on order until cross coralation is added to script self.x = np.linspace(0,len(scidata[0]), num = len(scidata[0])) self.odo=oneDorders['16'] odo = self.odo[:] self.update_1dplot(odo,x) self.save_pickle(oneDorders) def update_plot(self, scidata): #self.a.cla() #cbar.self.a.cla() self.plt= self.a.imshow(scidata, vmin = 0, vmax = 255,origin = 'lower') cbar=self.f.colorbar(self.plt,shrink=.84,pad=0.01) self.canvas.draw() def update_ordersplot(self,ychunk,xchunk,lines): ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line self.e.cla() self.e.set_title("orders") self.plt=self.e.plot(ychunk,xchunk) self.e.hlines(lines,0,2000,color='purple',label='centers') self.canvas.draw() def update_1dplot(self,odo,x): ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line self.b.cla() self.plt=self.b.plot(x,self.odo) self.canvas.draw() def update_PHDplot(self,PHD): ## if you dont want to new airglow subtracted data to over plot but to replot, uncomment this next line self.c.cla() self.c.set_title('PHD') self.plt=self.c.hist(PHD,bins=80,histtype='stepfilled') self.canvas.draw() ## airglow button ## def buttonbg_clicked(self, widget, data): dialog = Gtk.FileChooserDialog("Please Choose Airglow File", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name('fits Files') filter.add_mime_type('fits') filter.add_pattern('*.fits') dialog.add_filter(filter) response = dialog.run() if response == Gtk.ResponseType.OK : fname = dialog.get_filename() global _AirglowFile _AirglowFile = fname #print "open file" + fname #self.statusbar.push(0,'Opened File:' + fname) dialog.destroy() self.open_Airglowfile(_AirglowFile) elif response == Gtk.ResponseType.CANCEL: dialog.destroy() # opening aiglow fits file def open_Airglowfile(self,_AirglowFile): #this opens up the fits file hdulist = fits.open(_AirglowFile) ## this line will need to be used for real data #self.targname = hdulist[0].header['targname'] #targname = self.targname ## but for now targname = 'HD128627J' # this picks out the actual data from fits file, and turns it into numpy array self.glowdata = hdulist[0].data hdulist.close() # simple subtraction of aurglow image from science image scidata = self.scidata - self.glowdata self.science(scidata,targname) ## gauss fitting button def on_button3_clicked(self, widget, data): self.statusbar.push(data,'Ready to fit. Click on both sides of the emission feature you wish to fit') self.xdata = [] def onclick(event): if self.button3.get_active(): self.xdata.append(event.xdata) self.statusbar.push(data,'one more click...') if len(self.xdata) == 2: self.statusbar.push(data,'Ready to fit. Click on both sides of the emission feature you wish to fit') xdata=self.xdata self.gauss_fit(xdata) # mouse click event on 1d cid = self.canvas.mpl_connect('button_press_event', onclick) if [self.button3.get_active()] == [False]: self.statusbar.push(0,'Opened File:' + _File) ### guass fitting ### def gauss_fit(self,xdata): x = list(self.x) xg=[] xg.append(int(xdata[0])) xg.append(int(xdata[1])) xg1 = min(xg) xg2 = max(xg) xgauss = x[xg1:xg2] ygauss = self.odo[xg1:xg2] right = ygauss[len(xgauss)-4:len(xgauss)] left = ygauss[0:4] # background subtraction averight = sum(right)/len(right) aveleft = sum(left)/len(left) bg_y = [averight,aveleft] rightx = xgauss[len(xgauss)-4:len(xgauss)] leftx = xgauss[0:4] averightx = sum(rightx)/len(rightx) aveleftx = sum(leftx)/len(leftx) bg_x = [averightx,aveleftx] m,b = np.polyfit(bg_x,bg_y,1) slopex = [i * m for i in xgauss] bg_fit = slopex+b # makeing a model gauss def gauss(xgauss,MAX,mu,sigma): return MAX*np.exp(-(xgauss-mu)**2/(2.*sigma**2)) avex = sum(xgauss)/len(xgauss) guess = [1.,avex,1.] # plugging in model to matplotlibs curve_fit() coeff, var_matrix = curve_fit(gauss,xgauss,ygauss-bg_fit,p0=guess) fit = gauss(xgauss, *coeff) sigma = coeff[2] FWHM = sigma*2*np.sqrt(2*np.log(2)) FWHM = round(FWHM,2) fitplot = plt.plot(xgauss,ygauss,color='k') plt.plot(xgauss,fit+bg_fit,color = 'b',linewidth = 1.5) xpos = xgauss[0]+.01*(coeff[1]-xgauss[0]) strFWHM = str(FWHM) plt.text(xpos,.9*max(ygauss),'FWHM = '+strFWHM+'',color = 'purple',fontweight = 'bold') center = str(round(coeff[1],2)) plt.text(xpos,.95*max(ygauss),'Center = '+center+'',color = 'green',fontweight = 'bold') plt.plot(xgauss,bg_fit,'r--') plt.show() self.xdata = [] ### count rate button def on_button1_clicked(self, widget,data): if self.button1.get_active(): self.statusbar.push(data,'Use zoom feature in navigation bar to select count rate region') else: self.statusbar.push(0,'Opened File:' + _File) def onclick2(event): if self.button1.get_active(): self.dragbox = [] #print event.xdata, event.ydata self.dragbox.append(event.xdata) self.dragbox.append(event.ydata) def offclick2(event): # print event.xdata, event.ydata if self.button1.get_active(): self.dragbox.append(event.xdata) self.dragbox.append(event.ydata) dragbox = self.dragbox self.cnt_rate(dragbox,data) cid2 = self.canvas.mpl_connect('button_press_event',onclick2 ) cid3 = self.canvas.mpl_connect('button_release_event',offclick2 ) ### count rate ##### def cnt_rate(self,dragbox,data): # fake exposure time in seconds datafake = '/home/rachel/codes/chesstest.fits' hdu = fits.open(datafake) exptime = hdu[0].header['EXPOSURE'] dragbox = [int(x) for x in dragbox] cntbox = self.scidata[dragbox[0]:dragbox[2],1024-dragbox[1]:1024-dragbox[3]] totpix = np.size(cntbox) cntrate = np.sum(cntbox)/exptime totpix = str(totpix) cntrate = str(cntrate) self.statusbar.push(data,'count rate in box = '+cntrate+' cnt/sec, pixels in box = '+totpix+'') return cntrate #### phd filter button ## def on_button2_clicked(self,widget,data): self.phd_window = Gtk.MessageDialog(image = None) self.phd_window.set_size_request(500,100) self.phd_window.move(400, 300) #self.phd_window.connect("delet_event",lambda w,e:) mainbox = self.phd_window.get_content_area() self.phd_window.add(mainbox) thebox = Gtk.HBox(False, 0) label = Gtk.Label("Discard PHD between") label2 = Gtk.Label('and') label.show() label2.show() self.okbutton = Gtk.Button('Okay') self.okbutton.connect('clicked',self.phd_entry_button) self.entry = Gtk.Entry() self.entry.set_activates_default(True) self.entry2 = Gtk.Entry() self.entry2.set_activates_default(True) self.entry.show() self.entry2.show() self.okbutton.show() thebox.pack_start(label,False,False,0) thebox.pack_start(self.entry,False,False,0) thebox.pack_start(label2,False,False,0) thebox.pack_start(self.entry2,False,False,0) mainbox.pack_start(thebox,True,True,0) mainbox.pack_start(self.okbutton,True,False,0) mainbox.show() thebox.show() self.phd_window.show() def phd_entry_button(self,widget): minphd = self.entry.get_text() maxphd = self.entry2.get_text() phdfilt = [minphd,maxphd] self.phd_window.destroy() self.filter_phd(phdfilt) # ### phd filter function ## def filter_phd(self, phdfilt): phdfilt = [int(x) for x in phdfilt] fakedata = '/home/rachel/codes/chesstest.fits' hdu = fits.open(fakedata) PHD = hdu[1].data['PHD'] PHD = np.array(PHD) data = hdu[1].data newdata = data[(PHD > phdfilt[0]) & (PHD < phdfilt[1])] plt.subplot(221) oldplot = plt.plot(data['X'],data['Y'],linestyle = '',marker = '.') plt.subplot(222) newplot = plt.plot(newdata['X'],newdata['Y'],linestyle = '',marker = '.') plt.show() ### mouse click on remove orders ### def orderbutton_clicked(self, widget, data): self.statusbar.push(data,'click on the order that you want to exclude.') self.remove = [] lines = self.lines def onclick_order(event): if self.orderbutton.get_active(): self.remove.append(event.ydata) remove = self.remove self.remove_orders(remove,lines) cid4 = self.canvas.mpl_connect('button_press_event',onclick_order) if [self.orderbutton.get_active()] == [False]: self.statusbar.push(0,'Opened File:' + _File) ### removing orders def remove_orders(self,remove,lines): bad_orders = [] bad_orders_index = [] for i in range(0,len(remove)): bad = min(lines, key=lambda x:abs(x-remove[i])) bad_orders.append(bad) bad_orders_index.append( np.where(lines == bad) ) newlines = filter(lambda lines: lines not in bad_orders,lines) xchunk = self.xchunk ychunk = self.ychunk self.e.cla() lines = newlines self.update_ordersplot(ychunk,xchunk,lines) #for key in oneDorders.keys(): print key print 'number or original orders',len(oneDorders) self.new_dictionary(bad_orders_index) def new_dictionary(self,bad_orders_index): maxint = len(oneDorders) for i in range(0,len(bad_orders_index)): num = int(bad_orders_index[i][0]) if oneDorders.get(str(num),None): oneDorders.pop(str(num)) for key in oneDorders.keys(): k = int(key) if k > num: oneDorders[str(k-1)] = oneDorders.get(key) if k == maxint-1: oneDorders.pop(key) # print 'corrected number of orders',len(oneDorders) self.save_pickle(oneDorders) #for key in oneDorders.keys(): print key def save_pickle(self, ondDorders): order_dict = oneDorders now = datetime.datetime.now() date = now.strftime("%m_%d_%Y") targname = str(self.targname) pickle.dump(order_dict,open(''+targname+'_1D_'+date+'.p','wb'))
class MyWindow(Gtk.Window): def __init__(self): #Janela Gtk.Window.__init__(self, title="Testando") self.set_resizable(False) self.set_size_request(600, 600) self.set_position(Gtk.WindowPosition.CENTER) self.set_border_width(10) principal = Gtk.Box(spacing=10) principal.set_homogeneous(False) vbox_left = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox_left.set_homogeneous(False) vbox_right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox_right.set_homogeneous(False) ### Boxes que ficarão dentro da vbox_left ### hbox_MDIST = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_MDIST.set_homogeneous(False) hbox_labels = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_labels.set_homogeneous(False) hbox_RDIST = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_RDIST.set_homogeneous(True) hbox_TOTEX = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_TOTEX.set_homogeneous(True) hbox_MVELO = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_MVELO.set_homogeneous(True) #criando os elementos das boxes Mdist, Labels, Rdist, TOTEX, Mvelo ##MDIST## label_Mdist = Gtk.Label('MDIST') self.entry_Mdist = Gtk.Entry() self.entry_Mdist.set_editable(False) self.entry_Mdist.set_max_length(max=8) ##LABELS## label_vazia = Gtk.Label(' ') label_AP = Gtk.Label('AP') label_ML = Gtk.Label('ML') label_TOTAL = Gtk.Label('TOTAL') ##RDIST## label_Rdist = Gtk.Label('RDIST') self.entry_Rdist_AP = Gtk.Entry() self.entry_Rdist_AP.set_editable(False) self.entry_Rdist_AP.set_max_length(max=8) self.entry_Rdist_ML = Gtk.Entry() self.entry_Rdist_ML.set_editable(False) self.entry_Rdist_ML.set_max_length(max=8) self.entry_Rdist_TOTAL = Gtk.Entry() self.entry_Rdist_TOTAL.set_editable(False) self.entry_Rdist_TOTAL.set_max_length(max=8) ##TOTEX## label_TOTEX = Gtk.Label('TOTEX') self.entry_TOTEX_AP = Gtk.Entry() self.entry_TOTEX_AP.set_editable(False) self.entry_TOTEX_AP.set_max_length(max=8) self.entry_TOTEX_ML = Gtk.Entry() self.entry_TOTEX_ML.set_editable(False) self.entry_TOTEX_ML.set_max_length(max=8) self.entry_TOTEX_TOTAL = Gtk.Entry() self.entry_TOTEX_TOTAL.set_editable(False) self.entry_TOTEX_TOTAL.set_max_length(max=8) ##MVELO## label_MVELO = Gtk.Label('MVELO') self.entry_MVELO_AP = Gtk.Entry() self.entry_MVELO_AP.set_editable(False) self.entry_MVELO_AP.set_max_length(max=8) self.entry_MVELO_ML = Gtk.Entry() self.entry_MVELO_ML.set_editable(False) self.entry_MVELO_ML.set_max_length(max=8) self.entry_MVELO_TOTAL = Gtk.Entry() self.entry_MVELO_TOTAL.set_editable(False) self.entry_MVELO_TOTAL.set_max_length(max=8) #colocando cada elemento dentro da sua box hbox_MDIST.pack_start(label_Mdist,True,True,0) hbox_MDIST.pack_start(self.entry_Mdist,True,True,0) hbox_labels.pack_start(label_vazia,True,True,0) hbox_labels.pack_start(label_AP,True,True,0) hbox_labels.pack_start(label_ML,True,True,0) hbox_labels.pack_start(label_TOTAL,True,True,0) hbox_RDIST.pack_start(label_Rdist,True,True,0) hbox_RDIST.pack_start(self.entry_Rdist_AP,True,True,0) hbox_RDIST.pack_start(self.entry_Rdist_ML,True,True,0) hbox_RDIST.pack_start(self.entry_Rdist_TOTAL,True,True,0) hbox_TOTEX.pack_start(label_TOTEX, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_AP, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_ML, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_TOTAL, True, True, 0) hbox_MVELO.pack_start(label_MVELO, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_AP, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_ML, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_TOTAL, True, True, 0) #colocando as boxes pequenas dentro das box vbox_left vbox_left.pack_start(hbox_MDIST, True, True, 0) vbox_left.pack_start(hbox_labels, True, True, 0) vbox_left.pack_start(hbox_RDIST, True, True, 0) vbox_left.pack_start(hbox_TOTEX, True, True, 0) vbox_left.pack_start(hbox_MVELO, True, True, 0) #elementos da vbox_right) #Notebook self.notebook = Gtk.Notebook() self.fig = plt.figure() self.axis = self.fig.add_subplot(111) self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(500, 500) self.page1 = Gtk.Box() self.page1.set_border_width(10) self.page1.add(self.canvas) self.notebook.append_page(self.page1, Gtk.Label('Gráfico do CoP')) ##aqui fica a segunda janela do notebook self.fig2 = plt.figure() self.axis2 = self.fig2.add_subplot(111) self.axis2.set_ylabel('ML') self.axis2.set_xlabel('AP') self.canvas2 = FigureCanvas(self.fig2) self.canvas2.set_size_request(500, 500) self.page2 = Gtk.Box() self.page2.set_border_width(10) self.page2.add(self.canvas2) self.notebook.append_page(self.page2, Gtk.Label('Gráfico na frequência')) #criando os botoes hbox_botoes = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_botoes.set_homogeneous(True) self.button1 = Gtk.Button(label="Capturar") self.button1.connect("clicked", self.on_button1_clicked) self.button2 = Gtk.Button(label="Processar") self.button2.connect("clicked", self.on_button2_clicked) #self.set_resizable(True #colocando os botoes nas boxes hbox_botoes.pack_start(self.button1, True, True,0) hbox_botoes.pack_start(self.button2, True, True, 0) vbox_right.pack_start(self.notebook, True, True, 0) vbox_right.pack_start(hbox_botoes, True, True, 0) # colocando as boxes verticais dentro da box principal principal.pack_start(vbox_left, True, True, 0) principal.pack_start(vbox_right, True, True, 0) #Adicionano os elementos na box exterior self.add(principal) def on_button1_clicked(self, widget): global APs, MLs pos_AP = [] pos_ML = [] AP_aux = [] ML_aux = [] ref_arquivo_x_diferente = open("/home/thales/wiibalance/cwiid/python/xdiferente.txt", "r") ref_arquivo_y_diferente = open("/home/thales/wiibalance/cwiid/python/ydiferente.txt", "r") for linhax in ref_arquivo_x_diferente: AP_aux.append(ref_arquivo_x_diferente.readline(13)) for linhay in ref_arquivo_y_diferente: ML_aux.append(ref_arquivo_y_diferente.readline(13)) ref_arquivo_x_diferente.close() ref_arquivo_y_diferente.close() for i in range(len(AP_aux)): e = AP_aux[i] try: APs.append(float(e)) except: #print('posicao de x que nao conseguiu pegar o valor real:', i) pos_AP.append(i) for j in range(len(ML_aux)): e = ML_aux[j] try: MLs.append(float(e)) except: #print('posicao de y que nao conseguiu pegar o valor real:', j) pos_ML.append(j) #APs, MLs = calc.geraNumeroAleatorio(-3, 7, -4, 6, 40) if calc.diferentes(APs,MLs): if len(APs) > len(MLs): APs = calc.delEleAP(APs, pos_ML) else: MLs =calc.delEleML(MLs, pos_AP) max_absoluto_AP = calc.valorAbsoluto(min(APs), max(APs)) max_absoluto_ML = calc.valorAbsoluto(min(MLs), max(MLs)) print('max_absoluto_AP:',max_absoluto_AP,'max_absoluto_ML:',max_absoluto_ML) self.axis.clear() self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.axis.set_xlim(-max_absoluto_AP, max_absoluto_AP) self.axis.set_ylim(-max_absoluto_ML, max_absoluto_ML) self.axis.plot(APs, MLs,'-',color='r') self.canvas.draw() def on_button2_clicked(self, widget): global APs, MLs APs, MLs = calc.geraAP_ML(APs, MLs) dis_resultante_total = calc.distanciaResultante(APs, MLs) dis_resultante_AP = calc.distanciaResultanteParcial(APs) dis_resultante_ML = calc.distanciaResultanteParcial(MLs) dis_media = calc.distanciaMedia(dis_resultante_total) dis_rms_total = calc.dist_RMS(dis_resultante_total) dis_rms_AP = calc.dist_RMS(dis_resultante_AP) dis_rms_ML = calc.dist_RMS(dis_resultante_ML) totex_total = calc.totex(APs, MLs) totex_AP = calc.totexParcial(APs) totex_ML = calc.totexParcial(MLs) mvelo_total = calc.mVelo(totex_total, 20) mvelo_AP = calc.mVelo(totex_AP, 20) mvelo_ML = calc.mVelo(totex_ML, 20) self.entry_Mdist.set_text(str(dis_media)) self.entry_Rdist_TOTAL.set_text(str(dis_rms_total)) self.entry_Rdist_AP.set_text(str(dis_rms_AP)) self.entry_Rdist_ML.set_text(str(dis_rms_ML)) self.entry_TOTEX_TOTAL.set_text(str(totex_total)) self.entry_TOTEX_AP.set_text(str(totex_AP)) self.entry_TOTEX_ML.set_text(str(totex_ML)) self.entry_MVELO_TOTAL.set_text(str(mvelo_total)) self.entry_MVELO_AP.set_text(str(mvelo_AP)) self.entry_MVELO_ML.set_text(str(mvelo_ML)) max_absoluto_AP = calc.valorAbsoluto(min(APs), max(APs)) max_absoluto_ML = calc.valorAbsoluto(min(MLs), max(MLs)) print('max_absoluto_AP:', max_absoluto_AP, 'max_absoluto_ML:', max_absoluto_ML) self.axis.clear() self.axis.set_xlim(-max_absoluto_AP, max_absoluto_AP) self.axis.set_ylim(-max_absoluto_ML, max_absoluto_ML) self.axis.plot(APs, MLs,'.-',color='g') self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.canvas.draw()
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 RotationDialog(object): """ This class controls the appearance and signals of the data-rotation dialog. This class pulls the rotation dialog from the Glade file, intilizes the widgets and has methods for the signals defined in Glade. """ def __init__(self, main_window, settings, data, add_layer_dataset, add_feature, redraw_main): """ Initializes the RotationDialog class. Requires the main_window object, the settings object (PlotSettings class) and the data rows to initialize. All the necessary widgets are loaded from the Glade file. A matplotlib figure is set up and added to the scrolledwindow. Two axes are set up that show the original and rotated data. """ self.builder = Gtk.Builder() self.builder.set_translation_domain(i18n().get_ts_domain()) script_dir = os.path.dirname(__file__) rel_path = "gui_layout.glade" abs_path = os.path.join(script_dir, rel_path) self.builder.add_objects_from_file( abs_path, ("dialog_rotation", "adjustment_rotation_dipdir", "adjustment_rotation_dip", "adjustment_rotation_angle")) self.dialog = self.builder.get_object("dialog_rotation") self.dialog.set_transient_for(main_window) self.settings = settings self.data = data self.trans = self.settings.get_transform() self.add_layer_dataset = add_layer_dataset self.add_feature = add_feature self.redraw_main = redraw_main self.adjustment_rotation_dipdir = self.builder.get_object( "adjustment_rotation_dipdir") self.adjustment_rotation_dip = self.builder.get_object( "adjustment_rotation_dip") self.adjustment_rotation_angle = self.builder.get_object( "adjustment_rotation_angle") self.spinbutton_rotation_dipdir = self.builder.get_object( "spinbutton_rotation_dipdir") self.spinbutton_rotation_dip = self.builder.get_object( "spinbutton_rotation_dip") self.spinbutton_rotation_angle = self.builder.get_object( "spinbutton_rotation_angle") self.scrolledwindow_rotate = self.builder.get_object( "scrolledwindow_rotate") self.fig = Figure(dpi=self.settings.get_pixel_density()) self.canvas = FigureCanvas(self.fig) self.scrolledwindow_rotate.add_with_viewport(self.canvas) gridspec = GridSpec(1, 2) original_sp = gridspec.new_subplotspec((0, 0), rowspan=1, colspan=1) rotated_sp = gridspec.new_subplotspec((0, 1), rowspan=1, colspan=1) self.original_ax = self.fig.add_subplot( original_sp, projection=self.settings.get_projection()) self.rotated_ax = self.fig.add_subplot( rotated_sp, projection=self.settings.get_projection()) self.canvas.draw() self.redraw_plot() self.dialog.show_all() self.builder.connect_signals(self) if sys.platform == "win32": translate_gui(self.builder) def run(self): """ Runs the dialog. Called from the MainWindow class. Initializes and shows the dialog. """ self.dialog.run() def on_dialog_rotation_destroy(self, widget): """ Hides the dialog on destroy. When the dialog is destroyed it is hidden. """ self.dialog.hide() def on_button_cancel_rotation_clicked(self, button): """ Exits the rotation dialog and makes no changes to the project. When the user clicks on Cancel the dialog is hidden, and no changes are made to the project structure. """ self.dialog.hide() def on_button_apply_rotate_clicked(self, button): """ Adds the rotated layers to the project. When the user clicks on "apply the rotation", the rotated data is added to the project as new datasets. """ raxis_dipdir = self.spinbutton_rotation_dipdir.get_value() raxis_dip = self.spinbutton_rotation_dip.get_value() raxis = [raxis_dipdir, raxis_dip] raxis_angle = self.spinbutton_rotation_angle.get_value() for lyr_obj in self.data: lyr_type = lyr_obj.get_layer_type() lyr_store = lyr_obj.get_data_treestore() if lyr_type == "plane": dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az = \ self.parse_plane(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("plane") for dipdir, dip, strt in zip(dipdir_az, dips_lst, strat): self.add_feature("plane", store, dipdir, dip, strt) elif lyr_type == "line": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense = \ self.parse_line(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("line") for dipdir, dip, sns in zip(ldipdir_lst, ldips_lst, sense): self.add_feature("line", store, dipdir, dip, sns) elif lyr_type == "smallcircle": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, angle = \ self.parse_line(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("smallcircle") for dipdir, dip, ang in zip(ldipdir_lst, ldips_lst, angle): self.add_feature("smallcircle", store, dipdir, dip, ang) elif lyr_type == "faultplane": rtrn = self.parse_faultplane(lyr_store, raxis, raxis_angle) dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, \ ldips_org, ldipdir_lst, ldips_lst, sense, dipdir_az = rtrn[0], \ rtrn[1], rtrn[2], rtrn[3], rtrn[4], rtrn[5], rtrn[6], rtrn[7], \ rtrn[8], rtrn[9] store, new_lyr_obj = self.add_layer_dataset("faultplane") for dipdir, dip, ldipdir, ldip, sns in zip( dipdir_az, dips_lst, ldipdir_lst, ldips_lst, sense): self.add_feature("faultplane", store, dipdir, dip, ldipdir, ldip, sns) new_lyr_obj.set_properties(lyr_obj.get_properties()) self.dialog.hide() self.redraw_main() def on_spinbutton_rotation_dipdir_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def on_spinbutton_rotation_dip_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def on_spinbutton_rotation_angle_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def convert_lonlat_to_dipdir(self, lon, lat): """ Converts lat-lon data to dip-direction and dip. Expects a longitude and a latitude value. The measurment is forward transformed into stereonet-space. Then the azimut (dip-direction) and diping angle are calculated. Returns two values: dip-direction and dip. """ #The longitude and latitude have to be forward-transformed to get #the corect azimuth angle xy = np.array([[lon, lat]]) xy_trans = self.trans.transform(xy) x = float(xy_trans[0, 0:1]) y = float(xy_trans[0, 1:2]) alpha = np.arctan2(x, y) alpha_deg = np.degrees(alpha) if alpha_deg < 0: alpha_deg += 360 #Longitude and Latitude don't need to be converted for rotation. #The correct dip is the array[1] value once the vector has been #rotated in north-south position. array = mplstereonet.stereonet_math._rotate(np.degrees(lon), np.degrees(lat), alpha_deg * (-1)) gamma = float(array[1]) gamma_deg = 90 - np.degrees(gamma) #If the longitude is larger or small than pi/2 the measurment lies #on the upper hemisphere and needs to be corrected. if lon > (np.pi / 2) or lon < (-np.pi / 2): alpha_deg = alpha_deg + 180 return alpha_deg, gamma_deg def rotate_data(self, raxis, raxis_angle, dipdir, dip): """ Rotates a measurment around a rotation axis a set number of degrees. Expects a rotation-axis, a rotation-angle, a dip-direction and a dip angle. The measurement is converted to latlot and then passed to the mplstereonet rotate function. """ lonlat = mplstereonet.line(dip, dipdir) #Rotation around x-axis until rotation-axis azimuth is east-west rot1 = (90 - raxis[0]) lon1 = np.degrees(lonlat[0]) lat1 = np.degrees(lonlat[1]) lon_rot1, lat_rot1 = mplstereonet.stereonet_math._rotate(lon1, lat1, theta=rot1, axis="x") #Rotation around z-axis until rotation-axis dip is east-west rot2 = -(90 - raxis[1]) lon2 = np.degrees(lon_rot1) lat2 = np.degrees(lat_rot1) lon_rot2, lat_rot2 = mplstereonet.stereonet_math._rotate(lon2, lat2, theta=rot2, axis="z") #Rotate around the x-axis for the specified rotation: rot3 = raxis_angle lon3 = np.degrees(lon_rot2) lat3 = np.degrees(lat_rot2) lon_rot3, lat_rot3 = mplstereonet.stereonet_math._rotate(lon3, lat3, theta=rot3, axis="x") #Undo the z-axis rotation rot4 = -rot2 lon4 = np.degrees(lon_rot3) lat4 = np.degrees(lat_rot3) lon_rot4, lat_rot4 = mplstereonet.stereonet_math._rotate(lon4, lat4, theta=rot4, axis="z") #Undo the x-axis rotation rot5 = -rot1 lon5 = np.degrees(lon_rot4) lat5 = np.degrees(lat_rot4) lon_rot5, lat_rot5 = mplstereonet.stereonet_math._rotate(lon5, lat5, theta=rot5, axis="x") dipdir5, dip5 = self.convert_lonlat_to_dipdir(lon_rot5, lat_rot5) return dipdir5, dip5 def parse_plane(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a plane layer. Expects a TreeStore of a layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ dipdir_org = [] dips_org = [] dipdir_lst = [] dips_lst = [] dipdir_az = [] strat = [] for row in lyr_store: dipdir_org.append(row[0] - 90) dips_org.append(row[1]) #Planes and faultplanes are rotated using their poles dipdir, dip = self.rotate_data(raxis, raxis_angle, row[0] + 180, 90 - row[1]) dipdir_lst.append(dipdir + 90) dipdir_az.append(dipdir + 180) dips_lst.append(90 - dip) strat.append(row[2]) return dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az def parse_line(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a linear or smallcircle layer. Expects a TreeStore of a layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ ldipdir_org = [] ldips_org = [] ldipdir_lst = [] ldips_lst = [] third_col = [] for row in lyr_store: ldipdir_org.append(row[0]) ldips_org.append(row[1]) ldipdir, ldip = self.rotate_data(raxis, raxis_angle, row[0], row[1]) ldipdir_lst.append(ldipdir) ldips_lst.append(ldip) third_col.append(row[2]) return ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, third_col def parse_faultplane(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a faultplane layer. Expects a TreeStore of a faultplane layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ dipdir_org = [] dips_org = [] dipdir_lst = [] dips_lst = [] ldipdir_org = [] ldips_org = [] ldipdir_lst = [] ldips_lst = [] dipdir_az = [] sense = [] for row in lyr_store: dipdir_org.append(row[0] - 90) dips_org.append(row[1]) #Planes and faultplanes are rotated using their poles dipdir, dip = self.rotate_data(raxis, raxis_angle, row[0] + 180, 90 - row[1]) dipdir_lst.append(dipdir + 90) dipdir_az.append(dipdir + 270) dips_lst.append(90 - dip) ldipdir_org.append(row[2]) ldips_org.append(row[3]) ldipdir, ldip = self.rotate_data(raxis, raxis_angle, row[2], row[3]) ldipdir_lst.append(ldipdir) ldips_lst.append(ldip) sense.append(row[4]) return (dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense, dipdir_az) def redraw_plot(self): """ Redraws the plot using the current settings of the dialog's spinbuttons. This method clears the two axes and adds the annotations. The current values of the rotation axis and rotation angle spinbuttons are retrieved. The data is parsed, and the features are then drawn. In addition the rotation-axis is drawn. """ self.original_ax.cla() self.rotated_ax.cla() self.original_ax.grid(False) self.rotated_ax.grid(False) self.original_ax.set_azimuth_ticks([0], labels=["N"]) self.rotated_ax.set_azimuth_ticks([0], labels=["N"]) bar = 0.05 self.original_ax.annotate("", xy=(-bar, 0), xytext=(bar, 0), xycoords="data", arrowprops=dict(arrowstyle="-", connectionstyle="arc3")) self.original_ax.annotate("", xy=(0, -bar), xytext=(0, bar), xycoords="data", arrowprops=dict(arrowstyle="-", connectionstyle="arc3")) self.rotated_ax.annotate("", xy=(-bar, 0), xytext=(bar, 0), xycoords="data", arrowprops=dict(arrowstyle="-", connectionstyle="arc3")) self.rotated_ax.annotate("", xy=(0, -bar), xytext=(0, bar), xycoords="data", arrowprops=dict(arrowstyle="-", connectionstyle="arc3")) raxis_dipdir = self.spinbutton_rotation_dipdir.get_value() raxis_dip = self.spinbutton_rotation_dip.get_value() raxis = [raxis_dipdir, raxis_dip] raxis_angle = self.spinbutton_rotation_angle.get_value() for lyr_obj in self.data: lyr_type = lyr_obj.get_layer_type() lyr_store = lyr_obj.get_data_treestore() if lyr_type == "plane": dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az = \ self.parse_plane(lyr_store, raxis, raxis_angle) self.original_ax.plane(dipdir_org, dips_org, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.rotated_ax.plane(dipdir_lst, dips_lst, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) elif lyr_type == "line": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense = \ self.parse_line(lyr_store, raxis, raxis_angle) self.original_ax.line( ldips_org, ldipdir_org, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) self.rotated_ax.line( ldips_lst, ldipdir_lst, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) elif lyr_type == "smallcircle": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, angle = \ self.parse_line(lyr_store, raxis, raxis_angle) self.original_ax.cone(ldips_org, ldipdir_org, angle, facecolor="None", color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), label=lyr_obj.get_label(), linestyle=lyr_obj.get_line_style()) self.rotated_ax.cone(ldips_lst, ldipdir_lst, angle, facecolor="None", color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), label=lyr_obj.get_label(), linestyle=lyr_obj.get_line_style()) elif lyr_type == "faultplane": rtrn = self.parse_faultplane(lyr_store, raxis, raxis_angle) dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, \ ldips_org, ldipdir_lst, ldips_lst, sense = rtrn[0], rtrn[1], \ rtrn[2], rtrn[3], rtrn[4], rtrn[5], rtrn[6], rtrn[7], rtrn[8] self.original_ax.plane(dipdir_org, dips_org, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.rotated_ax.plane(dipdir_lst, dips_lst, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.original_ax.line( ldips_org, ldipdir_org, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) self.rotated_ax.line( ldips_lst, ldipdir_lst, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) #Plot rotation axis self.original_ax.line(raxis_dip, raxis_dipdir, marker="o", markersize=10, color="#ff0000", markeredgewidth=1, markeredgecolor="#000000", alpha=1, clip_on=False) self.canvas.draw()
class ATWindow(Gtk.Window): def __init__(self, pid): Gtk.Window.__init__(self, title="Auto-tune") self.pid = pid vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) f = matplotlib.figure.Figure() self.axes = f.add_subplot(111) self.axes.set_xlabel('Time (sec.)') self.axes.set_ylabel('Temperature (C)') self.axes.autoscale() self.out_axes = self.axes.twinx() self.out_axes.set_ylabel('OUT (%)') self.out_axes.autoscale() self.axes.grid() self.pv_x = [] self.pv_y = [] self.sv_x = [] self.sv_y = [] self.out_x = [] self.out_y = [] self.pv_plot, = self.axes.plot(self.pv_x, self.pv_y, 'b--') #b self.sv_plot, = self.axes.plot(self.sv_x, self.sv_y, 'k-') #k self.out_plot, = self.out_axes.plot(self.out_x, self.out_y, 'r:') #r self.canvas = FigureCanvas(f) self.canvas.set_size_request(800,600) vbox.add(self.canvas) hbox = Gtk.Box() self.start = Gtk.Button('Start', Gtk.STOCK_EXECUTE) self.start.connect('clicked', self.on_start) self.start.set_sensitive(False) hbox.pack_start(self.start, False, False, 0) self.stop = Gtk.Button('Stop', Gtk.STOCK_STOP) self.stop.connect('clicked', self.on_stop) self.stop.set_sensitive(False) hbox.pack_start(self.stop, False, False, 0) button = Gtk.Button('Close', Gtk.STOCK_CLOSE) button.connect('clicked', self.on_close) hbox.pack_end(button, False, False, 0) vbox.add(hbox) self.add(vbox) self.run = True self.start_at = True self.stop_at = False self.d = self.loop() self.d.addErrback(lambda x: None) self.connect('delete-event', self.on_delete) def on_start(self, widget): self.start_at = True def on_stop(self, widget): self.stop_at = True def on_delete(self, widget, event): self.run = False def on_close(self, widget): self.emit('delete-event', None) self.destroy() @twisted.internet.defer.inlineCallbacks def loop(self): start = time.time() while self.run: try: d = yield self.pid.flags() active = d['AT'] if active: self.stop.set_sensitive(True) self.start.set_sensitive(False) if self.stop_at: yield self.pid.coil('NAT') pv, mult = yield self.pid.holding_read('PV') self.pv_x.append(time.time() - start) self.pv_y.append(pv) self.pv_plot.set_data(self.pv_x, self.pv_y) self.axes.relim() self.axes.autoscale() self.canvas.draw() sv, mult = yield self.pid.holding_read('dSV') self.sv_x.append(time.time() - start) self.sv_y.append(sv) self.sv_plot.set_data(self.sv_x, self.sv_y) self.axes.relim() self.axes.autoscale() self.canvas.draw() out, mult = yield self.pid.holding_read('OUT') self.out_x.append(time.time() - start) self.out_y.append(out) self.out_plot.set_data(self.out_x, self.out_y) self.out_axes.relim() self.out_axes.autoscale() self.canvas.draw() else: self.start.set_sensitive(True) self.stop.set_sensitive(False) if self.start_at: yield self.pid.raw('ModL', 'SV') yield self.pid.raw('At', 'On') start = time.time() self.pv_x = [] self.pv_y = [] self.sv_x = [] self.sv_y = [] self.out_x = [] self.out_y = [] self.start_at = False self.stop_at = False except: pass d = twisted.internet.defer.Deferred() twisted.internet.reactor.callLater(1, d.callback, None) yield d yield self.pid.coil('NAT')
class Handler: ## == initialization == def __init__(self): #{{{ self.lockTreeViewEvents = False np.seterr(all='ignore') ## Plotting initialization self.fig = matplotlib.figure.Figure(figsize=(8, 8), dpi=96, facecolor='#eeeeee', tight_layout=1) # (figure is static, axes clear on every replot) self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(300, 300) self.toolbar = matplotlib.backends.backend_gtk3.NavigationToolbar2GTK3( self.canvas, w('box4').get_parent_window()) self.xlim, self.ylim = None, None self.sw = Gtk.ScrolledWindow() self.sw.add_with_viewport(self.canvas) w('box4').pack_start(self.toolbar, False, True, 0) #self.toolbar.append_item(Gtk.Button('tetet')) ## TODO find out how to modify the NavigationToolbar... w('box4').pack_start(self.sw, True, True, 0) self.toolbar.pan() #TODO - define global shortcuts as a superset of the Matplotlib-GUI's internal, include also: #toolbar.zoom() #toolbar.home() #toolbar.back() #toolbar.forward() #toolbar.save_figure(toolbar) #TODO http://stackoverflow.com/questions/26433169/personalize-matplotlib-toolbar-with-log-feature #TODO http://dalelane.co.uk/blog/?p=778 self.opj_file_cache = {} ## TreeStore and ListStore initialization self.tsFiles = Gtk.TreeStore(str, Pixbuf, str, Pixbuf, int, int, str) self.treeStoreColumns = { 'filepath': 0, 'icon': 1, 'name': 2, 'plotstyleicon': 3, 'column': 4, 'spreadsheet': 5, 'rowtype': 6 } self.dummy_treestore_row = [None for x in self.treeStoreColumns.keys()] treeViewCol0 = Gtk.TreeViewColumn("Plot") # Create a TreeViewColumn colCellPlot = Gtk.CellRendererPixbuf( ) # Create a column cell to display text treeViewCol0.pack_start(colCellPlot, expand=True) treeViewCol0.add_attribute(colCellPlot, "pixbuf", 3) # set params for icon w('treeview1').append_column( treeViewCol0) # Append the columns to the TreeView treeViewCol = Gtk.TreeViewColumn("File") # Create a TreeViewColumn colCellImg = Gtk.CellRendererPixbuf( ) # Create a column cell to display an image colCellText = Gtk.CellRendererText( ) # Create a column cell to display text treeViewCol.pack_start(colCellImg, expand=False) # Add the cells to the column treeViewCol.pack_start(colCellText, expand=True) treeViewCol.add_attribute( colCellImg, "pixbuf", 1) # Bind the image cell to column 1 of the tree's model treeViewCol.add_attribute( colCellText, "text", 2) # Bind the text cell to column 0 of the tree's model w('treeview1').append_column( treeViewCol) # Append the columns to the TreeView w('treeview1').set_expander_column(treeViewCol) w('treeview1').set_model( self.tsFiles) # Append the columns to the TreeView w('treeview1').get_selection().set_select_function( self.treeview1_selectmethod, data=None) # , full=True ## TODO: If files are specified as arguments, select these at start, and plot them at once ## If a directory is specified, just set it as the root of the file list. If none, use current working dir. self.populateTreeStore( self.tsFiles, reset_path=os.getcwd() if len(sys.argv) <= 1 else sys.argv[1]) self.plot_reset() self.plot_all_sel_records() ## Initialize the default plotting commands w('txt_rc').get_buffer().set_text(line_plot_command) w('txt_rc').modify_font(Pango.FontDescription("monospace 10")) ## Add the data cursor by default # TODO - make this work #cursor = Cursor(self.ax, useblit=True, color='red', linewidth=2) #}}} ## === FILE HANDLING === def is_dir(self, filename): # {{{ try: return stat.S_ISDIR(os.stat(filename).st_mode) except FileNotFoundError: ## this may be e.g. due to a broken symlink return False # }}} def row_type_from_fullpath(self, fullpath): # {{{ """ Known row types: #Type row_is_leaf row_can_plot row_icon dir 0 0 '' updir 1 0 'go-up' csvtwocolumn 1 1 '' csvmulticolumn 0 0 '' xlsfile 0 0 '' xlsspread 0 0 '' xlscolumn 1 1 '' opjfile 0 0 '' opjgraph 0 0 '' opjspread 0 0 '' opjcolumn 1 1 '' unknown 1 0 '' """ ## Note: Remaining row types not returned by this function (i.e. xlsspread, xlscolumn, opjgraph etc.) are ## never assigned to files; they are added only when a file or spreadsheet is unpacked and populated. ## The determination of file type from its name is a bit sloppy, but it works and it is fast. assert isinstance(fullpath, str) if self.is_dir(fullpath): return 'dir' elif fullpath.lower().endswith('.xls'): ## TODO XLS support : determine if single spreadsheet, and/or if spreadsheet(s) contain single column return 'xlsfile' elif fullpath.lower().endswith('.opj'): return 'opjfile' elif fullpath.lower().endswith('.csv') or fullpath.lower().endswith( '.dat') or fullpath.lower().endswith('.txt'): try: ## Note: column number is incorrectly determined if header is longer than sizehint, but 10kB should be enough data_array, header, parameters = robust_csv_parser.loadtxt( fullpath, sizehint=SIZELIMIT_FOR_HEADER) print("LENHEADER", header) if len(header) <= 2: return 'csvtwocolumn' else: return 'csvmulticolumn' except ( IOError, RuntimeError ): # This error is usually returned for directories and non-data files return 'unknown' else: return 'unknown' # }}} def rowtype_is_leaf(self, rowtype): # {{{ """ Determines if row shall be selected (or unpacked, otherwise) Rows representing containers (e.g. directories, multicolumn CSV files or Origin files) are not "leaves", since they contain some structure that can be further unpacked. In contrast, ordinary two-column CSV files or columns of CSV files are "leaves" and can be directly plotted. """ return (rowtype in ('csvtwocolumn', 'csvcolumn', 'xlscolumn', 'opjcolumn', 'unknown')) # }}} def rowtype_can_plot(self, rowtype): # {{{ """ Determines if row shall be plotted """ return (rowtype in ('csvtwocolumn', 'xlscolumn', 'opjcolumn')) # }}} def rowtype_icon(self, rowtype, iconsize=8): # {{{ iconname = { 'updir': 'go-up', 'dir': 'folder', 'csvtwocolumn': 'empty', 'csvmulticolumn': 'zip', 'csvcolumn': 'empty', 'opjfile': 'zip', 'opjspread': 'go-next', 'opjgraph': 'go-previous', 'opjcolumn': 'empty', 'xlsfile': 'zip', 'xlsspread': 'go-next', 'xlscolumn': 'empty', 'unknown': 'stop' } return Gtk.IconTheme.get_default().load_icon(iconname[rowtype], iconsize, 0) # }}} def origin_parse_or_cache(self, basepath): # {{{ if basepath in self.opj_file_cache.keys(): return self.opj_file_cache[basepath] else: opj = liborigin.parseOriginFile(basepath) self.opj_file_cache[basepath] = opj return opj # }}} def decode_origin_label(self, bb, splitrows=False): # {{{ bb = bb.decode('utf-8', errors='ignore').replace('\r', '').strip() bb = bb.replace( '\\-', '_') ## this is the lower index - todo: use latex notation? for asc, greek in zip( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 'αβγδεζηθιjκλμνοπρςστυφχξψωΑΒΓΔΕΖΗΘΙJΚΛΜΝΟΠQΡΣΤΥΦΧΞΨΩ'): bb = bb.replace('\\g(%s)' % asc, greek) if not splitrows: return bb.replace('\n', ' ') else: return bb.split('\n') # }}} def populateTreeStore(self, treeStore, parent_row=None, reset_path=None): # {{{ ## without any parent specified, rows will be added to the very left of the TreeView, ## otherwise they will become childs thereof if parent_row is None: if reset_path is not None: basepath = reset_path else: if self.row_prop(self.tsFiles.get_iter_first(), 'rowtype') == 'updir': basepath = self.row_prop(self.tsFiles.get_iter_first(), 'filepath') else: raise AttributeError( 'Specify either parent_row, reset_path, or ensure the first row is of "updir" type' ) w('window1').set_title('PlotCommander: %s' % basepath) ## On startup, or when the 'updir' node is selected, we update the whole tree. ## Initially, it has to be cleared of all rows. ## During this operation, its selection will change, but the plots should not be updated so that it is fast. self.lockTreeViewEvents = True self.tsFiles.clear( ) ## TODO: remember the unpacked rows, and also the selected ones self.clearAllPlotIcons( self.tsFiles.get_iter_first()) ## TODO: obsolete, rm! self.lockTreeViewEvents = False ## The first node of cleared treeStore will point to the above directory, enabling one to browse whole filesystem plotstyleIcon = Pixbuf.new(Colorspace.RGB, True, 8, 10, 10) plotstyleIcon.fill(0xffffffff) currentIter = treeStore.append(None, [ basepath, self.rowtype_icon('updir'), '..', plotstyleIcon, None, None, 'updir' ]) ## ^^ FIXME basepath? or os.path.dirname(basepath) ? treeStore.append(currentIter, self.dummy_treestore_row) elif parent_row is not None and reset_path is None: ## If not resetting the whole tree, get the basepath from the parent row basepath = treeStore.get_value(parent_row, self.treeStoreColumns['filepath']) else: raise AttributeError() ## Prepare the lists of paths, column numbers and spreadsheet numbers to be added parentrowtype = self.row_prop(parent_row, 'rowtype') if parent_row else 'dir' assert not self.rowtype_is_leaf(parentrowtype) if parentrowtype == 'dir': ## Populate a directory with files/subdirs ## Get the directory contents and sort it alphabetically filenames = os.listdir(basepath) filenames = sorted(filenames, key=sort_alpha_numeric.split_alpha_numeric ) # intelligent alpha/numerical sorting fileFilterString = w('enFileFilter').get_text().strip() itemFullNames = [ os.path.join(basepath, filename) for filename in filenames ] # add the full path # dirs will be listed first and files below; filter the files ## FIXME: filter only through the file name, not full path! itemFullNames = [f for f in itemFullNames if self.is_dir(f)] + \ [f for f in itemFullNames if (not self.is_dir(f) and (fileFilterString == '' or re.findall(fileFilterString, f)))] itemShowNames = [os.path.split(f)[1] for f in itemFullNames ] # only file name without path will be shown columnNumbers = [None] * len( itemFullNames ) # obviously files/subdirs are assigned no column number spreadNumbers = [None] * len( itemFullNames) # nor they are assigned any spreadsheet number rowTypes = [self.row_type_from_fullpath(f) for f in itemFullNames] elif parentrowtype == 'csvmulticolumn': ## Note: Multicolumn means at least 3 columns (i.e. x-column and two or more y-columns) data_array, header, parameters = robust_csv_parser.loadtxt( basepath, sizehint=10000) columnFilterString = w('enColFilter').get_text().strip() columnNumbers, header = zip(*[ n for n in enumerate(header) if re.findall(columnFilterString, n[1]) ]) ## filter the columns #FIXME File "/home/dominecf/p/plotcommander/plotcommander.py", line 303, in populateTreeStore #columnNumbers, header = zip(*[n for n in enumerate(header) if re.findall(columnFilterString, n[1])]) ## filter the columns #ValueError: not enough values to unpack (expected 2, got 0) itemFullNames = [basepath] * len( header) # all columns are from one file itemShowNames = header # column numbers are either in file header, or auto-generated spreadNumbers = [None] * len( header) # there are no spreadsheets in CSV files rowTypes = ['csvcolumn'] * len(header) elif parentrowtype == 'opjfile': print("parentrowtype == 'opjfile':", basepath) opj = self.origin_parse_or_cache(basepath) ## Add "graphs" - which show the selected columns in presentation-ready format ## Fixme support for multiple opjlayers also here def generate_graph_annotation(graph): layerNumber = 0 ## Fixme support for multiple opjlayers: ["graphs"][1].layers[0].curves[3].xColumnName legend_box = self.decode_origin_label( graph.layers[0].legend.text, splitrows=True) comment = "" for legendline in legend_box: ## the legend may have format as such: ['\l(1) 50B', '\l(2) 48B', ...], needs to be pre-formatted: newline = re.sub(r'\\l\(\d\)\s', '', legendline) if newline == legendline: comment += newline + ' ' return comment itemShowNames = [ '%s; name: %s; label: %s' % (self.decode_origin_label( graph.name), self.decode_origin_label( graph.label), generate_graph_annotation(graph)) for graph in opj['graphs'] ] itemFullNames = [basepath] * len( itemShowNames) # all columns are from one file columnNumbers = [None] * len(itemShowNames) spreadNumbers = list(range(len(itemShowNames))) rowTypes = ['opjgraph'] * len(itemShowNames) ## Add "columns" - which enable to access all data in the file, including those not used in "graphs" for spread in opj['spreads']: print(spread.label, self.decode_origin_label(spread.label)) itemShowNames = itemShowNames + [ '%s "%s"' % (self.decode_origin_label( spread.name), self.decode_origin_label(spread.label)) for spread in opj['spreads'] ] itemFullNames = itemFullNames + [basepath] * len( itemShowNames) # all columns are from one file columnNumbers = columnNumbers + [None] * len(itemShowNames) spreadNumbers = spreadNumbers + list(range(len(itemShowNames))) rowTypes = rowTypes + ['opjspread'] * len(itemShowNames) elif parentrowtype == 'opjspread': opj = self.origin_parse_or_cache(basepath) parent_spreadsheet = self.row_prop(parent_row, 'spreadsheet') itemShowNames = [ self.decode_origin_label(column.name) for column in opj['spreads'][parent_spreadsheet].columns ] itemFullNames = [basepath] * len( itemShowNames) # all columns are from one file columnNumbers = list(range(len(itemShowNames))) spreadNumbers = [parent_spreadsheet] * len(itemShowNames) rowTypes = ['opjcolumn'] * len(itemShowNames) elif parentrowtype == 'opjgraph': opj = self.origin_parse_or_cache(basepath) parent_graph = self.row_prop( parent_row, 'spreadsheet' ) ## The key 'spreadsheet' is misused here to mean 'graph' layerNumber = 0 ## Fixme support for multiple opjlayers: ["graphs"][1].layers[0].curves[3].xColumnName ## Try to extract meaningful legend for each curve, assuming the legend box has the same number of lines curves = opj['graphs'][parent_graph].layers[layerNumber].curves legend_box = self.decode_origin_label( opj['graphs'][parent_graph].layers[layerNumber].legend.text, splitrows=True) legends = [] for legendline in legend_box: ## the legend may have format as such: ['\l(1) 50B', '\l(2) 48B', ...], needs to be pre-formatted: newline = re.sub(r'\\l\(\d\)\s', '', legendline) if newline != legendline: legends.append(newline) legends = legends[:len(curves)] + ( [''] * (len(curves) - len(legends)) ) ## trim or extend the legends to match the curves itemShowNames, itemFullNames, columnNumbers, spreadNumbers = [], [], [], [] for curve, legend in zip(curves, legends): ## FIXME add support for xColumn different than the first one in Spreadsheet, also here #print("curve t xCol yCol:", curve.dataName.self.decode_origin_label('utf-8'), #curve.xColumnName.self.decode_origin_label('utf-8'), curve.yColumnName.self.decode_origin_label('utf-8')) #print([spread.name for spread in opj['spreads']], (curve.dataName[2:])) ## Seek the corresponding spreadsheet and column by their name spreadsheet_index = [spread.name for spread in opj['spreads'] ].index(curve.dataName[2:]) spread = opj['spreads'][spreadsheet_index] y_column_index = [column.name for column in spread.columns ].index(curve.yColumnName) x_column_index = [column.name for column in spread.columns ].index(curve.xColumnName) #print(curve.dataName[2:].self.decode_origin_label('utf-8'), spreadsheet_index, curve.yColumnName.self.decode_origin_label('utf-8'), y_column_index) itemShowNames.append( '%s -> spread %s: column %s (against %s)' % (legend, self.decode_origin_label(spread.name), self.decode_origin_label( spread.columns[y_column_index].name), self.decode_origin_label( spread.columns[x_column_index].name))) itemFullNames.append(basepath) # all columns are from one file columnNumbers.append(y_column_index) spreadNumbers.append(spreadsheet_index) rowTypes = ['opjcolumn'] * len( itemShowNames) ## TODO or introduce opjgraphcurve ? else: warnings.warn( 'Not prepared yet to show listings of this file: %s' % parentrowtype) return ## Go through all items and populate the node for itemFullName, itemShowName, columnNumber, spreadNumber, rowtype in \ zip(itemFullNames, itemShowNames, columnNumbers, spreadNumbers, rowTypes): plotstyleIcon = Pixbuf.new(Colorspace.RGB, True, 8, 10, 10) plotstyleIcon.fill(0xffffffff) currentIter = treeStore.append(parent_row, [ itemFullName, self.rowtype_icon(rowtype), itemShowName, plotstyleIcon, columnNumber, spreadNumber, rowtype ]) if not self.rowtype_is_leaf( rowtype): ## TODO row---> parentrowtype treeStore.append( currentIter, self.dummy_treestore_row ) # shows the "unpacking arrow" left of the item # }}} ## === GRAPHICAL PRESENTATION === def clearAllPlotIcons(self, treeIter): # {{{ while treeIter != None: iterpixbuf = self.row_prop(treeIter, 'plotstyleicon') if iterpixbuf: iterpixbuf.fill(self.array2rgbhex( [.5, .5, 1], alpha=0)) ## some nodes may have pixbuf set to None self.clearAllPlotIcons(self.tsFiles.iter_children(treeIter)) treeIter = self.tsFiles.iter_next(treeIter) # }}} def array2rgbhex(self, arr3, alpha=1): # {{{ return int(arr3[0]*256-.5)*(256**3) +\ int(arr3[1]*256-.5)*(256**2) +\ int(arr3[2]*256-.5)*(256**1) +\ int(alpha*255 -.5) # }}} def plot_reset(self): # {{{ #self.ax.cla() ## TODO clearing matplotlib plot - this is inefficient, rewrite self.fig.clf() self.ax = self.fig.add_subplot(111) self.ax.callbacks.connect('xlim_changed', self.on_xlims_change) self.ax.callbacks.connect('ylim_changed', self.on_ylims_change) ## TODO: this may enable better handling of axes, but needs to make compatible with the object model of figure.Figure # self.ax = host_subplot(111, figure=self.fig.gcf() ) def recursive_clear_icon(treeIter): while treeIter != None: iterpixbuf = self.tsFiles.get_value(treeIter, 3) if iterpixbuf: iterpixbuf.fill(self.array2rgbhex( [.5, .5, 1], alpha=0)) ## some nodes may have pixbuf set to None recursive_clear_icon(self.tsFiles.iter_children(treeIter)) treeIter = self.tsFiles.iter_next(treeIter) recursive_clear_icon(self.tsFiles.get_iter_first()) w('treeview1').queue_draw() # }}} def load_row_data(self, row): # {{{ """ loads all relevant data for a given treestore row, and returns: x, y, label, parameters, xlabel, ylabel Plotting is "on-the-fly", i.e., program does not store any data (except OPJ file cache) and loads them from disk upon every (re)plot. """ ## Load the data rowfilepath = self.row_prop(row, 'filepath') rowtype = self.row_prop(row, 'rowtype') rowxcolumn = 0 ## TODO allow ordinate also on >0th column rowycolumn = self.row_prop(row, 'column') rowsheet = self.row_prop(row, 'spreadsheet') if rowtype == 'opjcolumn': opj = self.origin_parse_or_cache(rowfilepath) # TODO: what does opj['spreads'][3].multisheet mean? x, y = [ opj['spreads'][rowsheet].columns[c].data for c in [rowxcolumn, rowycolumn] ] if len(x) > 2 and x[-2] > x[-1] * 1e6: x = x[: -1] ## the last row from liborigin is sometimes erroneous zero if len(x) < len(y): y = y[0:len( x)] ## in any case, match the length of x- and y-data if len(y) < len(x): x = x[0:len(y)] try: ## fast string-to-float conversion x, y = [np.array(arr) for arr in (x, y)] ## TODO dtype=float except ValueError: ## failsafe string-to-float conversion x0, y0 = [], [] for x1, y1 in zip(x, y): try: xf, yf = float(x1), float(y1) x0.append(xf) y0.append(yf) except ValueError: pass x, y = x0, y0 xlabel, ylabel = [ self.decode_origin_label( opj['spreads'][rowsheet].columns[c].name) for c in [rowxcolumn, rowycolumn] ] parameters = { } ## todo: is it possible to load parameters from origin column? return x, y, ylabel, parameters, xlabel, ylabel elif rowtype == 'csvtwocolumn': ycolumn = 1 data_array, header, parameters = robust_csv_parser.loadtxt( rowfilepath, sizehint=SIZELIMIT_FOR_DATA) print() print() print() print(data_array, len(header), header) if len(header) == 1: data_array = np.vstack( [np.arange(len(data_array)), data_array.T]).T header = ['point number'] + header print(data_array, len(header), header) print() print() print() return data_array.T[0], data_array.T[1], os.path.split(rowfilepath)[1][:-4], parameters, \ header[0], header[1] ## LINES NAMED BY THEIR FILE NAME ##TODO make it automatic #return data_array.T[0], data_array.T[1], os.path.split(os.path.split(rowfilepath)[0])[1], parameters, header[0], header[1] ## TODO ## TODO replace os.path.split(rowfile)[-2] with a parameter reasonably recovered from the file name elif rowtype == 'csvcolumn': data_array, header, parameters = robust_csv_parser.loadtxt( rowfilepath, sizehint=SIZELIMIT_FOR_DATA) return data_array.T[rowxcolumn], data_array.T[rowycolumn], rowfilepath, parameters, \ header[rowxcolumn], header[rowycolumn] ## LINES NAMED BY THEIR FILE NAME ##TODO make it automatic #return data_array.T[rowxcolumn], data_array.T[rowycolumn], os.path.split(os.path.split(rowfilepath)[0])[1], parameters, \ #header[rowxcolumn], header[rowycolumn] ## LINES NAMED BY THEIR FILE DIRECTORY #elif rowtype == 'xls': # TODO a XLS file is a *container* with multiple sheets, a sheet may contain multiple columns #return #xl = pd.ExcelFile(infile, header=1) ## #print(xl.sheet_names) #print(xl.sheets[rowsheet]) #df = xl.parse() #x, y, xlabel, ylabel = df.values.T[rowxcolumn], df.values.T[rowycolumn], header[rowxcolumn], header[rowycolumn] ## TODO Should offer choice of columns ## FIXME clash with 'header'!! else: raise RuntimeError ## for all remaining filetypes, abort plotting quietly # }}} def plot_all_sel_records(self): # {{{ ## Setting persistent view is somewhat kafkaesque with matplotlib. ## self.xlim remembers the correct view from the last GUI resize , but ## ax.get_xlim from the start of this method returns the wrong (autoscaled) limits, why? if not w('chk_autoscale_x').get_active() and self.xlim: self.ax.set_xlim(self.xlim) if not w('chk_autoscale_y').get_active() and self.ylim: self.ax.set_ylim(self.ylim) ## Load all row data (model, pathlist) = w('treeview1').get_selection().get_selected_rows() if len(pathlist) == 0: return error_counter = 0 row_data = [] row_labels = [] plotted_paths = [] for path in pathlist: try: row_data.append(self.load_row_data( self.tsFiles.get_iter(path))) plotted_paths.append(path) except (RuntimeError, ValueError): traceback.print_exc() error_counter += 1 w('statusbar1').push( 0, ('%d records loaded' % len(pathlist)) + ('with %d errors' % error_counter) if error_counter else '') if row_data == []: return False xs, ys, labels, params, xlabels, ylabels = zip(*row_data) #for n,v in zip('xs, ys, labels, params, xlabels, ylabels'.split(), [xs, ys, labels, params, xlabels, ylabels]): #print(n,v) ## TODO: check if there is exactly one column in the 'params' table that differs among files: label="%s=%s" % (labelkey, labelval) ## If it is, append its name and value to the respective 'labels' field, so that all plotted lines are distinguishable by this value! ## If there is none, or too many, the curves will be labeled just by their column label found in the header. ## TODO allow also to name the curves by the file name, if the column names do not exist or are all the same! ## Generate the color palette for curves color_pre_map = np.linspace(0.05, .95, len(plotted_paths) + 1)[:-1] colors = matplotlib.cm.gist_rainbow( color_pre_map * .5 + np.sin(color_pre_map * np.pi / 2)**2 * .5) for path, color_from_palette in zip(plotted_paths, colors): ## If no exception occured during loading, colour the icon according to the line colour icon = self.row_prop(self.tsFiles.get_iter(path), 'plotstyleicon') if icon: icon.fill(self.array2rgbhex(color_from_palette)) plotted_paths.append(path) ## TODO: decide what is the distinguishing parameter for the given set of rows ---> <str> labelkey, <labelvals # 1) (almost) all curves should have it defined # 2) it should differ among (almost) all curves # 3) it may be in the params dict, or in the filename (for csvtwocolumn), or in the header of csvcolumn, opjcolumn etc. #print("self.ax.axis", self.ax.axis()) ## Plot all curves sequentially plot_cmd_buffer = w('txt_rc').get_buffer() plot_command = plot_cmd_buffer.get_text( plot_cmd_buffer.get_start_iter(), plot_cmd_buffer.get_end_iter(), include_hidden_chars=True) #print("BEFORE COMMAND") #print(row_data) if plot_command.strip() != '': #np = numpy def dedup(l): return list( dict.fromkeys(l[::-1]) )[:: -1] ## deduplicates items, preserves order of first occurence exec_env = { 'np': np, 'sc': sc, 'matplotlib': matplotlib, 'cm': matplotlib.cm, 'ax': self.ax, 'fig': self.fig, 'xs': np.array(xs), 'ys': np.array(ys), 'labels': labels, 'params': np.array(params), 'xlabels': xlabels, 'ylabels': ylabels, 'xlabelsdedup': ', '.join(dedup(xlabels))[:100], 'ylabelsdedup': ', '.join(dedup(ylabels))[:100], 'colors': colors } #self.fig.clf() ## clear figure try: exec(plot_command, exec_env) #print("JUST AFTER COMMAND") except SyntaxError: #print("SYNTAX ERROR:") traceback.print_exc() ## TODO locate the error except: #print("SOME ERROR") traceback.print_exc() ## TODO locate the error #print("AFTER COMMAND") #code = compile(plot_command, "somefile.py", 'exec') TODO #exec(code, global_vars, local_vars) #else: #plot_command = default_plot_command #plot_cmd_buffer.set_text(default_plot_command) cursor = Cursor( self.ax, color='red', linewidth=.5 ) ## , useblit=True .. fixme: useblit made the cursor disappear # Note: blit cannot be used: AttributeError: 'FigureCanvasGTK3Cairo' object has no attribute 'copy_from_bbox' #self.ax.legend(loc="best") self.ax.grid(True) self.ax.set_xscale( 'log' if w('chk_xlogarithmic').get_active() else 'linear') ##XXX self.ax.set_yscale( 'log' if w('chk_ylogarithmic').get_active() else 'linear') ##XXX self.ax.relim() self.ax.autoscale_view( ) # Autoscale the view limits using the data limits. self.canvas.draw() return True # }}} def on_xlims_change(self, axes): self.xlim = axes.get_xlim() ## dirty hack: Needs fixing in the future def on_ylims_change(self, axes): self.ylim = axes.get_ylim() ## dtto ## == FILE AND DATA UTILITIES == def row_prop(self, row, prop): # {{{ return self.tsFiles.get_value(row, self.treeStoreColumns[prop]) # }}} def remember_treeView_expanded_rows(self, treeStore, treeView): # {{{ ## returns a list of paths of expanded files/directories expanded_row_names = [] def remember_treeview_states(treeIter): while treeIter != None: if w('treeview1').row_expanded(treeStore.get_path(treeIter)): expanded_row_names.append(treeStore.get_value( treeIter, 0)) # get the full path of the position remember_treeview_states(treeStore.iter_children(treeIter)) treeIter = treeStore.iter_next(treeIter) remember_treeview_states(treeStore.get_iter_first()) return expanded_row_names # }}} def remember_treeView_selected_rows(self, treeStore, treeView): # {{{ ## returns a list of paths of selected files/directories (model, selectedPathList) = treeView.get_selection().get_selected_rows() selected_row_names = [] for treePath in selectedPathList: selected_row_names.append( treeStore.get_value(treeStore.get_iter(treePath), 0)) return selected_row_names # }}} def restore_treeView_expanded_rows(self, expanded_row_names): # {{{ def recursive_expand_rows(treeIter, ): while treeIter != None: if self.tsFiles.get_value(treeIter, 0) in expanded_row_names: self.lockTreeViewEvents = True w('treeview1').expand_row(self.tsFiles.get_path(treeIter), open_all=False) self.lockTreeViewEvents = False recursive_expand_rows(self.tsFiles.iter_children(treeIter)) treeIter = self.tsFiles.iter_next(treeIter) recursive_expand_rows(self.tsFiles.get_iter_first()) # }}} def restore_treeView_selected_rows(self, selected_row_names): # {{{ def recursive_select_rows(treeIter): while treeIter != None: if self.tsFiles.get_value(treeIter, 0) in selected_row_names: self.lockTreeViewEvents = True w('treeview1').get_selection().select_path( self.tsFiles.get_path(treeIter)) self.lockTreeViewEvents = False recursive_select_rows(self.tsFiles.iter_children(treeIter)) treeIter = self.tsFiles.iter_next(treeIter) recursive_select_rows(self.tsFiles.get_iter_first()) self.plot_all_sel_records() # }}} ## == USER INTERFACE HANDLERS == def possible_rc_filenames(self): (model, pathlist) = w('treeview1').get_selection().get_selected_rows() if pathlist: firstselfilenamepath = self.row_prop( self.tsFiles.get_iter(pathlist[0]), 'filepath') firstselfilepath, firstselfilename = os.path.dirname( firstselfilenamepath), os.path.basename(firstselfilenamepath) testrcfilenamepath1 = os.path.join( firstselfilepath, 'plotrc_%s.py' % firstselfilename) testrcfilenamepath2 = os.path.join(firstselfilepath, 'plotrc.py') return (testrcfilenamepath1, testrcfilenamepath2) else: return None def relevant_rc_filename(self): prf = self.possible_rc_filenames() if prf: rc_filename = prf[0] if os.path.isfile( prf[0]) else prf[1] if os.path.isfile(prf[1]) else None return rc_filename else: return None def load_plotcommand_from_rcfile(self): rc_filename = self.relevant_rc_filename() if rc_filename: with open(rc_filename) as rcfile: return rcfile.read() else: return '' def update_plotcommand_from_rcfile(self, allow_overwrite_by_empty=True): plotcommand = self.load_plotcommand_from_rcfile() if plotcommand or allow_overwrite_by_empty: w('txt_rc').get_buffer().set_text(plotcommand) def plotcommand_get_text(self): buf = w('txt_rc').get_buffer() return buf.get_text(buf.get_start_iter(), buf.get_end_iter(), include_hidden_chars=True) def plotcommand_set_text(self, text): buf = w('txt_rc').get_buffer() buf.set_text(text) def on_plotcommand_toggled(self, *args): # {{{ radiobutton = args[0] w('chk_xlogarithmic').set_active(False) if radiobutton is w('rad_plotstyle_rc'): if radiobutton.get_active(): ## selecting action self.update_plotcommand_from_rcfile() else: pass ## todo - ask whether to save the command file, if changed else: if radiobutton.get_active(): ## selecting action self.plotcommand_set_text( plot_commands[radiobutton.get_label().strip()]) else: ## deselecting action plot_commands[radiobutton.get_label().strip( )] = self.plotcommand_get_text() if radiobutton.get_active(): ## selecting action ## Update the graphical presentation self.plot_reset( ) ## first delete the curves, to hide (also) unselected plots self.plot_all_sel_records() ## then show the selected ones # }}} def on_btn_plotrc_save_clicked(self, *args): # {{{ rc_filename = self.relevant_rc_filename( ) or self.possible_rc_filenames()[0] with open(rc_filename, 'w') as rcfile: rcfile.write(self.plotcommand_get_text()) # }}} """ if radio changes to some defaultcommand ---> change to selected command if radio changes to rcfile if relevant rcfile exists ---> change to selected command ( LOAD rcfile command) ---> then REPLOT if rcfile does not exist ---> EMPTy txtbox if nothing selected ---> EMPTy txtbox if selection changes if relevant rcfile exists ---> CHANGE radio TO RCFILE ---> change to selected command ( LOAD rcfile command) (automatic event handler?) if rcfile does not exist ---> CHANGE radio TO DEFAULT if nothing selected ---> ---> then always REPLOT """ def on_treeview1_row_expanded(self, treeView, treeIter, treePath): # {{{ ## Add the children treeStore = treeView.get_model() newFilePath = treeStore.get_value( treeIter, 0) # get the full path of the position self.populateTreeStore(treeStore, parent_row=treeIter) ## The dummy row has to be removed AFTER this, otherwise the empty treeView row will NOT expand) if treeStore.iter_children(treeIter): treeStore.remove(treeStore.iter_children(treeIter)) #}}} def on_treeview1_row_collapsed(self, treeView, treeIter, treePath): # {{{ ## Remove all child nodes of the given row (useful mostly to prevent de-syncing from some changes in the filesystem) #if self.lockTreeViewEvents: return ## prevent event handlers triggering other events currentChildIter = self.tsFiles.iter_children(treeIter) while currentChildIter: self.tsFiles.remove(currentChildIter) currentChildIter = self.tsFiles.iter_children(treeIter) self.tsFiles.append(treeIter, self.dummy_treestore_row) # }}} def on_treeview1_selection_changed(self, *args): # {{{ ## triggers replot if self.lockTreeViewEvents: return ## prevent event handlers triggering other events ## Update the plot command if w('rad_plotstyle_rc').get_active(): ## TODO if it does not exist self.update_plotcommand_from_rcfile(allow_overwrite_by_empty=False) elif self.relevant_rc_filename(): w('rad_plotstyle_rc').set_active(True) ## Update the graphical presentation self.plot_reset( ) ## first delete the curves, to hide (also) unselected plots self.plot_all_sel_records() ## then show the selected ones # }}} def treeview1_selectmethod(self, selection, model, treePath, is_selected, user_data): # {{{ ## TODO reasonable behaviour for block-selection over different unpacked directories/files ## Expand a directory by clicking, but do not allow user to select it treeIter = self.tsFiles.get_iter(treePath) #self.treeStoreColumns= {'filepath':0, 'icon':1, 'name':2, 'plotstyleicon':3, 'column':4, 'spreadsheet':5, 'rowtype':6} if self.lockTreeViewEvents: return ## during folding, prevent triggering 'on_select' events on all node children #lockTreeViewEvents_tmp = self.lockTreeViewEvents ## TODO understand and clear out when select events can occur #self.lockTreeViewEvents = True # ... #self.lockTreeViewEvents = lockTreeViewEvents_tmp ## Clicking action of non-leaf un-selectable rows: rowtype = self.tsFiles.get_value(treeIter, self.treeStoreColumns['rowtype']) if self.rowtype_is_leaf(rowtype): return True ## allow selecting or unselecting else: if rowtype == "updir": ## If the expanded row was "..", do not expand it, instead change to up-dir and refresh whole tree expanded_row_names = self.remember_treeView_expanded_rows( self.tsFiles, w('treeview1')) selected_row_names = self.remember_treeView_selected_rows( self.tsFiles, w('treeview1')) self.populateTreeStore(self.tsFiles, reset_path=os.path.dirname( self.row_prop(treeIter, 'filepath'))) elif w('treeview1').row_expanded(treePath): w('treeview1').collapse_row(treePath) elif not w('treeview1').row_expanded(treePath): w('treeview1').expand_row(treePath, open_all=False) return False # }}} def on_enFileFilter_activate(self, *args): # {{{ expanded_row_names = self.remember_treeView_expanded_rows( self.tsFiles, w('treeview1')) selected_row_names = self.remember_treeView_selected_rows( self.tsFiles, w('treeview1')) # Passing parent=None will populate the whole tree again #self.lockTreeViewEvents = True self.populateTreeStore(self.tsFiles, reset_path=None) #self.lockTreeViewEvents = False self.restore_treeView_expanded_rows(expanded_row_names) self.restore_treeView_selected_rows(selected_row_names) # }}} def on_enFileFilter_focus_out_event(self, *args): # {{{ self.on_enFileFilter_activate(self) # }}} def on_enColFilter_activate(self, *args): # {{{ TODO pass def on_enColFilter_focus_out_event(self, *args): pass # }}} def on_window1_delete_event(self, *args): # {{{ Gtk.main_quit(*args) # }}}
class MyWindow(Gtk.Window): @timing def __init__(self): Gtk.Window.__init__(self, title="Echelle Reduction GUI") self.set_default_size(1000, 800) self.figure = Figure(figsize=(5,7), dpi=100) self.plot_1D = self.figure.add_subplot(212) self.plot_2D = self.figure.add_subplot(232) self.plot_PHD = self.figure.add_subplot(231) self.plot_orders = self.figure.add_subplot(233) self.plot_orders.tick_params(axis='both', labelsize=6) self.plot_orders.set_title("Orders") self.plot_2D.tick_params(axis='both', labelsize=7) self.plot_2D.set_title("2D Raw Data") self.plot_1D.set_title("1D Extracted Data") self.plot_1D.set_xlabel('pixels') self.plot_1D.set_ylabel('intensity') self.plot_1D.tick_params(axis='both', labelsize=7) self.plot_PHD.set_title('Pulse Height Data') self.plot_PHD.tick_params(axis='both', labelsize=7) self.canvas = FigureCanvas(self.figure) menubar = Gtk.MenuBar() menubar_file = Gtk.MenuItem("File") filemenu = Gtk.Menu() menubar_file.set_submenu(filemenu) menubar.append(menubar_file) filemenu_open = Gtk.MenuItem("Open") filemenu_open.connect('activate', self.open_file_dialog) filemenu.append(filemenu_open) filemenu.append(Gtk.SeparatorMenuItem()) filemenu_save = Gtk.MenuItem("Save Orders") filemenu_save.connect('activate', self.on_filemenu_save_clicked) filemenu.append(filemenu_save) filemenu_load = Gtk.MenuItem("Load Orders") filemenu_load.connect('activate', self.on_filemenu_load_clicked) filemenu.append(filemenu_load) filemenu.append(Gtk.SeparatorMenuItem()) filemenu_exit = Gtk.MenuItem('Exit') filemenu_exit.connect('activate', Gtk.main_quit) filemenu.append(filemenu_exit) menubox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) menubox.pack_start(menubar, False, False, 0) toolbar = NavigationToolbar(self.canvas, self) main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(main_box) self.statusbar = Gtk.Statusbar() self.context_id = self.statusbar.get_context_id("stat bar example") self.statusbar.push(0, 'Please Open 2D Fits Data File') hbutton_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.count_rate_button = Gtk.ToggleButton(label='Raw count rate') self.count_rate_button.connect("toggled", self.on_count_rate_button_clicked, self.context_id) self.filter_phd_button = Gtk.Button('Filter PHD') self.filter_phd_button.connect("clicked", self.on_filter_phd_button_clicked, self.context_id) self.gauss_fit_button = Gtk.ToggleButton(label='Fit 1D Gauss') self.gauss_fit_button.set_active(False) self.gauss_fit_button.connect("toggled", self.on_gauss_fit_button_clicked, self.context_id) self.remove_orders_button = Gtk.ToggleButton(label='Remove Orders') self.remove_orders_button.connect("toggled", self.on_remove_orders_button_clicked, self.context_id) self.add_orders_button = Gtk.ToggleButton(label='Add Orders') self.add_orders_button.connect("toggled", self.on_add_orders_button_clicked, self.context_id) self.buttonbg = Gtk.Button('Airglow') self.buttonbg.connect('clicked', self.buttonbg_clicked, self.context_id) self.recalculate_button = Gtk.Button('Recalculate') self.recalculate_button.connect('clicked', self.on_recalculate_button_clicked, self.context_id) hbutton_box.pack_start(self.count_rate_button, True, True, 0) hbutton_box.pack_start(self.filter_phd_button, True, True, 0) hbutton_box.pack_start(self.gauss_fit_button, True, True, 0) hbutton_box.pack_start(self.remove_orders_button, True, True, 0) hbutton_box.pack_start(self.add_orders_button, True, True, 0) hbutton_box.pack_start(self.buttonbg, True, True, 0) hbutton_box.pack_start(self.recalculate_button, True, True, 0) main_box.pack_start(menubox, False, False, 0) main_box.pack_start(toolbar, False, False, 0) main_box.pack_start(self.canvas, True, True, 0) main_box.pack_start(hbutton_box, False, False, 0) main_box.pack_start(self.statusbar, False, False, 0) #~ self.filename = './2014-03-14-185937.fits' #~ self.open_file(self.filename) @timing def open_file_dialog(self, widget): dialog = Gtk.FileChooserDialog( "Please Choose a File", self, Gtk.FileChooserAction.OPEN, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK ) ) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name('fits Files') filter.add_mime_type('fits') filter.add_pattern('*.fits') dialog.add_filter(filter) response = dialog.run() if response == Gtk.ResponseType.OK : self.filename = dialog.get_filename() self.statusbar.push(0, 'Opened File: ' + self.filename) dialog.destroy() del dialog self.open_file(self.filename) elif response == Gtk.ResponseType.CANCEL: dialog.destroy() del dialog @timing def open_file(self, filename): hdulist = fits.open(filename) self.photon_list = hdulist[1].data hdulist.close() # Init for new file self.orders = [] self.science(self.photon_list) @timing def science(self, photon_list, min_phd=0, max_phd=255, orders=[]): self.recalculate_button.set_sensitive(False) self.min_phd = min_phd self.max_phd = max_phd PHD = np.array(photon_list['PHD']) photon_list_filtered = photon_list[(PHD >= min_phd) & (PHD <= max_phd)] image, xedges, yedges = np.histogram2d(photon_list_filtered['X'], photon_list_filtered['Y'], bins=2048, range=[[0,8192],[0,8192]]) self.update_2D_plot(image) # Collapse 2D data into 1D to view orders as peaks peaks = [] for i in range(0, len(image[0])): # sums ith row peaks.append(np.sum(image[i,:])) # Smooth peaks by convolving with a boxcar boxcar = np.zeros(300) boxcar[125:175] = 1.0 smooth = np.convolve(peaks, boxcar, mode='same') #~ self.peaks_smoothed = peaks / smooth self.peaks_smoothed = (peaks/max(peaks)) - (smooth/max(smooth)/2) # Only find orders if there are no orders yet, eg. first run if not self.orders and not orders: print 'Finding peaks...' # Requires scipy version .11.0 or greater self.orders = signal.find_peaks_cwt(self.peaks_smoothed, np.arange(10, 20)) elif orders: print 'Using premade peaks list...' self.orders = orders # Plot 1D data with lines through the centers of the # orders to double check how the peak finder did self.update_orders_plot(self.peaks_smoothed, self.orders) # self.update_PHD_plot(PHD, min_phd, max_phd) self.dragbox = [] ### extraction of orders ### # find the widths of the orders (difference between peaks) peak_widths = [] for i in range(1, len(self.orders)): peak_widths.append(self.orders[i] - self.orders[i - 1]) # Double last entry to make peak_widths the right length peak_widths.append(peak_widths[-1]) # Add up spectrum lines from 2D plot where y coord is an order +/-FWHM # Remember, peak_widths[0] is orders[1]-orders[0]. FWHM = 0.63 # full width half max spectrum = [] for i in range(len(self.orders)): peak = int(self.orders[i]) #~ width = int(peak_widths[i]*FWHM) width = 1 for j in range(0, width): # Find chord length of circular image r = len(image[0])/2 start = int(r - (peak * (2*r - peak))**0.5) end = int(2 * r - start) for k in range(start, end): spectrum.append(image[peak-width/2+j][k]) self.update_1D_plot(spectrum) self.statusbar.push(0, 'Done! ' + self.filename) @timing def update_2D_plot(self, image): self.plot_2D.cla() self.plot_2D.tick_params(axis='both', labelsize=7) self.plot_2D.set_title("2D Raw Data") max = np.amax(image) self.plot_2D.imshow(image, norm=LogNorm(vmin=0.1, vmax=max/2), origin='lower') self.canvas.draw() @timing def update_orders_plot(self, peaks, orders): self.plot_orders.cla() self.plot_orders_line, = self.plot_orders.plot([],[]) self.plot_orders.tick_params(axis='both', labelsize=6) self.plot_orders.set_title("Orders") self.plot_orders_line.set_xdata(peaks) self.plot_orders_line.set_ydata(np.arange(len(peaks))) self.plot_orders.hlines(orders, min(peaks), max(peaks), color='purple') self.plot_orders.relim() self.plot_orders.autoscale_view(True, True, True) self.canvas.draw() # Seems a max of 18980 x values are supported by Cairo. Since # we have more than the max we have to reduce the spectrum # resolution to fit. # Maybe do this dynamically based on xlim()? # xmin, xmax = xlim() # return the current xlim @timing def update_1D_plot(self, spectrum): self.plot_1D.cla() self.plot_1D_line, = self.plot_1D.plot([],[]) self.plot_1D.set_title("1D Extracted Data") self.plot_1D.set_xlabel('pixels') self.plot_1D.set_ylabel('intensity') self.plot_1D.tick_params(axis='both', labelsize=7) MAX = 18980 #16384 # 2^14 scale = int(len(spectrum)/MAX) + 1 print len(spectrum), scale, len(spectrum)/scale # When we get wavelength calibration, change this line to #~ x = np.linspace(minWL, maxWL, num = len(spectrum)/scale) x = np.arange(0, len(spectrum), scale) spectrum = [np.sum(spectrum[i:i+scale]) for i in x] self.plot_1D_line.set_xdata(x) self.plot_1D_line.set_ydata(spectrum) self.plot_1D.relim() self.plot_1D.autoscale_view(True, True, True) self.canvas.draw() @timing def update_PHD_plot(self, PHD, min_phd, max_phd): self.plot_PHD.cla() self.plot_PHD.set_title('Pulse Height Data') self.plot_PHD.tick_params(axis='both', labelsize=7) self.plot_PHD.axvline(x=min_phd, color='purple') self.plot_PHD.axvline(x=max_phd, color='purple') self.plot_PHD.hist(PHD, bins=256, range=[0,255], histtype='stepfilled') self.plot_PHD.relim() self.plot_PHD.autoscale_view(True, True, True) self.canvas.draw() ## airglow button ## def buttonbg_clicked(self, widget, data): dialog = Gtk.FileChooserDialog("Please Choose Airglow File", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name('fits Files') filter.add_mime_type('fits') filter.add_pattern('*.fits') dialog.add_filter(filter) response = dialog.run() if response == Gtk.ResponseType.OK: self.airglow_filename = dialog.get_filename() #~ global _AirglowFile #~ _AirglowFile = fname #print "open file" + fname #self.statusbar.push(0,'Opened File:' + fname) dialog.destroy() self.open_airglow_file(self.airglow_filename) elif response == Gtk.ResponseType.CANCEL: dialog.destroy() # opening aiglow fits file def open_airglow_file(self, airglow_filename): #this opens up the fits file hdulist = fits.open(airglow_filename) ## this line will need to be used for real data targname = hdulist[0].header['targname'] #targname = self.targname ## but for now #~ targname = 'HD128627J' # this picks out the actual data from fits file, and turns it into numpy array self.glowdata = hdulist[0].data hdulist.close() # simple subtraction of aurglow image from science image scidata = self.scidata - self.glowdata self.science(scidata, targname) ## gauss fitting button def on_gauss_fit_button_clicked(self, widget, data): self.statusbar.push(data,'Ready to fit. Click on both sides of the emission feature you wish to fit') self.xdata = [] def onclick(event): if self.gauss_fit_button.get_active(): self.xdata.append(event.xdata) self.statusbar.push(data, 'one more click...') if len(self.xdata) == 2: self.statusbar.push(data, 'Ready to fit. Click on both sides of the emission feature you wish to fit') xdata = self.xdata self.gauss_fit(xdata) # mouse click event on 1d cid = self.canvas.mpl_connect('button_press_event', onclick) if [self.gauss_fit_button.get_active()] == [False]: self.statusbar.push(0, 'Opened File:' + File) ### gauss fitting ### def gauss_fit(self, xdata): x = list(self.x) xg = [] xg.append(int(xdata[0])) xg.append(int(xdata[1])) xg1 = min(xg) xg2 = max(xg) xgauss = x[xg1:xg2] ygauss = self.odo[xg1:xg2] right = ygauss[len(xgauss)-4:len(xgauss)] left = ygauss[0:4] # background subtraction averight = sum(right) / len(right) aveleft = sum(left) / len(left) bg_y = [averight, aveleft] rightx = xgauss[len(xgauss)-4:len(xgauss)] leftx = xgauss[0:4] averightx = sum(rightx) / len(rightx) aveleftx = sum(leftx) / len(leftx) bg_x = [averightx, aveleftx] m,b = np.polyfit(bg_x, bg_y, 1) slopex = [i * m for i in xgauss] bg_fit = slopex + b # making a model gauss def gauss(xgauss, MAX, mu, sigma): return MAX * np.exp(-(xgauss - mu)**2 / (2.0 * sigma**2)) avex = sum(xgauss) / len(xgauss) guess = [1.0, avex, 1.0] # plugging in model to matplotlibs curve_fit() coeff, var_matrix = curve_fit(gauss, xgauss, ygauss-bg_fit, p0=guess) fit = gauss(xgauss, *coeff) sigma = coeff[2] FWHM = sigma * 2 * np.sqrt(2 * np.log(2)) FWHM = round(FWHM, 2) fitplot = plt.plot(xgauss, ygauss, color='k') plt.plot(xgauss, fit + bg_fit, color='b', linewidth=1.5) xpos = xgauss[0] + 0.01 * (coeff[1] - xgauss[0]) strFWHM = str(FWHM) plt.text(xpos, 0.9 * max(ygauss), 'FWHM = ' + strFWHM + '', color='purple', fontweight='bold') center = str(round(coeff[1], 2)) plt.text(xpos, 0.95 * max(ygauss), 'Center = ' + center + '', color='green', fontweight='bold') plt.plot(xgauss, bg_fit, 'r--') plt.show() self.xdata = [] ### count rate button def on_count_rate_button_clicked(self, widget, data): if self.count_rate_button.get_active(): self.statusbar.push(data,'Use zoom feature in navigation bar to select count rate region') else: self.statusbar.push(0, 'Opened File:' + self.filename) dragbox = [] def onclick(event): if self.count_rate_button.get_active(): dragbox.append(event.xdata) dragbox.append(event.ydata) def offclick(event): if self.count_rate_button.get_active(): dragbox.append(event.xdata) dragbox.append(event.ydata) self.count_rate(dragbox, data) self.canvas.mpl_connect('button_press_event', onclick) self.canvas.mpl_connect('button_release_event', offclick) def count_rate(self, dragbox, data): # fake exposure time in seconds datafake = './chesstest.fits' hdu = fits.open(datafake) exptime = hdu[0].header['EXPOSURE'] dragbox = [int(x) for x in dragbox] cntbox = self.scidata[ dragbox[0]:dragbox[2], 1024-dragbox[1]:1024-dragbox[3] ] totpix = np.size(cntbox) cntrate = np.sum(cntbox) / exptime totpix = str(totpix) cntrate = str(cntrate) self.statusbar.push(data, 'count rate in box = ' + cntrate + ' cnt/sec, pixels in box = ' + totpix + '') return cntrate @timing def on_filter_phd_button_clicked(self, widget, data): self.phd_window = Gtk.MessageDialog(image = None) self.phd_window.set_size_request(500, 100) self.phd_window.move(400, 300) #self.phd_window.connect("delet_event",lambda w,e:) #~ self.phd_window.connect("delete-event", self.phd_window.destroy) mainbox = self.phd_window.get_content_area() thebox = Gtk.HBox(False, 0) label = Gtk.Label("Keep PHD between") label2 = Gtk.Label('and') label.show() label2.show() self.ok_button = Gtk.Button('Okay') self.ok_button.connect('clicked', self.phd_entry_button_clicked) self.min_phd_entry = Gtk.Entry() self.min_phd_entry.set_activates_default(True) self.max_phd_entry = Gtk.Entry() #~ self.max_phd_entry.set_activates_default(True) self.min_phd_entry.show() self.max_phd_entry.show() self.ok_button.show() thebox.pack_start(label,False,False,0) thebox.pack_start(self.min_phd_entry,False,False,0) thebox.pack_start(label2,False,False,0) thebox.pack_start(self.max_phd_entry,False,False,0) mainbox.pack_start(thebox,True,True,0) mainbox.pack_start(self.ok_button,True,False,0) mainbox.show() thebox.show() self.phd_window.show() @timing def phd_entry_button_clicked(self, widget): min_phd = int(self.min_phd_entry.get_text()) max_phd = int(self.max_phd_entry.get_text()) self.phd_window.destroy() self.statusbar.push(0, 'Filtering by: ' + str(min_phd) + ' < PHD < ' + str(max_phd)) self.orders = [] self.science(self.photon_list, min_phd, max_phd) @timing def on_add_orders_button_clicked(self, widget, data): if [self.add_orders_button.get_active()] == [True]: self.remove_orders_button.set_sensitive(False) self.statusbar.push(data, 'Click where to add an order.') def onclick_peak(event): if self.add_orders_button.get_active(): self.add_order(event.ydata) self.cid_add = self.canvas.mpl_connect('button_press_event', onclick_peak) else: self.canvas.mpl_disconnect(self.cid_add) del self.cid_add self.remove_orders_button.set_sensitive(True) self.statusbar.push(0, 'Opened File:' + self.filename) @timing def add_order(self, ydata): order = ydata print 'Number of original orders', len(self.orders) self.orders.append(order) self.orders.sort() self.update_orders_plot(self.peaks_smoothed, self.orders) self.recalculate_button.set_sensitive(True) print 'Corrected number of orders', len(self.orders) @timing def on_remove_orders_button_clicked(self, widget, data): if [self.remove_orders_button.get_active()] == [True]: self.add_orders_button.set_sensitive(False) self.statusbar.push(data, 'Click on the order to remove.') def onclick_order(event): if self.remove_orders_button.get_active(): self.remove_order(event.ydata) self.cid_remove = self.canvas.mpl_connect('button_press_event', onclick_order) else: self.canvas.mpl_disconnect(self.cid_remove) del self.cid_remove self.add_orders_button.set_sensitive(True) self.statusbar.push(0, 'Opened File:' + self.filename) @timing def remove_order(self, ydata): order = min(self.orders, key=lambda x:abs(x - ydata)) print 'Number of original orders', len(self.orders) self.orders.remove(order) self.update_orders_plot(self.peaks_smoothed, self.orders) self.recalculate_button.set_sensitive(True) print 'Corrected number of orders', len(self.orders) def on_recalculate_button_clicked(self, widget, data): self.science(self.photon_list, self.min_phd, self.max_phd) def on_filemenu_save_clicked(self, widget): now = datetime.datetime.now() date = now.strftime("%Y_%m_%d_%H_%M_%S") temp = self.orders, self.min_phd, self.max_phd pickle.dump(temp, open(self.filename[:-5] + '_1D_' + date + '.orders', 'wb')) self.statusbar.push(0, 'Saved Orders: ' + self.filename[:-5] + '_1D_' + date + '.orders') def on_filemenu_load_clicked(self, widget): dialog = Gtk.FileChooserDialog( "Please Choose a File", self, Gtk.FileChooserAction.OPEN, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK ) ) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name('orders Files') filter.add_mime_type('orders') filter.add_pattern('*.orders') dialog.add_filter(filter) response = dialog.run() if response == Gtk.ResponseType.OK : filename = dialog.get_filename() self.statusbar.push(0, 'Loaded Orders: ' + filename) dialog.destroy() del dialog orders, min_phd, max_phd = pickle.load(open(filename, 'rb')) self.science(self.photon_list, min_phd, max_phd, orders) elif response == Gtk.ResponseType.CANCEL: dialog.destroy() del dialog
class RotationDialog(object): """ This class controls the appearance and signals of the data-rotation dialog. This class pulls the rotation dialog from the Glade file, intilizes the widgets and has methods for the signals defined in Glade. """ def __init__(self, main_window, settings, data, add_layer_dataset, add_feature, redraw_main): """ Initializes the RotationDialog class. Requires the main_window object, the settings object (PlotSettings class) and the data rows to initialize. All the necessary widgets are loaded from the Glade file. A matplotlib figure is set up and added to the scrolledwindow. Two axes are set up that show the original and rotated data. """ self.builder = Gtk.Builder() self.builder.set_translation_domain(i18n().get_ts_domain()) script_dir = os.path.dirname(__file__) rel_path = "gui_layout.glade" abs_path = os.path.join(script_dir, rel_path) self.builder.add_objects_from_file(abs_path, ("dialog_rotation", "adjustment_rotation_dipdir", "adjustment_rotation_dip", "adjustment_rotation_angle")) self.dialog = self.builder.get_object("dialog_rotation") self.dialog.set_transient_for(main_window) self.settings = settings self.data = data self.trans = self.settings.get_transform() self.add_layer_dataset = add_layer_dataset self.add_feature = add_feature self.redraw_main = redraw_main self.adjustment_rotation_dipdir = self.builder.get_object("adjustment_rotation_dipdir") self.adjustment_rotation_dip = self.builder.get_object("adjustment_rotation_dip") self.adjustment_rotation_angle = self.builder.get_object("adjustment_rotation_angle") self.spinbutton_rotation_dipdir = self.builder.get_object("spinbutton_rotation_dipdir") self.spinbutton_rotation_dip = self.builder.get_object("spinbutton_rotation_dip") self.spinbutton_rotation_angle = self.builder.get_object("spinbutton_rotation_angle") self.scrolledwindow_rotate = self.builder.get_object("scrolledwindow_rotate") self.fig = Figure(dpi=self.settings.get_pixel_density()) self.canvas = FigureCanvas(self.fig) self.scrolledwindow_rotate.add_with_viewport(self.canvas) gridspec = GridSpec(1, 2) original_sp = gridspec.new_subplotspec((0, 0), rowspan=1, colspan=1) rotated_sp = gridspec.new_subplotspec((0, 1), rowspan=1, colspan=1) self.original_ax = self.fig.add_subplot(original_sp, projection=self.settings.get_projection()) self.rotated_ax = self.fig.add_subplot(rotated_sp, projection=self.settings.get_projection()) self.canvas.draw() self.redraw_plot() self.dialog.show_all() self.builder.connect_signals(self) if sys.platform == "win32": translate_gui(self.builder) def run(self): """ Runs the dialog. Called from the MainWindow class. Initializes and shows the dialog. """ self.dialog.run() def on_dialog_rotation_destroy(self, widget): """ Hides the dialog on destroy. When the dialog is destroyed it is hidden. """ self.dialog.hide() def on_button_cancel_rotation_clicked(self, button): """ Exits the rotation dialog and makes no changes to the project. When the user clicks on Cancel the dialog is hidden, and no changes are made to the project structure. """ self.dialog.hide() def on_button_apply_rotate_clicked(self, button): """ Adds the rotated layers to the project. When the user clicks on "apply the rotation", the rotated data is added to the project as new datasets. """ raxis_dipdir = self.spinbutton_rotation_dipdir.get_value() raxis_dip = self.spinbutton_rotation_dip.get_value() raxis = [raxis_dipdir, raxis_dip] raxis_angle = self.spinbutton_rotation_angle.get_value() for lyr_obj in self.data: lyr_type = lyr_obj.get_layer_type() lyr_store = lyr_obj.get_data_treestore() if lyr_type == "plane": dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az = \ self.parse_plane(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("plane") for dipdir, dip, strt in zip(dipdir_az, dips_lst, strat): self.add_feature("plane", store, dipdir, dip, strt) elif lyr_type == "line": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense = \ self.parse_line(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("line") for dipdir, dip, sns in zip(ldipdir_lst, ldips_lst, sense): self.add_feature("line", store, dipdir, dip, sns) elif lyr_type == "smallcircle": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, angle = \ self.parse_line(lyr_store, raxis, raxis_angle) store, new_lyr_obj = self.add_layer_dataset("smallcircle") for dipdir, dip, ang in zip(ldipdir_lst, ldips_lst, angle): self.add_feature("smallcircle", store, dipdir, dip, ang) elif lyr_type == "faultplane": rtrn = self.parse_faultplane(lyr_store, raxis, raxis_angle) dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, \ ldips_org, ldipdir_lst, ldips_lst, sense, dipdir_az = rtrn[0], \ rtrn[1], rtrn[2], rtrn[3], rtrn[4], rtrn[5], rtrn[6], rtrn[7], \ rtrn[8], rtrn[9] store, new_lyr_obj = self.add_layer_dataset("faultplane") for dipdir, dip, ldipdir, ldip, sns in zip(dipdir_az, dips_lst, ldipdir_lst, ldips_lst, sense): self.add_feature("faultplane", store, dipdir, dip, ldipdir, ldip, sns) new_lyr_obj.set_properties(lyr_obj.get_properties()) self.dialog.hide() self.redraw_main() def on_spinbutton_rotation_dipdir_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def on_spinbutton_rotation_dip_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def on_spinbutton_rotation_angle_value_changed(self, spinbutton): """ Redraws the plot. When the value of the spinbutton is changed, the redraw_plot method is called, which rotates the data according to the new setting. """ self.redraw_plot() def convert_lonlat_to_dipdir(self, lon, lat): """ Converts lat-lon data to dip-direction and dip. Expects a longitude and a latitude value. The measurment is forward transformed into stereonet-space. Then the azimut (dip-direction) and diping angle are calculated. Returns two values: dip-direction and dip. """ #The longitude and latitude have to be forward-transformed to get #the corect azimuth angle xy = np.array([[lon, lat]]) xy_trans = self.trans.transform(xy) x = float(xy_trans[0,0:1]) y = float(xy_trans[0,1:2]) alpha = np.arctan2(x, y) alpha_deg = np.degrees(alpha) if alpha_deg < 0: alpha_deg += 360 #Longitude and Latitude don't need to be converted for rotation. #The correct dip is the array[1] value once the vector has been #rotated in north-south position. array = mplstereonet.stereonet_math._rotate(np.degrees(lon), np.degrees(lat), alpha_deg * (-1)) gamma = float(array[1]) gamma_deg = 90 - np.degrees(gamma) #If the longitude is larger or small than pi/2 the measurment lies #on the upper hemisphere and needs to be corrected. if lon > (np.pi / 2) or lon < (-np.pi / 2): alpha_deg = alpha_deg + 180 return alpha_deg, gamma_deg def rotate_data(self, raxis, raxis_angle, dipdir, dip): """ Rotates a measurment around a rotation axis a set number of degrees. Expects a rotation-axis, a rotation-angle, a dip-direction and a dip angle. The measurement is converted to latlot and then passed to the mplstereonet rotate function. """ lonlat = mplstereonet.line(dip, dipdir) #Rotation around x-axis until rotation-axis azimuth is east-west rot1 = (90 - raxis[0]) lon1 = np.degrees(lonlat[0]) lat1 = np.degrees(lonlat[1]) lon_rot1, lat_rot1 = mplstereonet.stereonet_math._rotate(lon1, lat1, theta=rot1, axis="x") #Rotation around z-axis until rotation-axis dip is east-west rot2 = -(90 - raxis[1]) lon2 = np.degrees(lon_rot1) lat2 = np.degrees(lat_rot1) lon_rot2, lat_rot2 = mplstereonet.stereonet_math._rotate(lon2, lat2, theta=rot2, axis="z") #Rotate around the x-axis for the specified rotation: rot3 = raxis_angle lon3 = np.degrees(lon_rot2) lat3 = np.degrees(lat_rot2) lon_rot3, lat_rot3 = mplstereonet.stereonet_math._rotate(lon3, lat3, theta=rot3, axis="x") #Undo the z-axis rotation rot4 = -rot2 lon4 = np.degrees(lon_rot3) lat4 = np.degrees(lat_rot3) lon_rot4, lat_rot4 = mplstereonet.stereonet_math._rotate(lon4, lat4, theta=rot4, axis="z") #Undo the x-axis rotation rot5 = -rot1 lon5 = np.degrees(lon_rot4) lat5 = np.degrees(lat_rot4) lon_rot5, lat_rot5 = mplstereonet.stereonet_math._rotate(lon5, lat5, theta=rot5, axis="x") dipdir5, dip5 = self.convert_lonlat_to_dipdir(lon_rot5, lat_rot5) return dipdir5, dip5 def parse_plane(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a plane layer. Expects a TreeStore of a layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ dipdir_org = [] dips_org = [] dipdir_lst = [] dips_lst = [] dipdir_az = [] strat = [] for row in lyr_store: dipdir_org.append(row[0] - 90) dips_org.append(row[1]) #Planes and faultplanes are rotated using their poles dipdir, dip = self.rotate_data(raxis, raxis_angle, row[0] + 180, 90 - row[1]) dipdir_lst.append(dipdir + 90) dipdir_az.append(dipdir + 180) dips_lst.append(90 - dip) strat.append(row[2]) return dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az def parse_line(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a linear or smallcircle layer. Expects a TreeStore of a layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ ldipdir_org = [] ldips_org = [] ldipdir_lst = [] ldips_lst = [] third_col = [] for row in lyr_store: ldipdir_org.append(row[0]) ldips_org.append(row[1]) ldipdir, ldip = self.rotate_data(raxis, raxis_angle, row[0], row[1]) ldipdir_lst.append(ldipdir) ldips_lst.append(ldip) third_col.append(row[2]) return ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, third_col def parse_faultplane(self, lyr_store, raxis, raxis_angle): """ Parses and rotates data of a faultplane layer. Expects a TreeStore of a faultplane layer, the rotation axis and the angle of rotation. The method returns each column unrotated and rotated. """ dipdir_org = [] dips_org = [] dipdir_lst = [] dips_lst = [] ldipdir_org = [] ldips_org = [] ldipdir_lst = [] ldips_lst = [] dipdir_az = [] sense = [] for row in lyr_store: dipdir_org.append(row[0] - 90) dips_org.append(row[1]) #Planes and faultplanes are rotated using their poles dipdir, dip = self.rotate_data(raxis, raxis_angle, row[0] + 180, 90 - row[1]) dipdir_lst.append(dipdir + 90) dipdir_az.append(dipdir + 270) dips_lst.append(90 - dip) ldipdir_org.append(row[2]) ldips_org.append(row[3]) ldipdir, ldip = self.rotate_data(raxis, raxis_angle, row[2], row[3]) ldipdir_lst.append(ldipdir) ldips_lst.append(ldip) sense.append(row[4]) return (dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense, dipdir_az) def redraw_plot(self): """ Redraws the plot using the current settings of the dialog's spinbuttons. This method clears the two axes and adds the annotations. The current values of the rotation axis and rotation angle spinbuttons are retrieved. The data is parsed, and the features are then drawn. In addition the rotation-axis is drawn. """ self.original_ax.cla() self.rotated_ax.cla() self.original_ax.grid(False) self.rotated_ax.grid(False) self.original_ax.set_azimuth_ticks([0], labels=["N"]) self.rotated_ax.set_azimuth_ticks([0], labels=["N"]) bar = 0.05 self.original_ax.annotate("", xy = (-bar, 0), xytext = (bar, 0), xycoords = "data", arrowprops = dict(arrowstyle = "-", connectionstyle = "arc3")) self.original_ax.annotate("", xy = (0, -bar), xytext = (0, bar), xycoords = "data", arrowprops = dict(arrowstyle = "-", connectionstyle = "arc3")) self.rotated_ax.annotate("", xy = (-bar, 0), xytext = (bar, 0), xycoords = "data", arrowprops = dict(arrowstyle = "-", connectionstyle = "arc3")) self.rotated_ax.annotate("", xy = (0, -bar), xytext = (0, bar), xycoords = "data", arrowprops = dict(arrowstyle = "-", connectionstyle = "arc3")) raxis_dipdir = self.spinbutton_rotation_dipdir.get_value() raxis_dip = self.spinbutton_rotation_dip.get_value() raxis = [raxis_dipdir, raxis_dip] raxis_angle = self.spinbutton_rotation_angle.get_value() for lyr_obj in self.data: lyr_type = lyr_obj.get_layer_type() lyr_store = lyr_obj.get_data_treestore() if lyr_type == "plane": dipdir_org, dips_org, dipdir_lst, dips_lst, strat, dipdir_az = \ self.parse_plane(lyr_store, raxis, raxis_angle) self.original_ax.plane(dipdir_org, dips_org, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.rotated_ax.plane(dipdir_lst, dips_lst, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) elif lyr_type == "line": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, sense = \ self.parse_line(lyr_store, raxis, raxis_angle) self.original_ax.line(ldips_org, ldipdir_org, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) self.rotated_ax.line(ldips_lst, ldipdir_lst, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) elif lyr_type == "smallcircle": ldipdir_org, ldips_org, ldipdir_lst, ldips_lst, angle = \ self.parse_line(lyr_store, raxis, raxis_angle) self.original_ax.cone(ldips_org, ldipdir_org, angle, facecolor="None", color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), label=lyr_obj.get_label(), linestyle=lyr_obj.get_line_style()) self.rotated_ax.cone(ldips_lst, ldipdir_lst, angle, facecolor="None", color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), label=lyr_obj.get_label(), linestyle=lyr_obj.get_line_style()) elif lyr_type == "faultplane": rtrn = self.parse_faultplane(lyr_store, raxis, raxis_angle) dipdir_org, dips_org, dipdir_lst, dips_lst, ldipdir_org, \ ldips_org, ldipdir_lst, ldips_lst, sense = rtrn[0], rtrn[1], \ rtrn[2], rtrn[3], rtrn[4], rtrn[5], rtrn[6], rtrn[7], rtrn[8] self.original_ax.plane(dipdir_org, dips_org, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.rotated_ax.plane(dipdir_lst, dips_lst, color=lyr_obj.get_line_color(), linewidth=lyr_obj.get_line_width(), linestyle=lyr_obj.get_line_style(), dash_capstyle=lyr_obj.get_capstyle(), alpha=lyr_obj.get_line_alpha(), clip_on=False) self.original_ax.line(ldips_org, ldipdir_org, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) self.rotated_ax.line(ldips_lst, ldipdir_lst, marker=lyr_obj.get_marker_style(), markersize=lyr_obj.get_marker_size(), color=lyr_obj.get_marker_fill(), markeredgewidth=lyr_obj.get_marker_edge_width(), markeredgecolor=lyr_obj.get_marker_edge_color(), alpha=lyr_obj.get_marker_alpha(), clip_on=False) #Plot rotation axis self.original_ax.line(raxis_dip, raxis_dipdir, marker="o", markersize=10, color="#ff0000", markeredgewidth=1, markeredgecolor="#000000", alpha=1, clip_on=False) self.canvas.draw()
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 Plot: # TODO scaling of size when resizing # TODO tighter fit of plot # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes # Plot object used GUI def __init__(self, builder, GPU, maxpoints, precision, linux_kernelmain, linux_kernelsub): # Can used for kernel specific workarounds self.linux_kernelmain = linux_kernelmain self.linux_kernelsub = linux_kernelsub self.precision = precision self.builder = builder self.GPU = GPU self.maxpoints = maxpoints self.fig = Figure(figsize=(1000, 150), dpi=100, facecolor="#00000000") self.fig.set_tight_layout(True) self.ax = self.fig.add_subplot(111) # enable, name, unit, mean, max, current self.signalstore = Gtk.ListStore(bool, bool, str, str, str, str, str, str) self.Plotsignals = self.init_signals(self.GPU) self.init_treeview() self.update_signals() self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(1000, 150) self.object = self.builder.get_object("matplot") self.object.add(self.canvas) self.object.show_all() def init_signals(self, GPU): Plotsignals = [] # Define signals with: names units max min path plotenable plotnormalise plotcolor parser and outputargument used from parser Plotsignals.append( Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1], GPU.pstate_clock[0], "/pp_dpm_sclk", True, True, "#1f77b4", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("GPU State", "[-]", len(GPU.pstate_clock) - 1, 0, "/pp_dpm_sclk", True, True, "#ff7f0e", GPU.get_current_clock, 1)) Plotsignals.append( Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1], GPU.pmem_clock[0], "/pp_dpm_mclk", True, True, "#d62728", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("MEM State", "[-]", len(GPU.pmem_clock) - 1, 0, "/pp_dpm_mclk", True, True, "#9467bd", GPU.get_current_clock, 1)) if GPU.fanpwmsensors is not None: Plotsignals.append( Plotsignal("FAN Speed", "[0-255]", GPU.fanpwmsensors.read_attribute('_max', True), 0, GPU.fanpwmsensors.path, True, True, "#8c564b", GPU.fanpwmsensors.read)) if GPU.tempsensors is not None: Plotsignals.append( Plotsignal("TEMP 1", "[m°C]", GPU.tempsensors.read_attribute('_crit', True), 0, GPU.tempsensors.path, True, True, "#e377c2", GPU.tempsensors.read)) if GPU.powersensors is not None: Plotsignals.append( Plotsignal("POWER", "[µW]", GPU.powersensors.read_attribute('_cap', True), 0, GPU.powersensors.path, True, True, "#7f7f7f", GPU.powersensors.read)) # GPU busy percent only properly available in linux version 4.19+ if (self.linux_kernelmain == 4 and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5): Plotsignals.append( Plotsignal("GPU Usage", "[-]", 100, 0, "/gpu_busy_percent", True, True, "#2ca02c", parser=GPU.read_sensor)) return Plotsignals def init_treeview(self): textrenderer = Gtk.CellRendererText() plotrenderer = Gtk.CellRendererToggle() plotrenderer.connect("toggled", self.on_plot_toggled) normaliserenderer = Gtk.CellRendererToggle() normaliserenderer.connect("toggled", self.on_normalise_toggled) self.tree = self.builder.get_object("Signal Selection") self.tree.append_column( Gtk.TreeViewColumn("Plot", plotrenderer, active=0)) self.tree.append_column( Gtk.TreeViewColumn("Scale", normaliserenderer, active=1)) columnnames = ["Name", "Unit", "mean", "max", "current"] for i, column in enumerate(columnnames): tcolumn = Gtk.TreeViewColumn(column, textrenderer, text=i + 2, foreground=7) self.tree.append_column(tcolumn) for plotsignal in self.Plotsignals: self.signalstore.append([ plotsignal.plotenable, plotsignal.plotnormalise, plotsignal.name, convert_to_si(plotsignal.unit)[0], '0', '0', '0', plotsignal.plotcolor ]) self.tree.set_model(self.signalstore) def update_signals(self): # Retrieve signal and set appropriate values in signalstore to update left pane in GUI for i, Plotsignal in enumerate(self.Plotsignals): Plotsignal.retrieve_data(self.maxpoints) self.signalstore[i][4] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1], self.precision)) self.signalstore[i][5] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1], self.precision)) self.signalstore[i][6] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_last_value())[1], self.precision)) def on_plot_toggled(self, widget, path): self.signalstore[path][0] = not self.signalstore[path][0] self.Plotsignals[int( path)].plotenable = not self.Plotsignals[int(path)].plotenable self.update_plot() def on_normalise_toggled(self, widget, path): self.signalstore[path][1] = not self.signalstore[path][1] self.Plotsignals[int(path)].plotnormalise = not self.Plotsignals[int( path)].plotnormalise self.update_plot() def update_plot(self): self.ax.clear() for Plotsignal in self.Plotsignals: if Plotsignal.plotenable: if Plotsignal.plotnormalise: data = Plotsignal.get_normalised_values() self.ax.plot(data, color=Plotsignal.plotcolor) else: data = Plotsignal.get_values() self.ax.plot(convert_to_si(Plotsignal.unit, data)[1], color=Plotsignal.plotcolor) self.ax.grid(True) self.ax.get_yaxis().tick_right() self.ax.get_yaxis().set_visible(True) self.ax.get_xaxis().set_visible(False) all_normalised = True iter = self.signalstore.get_iter(0) while iter is not None: if self.signalstore[iter][1] == False: all_normalised = False break iter = self.signalstore.iter_next(iter) if all_normalised: self.ax.set_yticks(np.arange(0, 1.1, step=0.1)) else: self.ax.yaxis.set_major_locator(AutoLocator()) self.canvas.draw() self.canvas.flush_events() def refresh(self): # This is run in thread self.update_signals() self.update_plot()
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 Plot: # TODO scaling of size when resizing # TODO tighter fit of plot # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes # Plot object used GUI def __init__(self, builder, GPU, maxpoints, precision, linux_kernelmain, linux_kernelsub): # Can used for kernel specific workarounds self.linux_kernelmain = linux_kernelmain self.linux_kernelsub = linux_kernelsub self.precision = precision self.builder = builder self.GPU = GPU self.maxpoints = maxpoints self.fig = Figure(figsize=(1000, 150), dpi=100) self.fig.set_tight_layout(True) self.ax = self.fig.add_subplot(111) # enable, name, unit, mean, max, current self.signalstore = Gtk.ListStore(bool, str, str, str, str, str) self.Plotsignals = self.init_signals(self.GPU) self.init_treeview() self.update_signals() self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(1000, 150) self.object = self.builder.get_object("matplot") self.object.add(self.canvas) self.object.show_all() def init_signals(self, GPU): Plotsignals = [] # Define signals with: names units min max path plotenable plotcolor parser and outputargument used from parser Plotsignals.append( Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1], GPU.pstate_clock[0], "/pp_dpm_sclk", True, "#1f77b4", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("GPU State", "[-]", 0, len(GPU.pstate_clock), "/pp_dpm_sclk", True, "#ff7f0e", GPU.get_current_clock, 1)) Plotsignals.append( Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1], GPU.pmem_clock[0], "/pp_dpm_mclk", True, "#d62728", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("MEM State", "[-]", 0, len(GPU.pmem_clock), "/pp_dpm_mclk", True, "#9467bd", GPU.get_current_clock, 1)) Plotsignals.append( Plotsignal("FAN Speed", "[RPM]", 0, 255, "/hwmon/hwmon0/subsystem/hwmon0/fan1_input", True, "#8c564b", GPU.read_sensor)) Plotsignals.append( Plotsignal( "TEMP 1", "[m°C]", 0, GPU.read_sensor("/hwmon/hwmon0/subsystem/hwmon0/temp1_crit"), "/hwmon/hwmon0/subsystem/hwmon0/temp1_input", True, "#e377c2", GPU.read_sensor)) Plotsignals.append( Plotsignal("POWER", "[µW]", 0, GPU.read_sensor("/hwmon/hwmon0/power1_cap_max"), "/hwmon/hwmon0/power1_average", True, "#7f7f7f", GPU.read_sensor)) # GPU busy percent only properly available in linux version 4.19+ if (self.linux_kernelmain == 4 and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5): Plotsignals.append( Plotsignal("GPU Usage", "[-]", 1, 0, "/gpu_busy_percent", True, "#2ca02c", parser=GPU.read_sensor)) return Plotsignals def init_treeview(self): textrenderer = Gtk.CellRendererText() boolrenderer = Gtk.CellRendererToggle() boolrenderer.connect("toggled", self.on_cell_toggled) self.tree = self.builder.get_object("Signal Selection") self.tree.append_column( Gtk.TreeViewColumn("Plot", boolrenderer, active=0)) columnnames = ["Name", "Unit", "mean", "max", "current"] for i, column in enumerate(columnnames): tcolumn = Gtk.TreeViewColumn(column, textrenderer, text=i + 1) self.tree.append_column(tcolumn) #if i == 0: # tcolumn.set_sort_column_id(i+1) for plotsignal in self.Plotsignals: self.signalstore.append([ plotsignal.plotenable, plotsignal.name, convert_to_si(plotsignal.unit)[0], '0', '0', '0' ]) self.tree.set_model(self.signalstore) def update_signals(self): # Retrieve signal and set appropriate values in signalstore to update left pane in GUI for i, Plotsignal in enumerate(self.Plotsignals): Plotsignal.retrieve_data(self.maxpoints) self.signalstore[i][3] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1], self.precision)) self.signalstore[i][4] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1], self.precision)) self.signalstore[i][5] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_last_value())[1], self.precision)) def on_cell_toggled(self, widget, path): self.signalstore[path][0] = not self.signalstore[path][0] self.Plotsignals[int( path)].plotenable = not self.Plotsignals[int(path)].plotenable self.update_plot() def update_plot(self): self.ax.clear() for Plotsignal in self.Plotsignals: if Plotsignal.plotenable: self.ax.plot(convert_to_si(Plotsignal.unit, Plotsignal.get_values())[1], color=Plotsignal.plotcolor) #self.plots = [self.ax.plot(values, color=plotcolor) for values, plotcolor in zip(self.y.T, self.signalcolors)] self.ax.get_yaxis().set_visible(True) self.ax.get_xaxis().set_visible(True) self.canvas.draw() self.canvas.flush_events() def refresh(self): # This is run in thread self.update_signals() self.update_plot()
class App(Gtk.Application): def __init__(self): ''' Build GUI ''' # build GUI from glade file self.builder = Gtk.Builder() self.glade_file = os.path.join(curr_dir, 'kfit.glade') self.builder.add_from_file(self.glade_file) # get the necessary ui objects self.main_window = self.builder.get_object('main_window') self.graph_box = self.builder.get_object('graph_box') self.gau_sw = self.builder.get_object('param_scroll_gau') self.lor_sw = self.builder.get_object('param_scroll_lor') self.voi_sw = self.builder.get_object('param_scroll_voi') self.lin_sw = self.builder.get_object('param_scroll_lin') self.param_viewport_gau = self.builder.get_object('param_viewport_gau') self.param_viewport_lor = self.builder.get_object('param_viewport_lor') self.param_viewport_voi = self.builder.get_object('param_viewport_voi') self.param_viewport_lin = self.builder.get_object('param_viewport_lin') self.statusbar_viewport = self.builder.get_object('statusbar_viewport') self.data_treeview = self.builder.get_object('data_treeview') self.column_entry_x = self.builder.get_object('column_entry_x') self.column_entry_y = self.builder.get_object('column_entry_y') self.graph_box = self.builder.get_object('graph_box') self.fname_textview = self.builder.get_object('fname_textview') self.fit_button = self.builder.get_object('fit_button') self.reset_button = self.builder.get_object('reset_button') self.settings_button = self.builder.get_object('settings_button') self.import_button = self.builder.get_object('import_button') self.export_button = self.builder.get_object('export_button') self.help_button = self.builder.get_object('help_button') self.output_textview = self.builder.get_object('output_textview') self.settings_dialog = self.builder.get_object('settings_dialog') self.sep_entry = self.builder.get_object('sep_entry') self.header_entry = self.builder.get_object('header_entry') self.skiprows_entry = self.builder.get_object('skiprows_entry') self.dtype_entry = self.builder.get_object('dtype_entry') self.encoding_entry = self.builder.get_object('encoding_entry') self.fit_method_entry = self.builder.get_object('fit_method_entry') self.add_gau = self.builder.get_object('add_gau') self.rem_gau = self.builder.get_object('rem_gau') self.add_lor = self.builder.get_object('add_lor') self.rem_lor = self.builder.get_object('rem_lor') self.add_voi = self.builder.get_object('add_voi') self.rem_voi = self.builder.get_object('rem_voi') self.add_lin = self.builder.get_object('add_lin') self.rem_lin = self.builder.get_object('rem_lin') # define other class attributes self.title = 'kfit' self.file_name = '' self.xcol_idx = 0 self.ycol_idx = 1 self.cmode_state = 0 self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) # configure help button self.help_button.set_label(' Help') help_image = Gtk.Image() help_image.set_from_file( os.path.join(curr_dir, '../images/dialog-question-symbolic.svg') ) self.help_button.set_image(help_image) self.help_button.set_always_show_image(True) # for graph... x = np.linspace(0, 10, 500) y = models.gauss(x, 0.5, 4, 0.4) + \ models.gauss(x, 0.8, 5, 0.2) + \ models.gauss(x, 0.4, 6, 0.3) + 0.2 self.data = pd.DataFrame([x, y]).T self.data.columns = ['x', 'y'] self.x = self.data['x'].values self.y = self.data['y'].values self.xmin = self.data['x'].min() self.xmax = self.data['x'].max() plt.style.use(os.path.join(curr_dir, 'kfit.mplstyle')) self.figure = Figure(figsize=(10, 4), dpi=60) self.canvas = FigureCanvas(self.figure) self.canvas.set_size_request(900, 400) self.toolbar = NavigationToolbar(self.canvas, self.main_window) self.graph_box.pack_start(self.toolbar, True, True, 0) self.graph_box.pack_start(self.canvas, True, True, 0) self.cmode_toolitem = Gtk.ToolItem() self.cmode_box = Gtk.Box() self.cmode_box.set_margin_start(24) self.cmode_box.set_margin_end(36) self.cmode_radio_off = Gtk.RadioButton.new_with_label_from_widget( None, label='off' ) self.cmode_radio_off.connect('toggled', self.toggle_copy_mode) self.cmode_radio_x = Gtk.RadioButton.new_from_widget( self.cmode_radio_off ) self.cmode_radio_x.set_label('x') self.cmode_radio_x.connect('toggled', self.toggle_copy_mode) self.cmode_radio_y = Gtk.RadioButton.new_from_widget( self.cmode_radio_off ) self.cmode_radio_y.set_label('y') self.cmode_radio_y.connect('toggled', self.toggle_copy_mode) self.cmode_box.pack_start( Gtk.Label(label='Copy mode:'), False, False, 0 ) self.cmode_box.pack_start(self.cmode_radio_off, False, False, 0) self.cmode_box.pack_start(self.cmode_radio_x, False, False, 0) self.cmode_box.pack_start(self.cmode_radio_y, False, False, 0) self.cmode_toolitem.add(self.cmode_box) self.toolbar.insert(self.cmode_toolitem, -1) # for fit... self.fit_method = 'least_squares' self.model = None self.result = None self.yfit = None self.ngau = 0 self.nlor = 0 self.nvoi = 0 self.nlin = 1 self.curves_df = None self.params_df = None self.params = Parameters() self.guesses = { 'value': {}, 'min': {}, 'max': {} } self.usr_vals = { 'value': {}, 'min': {}, 'max': {} } self.usr_entry_widgets = {} self.cid = None # for data view... self.fname_buffer = Gtk.TextBuffer() self.display_data() # for output... self.output_buffer = Gtk.TextBuffer() self.output_textview.set_buffer(self.output_buffer) # file import settings self.sep = ',' self.header = 'infer' self.index_col = None self.skiprows = None self.dtype = None self.encoding = None # show initial plot self.plot() # add statusbar self.statusbar = Gtk.Statusbar() self.statusbar.set_margin_top(0) self.statusbar.set_margin_bottom(0) self.statusbar.set_margin_start(0) self.statusbar.set_margin_end(0) self.statusbar_viewport.add(self.statusbar) # connect signals events = { 'on_fit_button_clicked': self.fit, 'on_import_button_clicked': self.get_data, 'on_settings_button_clicked': self.run_settings_dialog, 'on_reset_button_clicked': self.hard_reset, 'on_add_gau_clicked': self.on_add_gau_clicked, 'on_rem_gau_clicked': self.on_rem_gau_clicked, 'on_add_lor_clicked': self.on_add_lor_clicked, 'on_rem_lor_clicked': self.on_rem_lor_clicked, 'on_add_voi_clicked': self.on_add_voi_clicked, 'on_rem_voi_clicked': self.on_rem_voi_clicked, 'on_add_lin_clicked': self.on_add_lin_clicked, 'on_rem_lin_clicked': self.on_rem_lin_clicked, 'on_column_entry_changed': self.get_column_index, 'on_export_button_clicked': self.export_data, } self.builder.connect_signals(events) # add accelerators / keyboard shortcuts self.accelerators = Gtk.AccelGroup() self.main_window.add_accel_group(self.accelerators) self.add_accelerator(self.fit_button, '<Control>f') self.add_accelerator(self.reset_button, '<Control>r') self.add_accelerator(self.settings_button, '<Control>p') self.add_accelerator(self.import_button, '<Control>o') self.add_accelerator(self.export_button, '<Control>s') self.add_accelerator(self.add_gau, 'g') self.add_accelerator(self.rem_gau, '<Shift>g') self.add_accelerator(self.add_lor, 'l') self.add_accelerator(self.rem_lor, '<Shift>l') self.add_accelerator(self.add_voi, 'v') self.add_accelerator(self.rem_voi, '<Shift>v') self.add_accelerator(self.add_lin, 'n') self.add_accelerator(self.rem_lin, '<Shift>n') # configure interface self.main_window.connect('destroy', Gtk.main_quit) self.init_param_widgets() # show the app window self.main_window.show_all() def plot(self): self.figure.clear() self.axis = self.figure.add_subplot(111) self.set_xlims() if len(self.x) >= 1000: self.axis.plot( self.x, self.y, c='#af87ff', linewidth=12, label='data' ) else: self.axis.scatter( self.x, self.y, s=200, c='#af87ff', edgecolors='black', linewidth=1, label='data' ) if self.result is not None: self.yfit = self.result.best_fit self.axis.plot(self.x, self.yfit, c='r', linewidth=2.5) cmap = cm.get_cmap('gnuplot') components = self.result.eval_components() for i, comp in enumerate(components): self.axis.plot( self.x, components[comp], linewidth=2.5, linestyle='--', c=cmap(i/len(components)), label=comp[:comp.find('_')] ) self.axis.set_xlabel(self.data.columns[self.xcol_idx]) self.axis.set_ylabel(self.data.columns[self.ycol_idx]) self.axis.legend(loc='best') self.axis.set_xlim([self.xmin, self.xmax]) self.canvas.draw() def fit(self, source=None, event=None): self.cmode_radio_off.set_active(True) self.toggle_copy_mode(self.cmode_radio_off) self.set_xrange_to_zoom() self.filter_nan() self.set_params() self.result = self.model.fit( data=self.y, params=self.params, x=self.x, method=self.fit_method ) self.output_buffer.set_text(self.result.fit_report()) self.plot() # overwrite widgets to clear input (not ideal method..) self.init_param_widgets() def init_model(self): # note: increment() ensures nlin >= 1 self.model = models.line_mod(self.nlin) if self.ngau != 0: self.model += models.gauss_mod(self.ngau) if self.nlor != 0: self.model += models.lor_mod(self.nlor) if self.nvoi != 0: self.model += models.voigt_mod(self.nvoi) self.statusbar.push( self.statusbar.get_context_id('info'), "Model updated: " + str([self.ngau, self.nlor, self.nvoi, self.nlin]) ) def init_param_widgets(self): self.init_model() self.usr_entry_widgets = { 'value': {}, 'min': {}, 'max': {} } labels = {} rnd = 3 # decimals to round to in placeholder text self.clear_param_viewports() # main boxes to hold user entry widgets self.vbox_gau = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.vbox_lor = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.vbox_voi = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.vbox_lin = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) for param_name in self.model.param_names: # set param label text labels[param_name] = Gtk.Label() labels[param_name].set_text(param_name) # make user entry widgets for key in self.usr_entry_widgets: self.usr_entry_widgets[key][param_name] = Gtk.Entry() if param_name in self.usr_vals[key]: self.usr_entry_widgets[key][param_name]\ .set_placeholder_text( str(round(self.usr_vals[key][param_name], rnd)) ) else: self.usr_entry_widgets[key][param_name]\ .set_placeholder_text(key) # set up connections self.usr_entry_widgets[key][param_name].connect( 'changed', self.update_usr_vals, self.usr_entry_widgets ) # add widgets to respective layouts vbox_sub = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) for key in self.usr_entry_widgets: vbox_sub.pack_start( self.usr_entry_widgets[key][param_name], False, False, pad ) if param_name.find('gau') != -1: self.vbox_gau.pack_start(labels[param_name], False, False, pad) self.vbox_gau.pack_start(vbox_sub, False, False, pad) self.vbox_gau.set_halign(Gtk.Align.CENTER) if param_name.find('lor') != -1: self.vbox_lor.pack_start(labels[param_name], False, False, pad) self.vbox_lor.pack_start(vbox_sub, False, False, pad) self.vbox_lor.set_halign(Gtk.Align.CENTER) if param_name.find('voi') != -1: self.vbox_voi.pack_start(labels[param_name], False, False, pad) self.vbox_voi.pack_start(vbox_sub, False, False, pad) self.vbox_voi.set_halign(Gtk.Align.CENTER) if param_name.find('lin') != -1: self.vbox_lin.pack_start(labels[param_name], False, False, pad) self.vbox_lin.pack_start(vbox_sub, False, False, pad) self.vbox_lin.set_halign(Gtk.Align.CENTER) # Resize all of the entry widgets for key in self.usr_entry_widgets: for param, widget in self.usr_entry_widgets[key].items(): widget.set_width_chars(7) # add/replace box in viewport self.param_viewport_gau.add(self.vbox_gau) self.param_viewport_lor.add(self.vbox_lor) self.param_viewport_voi.add(self.vbox_voi) self.param_viewport_lin.add(self.vbox_lin) for viewport in [self.param_viewport_gau, self.param_viewport_lor, self.param_viewport_voi, self.param_viewport_lin]: viewport.show_all() if self.result is not None: self.set_params() self.update_param_widgets() def update_usr_vals(self, widget, entry_widget_dict): # get text input from each usr_entry_widget for val_type, param_dict in entry_widget_dict.items(): for param, param_widget in param_dict.items(): try: self.usr_vals[val_type][param] = \ float(param_widget.get_text()) except Exception: pass def update_param_widgets(self): rnd = 3 # the 'value' placeholder text is the result for that param # taken from self.result # the 'min' and 'max' text is from either the self.guesses # or from self.usr_vals for param in self.params: if param in self.result.best_values: self.usr_entry_widgets['value'][param].set_placeholder_text( str(round(self.result.best_values[param], rnd)) ) self.usr_entry_widgets['min'][param].set_placeholder_text( str(round(self.params[param].min, rnd)) ) self.usr_entry_widgets['max'][param].set_placeholder_text( str(round(self.params[param].max, rnd)) ) def guess_params(self, source=None, event=None): for comp in self.model.components: if comp.prefix.find('gau') != -1 or \ comp.prefix.find('lor') != -1 or \ comp.prefix.find('voi') != -1: # need to define explicitly to make proper guesses c = comp.prefix + 'center' a = comp.prefix + 'amplitude' s = comp.prefix + 'sigma' f = comp.prefix + 'fraction' self.guesses['value'][c] = \ self.data.iloc[:, self.xcol_idx].mean() self.guesses['value'][a] = \ self.data.iloc[:, self.ycol_idx].mean() self.guesses['value'][s] = \ self.data.iloc[:, self.xcol_idx].std() self.guesses['min'][c] = None self.guesses['min'][a] = 0 self.guesses['min'][s] = 0 self.guesses['max'][c] = None self.guesses['max'][a] = None self.guesses['max'][s] = None if comp.prefix.find('voi') != -1: self.guesses['value'][f] = 0.5 self.guesses['min'][f] = 0 self.guesses['max'][f] = 1 else: slope = comp.prefix + 'slope' intc = comp.prefix + 'intercept' for p in [slope, intc]: self.guesses['value'][p] = \ self.data.iloc[:, self.ycol_idx].mean() self.guesses['min'][p] = None self.guesses['max'][p] = None def set_params(self, source=None, event=None): self.params = Parameters() self.guess_params() self.update_usr_vals(None, self.usr_entry_widgets) vals = {} # fill params with any user-entered values # fill in blanks with guesses for param_name in self.model.param_names: for val_type in ['value', 'min', 'max']: if param_name in self.usr_vals[val_type]: vals[val_type] = self.usr_vals[val_type][param_name] else: vals[val_type] = self.guesses[val_type][param_name] self.params.add( name=param_name, value=vals['value'], vary=True, min=vals['min'], max=vals['max'] ) def set_xlims(self, source=None, event=None): self.xmin = np.min(self.x) - 0.02*(np.max(self.x) - np.min(self.x)) self.xmax = np.max(self.x) + 0.02*(np.max(self.x) - np.min(self.x)) def set_xrange_to_zoom(self): self.xmin, self.xmax = self.axis.get_xlim() range_bool = (self.x >= self.xmin) & (self.x <= self.xmax) self.x = self.x[range_bool] self.y = self.y[range_bool] def filter_nan(self): if True in np.isnan(self.x) or True in np.isnan(self.y): nanbool = (~np.isnan(self.x) & ~np.isnan(self.y)) self.x = self.x[nanbool] self.y = self.y[nanbool] def increment(self, val, add): if add: if val == 'gau': self.ngau += 1 if val == 'lor': self.nlor += 1 if val == 'voi': self.nvoi += 1 if val == 'lin': self.nlin += 1 if not add: if val == 'gau': self.ngau -= 1 if val == 'lor': self.nlor -= 1 if val == 'voi': self.nvoi -= 1 if val == 'lin': self.nlin -= 1 # make sure value doesn't go below zero if self.ngau < 0: self.ngau = 0 if self.nlor < 0: self.nlor = 0 if self.nvoi < 0: self.nvoi = 0 if self.nlin < 1: self.nlin = 1 def clear_param_viewports(self): # clear any existing widgets from viewports for viewport in [self.param_viewport_gau, self.param_viewport_lin, self.param_viewport_lor, self.param_viewport_voi]: if viewport.get_child(): viewport.remove(viewport.get_child()) def hard_reset(self, source=None, event=None): self.clear_param_viewports() self.ngau = 0 self.nlor = 0 self.nvoi = 0 self.nlin = 1 self.init_model() self.params = Parameters() self.result = None self.params_df = None self.curves_df = None self.guesses = { 'value': {}, 'min': {}, 'max': {} } self.usr_vals = { 'value': {}, 'min': {}, 'max': {} } self.output_buffer.set_text('') self.init_param_widgets() self.get_column_index() self.plot() self.cmode_radio_off.set_active(True) self.toggle_copy_mode(self.cmode_radio_off) def on_add_gau_clicked(self, source=None, event=None): self.increment('gau', True) self.init_param_widgets() def on_rem_gau_clicked(self, source=None, event=None): self.increment('gau', False) self.init_param_widgets() def on_add_lor_clicked(self, source=None, event=None): self.increment('lor', True) self.init_param_widgets() def on_rem_lor_clicked(self, source=None, event=None): self.increment('lor', False) self.init_param_widgets() def on_add_voi_clicked(self, source=None, event=None): self.increment('voi', True) self.init_param_widgets() def on_rem_voi_clicked(self, source=None, event=None): self.increment('voi', False) self.init_param_widgets() def on_add_lin_clicked(self, source=None, event=None): self.increment('lin', True) self.init_param_widgets() def on_rem_lin_clicked(self, source=None, event=None): self.increment('lin', False) self.init_param_widgets() def get_data(self, source=None, event=None): self.cmode_radio_off.set_active(True) self.toggle_copy_mode(self.cmode_radio_off) # reset column indices self.xcol_idx = 0 self.column_entry_x.set_text(str(self.xcol_idx)) self.ycol_idx = 1 self.column_entry_y.set_text(str(self.ycol_idx)) # open file dialog self.dialog = Gtk.FileChooserDialog( title='Import data file...', parent=self.main_window, action=Gtk.FileChooserAction.OPEN, ) self.dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK ) filter_csv = Gtk.FileFilter() filter_csv.set_name('.csv files') filter_csv.add_mime_type('text/csv') self.dialog.add_filter(filter_csv) filter_any = Gtk.FileFilter() filter_any.set_name('All files') filter_any.add_pattern("*") self.dialog.add_filter(filter_any) self.dialog.set_default_size(800, 400) response = self.dialog.run() if response == Gtk.ResponseType.OK: self.file_name = self.dialog.get_filename() try: df = tools.to_df( self.file_name, sep=self.sep, header=self.header, index_col=self.index_col, skiprows=self.skiprows, dtype=self.dtype, encoding=self.encoding ) df.iloc[:, self.xcol_idx] df.iloc[:, self.ycol_idx] except Exception: self.statusbar.push( self.statusbar.get_context_id('import_error'), file_import_error_msg ) self.dialog.destroy() return else: self.file_name = None self.statusbar.push( self.statusbar.get_context_id('import_canceled'), 'Import canceled.' ) self.dialog.destroy() return self.dialog.destroy() self.data = df self.display_data() self.result = None # reset x, y, and xlim self.x = self.data.iloc[:, self.xcol_idx].values self.y = self.data.iloc[:, self.ycol_idx].values self.filter_nan() self.set_xlims() self.plot() self.statusbar.push( self.statusbar.get_context_id('import_finished'), 'Imported {}'.format(self.file_name) ) def display_data(self): # remove any pre-existing columns from treeview for col in self.data_treeview.get_columns(): self.data_treeview.remove_column(col) # create model # TODO: allow for other types, and infer from data col_types = [float for col in self.data.columns] list_store = Gtk.ListStore(*col_types) # fill model with data for row in self.data.itertuples(): list_store.append( [row[i+1] for i, col in enumerate(self.data.columns)] ) # set it as TreeView model self.data_treeview.set_model(list_store) # Create and append columns for i, col in enumerate(self.data.columns): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(col, renderer, text=i) self.data_treeview.append_column(column) self.fname_buffer.set_text('Source: {}'.format(self.file_name)) self.fname_textview.set_buffer(self.fname_buffer) def export_data(self, source=None, event=None): ''' Export fit data and parameters to .csv ''' self.file_export_dialog = Gtk.FileChooserDialog( title='Export results file...', parent=self.main_window, action=Gtk.FileChooserAction.SAVE, ) self.file_export_dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK ) filter_csv = Gtk.FileFilter() filter_csv.set_name('.csv files') filter_csv.add_mime_type('text/csv') self.file_export_dialog.add_filter(filter_csv) response = self.file_export_dialog.run() if response == Gtk.ResponseType.OK: export_filename = self.file_export_dialog.get_filename() if export_filename.find('.csv') == -1: export_filename += '.csv' self.process_results() self.curves_df.to_csv(export_filename) self.params_df.to_csv( '{}.params.csv'.format( export_filename[:export_filename.find('.csv')] ) ) self.statusbar.push( self.statusbar.get_context_id('export_results'), 'Exported: {}'.format(export_filename) ) else: self.statusbar.push( self.statusbar.get_context_id('export_canceled'), 'Export canceled.' ) self.file_export_dialog.hide() def process_results(self): if self.result is not None: self.params_df = pd.DataFrame.from_dict( self.result.best_values, orient='index' ) self.params_df.index.name = 'parameter' self.params_df.columns = ['value'] curves_dict = { 'data': self.y, 'total_fit': self.result.best_fit, } components = self.result.eval_components() for i, comp in enumerate(components): curves_dict[comp[:comp.find('_')]] = components[comp] self.curves_df = pd.DataFrame.from_dict(curves_dict) self.curves_df.index = self.x self.curves_df.index.name = self.data.columns[self.xcol_idx] else: self.statusbar.push( self.statusbar.get_context_id('no_fit_results'), 'No fit results to export!' ) self.file_export_dialog.hide() def get_column_index(self, source=None, event=None): # make sure user enters index that can be converted to int try: idx_x = int(self.column_entry_x.get_text()) except ValueError: self.statusbar.push( self.statusbar.get_context_id('idx_type_error'), idx_type_error_msg ) self.column_entry_x.set_text('') return try: idx_y = int(self.column_entry_y.get_text()) except ValueError: self.statusbar.push( self.statusbar.get_context_id('idx_type_error'), idx_type_error_msg ) self.column_entry_y.set_text('') return self.xcol_idx = idx_x self.ycol_idx = idx_y self.result = None # make sure user enters an index that's in the data range try: self.x = self.data.iloc[:, self.xcol_idx] except IndexError: self.statusbar.push( self.statusbar.get_context_id('idx_range_error'), idx_range_error_msg ) self.column_entry_x.set_text(None) return try: self.y = self.data.iloc[:, self.ycol_idx] except IndexError: self.statusbar.push( self.statusbar.get_context_id('idx_range_error'), idx_range_error_msg ) self.column_entry_y.set_text(None) return self.xmin = np.min(self.x) self.xmax = np.max(self.x) self.statusbar.push( self.statusbar.get_context_id('new_idx_success'), 'Column Index (X) = ' + str(self.xcol_idx) + ', ' + 'Column Index (Y) = ' + str(self.ycol_idx) ) self.plot() def run_settings_dialog(self, source=None, event=None): ''' Opens the settings dialog window and controls its behavior. ''' # set label text for help button # couldn't do this in glade for some reason... import_help_button = self.builder.get_object('import_help_button') import_help_button.set_label('help') fit_help_button = self.builder.get_object('fit_help_button') fit_help_button.set_label('help') # run the dialog response = self.settings_dialog.run() if response == Gtk.ResponseType.APPLY: self.sep = self.sep_entry.get_text() if self.header_entry.get_text() != 'infer': self.header = int(self.header_entry.get_text()) else: self.header = 'infer' if self.skiprows_entry.get_text() == 'None': self.skiprows = None else: self.skiprows = int(self.skiprows_entry.get_text()) if self.dtype_entry.get_text() == 'None': self.dtype = None else: self.dtype = self.dtype_entry.get_text() if self.encoding_entry.get_text() == 'None': self.encoding = None else: self.encoding = self.encoding_entry.get_text() if self.fit_method_entry.get_text() != 'least_squares': self.fit_method = self.fit_method_entry.get_text() else: self.fit_method = 'least_squares' else: self.settings_dialog.hide() self.settings_dialog.hide() def toggle_copy_mode(self, button): state_messages = { 0: 'Copy mode off', 1: 'Copy mode on | x-value', 2: 'Copy mode on | y-value', } if button.get_active(): if button.get_label() == 'x': self.cmode_state = 1 self.mpl_cursor = Cursor( self.axis, lw=1, c='red', linestyle='--' ) self.cid = self.canvas.mpl_connect( 'button_press_event', self.get_coord_click ) elif button.get_label() == 'y': self.cmode_state = 2 self.mpl_cursor = Cursor( self.axis, lw=1, c='red', linestyle='--' ) self.cid = self.canvas.mpl_connect( 'button_press_event', self.get_coord_click ) else: # copy mode off self.cmode_state = 0 self.mpl_cursor = None self.canvas.mpl_disconnect(self.cid) self.statusbar.push( self.statusbar.get_context_id('cmode_state'), state_messages[self.cmode_state] ) def get_coord_click(self, event): x_copy, y_copy = str(round(event.xdata, 3)), str(round(event.ydata, 3)) if self.cmode_state == 1: self.clipboard.set_text(x_copy, -1) self.statusbar.push( self.statusbar.get_context_id('copied_x'), 'Copied X=' + str(x_copy) + ' to clipboard!' ) if self.cmode_state == 2: self.clipboard.set_text(y_copy, -1) self.statusbar.push( self.statusbar.get_context_id('copied_y'), 'Copied Y=' + str(y_copy) + ' to clipboard!' ) def add_accelerator(self, widget, accelerator, signal="activate"): ''' Adds keyboard shortcuts ''' if accelerator is not None: key, mod = Gtk.accelerator_parse(accelerator) widget.add_accelerator( signal, self.accelerators, key, mod, Gtk.AccelFlags.VISIBLE )
class ConanReportGraphsPresenter(Presenter[Gtk.Stack]): spinner: TemplateChild[Gtk.Spinner] = TemplateChild('spinner') figure_container = TemplateChild('figure_container') # type: TemplateChild[Gtk.Container] _analyses = () @inject def __init__(self, graphs_service: ConanReportGraphsService) -> None: self.graphs_service = graphs_service def after_view_init(self) -> None: self.figure = Figure(tight_layout=False) self.figure_canvas = FigureCanvas(self.figure) self.figure_canvas.props.hexpand = True self.figure_canvas.props.vexpand = True self.figure_canvas.props.visible = True self.figure_container.add(self.figure_canvas) self.figure_canvas_mapped = False self.figure_canvas.connect('map', self.canvas_map) self.figure_canvas.connect('unmap', self.canvas_unmap) self.figure_canvas.connect('size-allocate', self.canvas_size_allocate) left_ca_ax, right_ca_ax = self.figure.subplots(2, 1, sharex='col') left_ca_ax.set_ylabel('Left CA [°]') left_ca_ax.tick_params(axis='x', direction='inout') right_ca_ax.xaxis.set_ticks_position('both') right_ca_ax.set_ylabel('Right CA [°]') right_ca_ax.tick_params(axis='x', direction='inout') left_ca_ax.tick_params(axis='y', left=False, labelleft=False, right=True, labelright=True) right_ca_ax.tick_params(axis='y', left=False, labelleft=False, right=True, labelright=True) # Format the labels to scale to the right units. left_ca_ax.get_yaxis().set_major_formatter( FuncFormatter(lambda x, _: '{:.4g}'.format(math.degrees(x))) ) right_ca_ax.get_yaxis().set_major_formatter( FuncFormatter(lambda x, _: '{:.4g}'.format(math.degrees(x))) ) left_ca_ax.grid(axis='x', linestyle='--', color="#dddddd") left_ca_ax.grid(axis='y', linestyle='-', color="#dddddd") right_ca_ax.grid(axis='x', linestyle='--', color="#dddddd") right_ca_ax.grid(axis='y', linestyle='-', color="#dddddd") self._left_ca_ax = left_ca_ax self._left_ca_line = left_ca_ax.plot([], marker='o', color='#0080ff')[0] self._right_ca_line = right_ca_ax.plot([], marker='o', color='#ff8000')[0] self.graphs_service.connect('notify::left-angle', self.data_changed) self.graphs_service.connect('notify::right-angle', self.data_changed) self.data_changed() def canvas_map(self, *_) -> None: self.figure_canvas_mapped = True self.figure_canvas.draw_idle() def canvas_unmap(self, *_) -> None: self.figure_canvas_mapped = False def canvas_size_allocate(self, *_) -> None: self.figure.tight_layout(pad=2.0, h_pad=0) self.figure.subplots_adjust(hspace=0) @install @GObject.Property def analyses(self) -> Sequence[ConanAnalysisJob]: return self._analyses @analyses.setter def analyses(self, analyses: Iterable[ConanAnalysisJob]) -> None: self._analyses = tuple(analyses) self.graphs_service.analyses = analyses def data_changed(self, *_) -> None: left_angle_data = self.graphs_service.left_angle right_angle_data = self.graphs_service.right_angle if left_angle_data.shape[1] <= 1 or right_angle_data.shape[1] <=1: self.show_waiting_placeholder() return self.hide_waiting_placeholder() self._left_ca_line.set_data(left_angle_data) self._right_ca_line.set_data(right_angle_data) self.update_xlim() self._left_ca_line.axes.relim() self._left_ca_line.axes.margins(y=0.1) self._right_ca_line.axes.relim() self._right_ca_line.axes.margins(y=0.1) self.figure_canvas.draw() def update_xlim(self) -> None: all_xdata = ( *self._left_ca_line.get_xdata(), *self._right_ca_line.get_xdata(), ) if len(all_xdata) <= 1: return xmin = min(all_xdata) xmax = max(all_xdata) if xmin == xmax: return self._left_ca_ax.set_xlim(xmin, xmax) def show_waiting_placeholder(self) -> None: self.host.set_visible_child(self.spinner) self.spinner.start() def hide_waiting_placeholder(self) -> None: self.host.set_visible_child(self.figure_container) self.spinner.stop()
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 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', 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 = 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_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: query_filter = None if 'campaign_id' in client_rpc.database_table_objects[table].__slots__: query_filter = {'campaign_id': self.config['campaign_id']} info_cache[table] = tuple(self.rpc.remote_table(table, query_filter=query_filter)) for ax in self.axes: ax.clear() if self._legend is not None: self._legend.remove() self._legend = None self._load_graph(info_cache) self.figure.suptitle( self.graph_title, color=self.get_color('fg', ColorHexCode.BLACK), size=14, weight='bold', y=0.97 ) self.canvas.draw() return info_cache def 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 Test: x1 = [] y1 = [] x2 = [] y2 = [] def __init__(self): self.interface = Gtk.Builder() self.interface.add_from_file('UI.glade') self.interface.connect_signals(self) self.window = self.interface.get_object('Main') # first self.sw1 = self.interface.get_object('firstScrolled') self.fig1 = Figure() self.a1 = self.fig1.add_subplot(111) self.a1.plot(self.x1, self.y1) self.canvas1 = FigureCanvas(self.fig1) self.sw1.add_with_viewport(self.canvas1) # second self.sw2 = self.interface.get_object('secondScrolled') self.fig2 = Figure(figsize=(5, 5), dpi=100) self.a2 = self.fig2.add_subplot(111) self.a2.plot(self.x2, self.y2) self.canvas2 = FigureCanvas(self.fig2) self.sw2.add_with_viewport(self.canvas2) self.window.show_all() def on_button1_clicked(self, widget): print("Exit") sys.exit() def on_button2_clicked(self, widget): print("Plot2") self.x1.clear() self.y1.clear() i = 0 while i < 20: self.x1.append(random.random() * 100) self.y1.append(random.random() * 100) i = i + 1 print(self.x1) print(self.y1) self.a1.clear() self.a1.plot(self.x1, self.y1) self.canvas1 = FigureCanvas(self.fig1) self.canvas1.draw() self.window.show_all() self.window.queue_draw() def on_button3_clicked(self, widget): self.x2.clear() self.y2.clear() print("Plot1") i = 0 while i < 20: self.x2.append(random.random() * 100) self.y2.append(random.random() * 100) i = i + 1 print(self.x2) print(self.y2) self.a2.clear() self.a2.plot(self.x2, self.y2) self.canvas2 = FigureCanvas(self.fig2) self.canvas2.draw() self.window.show_all() self.window.queue_draw()
class Signal: '''Creates integration signal plot.''' X_Data = [] Y_Data = [] LifeZero = 0.0 IntegrationStart = 0.0 LifeZero = 0.0 def __init__(self): n = 1000 #print X #dataY = c.execute("SELECT data_value FROM MktSensorData WHERE data_creator = 1 and data_trigger = 1") #X =[] #Y =[] # # X.append(row[0]) # Y.append(row[1]) #print X #xsin = linspace(-pi, pi, n, endpoint=True) #xcos = linspace(-pi, pi, n, endpoint=True) #ysin = sin(xsin) #ycos = cos(xcos) #sinwave = ax.plot(dataX, dataY, color='blue', label='Signal') #formatter = DateFormatter('%S') self.fig = Figure(figsize=(10,10), dpi=80, facecolor='white') self.ax = self.fig.add_subplot(111) #ax.xaxis.set_major_formatter(formatter) #ax.set_xlim(-pi,pi) #ax.set_ylim(-1.2,1.2) #ax.fill_between(xsin, 0, ysin, (ysin - 1) > -1, color='blue', alpha=.3) #ax.fill_between(xsin, 0, ysin, (ysin - 1) < -1, color='red', alpha=.3) #ax.fill_between(xcos, 0, ycos, (ycos - 1) > -1, color='blue', alpha=.3) #ax.fill_between(xcos, 0, ycos, (ycos - 1) < -1, color='red', alpha=.3) #print "test 1!" #self.ax.legend(loc='upper left') #print "test 2!" #ax = fig.gca() #ax.spines['right'].set_color('none') #ax.spines['top'].set_color('none') #ax.xaxis.set_ticks_position('bottom') #ax.spines['bottom'].set_position(('data',0)) #ax.yaxis.set_ticks_position('left') #ax.spines['left'].set_position(('data',0)) # self.canvas = FigureCanvas(self.fig) #GLib.timeout_add_seconds(1, self.fast_update) #self.read = ReadStdin(self) #self.read.start() def clean_data(self): self.X_Data.clean() def auto_update(self): Cursor = self.Conn.cursor() print "SELECT ref_id,data_changed,data_value,data_type FROM "+self.data_base+" WHERE ref_id > "+str(self.last_id)+" and data_creator = "+str(self.data_creator)+" and data_trigger = "+str(self.data_trigger)+" LIMIT 6" data = Cursor.execute("SELECT ref_id,data_changed,data_value,data_type FROM "+self.data_base+" WHERE ref_id > "+str(self.last_id)+" and data_creator = "+str(self.data_creator)+" and data_trigger = "+str(self.data_trigger)+" LIMIT 6") life_zero = 0.0 starti = 0.0 stopi = 0.0 is_zero = False r = data.fetchone() if r != None : for row in data: self.last_id=row[0] self.X.append(row[1]-self.start_time) self.Y.append(row[2]) if row[3] == self.ULTIMATE_SIGNAL_JUSTIFICATED: life_zero = r[2] is_zero = True if row[3] == self.ULTIMATE_SIGNAL_INTEGRATION_START: starti = row[1]-self.start_time if row[3] == self.ULTIMATE_SIGNAL_INTEGRATION_STOP: stopi = row[1]-self.start_time self.ax.plot(self.X, self.Y, color='blue', label='Signal', linewidth=2) if is_zero == True: l = self.ax.axhline(y=life_zero,linewidth=1, color='r') if starti > 0.0: lsi = self.ax.axvline(x=starti,linewidth=1, color='g') if stopi > 0.0: lsti = self.ax.axvline(x=stopi,linewidth=2, color='r') self.canvas.draw() return True def update(self): print "Update signal plot" self.last_id = 0 self.start_time = 0.0 self.data_base = ManSettings.get_value(self.DATABASE).get_string() self.data_creator = ManSettings.get_value(self.REFERENCE).get_uint64() self.data_trigger = ManSettings.get_value(self.TRIGGER).get_uint32() self.ax.clear() self.ax.grid(True) Cursor = self.Conn.cursor() data = Cursor.execute("SELECT ref_id,data_changed,data_value,data_type FROM "+self.data_base+" WHERE data_creator = "+str(self.data_creator)+" and data_trigger = "+str(self.data_trigger)) #print "selected "+str(len(data.rowcount))+" data" #if data.rowcount > 0 : # print "selected "+str(data.rowcoun)+" data" r = data.fetchone() life_zero = 0.0 starti = 0.0 stopi = 0.0 is_zero = False self.X = array('f',[]) self.Y = array('f',[]) print "search data... "+self.data_base+" data creator="+str(self.data_creator)+" data_trigger="+str(self.data_trigger) if r != None : print "Find data ... " self.start_time = r[1] print str(self.start_time) + "test " + str(self.start_time-r[1]) for row in data: self.last_id=row[0] self.X.append(row[1]-self.start_time) self.Y.append(row[2]) if row[3] == self.ULTIMATE_SIGNAL_JUSTIFICATED: life_zero = r[2] is_zero = True if row[3] == self.ULTIMATE_SIGNAL_INTEGRATION_START: starti = row[1]-self.start_time if row[3] == self.ULTIMATE_SIGNAL_INTEGRATION_STOP: stopi = row[1]-self.start_time print "Stop I = "+str(starti)+"Stop I = "+str(stopi)+" data ... " self.ax.plot(self.X, self.Y, color='blue', label='Signal', linewidth=2) self.ax.set_ylim(-0.05,1.0) self.ax.set_ylabel("Signal [fsr]") self.ax.set_xlabel("Time [sec]") if is_zero == True: l = self.ax.axhline(y=life_zero,linewidth=1, color='r') if starti > 0.0: lsi = self.ax.axvline(x=starti,linewidth=1, color='g') if stopi > 0.0: lsti = self.ax.axvline(x=stopi,linewidth=2, color='r') else : self.ax.plot(self.X, self.Y, color='blue', label='Signal', linewidth=2) self.ax.set_ylim(-0.05,1.0) self.ax.set_ylabel("Signal [fsr]") self.ax.set_xlabel("Time [sec]") self.canvas.draw() def changed_main_settings(self,setting,property): if property == 'signal-trigger': self.auto_update() elif property != 'plug-id': self.update() print "change settings property "+property
class CampaignGraph(object): """ A basic graph provider for using :py:mod:`matplotlib` to create graph representations of campaign data. This class is meant to be subclassed by real providers. """ name = 'Unknown' """The name of the graph provider.""" name_human = 'Unknown' """The human readable name of the graph provider used for UI identification.""" graph_title = 'Unknown' """The title that will be given to the graph.""" table_subscriptions = [] """A list of tables from which information is needed to produce the graph.""" is_available = True def __init__(self, config, parent, size_request=None): """ :param dict config: The King Phisher client configuration. :param parent: The parent window for this object. :type parent: :py:class:`Gtk.Window` :param tuple size_request: The size to set for the canvas. """ self.config = config """A reference to the King Phisher client configuration.""" self.parent = parent """The parent :py:class:`Gtk.Window` instance.""" self.figure, _ = pyplot.subplots() self.axes = self.figure.get_axes() self.canvas = FigureCanvas(self.figure) self.manager = None if size_request: self.canvas.set_size_request(*size_request) self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed) self.canvas.show() self.navigation_toolbar = NavigationToolbar(self.canvas, self.parent) self.popup_menu = Gtk.Menu.new() menu_item = Gtk.MenuItem.new_with_label('Export') menu_item.connect('activate', self.signal_activate_popup_menu_export) self.popup_menu.append(menu_item) menu_item = Gtk.MenuItem.new_with_label('Refresh') menu_item.connect('activate', lambda action: self.refresh()) self.popup_menu.append(menu_item) menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar') menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar) self._menu_item_show_toolbar = menu_item self.popup_menu.append(menu_item) self.popup_menu.show_all() self.navigation_toolbar.hide() def _load_graph(self, info_cache): raise NotImplementedError() def _graph_bar_set_yparams(self, top_lim): min_value = top_lim + (top_lim * 0.075) if min_value <= 25: scale = 5 else: scale = scale = 10 ** (len(str(int(min_value))) - 1) inc_scale = scale while scale <= min_value: scale += inc_scale top_lim = scale ax = self.axes[0] yticks = set((round(top_lim * 0.5), top_lim)) ax.set_yticks(tuple(yticks)) ax.set_ylim(top=top_lim) return def _graph_null_pie(self, title): ax = self.axes[0] ax.pie((100,), labels=(title,), colors=(MPL_COLOR_NULL,), autopct='%1.0f%%', shadow=True, startangle=90) ax.axis('equal') return def add_legend_patch(self, legend_rows, fontsize=None): handles = [] if not fontsize: scale = self.markersize_scale if scale < 5: fontsize = 'xx-small' elif scale < 7: fontsize = 'x-small' elif scale < 9: fontsize = 'small' else: fontsize = 'medium' for row in legend_rows: handles.append(patches.Patch(color=row[0], label=row[1])) self.axes[0].legend(handles=handles, fontsize=fontsize, loc='lower right') def graph_bar(self, bars, color=None, xticklabels=None, ylabel=None): """ Create a standard bar graph with better defaults for the standard use cases. :param list bars: The values of the bars to graph. :param color: The color of the bars on the graph. :type color: list, str :param list xticklabels: The labels to use on the x-axis. :param str ylabel: The label to give to the y-axis. :return: The bars created using :py:mod:`matplotlib` :rtype: `matplotlib.container.BarContainer` """ color = color or MPL_COLOR_NULL width = 0.25 ax = self.axes[0] self._graph_bar_set_yparams(max(bars) if bars else 0) bars = ax.bar(range(len(bars)), bars, width, color=color) ax.set_xticks([float(x) + (width / 2) for x in range(len(bars))]) if xticklabels: ax.set_xticklabels(xticklabels, rotation=30) for col in bars: height = col.get_height() ax.text(col.get_x() + col.get_width() / 2.0, height, "{0:,}".format(height), ha='center', va='bottom') if ylabel: ax.set_ylabel(ylabel) self.figure.subplots_adjust(bottom=0.25) return bars def make_window(self): """ Create a window from the figure manager. :return: The graph in a new, dedicated window. :rtype: :py:class:`Gtk.Window` """ if self.manager == None: self.manager = FigureManager(self.canvas, 0) self.navigation_toolbar.destroy() self.navigation_toolbar = self.manager.toolbar self._menu_item_show_toolbar.set_active(True) window = self.manager.window window.set_transient_for(self.parent) window.set_title(self.graph_title) return window @property def markersize_scale(self): bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted()) return max(bbox.width, bbox.width) * self.figure.dpi * 0.01 def mpl_signal_canvas_button_pressed(self, event): if event.button != 3: return self.popup_menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time()) return True def signal_activate_popup_menu_export(self, action): dialog = gui_utilities.UtilityFileChooser('Export Graph', self.parent) file_name = self.config['campaign_name'] + '.png' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] self.figure.savefig(destination_file, format='png') def signal_toggled_popup_menu_show_toolbar(self, widget): if widget.get_property('active'): self.navigation_toolbar.show() else: self.navigation_toolbar.hide() def load_graph(self): """Load the graph information via :py:meth:`.refresh`.""" self.refresh() def refresh(self, info_cache=None, stop_event=None): """ Refresh the graph data by retrieving the information from the remote server. :param dict info_cache: An optional cache of data tables. :param stop_event: An optional object indicating that the operation should stop. :type stop_event: :py:class:`threading.Event` :return: A dictionary of cached tables from the server. :rtype: dict """ info_cache = (info_cache or {}) if not self.parent.rpc: return info_cache for table in self.table_subscriptions: if stop_event and stop_event.is_set(): return info_cache if not table in info_cache: info_cache[table] = tuple(self.parent.rpc.remote_table('campaign/' + table, self.config['campaign_id'])) for ax in self.axes: ax.clear() self._load_graph(info_cache) self.axes[0].set_title(self.graph_title, y=1.03) self.canvas.draw() return info_cache
class CampaignGraph(object): """ A basic graph provider for using :py:mod:`matplotlib` to create graph representations of campaign data. This class is meant to be subclassed by real providers. """ name = 'Unknown' """The name of the graph provider.""" name_human = 'Unknown' """The human readable name of the graph provider used for UI identification.""" graph_title = 'Unknown' """The title that will be given to the graph.""" table_subscriptions = [] """A list of tables from which information is needed to produce the graph.""" is_available = True def __init__(self, application, size_request=None, style_context=None): """ :param tuple size_request: The size to set for the canvas. """ self.application = application self.style_context = style_context self.config = application.config """A reference to the King Phisher client configuration.""" self.figure, _ = pyplot.subplots() self.figure.set_facecolor(self.get_color('bg', ColorHexCode.WHITE)) self.axes = self.figure.get_axes() self.canvas = FigureCanvas(self.figure) self.manager = None if size_request: self.canvas.set_size_request(*size_request) self.canvas.mpl_connect('button_press_event', self.mpl_signal_canvas_button_pressed) self.canvas.show() self.navigation_toolbar = NavigationToolbar(self.canvas, self.application.get_active_window()) self.popup_menu = Gtk.Menu.new() menu_item = Gtk.MenuItem.new_with_label('Export') menu_item.connect('activate', self.signal_activate_popup_menu_export) self.popup_menu.append(menu_item) menu_item = Gtk.MenuItem.new_with_label('Refresh') menu_item.connect('activate', lambda action: self.refresh()) self.popup_menu.append(menu_item) menu_item = Gtk.CheckMenuItem.new_with_label('Show Toolbar') menu_item.connect('toggled', self.signal_toggled_popup_menu_show_toolbar) self._menu_item_show_toolbar = menu_item self.popup_menu.append(menu_item) self.popup_menu.show_all() self.navigation_toolbar.hide() self._legend = None @property def rpc(self): return self.application.rpc @staticmethod def _ax_hide_ticks(ax): for tick in ax.yaxis.get_major_ticks(): tick.tick1On = False tick.tick2On = False @staticmethod def _ax_set_spine_color(ax, spine_color): for pos in ('top', 'right', 'bottom', 'left'): ax.spines[pos].set_color(spine_color) def _load_graph(self, info_cache): raise NotImplementedError() def add_legend_patch(self, legend_rows, fontsize=None): if self._legend is not None: self._legend.remove() self._legend = None if fontsize is None: scale = self.markersize_scale if scale < 5: fontsize = 'xx-small' elif scale < 7: fontsize = 'x-small' elif scale < 9: fontsize = 'small' else: fontsize = 'medium' legend_bbox = self.figure.legend( tuple(patches.Patch(color=patch_color) for patch_color, _ in legend_rows), tuple(label for _, label in legend_rows), borderaxespad=1.25, fontsize=fontsize, frameon=True, handlelength=1.5, handletextpad=0.75, labelspacing=0.3, loc='lower right' ) legend_bbox.legendPatch.set_linewidth(0) self._legend = legend_bbox def get_color(self, color_name, default): """ Get a color by its style name such as 'fg' for foreground. If the specified color does not exist, default will be returned. The underlying logic for this function is provided by :py:func:`~.gui_utilities.gtk_style_context_get_color`. :param str color_name: The style name of the color. :param default: The default color to return if the specified one was not found. :return: The desired color if it was found. :rtype: tuple """ color_name = 'theme_color_graph_' + color_name sc_color = gui_utilities.gtk_style_context_get_color(self.style_context, color_name, default) return (sc_color.red, sc_color.green, sc_color.blue) def make_window(self): """ Create a window from the figure manager. :return: The graph in a new, dedicated window. :rtype: :py:class:`Gtk.Window` """ if self.manager is None: self.manager = FigureManager(self.canvas, 0) self.navigation_toolbar.destroy() self.navigation_toolbar = self.manager.toolbar self._menu_item_show_toolbar.set_active(True) window = self.manager.window window.set_transient_for(self.application.get_active_window()) window.set_title(self.graph_title) return window @property def markersize_scale(self): bbox = self.axes[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted()) return bbox.width * self.figure.dpi * 0.01 def mpl_signal_canvas_button_pressed(self, event): if event.button != 3: return self.popup_menu.popup(None, None, None, None, event.button, Gtk.get_current_event_time()) return True def signal_activate_popup_menu_export(self, action): dialog = gui_utilities.FileChooser('Export Graph', self.application.get_active_window()) file_name = self.config['campaign_name'] + '.png' response = dialog.run_quick_save(file_name) dialog.destroy() if not response: return destination_file = response['target_path'] self.figure.savefig(destination_file, format='png') def signal_toggled_popup_menu_show_toolbar(self, widget): if widget.get_property('active'): self.navigation_toolbar.show() else: self.navigation_toolbar.hide() def load_graph(self): """Load the graph information via :py:meth:`.refresh`.""" self.refresh() def refresh(self, info_cache=None, stop_event=None): """ Refresh the graph data by retrieving the information from the remote server. :param dict info_cache: An optional cache of data tables. :param stop_event: An optional object indicating that the operation should stop. :type stop_event: :py:class:`threading.Event` :return: A dictionary of cached tables from the server. :rtype: dict """ info_cache = (info_cache or {}) if not self.rpc: return info_cache for table in self.table_subscriptions: if stop_event and stop_event.is_set(): return info_cache if not table in info_cache: info_cache[table] = tuple(self.rpc.remote_table(table, query_filter={'campaign_id': self.config['campaign_id']})) for ax in self.axes: ax.clear() if self._legend is not None: self._legend.remove() self._legend = None self._load_graph(info_cache) self.figure.suptitle( self.graph_title, color=self.get_color('fg', ColorHexCode.BLACK), size=14, weight='bold', y=0.97 ) self.canvas.draw() return info_cache
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 Plot: # TODO scaling of size when resizing # TODO tighter fit of plot # TODO BUG: weird redrawing issue on changing panes, probably should not redraw graph on changing panes # Plot object used GUI def __init__(self, builder, GPUs, maxpoints, precision, linux_kernelmain, linux_kernelsub): # Can used for kernel specific workarounds self.linux_kernelmain = linux_kernelmain self.linux_kernelsub = linux_kernelsub self.precision = precision self.builder = builder self.GPUs = GPUs self.GPU = GPUs[0] self.maxpoints = maxpoints self.fig = Figure(figsize=(1000, 150), dpi=100, facecolor="#00000000") self.fig.set_tight_layout(True) self.ax = self.fig.add_subplot(111) # enable, name, unit, mean, max, current self.signalstore = Gtk.ListStore(bool, bool, bool, str, str, str, str, str, str, str) self.Plotsignals = self.init_signals(self.GPU) # Set top panel height in accordance to number of signals (with saturation) height_top_panel = len(self.Plotsignals) * 32.5 if height_top_panel < 150: self.builder.get_object("MainPane").set_position(150) elif height_top_panel > 235: self.builder.get_object("MainPane").set_position(235) else: self.builder.get_object("MainPane").set_position(height_top_panel) self.init_treeview() self.update_signals() self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(1000, 150) self.object = self.builder.get_object("matplot") self.object.add(self.canvas) self.object.show_all() def change_GPU(self, cardnr): print(f"Changing plot to GPU {self.GPUs[cardnr].fancyname}") self.GPU = self.GPUs[cardnr] self.Plotsignals = self.init_signals(self.GPU) self.init_treeview() self.update_signals() def init_signals(self, GPU): Plotsignals = [] # Define signals with: names units max min path plotenable plotnormalise plotcolor parser and outputargument used from parser if GPU.gpu_clock != 'N/A': Plotsignals.append( Plotsignal("GPU Clock", "[MHz]", GPU.pstate_clock[-1], GPU.pstate_clock[0], "/pp_dpm_sclk", True, True, "#1f77b4", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("GPU State", "[-]", len(GPU.pstate_clock) - 1, 0, "/pp_dpm_sclk", True, True, "#ff7f0e", GPU.get_current_clock, 1)) if GPU.mem_clock != 'N/A': Plotsignals.append( Plotsignal("MEM Clock", "[MHz]", GPU.pmem_clock[-1], GPU.pmem_clock[0], "/pp_dpm_mclk", True, True, "#d62728", GPU.get_current_clock, 0)) Plotsignals.append( Plotsignal("MEM State", "[-]", len(GPU.pmem_clock) - 1, 0, "/pp_dpm_mclk", True, True, "#9467bd", GPU.get_current_clock, 1)) self.add_available_signal(GPU.sensors, Plotsignals, hwmonpath=GPU.hwmonpath) # GPU busy percent only properly available in linux version 4.19+ if (self.linux_kernelmain == 4 and self.linux_kernelsub > 18) or (self.linux_kernelmain >= 5): Plotsignals.append( Plotsignal("GPU Usage", "[-]", 100, 0, "/gpu_busy_percent", True, True, "#2ca02c", GPU.read_sensor)) # as final check remove signals that return None: checked_plotlist = [] for i, signal in enumerate(Plotsignals): signal.retrieve_data(self.maxpoints) if signal.get_last_value() is not None: checked_plotlist.append(signal) else: print( f"Removing {signal.name} from plotsignals, returning Nonetype" ) if len(checked_plotlist) == 0: print("Nothing to plot! Hiding the plot pane.") self.builder.get_object("Plot").hide() return checked_plotlist def add_available_signal(self, signals, Plotsignals, hwmonpath="", subsystem="", stop_recursion=False): for key, value in signals.items(): if key in subsystem_unit_color: subsystem = key stop_recursion = False if "path" in value: if subsystem == "": continue if any(path_sensor_to_plot in value["path"] for path_sensor_to_plot in sensors_to_plot): signallabel = value["path"][1:].split("_")[0] signalmax = 0 signalmin = 0 signalpath = hwmonpath + value["path"] if "min" in signals: signalmin = signals['min']['value'] stop_recursion = True if "max" in signals: signalmax = signals['max']['value'] stop_recursion = True if "crit" in signals: signalmax = signals['crit']['value'] stop_recursion = True if "label" in signals: signallabel = signals["label"]["value"] if signallabel == "vddgfx" and len( self.GPU.volt_range) > 0: signalmax = self.GPU.volt_range[1] signalmin = 0 stop_recursion = True if "cap" in signals: signalmax = signals["cap"]["value"] stop_recursion = True if "pwm" in value["path"]: signalmax = 255 signallabel = "(fan)" + signallabel Plotsignals.append( Plotsignal(signallabel, subsystem_unit_color[subsystem]["unit"], signalmax, signalmin, signalpath, True, True, subsystem_unit_color[subsystem]["color"], read)) else: if not stop_recursion: self.add_available_signal(value, Plotsignals, hwmonpath=hwmonpath, subsystem=subsystem, stop_recursion=stop_recursion) else: continue def init_treeview(self): textrenderer = Gtk.CellRendererText() self.plotrenderer = Gtk.CellRendererToggle() self.plotrenderer.connect("toggled", self.on_plot_toggled) self.normaliserenderer = Gtk.CellRendererToggle() self.normaliserenderer.connect("toggled", self.on_normalise_toggled) self.tree = self.builder.get_object("Signal Selection") self.tree.append_column( Gtk.TreeViewColumn("Plot", self.plotrenderer, active=0)) self.tree.append_column( Gtk.TreeViewColumn("Scale", self.normaliserenderer, active=1, activatable=2)) columnnames = ["Name", "Unit", "min", "mean", "max", "current"] for i, column in enumerate(columnnames): tcolumn = Gtk.TreeViewColumn(column, textrenderer, text=i + 3, foreground=9) self.tree.append_column(tcolumn) for plotsignal in self.Plotsignals: self.signalstore.append([ plotsignal.plotenable, plotsignal.plotnormalise, True, plotsignal.name, convert_to_si(plotsignal.unit)[0], '0', '0', '0', '0', plotsignal.plotcolor ]) self.tree.set_model(self.signalstore) def update_signals(self): # Retrieve signal and set appropriate values in signalstore to update left pane in GUI for i, Plotsignal in enumerate(self.Plotsignals): Plotsignal.retrieve_data(self.maxpoints) disable_scaling = len( Plotsignal.get_values()) > 3 and Plotsignal.all_equal( ) and Plotsignal.plotnormalise and (Plotsignal.max == Plotsignal.min) self.signalstore[i][2] = not disable_scaling if disable_scaling: print( f"cannot scale values of {self.signalstore[i][3]} disabling scaling" ) self.on_normalise_toggled(self.normaliserenderer, i, disable_refresh=True) if disable_plots_if_scaling_error: print( f"disabling {self.signalstore[i][3]} plot since disable_plots_if_scaling_error is set" ) self.on_plot_toggled(self.plotrenderer, i) self.signalstore[i][5] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_min())[1], self.precision)) self.signalstore[i][6] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_mean())[1], self.precision)) self.signalstore[i][7] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_max())[1], self.precision)) self.signalstore[i][8] = str( np.around( convert_to_si(Plotsignal.unit, Plotsignal.get_last_value())[1], self.precision)) def on_plot_toggled(self, widget, path, disable_refresh=False): self.signalstore[path][0] = not self.signalstore[path][0] self.Plotsignals[int( path)].plotenable = not self.Plotsignals[int(path)].plotenable if not disable_refresh: self.update_plot() def on_normalise_toggled(self, widget, path, disable_refresh=False): self.signalstore[path][1] = not self.signalstore[path][1] self.Plotsignals[int(path)].plotnormalise = not self.Plotsignals[int( path)].plotnormalise if not disable_refresh: self.update_plot() def update_plot(self): if len(self.Plotsignals) == 0: return self.ax.clear() for Plotsignal in self.Plotsignals: if Plotsignal.plotenable: if Plotsignal.plotnormalise: data = Plotsignal.get_normalised_values() * 100 self.ax.plot(data, color=Plotsignal.plotcolor) else: data = Plotsignal.get_values() self.ax.plot(convert_to_si(Plotsignal.unit, data)[1], color=Plotsignal.plotcolor) self.ax.grid(True) self.ax.get_yaxis().tick_right() self.ax.get_yaxis().set_label_position("right") self.ax.get_yaxis().set_visible(True) self.ax.get_xaxis().set_visible(False) all_normalised = True all_same_unit = True unit = "" iter = self.signalstore.get_iter(0) while iter is not None: if self.signalstore[iter][0] == True: if unit == "": unit = self.signalstore[iter][4] if self.signalstore[iter][1] == False: all_normalised = False if self.signalstore[iter][4] != unit: all_same_unit = False if not all_normalised and not all_same_unit: break iter = self.signalstore.iter_next(iter) if all_normalised: self.ax.set_yticks(np.arange(0, 101, step=25)) self.ax.set_ylabel('Percent [%]') else: self.ax.yaxis.set_major_locator(AutoLocator()) if all_same_unit: self.ax.set_ylabel(unit) else: self.ax.set_ylabel("") self.canvas.draw() self.canvas.flush_events() def refresh(self): # This is run in thread self.update_signals() self.update_plot()
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 Profile(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Profile") #f = Figure(figsize=(5,4), dpi=100) f = Figure() self.axes = f.add_subplot(111) self.axes.set_xlabel('Time (sec.)') self.axes.set_ylabel('Temperature (C)') self.axes.set_xlim(0, 5*60) self.axes.set_ylim(20, 300) self.axes.grid() self.axes.xaxis.set_major_formatter(FuncFormatter(self.format_xaxis)) self.x = [] self.y = [] self.plot, = self.axes.plot(self.x, self.y, 'o-', picker=5) self.minutes = False self.file_name = None self.canvas = FigureCanvas(f) self.canvas.set_size_request(800,600) self.canvas.mpl_connect('button_press_event', self.onclick) self.canvas.mpl_connect('button_release_event', self.onrelease) self.canvas.mpl_connect('pick_event', self.onpick) self.canvas.mpl_connect('motion_notify_event', self.onmotion) self.picking = None self.store = Gtk.ListStore(str, str) self.tree = Gtk.TreeView(self.store) renderer = Gtk.CellRendererText() renderer.set_property("editable", True) renderer.connect('edited', self.on_time_edited) column = Gtk.TreeViewColumn("Time", renderer, text=0) self.tree.append_column(column) renderer = Gtk.CellRendererText() renderer.set_property("editable", True) renderer.connect('edited', self.on_temp_edited) column = Gtk.TreeViewColumn("Temperature", renderer, text=1) self.tree.append_column(column) self.box = Gtk.Box() self.box.pack_start(self.canvas, False, False, 0) self.box.pack_start(self.tree, True, True, 0) action_group = Gtk.ActionGroup("profile_actions") action_group.add_actions([ ("FileMenu", None, "File", None, None, None), ("FileNew", Gtk.STOCK_NEW, "_New", "<control>N", None, self.on_file_new), ("FileOpen", Gtk.STOCK_OPEN, "_Open", "<control>O", None, self.on_file_open), ("FileSave", Gtk.STOCK_SAVE, "_Save", "<control>S", None, self.on_file_save), ("FileSaveAs", Gtk.STOCK_SAVE_AS, "Save _As…", "<shift><control>S", None, self.on_file_save_as), ("FileQuit", Gtk.STOCK_QUIT, "_Quit", "<control>Q", None, Gtk.main_quit) ]) uimanager = Gtk.UIManager() uimanager.add_ui_from_string(UI_INFO) accelgroup = uimanager.get_accel_group() self.add_accel_group(accelgroup) uimanager.insert_action_group(action_group) menubar = uimanager.get_widget("/MenuBar") self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.vbox.pack_start(menubar, False, False, 0) self.vbox.pack_start(self.box, True, True, 0) self.statusbar = Gtk.Statusbar() self.status_pos = self.statusbar.get_context_id("position") self.vbox.pack_start(self.statusbar, False, False, 0) self.add(self.vbox) def open_file(self, name): reader = csv.reader(open(name, 'rb')) x, y = zip(*reader) x = [ float(i) for i in x] y = [ float(i) for i in y] self.x, self.y = x, y self.file_name = name self.set_title('Profile - ' + name) self.update_data() self.update_scale() self.canvas.draw() def save_file(self, name): writer = csv.writer(open(name, 'wd')) writer.writerows(zip(self.x, self.y)) self.file_name = name self.set_title('Profile - ' + name) def on_file_new(self, widget): self.file_name = None self.set_title('Profile') self.x = [] self.y = [] self.store.clear() self.update_data() self.update_scale() self.canvas.draw() def on_file_open(self, widget): dialog = Gtk.FileChooserDialog("", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_current_folder_uri(Gio.file_new_for_path(os.curdir).get_uri()) response = dialog.run() if response == Gtk.ResponseType.OK: try: self.open_file(dialog.get_filename()) except: pass dialog.destroy() def on_file_save(self, widget): if self.file_name is None: self.on_file_save_as(widget) else: self.save_file(self.file_name) def on_file_save_as(self, widget): dialog = Gtk.FileChooserDialog("", self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE_AS, Gtk.ResponseType.OK)) dialog.set_current_folder_uri(Gio.file_new_for_path(os.curdir).get_uri()) response = dialog.run() if response == Gtk.ResponseType.OK: self.save_file(dialog.get_filename()) dialog.destroy() def format_xaxis(self, x, pos): return self.format_x(x) def format_x(self, val): m, s = 0, val if self.minutes: m = int(val / 60) s = val % 60 if m: return '%dm%.1fs'%(m,s) else: return '%.1fs'%s def format_y(self, val): return '%.1f°C'%val def on_time_edited(self, widget, path, text): treeiter = self.store.get_iter(path) try: m, s = "0", "0" if 'm' in text: m, text = text.split('m') if 's' in text: s, text = text.split('s') else: text = "" assert len(text) == 0 val = int(m) * 60.0 + round(float(s), 1) except: return at = max(0, int(path)) if at != 0: val = max(val, self.x[at-1]) if at != len(self.x)-1: val = min(val, self.x[at+1]) self.store.set(treeiter, 0, self.format_x(val)) self.x[int(path)] = val self.update_data() self.update_scale() self.canvas.draw() def on_temp_edited(self, widget, path, text): treeiter = self.store.get_iter(path) try: if '°' in text: text, _ = text.split('°') if 'c' in text.lower(): text, _ = text.lower().split('c') val = round(float(text), 1) except: return val = min(300.0, val) val = max(20.0, val) self.store.set(treeiter, 1, self.format_y(val)) self.y[int(path)] = val self.update_data() self.canvas.draw() def update_data(self): self.plot.set_data(self.x, self.y) def update_scale(self): if len(self.x) and self.x[-1] + 30 > 5 * 60: minutes = int((self.x[-1] + 90) / 60) else: minutes = 5 self.axes.set_xlim(0, minutes*60) def onclick(self, event): if self.picking is not None or event.button != 1: return xdata = round(event.xdata, 1) ydata = round(event.ydata, 1) at = bisect.bisect(self.x, xdata) self.x.insert(at, xdata) self.y.insert(at, ydata) self.store.insert(at, [self.format_x(xdata), self.format_y(ydata)]) self.update_data() self.update_scale() self.canvas.draw() def onrelease(self, event): if self.picking is None: return self.update_scale() self.canvas.draw() self.picking = None def onpick(self, event): on = event.artist ind = event.ind[0] if event.mouseevent.button == 1: self.picking = ind elif event.mouseevent.button == 3: self.x.pop(ind) self.y.pop(ind) self.store.remove(self.store.get_iter(Gtk.TreePath(ind))) self.update_data() self.update_scale() self.canvas.draw() def onmotion(self, event): self.statusbar.remove_all(self.status_pos) if event.xdata is None or event.ydata is None: return self.statusbar.push(self.status_pos, self.format_x(event.xdata) + ', ' + self.format_y(event.ydata)) if self.picking is None: return xdata = max(0, round(event.xdata, 1)) ydata = round(event.ydata, 1) ydata = min(300.0, ydata) ydata = max(20.0, ydata) if self.picking != 0: xdata = max(xdata, self.x[self.picking-1]) if self.picking != len(self.x)-1: xdata = min(xdata, self.x[self.picking+1]) self.x[self.picking] = xdata self.y[self.picking] = ydata treeiter = self.store.get_iter(Gtk.TreePath(self.picking)) self.store.set(treeiter, 0, self.format_x(xdata), 1, self.format_y(ydata)) self.update_data() self.canvas.draw()
class EditCSDSDistributionView(BaseView): builder = resource_filename(__name__, "glade/csds.glade") top = "tbl_csds_distr" def __init__(self, *args, **kwargs): BaseView.__init__(self, *args, **kwargs) self.graph_parent = self["distr_plot_box"] self.setup_matplotlib_widget() def setup_matplotlib_widget(self): #style = Gtk.Style() self.figure = Figure( dpi=72) #, edgecolor=str(style.bg[2]), facecolor=str(style.bg[2])) self.plot = self.figure.add_subplot(111) self.figure.subplots_adjust(bottom=0.20) self.matlib_canvas = FigureCanvasGTK(self.figure) self.plot.autoscale_view() self.graph_parent.add(self.matlib_canvas) self.graph_parent.show_all() def update_figure(self, distr): self.plot.cla() self.plot.hist(list(range(len(distr))), len(distr), weights=distr, normed=1, ec='b', histtype='stepfilled') self.plot.set_ylabel('') self.plot.set_xlabel('CSDS', size=14, weight="heavy") self.plot.relim() self.plot.autoscale_view() if self.matlib_canvas is not None: self.matlib_canvas.draw() def reset_params(self): tbl = self["tbl_params"] for child in tbl.get_children(): tbl.remove(child) tbl.resize(1, 2) def add_param_widget(self, name, label, minimum, maximum): tbl = self["tbl_params"] rows = tbl.get_property("n-rows") + 1 tbl.resize(rows, 2) lbl = Gtk.Label(label=label) lbl.set_alignment(1.0, 0.5) tbl.attach(lbl, 0, 1, rows - 1, rows, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL) inp = ScaleEntry(minimum, maximum, enforce_range=True) tbl.attach(inp, 1, 2, rows - 1, rows, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL) tbl.show_all() self[name] = inp inp.set_name(name) return inp
class SAOPControlWindow(Gtk.Window): def __init__(self, model: HMIModel): super(SAOPControlWindow, self).__init__(default_width=500, default_height=400, title="SAOP HMI") self.hmi_model = model self.hmi_model.set_controller(self) ## Actions self.plan_button = Gtk.Button(label="Create") self.plan_button.connect("clicked", self.on_plan_clicked) self.cancel_button = Gtk.Button(label="Stop") self.cancel_button.connect("clicked", self.on_cancel_clicked) self.home_button = Gtk.Button(label="Go home") self.home_button.connect("clicked", self.on_home_clicked, None) self.home_button.set_sensitive(False) self.home_more_button = Gtk.MenuButton.new() self.home_more_button.set_direction(Gtk.ArrowType.UP) self.home_more_button.set_use_popover(True) self.propagate_button = Gtk.Button(label="Propagate") self.propagate_button.connect("clicked", self.on_propagate_clicked) self.action_bar = Gtk.ActionBar() self.action_bar.pack_start(Gtk.Label("Plan:")) self.action_bar.pack_start(self.plan_button) self.action_bar.pack_start(self.cancel_button) home_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) Gtk.StyleContext.add_class(home_box.get_style_context(), "linked") home_box.pack_start(self.home_button, False, False, 0) home_box.pack_start(self.home_more_button, False, False, 0) self.action_bar.pack_start(home_box) self.action_bar.pack_start( Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)) self.action_bar.pack_start(Gtk.Label("Wildfire:")) self.action_bar.pack_start(self.propagate_button) ## Visualization control # Empty drawing area that will be replaced by a matplotlib figure default_fig = matplotlib.figure.Figure() self.canvas = FigureCanvas(default_fig) self.navigation_toolbar = NavigationToolbar(self.canvas, self) # Visualization Checkboxes scrolled_flowbox = Gtk.ScrolledWindow() scrolled_flowbox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) flowbox = Gtk.FlowBox() flowbox.set_valign(Gtk.Align.START) flowbox.set_max_children_per_line(30) flowbox.set_selection_mode(Gtk.SelectionMode.NONE) # Checkboxes def toggle_pause(button): self.set_update_plot(not button.props.active) pause_toggle = Gtk.ToggleButton("Pause") pause_toggle.props.active = False pause_toggle.connect("toggled", toggle_pause) pause_toggle.toggled() flowbox.add(pause_toggle) display_label = Gtk.Label("Display:") flowbox.add(display_label) def toggle_elevation(button): self.hmi_model.elevation_map.drawable = button.get_active() elevation_toggle = SAOPControlWindow._check_button_with_action( "Elevation", toggle_elevation) flowbox.add(elevation_toggle) def toggle_wildfire(button): self.hmi_model.wildfire_map.drawable = button.get_active() wildfire_toggle = SAOPControlWindow._check_button_with_action( "Wildfire", toggle_wildfire) flowbox.add(wildfire_toggle) def toggle_wildfire_observed(button): self.hmi_model.wildfire_map_observed.drawable = button.get_active() wildfire_observed_toggle = SAOPControlWindow._check_button_with_action( "Observed wildfire", toggle_wildfire_observed) flowbox.add(wildfire_observed_toggle) def toggle_wildfire_predicted(button): self.hmi_model.wildfire_map_predicted.drawable = button.get_active( ) wildfire_predicted_toggle = SAOPControlWindow._check_button_with_action( "Predicted wildfire", toggle_wildfire_predicted) flowbox.add(wildfire_predicted_toggle) def toggle_uav(button): self.hmi_model.uav_state.drawable = button.get_active() uav_toggle = SAOPControlWindow._check_button_with_action( "UAV", toggle_uav) flowbox.add(uav_toggle) def toggle_trails(button): self.hmi_model.uav_trail.drawable = button.get_active() trail_toggle = SAOPControlWindow._check_button_with_action( "UAV trails", toggle_trails) flowbox.add(trail_toggle) def toggle_plan(button): self.hmi_model.trajectories.drawable = button.get_active() plan_toggle = SAOPControlWindow._check_button_with_action( "Plan", toggle_plan) flowbox.add(plan_toggle) scrolled_flowbox.add(flowbox) self.figure_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0, border_width=0) self.figure_box.pack_start(self.navigation_toolbar, False, True, 0) self.figure_box.pack_start(self.canvas, True, True, 0) self.figure_box.pack_start(scrolled_flowbox, False, False, 0) ## Side panel right_panel = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) # View of ROS logs log_textview = Gtk.TextView() log_textview.set_editable(False) log_textview.set_monospace(True) log_textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) self.log_textbuffer = log_textview.get_buffer() self.tag_bold = self.log_textbuffer.create_tag( "bold", weight=Pango.Weight.BOLD) log_scrolled = Gtk.ScrolledWindow() log_scrolled.add(log_textview) right_panel.pack_start(log_scrolled, True, True, 0) # UAV controls # self.content_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6, # border_width=12) self.content_panned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL) self.content_panned.pack1(self.figure_box, True, True) self.content_panned.pack2(right_panel, False, True) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0, border_width=0) box.pack_start(self.content_panned, True, True, 0) box.pack_start(self.action_bar, False, False, 0) self.add(box) @staticmethod def _check_button_with_action(name, action): toggle = Gtk.CheckButton(name) toggle.props.active = True toggle.connect("toggled", action) toggle.toggled() return toggle def set_update_plot(self, state: bool): self.hmi_model.gdd_drawable = state def append_log(self, t: datetime.datetime, name: str, msg: str): self._append_text_with_markup( "<b>{}</b> <i>{}</i>: <tt>{}</tt>\n".format( datetime.datetime.strftime(t, "%x %X"), str(name), str(msg))) def _append_text_with_markup(self, text): end_iter = self.log_textbuffer.get_end_iter() self.log_textbuffer.insert_markup(end_iter, text, -1) def set_uav_controls(self): """Update UAV control panel""" pass def update_figure(self): self._update_figure() def _update_figure(self): """Redraw the matplotlib figure""" if self.hmi_model.gdd: self.canvas.figure = self.hmi_model.gdd.figure # a Gtk.DrawingArea self.canvas.draw() # TODO: verify whether we need it or not def on_propagate_clicked(self, button): self.hmi_model.propagate_command() def on_plan_clicked(self, button): known_uavs = self.hmi_model.known_uavs() if known_uavs: pdiag = PlanCreationWindow(self, known_uavs) if pdiag.run() == Gtk.ResponseType.OK: GLib.idle_add(self.hmi_model.plan_command, pdiag.vns_conf, pdiag.planning_duration, pdiag.mission_duration, pdiag.uav_selection, pdiag.trajectory_duration, pdiag.mission_start_delay) pdiag.destroy() else: dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "No UAVs available for planning") dialog.format_secondary_text( "Wait for UAVs to be detected or check Neptus and Dune are running" ) dialog.set_transient_for(self) dialog.run() dialog.destroy() def on_cancel_clicked(self, button): for uav in self.hmi_model.known_uavs(): self.hmi_model.stop_command(uav) def on_home_clicked(self, button, uav: ty.Optional[str]): # Go home self.hmi_model.home_command(uav) def on_new_uav(self, uav: str): print("Go Home!") if len(self.hmi_model.uav_state.data.keys()) < 1: return self.home_button.set_sensitive(True) menu = Gtk.Menu() for uav in [*self.hmi_model.uav_state.data.keys()]: def cb(menu_item, u): self.hmi_model.home_command(u) mi = Gtk.MenuItem(label=uav) mi.connect("activate", cb, uav) mi.show() menu.append(mi) self.home_more_button.set_use_popover(True) self.home_more_button.set_popup(menu)
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 MyWindow(Gtk.Window): def __init__(self): #Janela Gtk.Window.__init__(self, title="IEM-WBB") self.set_resizable(False) self.set_size_request(600, 600) self.set_position(Gtk.WindowPosition.CENTER) self.set_border_width(10) principal = Gtk.Box(spacing=10) principal.set_homogeneous(False) vbox_left = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox_left.set_homogeneous(False) vbox_right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox_right.set_homogeneous(False) ### Boxes que ficarão dentro da vbox_left ### hbox_MDIST = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_MDIST.set_homogeneous(False) hbox_labels = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_labels.set_homogeneous(False) hbox_RDIST = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_RDIST.set_homogeneous(True) hbox_TOTEX = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_TOTEX.set_homogeneous(True) hbox_MVELO = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_MVELO.set_homogeneous(True) #criando os elementos das boxes Mdist, Labels, Rdist, TOTEX, Mvelo ##MDIST## label_Mdist = Gtk.Label('MDIST') self.entry_Mdist = Gtk.Entry() self.entry_Mdist.set_editable(False) self.entry_Mdist.set_max_length(max=8) ##LABELS## label_vazia = Gtk.Label(' ') label_AP = Gtk.Label('AP') label_ML = Gtk.Label('ML') label_TOTAL = Gtk.Label('TOTAL') ##RDIST## label_Rdist = Gtk.Label('RDIST') self.entry_Rdist_AP = Gtk.Entry() self.entry_Rdist_AP.set_editable(False) self.entry_Rdist_AP.set_max_length(max=8) self.entry_Rdist_ML = Gtk.Entry() self.entry_Rdist_ML.set_editable(False) self.entry_Rdist_ML.set_max_length(max=8) self.entry_Rdist_TOTAL = Gtk.Entry() self.entry_Rdist_TOTAL.set_editable(False) self.entry_Rdist_TOTAL.set_max_length(max=8) ##TOTEX## label_TOTEX = Gtk.Label('TOTEX') self.entry_TOTEX_AP = Gtk.Entry() self.entry_TOTEX_AP.set_editable(False) self.entry_TOTEX_AP.set_max_length(max=8) self.entry_TOTEX_ML = Gtk.Entry() self.entry_TOTEX_ML.set_editable(False) self.entry_TOTEX_ML.set_max_length(max=8) self.entry_TOTEX_TOTAL = Gtk.Entry() self.entry_TOTEX_TOTAL.set_editable(False) self.entry_TOTEX_TOTAL.set_max_length(max=8) ##MVELO## label_MVELO = Gtk.Label('MVELO') self.entry_MVELO_AP = Gtk.Entry() self.entry_MVELO_AP.set_editable(False) self.entry_MVELO_AP.set_max_length(max=8) self.entry_MVELO_ML = Gtk.Entry() self.entry_MVELO_ML.set_editable(False) self.entry_MVELO_ML.set_max_length(max=8) self.entry_MVELO_TOTAL = Gtk.Entry() self.entry_MVELO_TOTAL.set_editable(False) self.entry_MVELO_TOTAL.set_max_length(max=8) #colocando cada elemento dentro da sua box hbox_MDIST.pack_start(label_Mdist, True, True, 0) hbox_MDIST.pack_start(self.entry_Mdist, True, True, 0) hbox_labels.pack_start(label_vazia, True, True, 0) hbox_labels.pack_start(label_AP, True, True, 0) hbox_labels.pack_start(label_ML, True, True, 0) hbox_labels.pack_start(label_TOTAL, True, True, 0) hbox_RDIST.pack_start(label_Rdist, True, True, 0) hbox_RDIST.pack_start(self.entry_Rdist_AP, True, True, 0) hbox_RDIST.pack_start(self.entry_Rdist_ML, True, True, 0) hbox_RDIST.pack_start(self.entry_Rdist_TOTAL, True, True, 0) hbox_TOTEX.pack_start(label_TOTEX, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_AP, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_ML, True, True, 0) hbox_TOTEX.pack_start(self.entry_TOTEX_TOTAL, True, True, 0) hbox_MVELO.pack_start(label_MVELO, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_AP, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_ML, True, True, 0) hbox_MVELO.pack_start(self.entry_MVELO_TOTAL, True, True, 0) #colocando as boxes pequenas dentro das box vbox_left vbox_left.pack_start(hbox_MDIST, True, True, 0) vbox_left.pack_start(hbox_labels, True, True, 0) vbox_left.pack_start(hbox_RDIST, True, True, 0) vbox_left.pack_start(hbox_TOTEX, True, True, 0) vbox_left.pack_start(hbox_MVELO, True, True, 0) #elementos da vbox_right) #Notebook self.notebook = Gtk.Notebook() self.fig = plt.figure() self.axis = self.fig.add_subplot(111) self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.canvas = FigureCanvas(self.fig) self.canvas.set_size_request(500, 500) self.page1 = Gtk.Box() self.page1.set_border_width(10) self.page1.add(self.canvas) self.notebook.append_page(self.page1, Gtk.Label('Gráfico do CoP')) ##aqui fica a segunda janela do notebook self.fig2 = plt.figure() self.axis2 = self.fig2.add_subplot(111) self.axis2.set_ylabel('ML') self.axis2.set_xlabel('AP') self.canvas2 = FigureCanvas(self.fig2) self.canvas2.set_size_request(500, 500) self.page2 = Gtk.Box() self.page2.set_border_width(10) self.page2.add(self.canvas2) self.notebook.append_page(self.page2, Gtk.Label('Gráfico na frequência')) #criando os botoes hbox_botoes = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) hbox_botoes.set_homogeneous(True) self.button1 = Gtk.Button(label="Capturar") self.button1.connect("clicked", self.on_button1_clicked) self.button2 = Gtk.Button(label="Processar") self.button2.connect("clicked", self.on_button2_clicked) #colocando os botoes nas boxes hbox_botoes.pack_start(self.button1, True, True, 0) hbox_botoes.pack_start(self.button2, True, True, 0) vbox_right.pack_start(self.notebook, True, True, 0) vbox_right.pack_start(hbox_botoes, True, True, 0) # colocando as boxes verticais dentro da box principal principal.pack_start(vbox_left, True, True, 0) principal.pack_start(vbox_right, True, True, 0) #Adicionano os elementos na box exterior self.add(principal) def on_button1_clicked(self, widget): global APs, MLs balance = calc.readWBB() for (x, y) in balance: APs.append(x) MLs.append(y) max_absoluto_AP = calc.valorAbsoluto(min(APs), max(APs)) max_absoluto_ML = calc.valorAbsoluto(min(MLs), max(MLs)) print('max_absoluto_AP:', max_absoluto_AP, 'max_absoluto_ML:', max_absoluto_ML) self.axis.clear() self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.axis.set_xlim(-max_absoluto_AP, max_absoluto_AP) self.axis.set_ylim(-max_absoluto_ML, max_absoluto_ML) self.axis.plot(APs, MLs, '-', color='r') self.canvas.draw() def on_button2_clicked(self, widget): global APs, MLs APs, MLs = calc.geraAP_ML(APs, MLs) dis_resultante_total = calc.distanciaResultante(APs, MLs) dis_resultante_AP = calc.distanciaResultanteParcial(APs) dis_resultante_ML = calc.distanciaResultanteParcial(MLs) dis_media = calc.distanciaMedia(dis_resultante_total) dis_rms_total = calc.dist_RMS(dis_resultante_total) dis_rms_AP = calc.dist_RMS(dis_resultante_AP) dis_rms_ML = calc.dist_RMS(dis_resultante_ML) totex_total = calc.totex(APs, MLs) totex_AP = calc.totexParcial(APs) totex_ML = calc.totexParcial(MLs) mvelo_total = calc.mVelo(totex_total, 20) mvelo_AP = calc.mVelo(totex_AP, 20) mvelo_ML = calc.mVelo(totex_ML, 20) self.entry_Mdist.set_text(str(dis_media)) self.entry_Rdist_TOTAL.set_text(str(dis_rms_total)) self.entry_Rdist_AP.set_text(str(dis_rms_AP)) self.entry_Rdist_ML.set_text(str(dis_rms_ML)) self.entry_TOTEX_TOTAL.set_text(str(totex_total)) self.entry_TOTEX_AP.set_text(str(totex_AP)) self.entry_TOTEX_ML.set_text(str(totex_ML)) self.entry_MVELO_TOTAL.set_text(str(mvelo_total)) self.entry_MVELO_AP.set_text(str(mvelo_AP)) self.entry_MVELO_ML.set_text(str(mvelo_ML)) max_absoluto_AP = calc.valorAbsoluto(min(APs), max(APs)) max_absoluto_ML = calc.valorAbsoluto(min(MLs), max(MLs)) max_absoluto_AP *= 1.25 max_absoluto_ML *= 1.25 print('max_absoluto_AP:', max_absoluto_AP, 'max_absoluto_ML:', max_absoluto_ML) self.axis.clear() self.axis.set_xlim(-max_absoluto_AP, max_absoluto_AP) self.axis.set_ylim(-max_absoluto_ML, max_absoluto_ML) self.axis.plot(APs, MLs, '-', color='g') self.axis.set_ylabel('ML') self.axis.set_xlabel('AP') self.canvas.draw()
class MainClass(): def __init__(self): # Gets all the objects of interest: windows, list boxes, graphviews etc self.builder = Gtk.Builder() self.builder.add_from_file('gui/igl-app-window.glade') self.builder.connect_signals(self) self.window = self.builder.get_object('window1') self.sw = self.builder.get_object('graphscrollwindow') self.sw2 = self.builder.get_object('graphtools') self.gtbrevealer = self.builder.get_object('graphtoolsrevealer') self.m1revealer = self.builder.get_object('method1revealer') self.m2revealer = self.builder.get_object('method2revealer') self.fnbox = self.builder.get_object('functioncbtext') self.aentry = self.builder.get_object('aentry') self.bentry = self.builder.get_object('bentry') # Use Headerbar for inputs self.hb = Gtk.HeaderBar() self.hb.set_show_close_button(True) self.hb.set_custom_title(self.builder.get_object('titlebox')) self.window.set_titlebar(self.hb) # Adds widgets that change the view as per method self.m1box = guisetup.MethodDetailsBox() self.m2box = guisetup.MethodDetailsBox() self.m1revealer.add(self.m1box) self.m2revealer.add(self.m2box) self.m1box.mc = self self.m2box.mc = self # TODO: Plot as per the defaults to get started self.fig = Figure(figsize=(5,5), dpi=80) self.ax = self.fig.add_subplot(111) self.canvas = FigureCanvas(self.fig) self.sw.add_with_viewport(self.canvas) self.toolbar = NavigationToolbar(self.canvas, self.window) self.sw2.add_with_viewport(self.toolbar) self.on_params_changed(None) def on_window1_destroy(self, widget): Gtk.main_quit() def toggle_allreveal(self, widget): self.toggle_m1reveal(widget) self.toggle_m2reveal(widget) self.toggle_gtbreveal(widget) # TODO make these change as per the toggle, rather than simply toggling def toggle_gtbreveal(self, widget): if self.gtbrevealer.get_reveal_child(): self.gtbrevealer.set_reveal_child(False) else: self.gtbrevealer.set_reveal_child(True) def toggle_m1reveal(self, widget): if self.m1revealer.get_reveal_child(): self.m1revealer.set_reveal_child(False) else: self.m1revealer.set_reveal_child(True) self.plotexact() def toggle_m2reveal(self, widget): if self.m2revealer.get_reveal_child(): self.m2revealer.set_reveal_child(False) else: self.m2revealer.set_reveal_child(True) self.plotexact() def resetplot(self): self.ax.cla() self.ax.grid(True) def plotexact(self): self.resetplot() n = 1000 xs = linspace(self.a, self.b, n+1, endpoint=True) fxs = self.f(xs) fxexact = self.ax.plot(xs, fxs, 'k-', label='f(x)') # def plotapprox(self): # pass # TODO: Make these happen somewhere else. Also, make these hide if the method revealer is hidden if self.m1revealer.get_reveal_child(): fxapprox1 = self.ax.plot(xs, self.m1box.fapprox(xs), 'b-', label='Method 1') if self.m2revealer.get_reveal_child(): fxapprox2 = self.ax.plot(xs, self.m2box.fapprox(xs), 'r-', label='Method 2') self.ax.legend() self.canvas.draw() def on_params_changed(self, widget): # print 'Integrand changed' try: self.f = eval('lambda x: '+self.fnbox.get_active_text()) self.a = eval(self.aentry.get_text()) self.b = eval(self.bentry.get_text()) self.m1box.set_exact_function_and_bounds(self.f,self.a,self.b) self.m2box.set_exact_function_and_bounds(self.f,self.a,self.b) self.plotexact() except SyntaxError,NameError: pass except: