class GraphDialog(QtGui.QDialog): def __init__(self, grid_size=(1, 1), parent=None): QtGui.QDialog.__init__(self, parent) self.fig = Figure((10, 10)) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self) self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui. QSizePolicy.Expanding) self.gs = gridspec.GridSpec(*grid_size) self.axes_list = [self.fig.add_subplot(s) for s in self.gs] self.gs.tight_layout(self.fig, rect=[0, 0, 1, 1]) def update_axes(self, fun, args): fun(self.axes_list, args) self.gs.tight_layout(self.fig) self.canvas.draw() def resizeEvent(self, re): QtGui.QDialog.resizeEvent(self, re) self.canvas.resize(re.size().width(), re.size().height()) self.gs.tight_layout(self.fig, rect=[0, 0, 1, 1], pad=0)
class GraphDialog(QtGui.QDialog): def __init__(self, grid_size=(1, 1), parent=None): QtGui.QDialog.__init__(self, parent) self.fig = Figure((10, 10)) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self) self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.gs = gridspec.GridSpec(*grid_size) self.axes_list = [self.fig.add_subplot(s) for s in self.gs] self.gs.tight_layout(self.fig, rect=[0, 0, 1, 1]) def update_axes(self, fun, *args, **kwargs): fun(self.axes_list, *args, **kwargs) self.gs.tight_layout(self.fig) self.canvas.draw() def resizeEvent(self, re): QtGui.QDialog.resizeEvent(self, re) self.canvas.resize(re.size().width(), re.size().height()) self.gs.tight_layout(self.fig, rect=[0, 0, 1, 1], pad=0)
class Plot(object): def __init__(self, figure, identifier, filepath): loader = UiLoader() self.ui = loader.load('plot_window.ui', PlotWindow()) # Tell Windows how to handle our windows in the the taskbar, making pinning work properly and stuff: if os.name == 'nt': self.ui.newWindow.connect(set_win_appusermodel) self.set_window_title(identifier, filepath) # figure.tight_layout() self.figure = figure self.canvas = FigureCanvas(figure) self.navigation_toolbar = NavigationToolbar(self.canvas, self.ui) self.lock_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/lock-unlock'), 'Lock axes', self.on_lock_axes_triggered) self.lock_action.setCheckable(True) self.lock_action.setToolTip('Lock axes') self.copy_to_clipboard_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/clipboard--arrow'), 'Copy to clipboard', self.on_copy_to_clipboard_triggered) self.copy_to_clipboard_action.setToolTip('Copy to clipboard') self.copy_to_clipboard_action.setShortcut(QtGui.QKeySequence.Copy) self.ui.verticalLayout_canvas.addWidget(self.canvas) self.ui.verticalLayout_navigation_toolbar.addWidget(self.navigation_toolbar) self.lock_axes = False self.axis_limits = None self.update_window_size() self.ui.show() def on_lock_axes_triggered(self): if self.lock_action.isChecked(): self.lock_axes = True self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock')) else: self.lock_axes = False self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock-unlock')) def on_copy_to_clipboard_triggered(self): lyse.figure_to_clipboard(self.figure) @inmain_decorator() def save_axis_limits(self): axis_limits = {} for i, ax in enumerate(self.figure.axes): # Save the limits of the axes to restore them afterward: axis_limits[i] = ax.get_xlim(), ax.get_ylim() self.axis_limits = axis_limits @inmain_decorator() def clear(self): self.figure.clear() @inmain_decorator() def restore_axis_limits(self): for i, ax in enumerate(self.figure.axes): try: xlim, ylim = self.axis_limits[i] ax.set_xlim(xlim) ax.set_ylim(ylim) except KeyError: continue @inmain_decorator() def set_window_title(self, identifier, filepath): self.ui.setWindowTitle(str(identifier) + ' - ' + os.path.basename(filepath)) @inmain_decorator() def update_window_size(self): l, w = self.figure.get_size_inches() dpi = self.figure.get_dpi() self.canvas.resize(int(l*dpi),int(w*dpi)) self.ui.adjustSize() @inmain_decorator() def draw(self): self.canvas.draw() def show(self): self.ui.show() @property def is_shown(self): return self.ui.isVisible()
def OnCreate(self, form): """ Called when the plugin form is created """ self.statisticsView = None # Get parent widget self.parent = self.FormToPySideWidget(form) w = QtGui.QWidget() #w.resize(200, 200) #w.move(100, 300) #w.setWindowTitle('Simple') # The slices will be ordered and plotted counter-clockwise. labels = 'User', 'Compiler', 'Other' sizes = self.pieStats #[15, 55, 30] colors = ['#80FFCC', '#ACC7FF', '#CC6600'] explode = (0, 0.1, 0) # only "explode" the 2nd slice (i.e. 'Hogs') plt.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90) # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') figure = plt.figure(figsize=(10, 0.2)) figure.set_facecolor(None) figure.patch.set_alpha(0.0) canvas = FigureCanvas(figure) axes = figure.add_subplot(111) axes.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90) self.results_tree = QtGui.QTreeWidget() self.results_tree.setSortingEnabled(False) self.results_tree.headerItem().setText(0, 'Function Type') self.results_tree.headerItem().setText(1, 'Function Name') #exampleFunctionTypes = {"User":["__ArrayUnwind(void *,uint,int,void (*)(void *))","std::char_traits<char>::compare(char const *,char const *,uint)","solve(int)","_main","std::_Tree_val<std::_Tset_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,0>>::~_Tree_val<std::_Tset_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,0>>(void)","std::basic_string<char,std::char_traits<char>,std::allocator<char>>::insert(uint,uint,char)","?erase@?$_Tree@V?$_Tset_traits@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@$0A@@std@@@std@@QAE?AV?$_Tree_const_iterator@V?$_Tree_val@V?$_Tset_traits@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@U?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$al_"],"Compiler":["std::bad_alloc::`vector deleting destructor'(uint)","std::set<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>::~set<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>(void)","std::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string<char,std::char_traits<char>,std::allocator<char>>(void)","std::basic_string<char,std::char_traits<char>,std::allocator<char>>::assign(char const *,uint)"],"Other":["??1bad_alloc@std@@UAE@XZ","operator delete(void *,void *)","__security_check_cookie(x)","std::exception::what(void)","std::exception::exception(std::exception const &)","operator delete(void *)"]} counter = 0 for funcType in self.funcTypeDict.keys(): funcTypeTree = QtGui.QTreeWidgetItem(self.results_tree) funcTypeTree.setText(0, funcType) funcTreeCounter = 0 for funcName in self.funcTypeDict[funcType]: funcItem = QtGui.QTreeWidgetItem(funcTypeTree) funcItem.setText(0, funcType) funcItem.setText(1, funcName) funcTreeCounter += 1 counter += 1 self.results_tree.addTopLevelItem(funcTypeTree) canvas.resize(20, 20) layout = QtGui.QVBoxLayout() layout.addWidget(canvas) w.setLayout(layout) w.setFixedWidth(500) w.setFixedHeight(500) self.results_tree.itemClicked.connect(self.item_click) layoutMain = QtGui.QGridLayout() layoutMain.addWidget(w, 0, 0) layoutMain.addWidget(self.results_tree, 0, 1) canvas.draw() # Populate PluginForm self.parent.setLayout(layoutMain)
class PlotView(QFrame): """PlotView presents a matplotlib canvas with interaction possibilities. (picking and tooltip)""" def __init__(self): """Create a PlotView instance""" QFrame.__init__(self) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # setup some default data values self.data = PlotData() self.data.x_data_type = "number" self.data.setValid(False) self.plot_figure = PlotFigure() self.plot_settings = PlotSettings() self.canvas = FigureCanvas(self.plot_figure.getFigure()) self.canvas.setParent(self) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mouse_handler = MouseHandler(self) def toggleMember(self, line): gid = int(line.get_gid()) if gid in self.plot_settings.getSelectedMembers(): self.plot_settings.unselectMember(gid) else: self.plot_settings.selectMember(gid) @ert_gui.widgets.util.may_take_a_long_time def drawPlot(self): self.plot_figure.drawPlot(self.data, self.plot_settings) self.canvas.draw() def resizeEvent(self, event): QFrame.resizeEvent(self, event) self.canvas.resize(event.size().width(), event.size().height()) def loadSettings(self, name): if self.data.isValid(): plot_config_loader = PlotSettingsLoader() if not plot_config_loader.load(name, self.plot_settings): self.drawPlot() def saveSettings(self): if self.data.isValid(): plot_config_saver = PlotSettingsSaver() plot_config_saver.save(self.data.getSaveName(), self.plot_settings) def setData(self, data): self.saveSettings() self.data = data self.loadSettings(self.data.getSaveName()) def setXZoomFactors(self, xminf, xmaxf): self.plot_settings.setMinXZoom(xminf) self.plot_settings.setMaxXZoom(xmaxf) def setYZoomFactors(self, yminf, ymaxf): self.plot_settings.setMinYZoom(yminf) self.plot_settings.setMaxYZoom(ymaxf) def save(self): self.saveSettings() plot_generator = PlotGenerator(self.plot_settings.getPlotPath(), self.plot_settings.getPlotConfigPath()) plot_generator.save(self.data) def saveAll(self): self.saveSettings() plot_generator = PlotGenerator(self.plot_settings.getPlotPath(), self.plot_settings.getPlotConfigPath()) plot_generator.saveAll() def copyPlotSettings(self): plot_config_loader = PlotSettingsLoader() plot_config_loader.copy(self.plot_settings) def setPlotPath(self, plot_path): self.plot_settings.setPlotPath(plot_path) def setPlotConfigPath(self, path): self.plot_settings.setPlotConfigPath(path) def _selectedMemberIdentifier(self, artist): return artist.get_gid() in self.plot_settings.getSelectedMembers() def clearSelection(self): selected_lines = self.plot_figure.fig.findobj(self._selectedMemberIdentifier) for line in selected_lines: self.plot_settings.unselectMember(line.get_gid()) def displayToolTip(self, event): if not self.data is None and not event.xdata is None and not event.ydata is None: if self.data.getXDataType() == "time": date = matplotlib.dates.num2date(event.xdata) self.setToolTip("x: %s y: %04f" % (date.strftime("%d/%m-%Y"), event.ydata)) else: self.setToolTip("x: %04f y: %04f" % (event.xdata, event.ydata)) else: self.setToolTip("") def annotate(self, label, x, y, xt=None, yt=None): self.plot_settings.addAnnotation(label, x, y, xt, yt) def removeAnnotation(self, annotation_artist): annotations = self.plot_settings.getAnnotations() for annotation in annotations: if annotation.getUserData() == annotation_artist: self.plot_settings.removeAnnotation(annotation) def moveAnnotation(self, annotation_artist, xt, yt): annotations = self.plot_settings.getAnnotations() for annotation in annotations: if annotation.getUserData() == annotation_artist: annotation.xt = xt annotation.yt = yt annotation_artist.xytext = (xt, yt) def draw(self): self.canvas.draw() def setMinYLimit(self, value): self.plot_settings.setMinYLimit(value) def setMaxYLimit(self, value): self.plot_settings.setMaxYLimit(value) def setMinXLimit(self, value): self.plot_settings.setMinXLimit(value) def setMaxXLimit(self, value): self.plot_settings.setMaxXLimit(value) def getPlotConfigList(self): return self.plot_settings.getPlotConfigList()
class Plot(object): def __init__(self, figure, identifier, filepath): loader = UiLoader() self.ui = loader.load('plot_window.ui', PlotWindow()) # Tell Windows how to handle our windows in the the taskbar, making pinning work properly and stuff: if os.name == 'nt': self.ui.newWindow.connect(set_win_appusermodel) self.set_window_title(identifier, filepath) # figure.tight_layout() self.figure = figure self.canvas = FigureCanvas(figure) self.navigation_toolbar = NavigationToolbar(self.canvas, self.ui) self.lock_action = self.navigation_toolbar.addAction( QtGui.QIcon(':qtutils/fugue/lock-unlock'), 'Lock axes', self.on_lock_axes_triggered) self.lock_action.setCheckable(True) self.lock_action.setToolTip('Lock axes') self.ui.verticalLayout_canvas.addWidget(self.canvas) self.ui.verticalLayout_navigation_toolbar.addWidget(self.navigation_toolbar) self.lock_axes = False self.axis_limits = None self.update_window_size() self.ui.show() def on_lock_axes_triggered(self): if self.lock_action.isChecked(): self.lock_axes = True self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock')) else: self.lock_axes = False self.lock_action.setIcon(QtGui.QIcon(':qtutils/fugue/lock-unlock')) @inmain_decorator() def save_axis_limits(self): axis_limits = {} for i, ax in enumerate(self.figure.axes): # Save the limits of the axes to restore them afterward: axis_limits[i] = ax.get_xlim(), ax.get_ylim() self.axis_limits = axis_limits @inmain_decorator() def clear(self): self.figure.clear() @inmain_decorator() def restore_axis_limits(self): for i, ax in enumerate(self.figure.axes): try: xlim, ylim = self.axis_limits[i] ax.set_xlim(xlim) ax.set_ylim(ylim) except KeyError: continue @inmain_decorator() def set_window_title(self, identifier, filepath): self.ui.setWindowTitle(str(identifier) + ' - ' + os.path.basename(filepath)) @inmain_decorator() def update_window_size(self): l, w = self.figure.get_size_inches() dpi = self.figure.get_dpi() self.canvas.resize(int(l*dpi),int(w*dpi)) self.ui.adjustSize() @inmain_decorator() def draw(self): self.canvas.draw() def show(self): self.ui.show() @property def is_shown(self): return self.ui.isVisible()
class CutePlot(QMainWindow): def __init__(self, parent=None): super(CutePlot, self).__init__(parent) # Default values for lower and upper bound self.LB_default = -10 self.UB_default = 10 # Create main plot area + menus + status bar self.create_main_frame() #self.textbox.setText() self.LB_UB_defaults() self.on_draw() self.statusBar() self.setWindowTitle('Graficador') self.create_menu() self.guardarImagen() def LB_UB_defaults(self): # Set default values for lower bound and upper bound self.lowerbound.setText(str(self.LB_default)) self.upperbound.setText(str(self.UB_default)) def create_main_frame(self): self.main_frame = QWidget() # 7x5 inches, 80 dots-per-inch self.dpi = 80 self.fig = Figure((7, 5), dpi = self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.guardarImagen() self.is_data = False self.axes = self.fig.add_subplot(111) # axis_state keeps track of how many subplots are present # axis_state = 0: main plot only # axis_state = 1: horizontal split (quadrants 1 and 2) # axis_state = 2: vertical split (quadrants 1 and 4) # axis_state = 3: show all 4 subplots self.axis_state = 0 self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # f(x) textbox self.title = QLabel('<font size=4><em>f</em> (<em>x </em>) =</font>') self.textbox = QLineEdit() self.textbox.setMinimumWidth(200) self.connect(self.textbox, SIGNAL('returnPressed()'), self.on_draw) # Lowerbound and upperbound textboxes self.LB_title = QLabel('<font size=4>Min:</font>') self.lowerbound = QLineEdit() self.lowerbound.setMaximumWidth(30) self.connect(self.lowerbound, SIGNAL('returnPressed()'), self.on_draw) self.UB_title = QLabel('<font size=4>Max:</font>') self.upperbound = QLineEdit() self.upperbound.setMaximumWidth(30) self.connect(self.upperbound, SIGNAL('returnPressed()'), self.on_draw) # Plot button self.draw_button = QPushButton("&Plot") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) # Hold checkbox self.hold_cb = QCheckBox("&Hold") self.hold_cb.setChecked(False) self.connect(self.hold_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.hold_cb.setToolTip('Prevent new plots from replacing old ones') self.hold_cb.setStatusTip('Prevent new plots from replacing old ones') # Log-x and log-y checkboxes self.logx_cb = QCheckBox("Log-&x") self.logx_cb.setChecked(False) self.connect(self.logx_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logx_cb.setToolTip('Change x-axis to logarithmic scale') self.logx_cb.setStatusTip('Change x-axis to logarithmic scale') self.logy_cb = QCheckBox("Log-&y") self.logy_cb.setChecked(False) self.connect(self.logy_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logy_cb.setToolTip('Change y-axis to logarithmic scale') self.logy_cb.setStatusTip('Change y-axis to logarithmic scale') # Truncated-log checkbox self.trunc_cb = QCheckBox("Show &Negative") self.trunc_cb.setChecked(False) self.connect(self.trunc_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.trunc_cb.setToolTip('Plot negative values of log-transformed functions') self.trunc_cb.setStatusTip('Plot negative values of log-transformed functions') # Grid checkbox self.grid_cb = QCheckBox("&Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.grid_cb.setToolTip('Show grid') self.grid_cb.setStatusTip('Show grid') # Grid layout grid = QGridLayout() grid.setSpacing(10) gridCol = 0 for w in [self.title, self.textbox, self.LB_title, self.lowerbound, self.UB_title, self.upperbound, self.draw_button]: grid.addWidget(w, 0, gridCol) gridCol += 1 grid2 = QGridLayout() grid2.setSpacing(10) gridCol = 0 for w in [self.logx_cb, self.logy_cb, self.trunc_cb, self.hold_cb, self.grid_cb]: grid2.addWidget(w, 0, gridCol) gridCol += 1 vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addLayout(grid2) vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def on_minor_change(self): self.on_draw(self.is_data) def on_draw(self, *args): # Get x-domain from user input self.LB_input = unicode(self.lowerbound.text()) self.UB_input = unicode(self.upperbound.text()) # Message box error if the domain inputs aren't int or float types # If float, round to the nearest 0.1 round_to = 10 try: self.LB_float = int(self.LB_input)*round_to self.UB_float = int(self.UB_input)*round_to except: self.LB_UB_defaults() QMessageBox.question(self, 'Error', '<center>Minimum and maximum values must be<br />\ integer or floating-point numbers.</center>', QMessageBox.Ok) # Make sure UB > LB if self.UB_float <= self.LB_float: self.LB_UB_defaults() QMessageBox.question(self, 'Error', '<center>Maximum must be greater\ than minimum value.</center>', QMessageBox.Ok) # If plotting a function, then get x and y values if len(args) == 0: self.is_data = False # Set x values (/round_to is to use range() with floating-point numbers) self.input_x = range(self.LB_float, self.UB_float + 1) self.input_x = [i/float(round_to) for i in self.input_x] # Calculate f(x) values for specified function fx = unicode(self.textbox.text()) # If the f(x) field is empty, then default to y = 0 plot if fx == '': self.y = [0 for i in self.input_x] # Otherwise, evaluate the specified function and get ready to plot else: # Replace exp with numbers fx = fx.replace('exp', str(exp(1)) + '**') # Allow users to enter ^ for powers (replace ^ with **) fx = fx.replace('^', '**') # Try and evaluate; if there is an error, then shift slightly to the right try: self.y = [eval(fx) for x in self.input_x] except: fx = fx.replace('x', '(x + 10**(-6))') self.y = [eval(fx) for x in self.input_x] self.plot_symbol = '-' if self.is_data: self.plot_symbol = 'o' # If the hold box is checked, then new plots do not erase old ones new_state = self.quad_check() if self.axis_state == 0: self.axes.hold(self.hold_cb.isChecked()) else: if self.hold_cb.isChecked(): # If 'hold' is checked, see what quadrants will be shown # - if the quadrant state changes, remove subplots # - otherwise retain subplots if self.axis_state == 0 and new_state == 0: self.axes.hold(self.hold_cb.isChecked()) elif self.axis_state == 3 and new_state == 3: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) self.axes_Q3.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) elif self.axis_state == 1 and new_state == 1: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) elif self.axis_state == 2 and new_state == 2: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) else: self.remove_subplots() else: self.remove_subplots() # If show negative box is unchecked if not self.trunc_cb.isChecked(): self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('linear') elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('log') self.axes.set_yscale('linear') elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('log') else: self.axes.set_xscale('log') self.axes.set_yscale('log') else: # Linear plot #if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): if new_state == 0: self.add_main() self.axes.plot(self.input_x,self.y,self.plot_symbol) # Log x, linear y plot #elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): elif new_state == 1: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x,self.y,self.plot_symbol) else: self.trunc_logx() # Linear x, log y plot #elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): elif new_state == 2: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x,self.y,self.plot_symbol) else: self.trunc_logy() # Log-log plot else: if not self.trunc_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x,self.y,self.plot_symbol) else: self.trunc_loglog() # Add grid if grid checkbox is checked if self.axis_state == 0: self.axes.grid(self.grid_cb.isChecked()) else: if hasattr(self, 'axes_Q1'): self.axes_Q1.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q2'): self.axes_Q2.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q3'): self.axes_Q3.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q4'): self.axes_Q4.grid(self.grid_cb.isChecked()) self.axes.set_xlabel('$x$') self.axes.set_ylabel('$f(x)$') self.canvas.draw() self.guardarImagen() def remove_subplots(self): # Remove all subplots and axis flip flags if hasattr(self, 'axes_Q1'): self.fig.delaxes(self.axes_Q1) del self.axes_Q1 if hasattr(self, 'axes_Q2'): self.fig.delaxes(self.axes_Q2) del self.axes_Q2 if hasattr(self, 'flip_Q2'): del self.flip_Q2 if hasattr(self, 'axes_Q3'): self.fig.delaxes(self.axes_Q3) del self.axes_Q3 del self.flip_Q3 if hasattr(self, 'axes_Q4'): self.fig.delaxes(self.axes_Q4) del self.axes_Q4 if hasattr(self, 'flip_Q4'): del self.flip_Q4 def add_main(self): # Reinsert the main plot if self.axis_state > 0: self.remove_subplots() self.axes = self.fig.add_subplot(111) self.axis_state = 0 def create_menu(self): exitAction = QAction('Quit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) menubar = self.menuBar() fileMenu = menubar.addMenu('&File') save_plot_action = self.create_action("&Save plot", shortcut = "Ctrl+S", slot = self.save_plot, tip = "Save image to file") import_data_action = self.create_action("&Import data", shortcut = "Ctrl+I", slot = self.import_data, tip = "Import data from file") fileMenu.addAction(save_plot_action) fileMenu.addAction(import_data_action) fileMenu.addAction(exitAction) helpMenu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut = 'F1', slot = self.on_about, tip = 'About CutePlot') helpMenu.addAction(about_action) def create_action(self, text, slot = None, shortcut = None, icon = None, tip = None, checkable = False, signal = "triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi = self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def import_data(self): file_choices = "*.csv;;*.txt;;*.tab;;*.dat;;*.*" self.path = QFileDialog.getOpenFileName(self, 'Import data', '', file_choices) if self.path: datafile = open(self.path[0], 'r') if datafile: self.is_data = True delimiter = ',' input_xy = [map(float, line.strip().split(delimiter)) for line in datafile] self.input_x, self.y = [[row[col] for row in input_xy] for col in [0, 1]] datafile.close() self.statusBar().showMessage('Imported data', 2000) self.on_draw(self.is_data) def on_about(self): msg = """<center><b>CutePlot v. 0.1</b></center> <center>Free, open-source plotting program,<br /> written in Python (PySide/Qt + matplotlib).</center> <center>(c) Jack Peterson, 2012</center> """ QMessageBox.about(self, "About", msg.strip()) def quad_check(self): # Q = quadrant Q1 = False Q2 = False Q3 = False Q4 = False # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1 = True elif self.input_x[j] < 0 and self.y[j] > 0: Q2 = True elif self.input_x[j] < 0 and self.y[j] < 0: Q3 = True elif self.input_x[j] > 0 and self.y[j] < 0: Q4 = True if (Q3 or (Q2 and Q4) or ((Q2 or Q4) and self.axis_state == 3)) and self.logx_cb.isChecked() and self.logy_cb.isChecked(): new_state = 3 elif (Q2 and self.logx_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 1): new_state = 1 elif (Q4 and self.logy_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 2): new_state = 2 else: new_state = 0 return new_state def trunc_logx(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) # If only Q1 is populated, then use an ordinary semilogx plot if Q2_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 2 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 1 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(122) self.axes_Q2 = self.fig.add_subplot(121) self.axes_Q1.autoscale(enable = True) self.axes_Q2.autoscale(enable = True) self.axes_Q1.semilogx(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.semilogx(Q2_x, Q2_y, self.plot_symbol) # Reverse Q2 x-axis if not hasattr(self, 'flip_Q2'): self.flip_Q2 = True self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) except: x_UB_Q1 = 2 x_LB_Q1 = -1 Q1_xlabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.xaxis.tick_bottom() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) except: x_UB_Q2 = 2 x_LB_Q2 = -1 Q2_xlabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.xaxis.tick_bottom() self.axes_Q2.yaxis.tick_left() def trunc_logy(self): # Q = quadrant Q1_x = [] Q1_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary semilogy plot if Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 1 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 2 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(211) self.axes_Q4 = self.fig.add_subplot(212) self.axes_Q1.autoscale(enable = True) self.axes_Q4.autoscale(enable = True) self.axes_Q1.semilogy(Q1_x, Q1_y, self.plot_symbol) self.axes_Q4.semilogy(Q4_x, Q4_y, self.plot_symbol) # Reverse Q4 y-axis if not hasattr(self, 'flip_Q4'): self.flip_Q4 = True self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_yticklabels([]) else: try: y_UB_Q1 = int(ceil(log10(max(Q1_y)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: y_UB_Q1 = 2 y_LB_Q1 = -1 Q1_ylabels = [] for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_yticklabels([]) else: try: y_UB_Q4 = int(ceil(log10(max(Q4_y)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: y_UB_Q4 = 2 y_LB_Q4 = -1 Q4_ylabels = [] for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def trunc_loglog(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] Q3_x = [] Q3_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] < 0: Q3_x.append(-self.input_x[j]) Q3_y.append(-self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary loglog plot if Q2_x == [] and Q3_x == [] and Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) self.axis_state = 3 # Create 4 subplots self.axes_Q1 = self.fig.add_subplot(222) self.axes_Q2 = self.fig.add_subplot(221) self.axes_Q3 = self.fig.add_subplot(223) self.axes_Q4 = self.fig.add_subplot(224) self.axes_Q1.autoscale(enable = True) self.axes_Q2.autoscale(enable = True) self.axes_Q3.autoscale(enable = True) self.axes_Q4.autoscale(enable = True) self.axes_Q1.loglog(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.loglog(Q2_x, Q2_y, self.plot_symbol) self.axes_Q3.loglog(Q3_x, Q3_y, self.plot_symbol) self.axes_Q4.loglog(Q4_x, Q4_y, self.plot_symbol) if not hasattr(self, 'flip_Q3'): self.flip_Q3 = True # Reverse Q2 x-axis self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Reverse Q3 x- and y-axes self.axes_Q3.set_xlim(self.axes_Q3.get_xlim()[::-1]) self.axes_Q3.set_ylim(self.axes_Q3.get_ylim()[::-1]) # Reverse Q4 y-axis self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) self.axes_Q1.set_yticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) y_UB_Q1 = int(ceil(log10(max(Q1_y)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: x_UB_Q1 = 2 y_UB_Q1 = 2 x_LB_Q1 = -1 y_LB_Q1 = -1 Q1_xlabels = [] Q1_ylabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) self.axes_Q2.set_yticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) y_UB_Q2 = int(ceil(log10(max(Q2_y)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) y_LB_Q2 = int(floor(log10(min(Q2_y)))) except: x_UB_Q2 = 2 y_UB_Q2 = 2 x_LB_Q2 = -1 y_LB_Q2 = -1 Q2_xlabels = [] Q2_ylabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q2, y_UB_Q2 + 1): Q2_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.set_yticklabels(Q2_ylabels) self.axes_Q2.xaxis.tick_top() self.axes_Q2.yaxis.tick_left() # Q3 axes if Q3_x == [] and not self.hold_cb.isChecked(): self.axes_Q3.set_xticklabels([]) self.axes_Q3.set_yticklabels([]) else: try: x_UB_Q3 = int(ceil(log10(max(Q3_x)))) y_UB_Q3 = int(ceil(log10(max(Q3_y)))) x_LB_Q3 = int(floor(log10(min(Q3_x)))) y_LB_Q3 = int(floor(log10(min(Q3_y)))) except: x_UB_Q3 = 2 y_UB_Q3 = 2 x_LB_Q3 = -1 y_LB_Q3 = -1 Q3_xlabels = [] Q3_ylabels = [] for i in range(x_LB_Q3, x_UB_Q3 + 1): Q3_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q3, y_UB_Q3 + 1): Q3_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q3.set_xticklabels(Q3_xlabels) self.axes_Q3.set_yticklabels(Q3_ylabels) self.axes_Q3.xaxis.tick_bottom() self.axes_Q3.yaxis.tick_left() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_xticklabels([]) self.axes_Q4.set_yticklabels([]) else: try: x_UB_Q4 = int(ceil(log10(max(Q4_x)))) y_UB_Q4 = int(ceil(log10(max(Q4_y)))) x_LB_Q4 = int(floor(log10(min(Q4_x)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: x_UB_Q4 = 2 y_UB_Q4 = 2 x_LB_Q4 = -1 y_LB_Q4 = -1 Q4_xlabels = [] Q4_ylabels = [] for i in range(x_LB_Q4, x_UB_Q4 + 1): Q4_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_xticklabels(Q4_xlabels) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def guardarImagen(self): path = os.path.abspath("untitled.png") self.canvas.resize(460,261 ) self.canvas.print_figure(path, dpi = self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) self.canvas.resize(560,361 )
class CutePlot(QMainWindow): def __init__(self, parent=None): super(CutePlot, self).__init__(parent) # Default values for lower and upper bound self.LB_default = -10 self.UB_default = 10 # Create main plot area + menus + status bar self.create_main_frame() #self.textbox.setText() self.LB_UB_defaults() self.on_draw() self.statusBar() self.setWindowTitle('Graficador') self.create_menu() self.guardarImagen() def LB_UB_defaults(self): # Set default values for lower bound and upper bound self.lowerbound.setText(str(self.LB_default)) self.upperbound.setText(str(self.UB_default)) def create_main_frame(self): self.main_frame = QWidget() # 7x5 inches, 80 dots-per-inch self.dpi = 80 self.fig = Figure((5, 3), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.is_data = False self.axes = self.fig.add_subplot(111) # axis_state keeps track of how many subplots are present # axis_state = 0: main plot only # axis_state = 1: horizontal split (quadrants 1 and 2) # axis_state = 2: vertical split (quadrants 1 and 4) # axis_state = 3: show all 4 subplots self.axis_state = 0 self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) # f(x) textbox self.title = QLabel('<font size=4><em>f</em> (<em>x </em>) =</font>') self.textbox = QLineEdit() self.textbox.setMinimumWidth(200) self.connect(self.textbox, SIGNAL('returnPressed()'), self.on_draw) # Lowerbound and upperbound textboxes self.LB_title = QLabel('<font size=4>Min:</font>') self.lowerbound = QLineEdit() self.lowerbound.setMaximumWidth(30) self.connect(self.lowerbound, SIGNAL('returnPressed()'), self.on_draw) self.UB_title = QLabel('<font size=4>Max:</font>') self.upperbound = QLineEdit() self.upperbound.setMaximumWidth(30) self.connect(self.upperbound, SIGNAL('returnPressed()'), self.on_draw) # Plot button self.draw_button = QPushButton("&Plot") self.connect(self.draw_button, SIGNAL('clicked()'), self.on_draw) # Hold checkbox self.hold_cb = QCheckBox("&Hold") self.hold_cb.setChecked(False) self.connect(self.hold_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.hold_cb.setToolTip('Prevent new plots from replacing old ones') self.hold_cb.setStatusTip('Prevent new plots from replacing old ones') # Log-x and log-y checkboxes self.logx_cb = QCheckBox("Log-&x") self.logx_cb.setChecked(False) self.connect(self.logx_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logx_cb.setToolTip('Change x-axis to logarithmic scale') self.logx_cb.setStatusTip('Change x-axis to logarithmic scale') self.logy_cb = QCheckBox("Log-&y") self.logy_cb.setChecked(False) self.connect(self.logy_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.logy_cb.setToolTip('Change y-axis to logarithmic scale') self.logy_cb.setStatusTip('Change y-axis to logarithmic scale') # Truncated-log checkbox self.trunc_cb = QCheckBox("Show &Negative") self.trunc_cb.setChecked(False) self.connect(self.trunc_cb, SIGNAL('stateChanged(int)'), self.on_draw) self.trunc_cb.setToolTip( 'Plot negative values of log-transformed functions') self.trunc_cb.setStatusTip( 'Plot negative values of log-transformed functions') # Grid checkbox self.grid_cb = QCheckBox("&Grid") self.grid_cb.setChecked(False) self.connect(self.grid_cb, SIGNAL('stateChanged(int)'), self.on_minor_change) self.grid_cb.setToolTip('Show grid') self.grid_cb.setStatusTip('Show grid') # Grid layout grid = QGridLayout() grid.setSpacing(10) gridCol = 0 for w in [ self.title, self.textbox, self.LB_title, self.lowerbound, self.UB_title, self.upperbound, self.draw_button ]: grid.addWidget(w, 0, gridCol) gridCol += 1 grid2 = QGridLayout() grid2.setSpacing(10) gridCol = 0 for w in [ self.logx_cb, self.logy_cb, self.trunc_cb, self.hold_cb, self.grid_cb ]: grid2.addWidget(w, 0, gridCol) gridCol += 1 vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addLayout(grid2) vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def on_minor_change(self): self.on_draw(self.is_data) def on_draw(self, *args): # Get x-domain from user input self.LB_input = unicode(self.lowerbound.text()) self.UB_input = unicode(self.upperbound.text()) # Message box error if the domain inputs aren't int or float types # If float, round to the nearest 0.1 round_to = 10 try: self.LB_float = int(self.LB_input) * round_to self.UB_float = int(self.UB_input) * round_to except: self.LB_UB_defaults() QMessageBox.question( self, 'Error', '<center>Minimum and maximum values must be<br />\ integer or floating-point numbers.</center>', QMessageBox.Ok) # Make sure UB > LB if self.UB_float <= self.LB_float: self.LB_UB_defaults() QMessageBox.question( self, 'Error', '<center>Maximum must be greater\ than minimum value.</center>', QMessageBox.Ok) # If plotting a function, then get x and y values if len(args) == 0: self.is_data = False # Set x values (/round_to is to use range() with floating-point numbers) self.input_x = range(self.LB_float, self.UB_float + 1) self.input_x = [i / float(round_to) for i in self.input_x] # Calculate f(x) values for specified function fx = unicode(self.textbox.text()) # If the f(x) field is empty, then default to y = 0 plot if fx == '': self.y = [0 for i in self.input_x] # Otherwise, evaluate the specified function and get ready to plot else: # Replace exp with numbers fx = fx.replace('exp', str(exp(1)) + '**') # Allow users to enter ^ for powers (replace ^ with **) fx = fx.replace('^', '**') # Try and evaluate; if there is an error, then shift slightly to the right try: self.y = [eval(fx) for x in self.input_x] except: fx = fx.replace('x', '(x + 10**(-6))') self.y = [eval(fx) for x in self.input_x] self.plot_symbol = '-' if self.is_data: self.plot_symbol = 'o' # If the hold box is checked, then new plots do not erase old ones new_state = self.quad_check() if self.axis_state == 0: self.axes.hold(self.hold_cb.isChecked()) else: if self.hold_cb.isChecked(): # If 'hold' is checked, see what quadrants will be shown # - if the quadrant state changes, remove subplots # - otherwise retain subplots if self.axis_state == 0 and new_state == 0: self.axes.hold(self.hold_cb.isChecked()) elif self.axis_state == 3 and new_state == 3: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) self.axes_Q3.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) elif self.axis_state == 1 and new_state == 1: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q2.hold(self.hold_cb.isChecked()) elif self.axis_state == 2 and new_state == 2: self.axes_Q1.hold(self.hold_cb.isChecked()) self.axes_Q4.hold(self.hold_cb.isChecked()) else: self.remove_subplots() else: self.remove_subplots() # If show negative box is unchecked if not self.trunc_cb.isChecked(): self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('linear') elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): self.axes.set_xscale('log') self.axes.set_yscale('linear') elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): self.axes.set_xscale('linear') self.axes.set_yscale('log') else: self.axes.set_xscale('log') self.axes.set_yscale('log') else: # Linear plot #if not self.logx_cb.isChecked() and not self.logy_cb.isChecked(): if new_state == 0: self.add_main() self.axes.plot(self.input_x, self.y, self.plot_symbol) # Log x, linear y plot #elif self.logx_cb.isChecked() and not self.logy_cb.isChecked(): elif new_state == 1: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) else: self.trunc_logx() # Linear x, log y plot #elif not self.logx_cb.isChecked() and self.logy_cb.isChecked(): elif new_state == 2: if not self.trunc_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) else: self.trunc_logy() # Log-log plot else: if not self.trunc_cb.isChecked(): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) else: self.trunc_loglog() # Add grid if grid checkbox is checked if self.axis_state == 0: self.axes.grid(self.grid_cb.isChecked()) else: if hasattr(self, 'axes_Q1'): self.axes_Q1.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q2'): self.axes_Q2.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q3'): self.axes_Q3.grid(self.grid_cb.isChecked()) if hasattr(self, 'axes_Q4'): self.axes_Q4.grid(self.grid_cb.isChecked()) self.axes.set_xlabel('$x$') self.axes.set_ylabel('$f(x)$') self.canvas.draw() self.guardarImagen() def remove_subplots(self): # Remove all subplots and axis flip flags if hasattr(self, 'axes_Q1'): self.fig.delaxes(self.axes_Q1) del self.axes_Q1 if hasattr(self, 'axes_Q2'): self.fig.delaxes(self.axes_Q2) del self.axes_Q2 if hasattr(self, 'flip_Q2'): del self.flip_Q2 if hasattr(self, 'axes_Q3'): self.fig.delaxes(self.axes_Q3) del self.axes_Q3 del self.flip_Q3 if hasattr(self, 'axes_Q4'): self.fig.delaxes(self.axes_Q4) del self.axes_Q4 if hasattr(self, 'flip_Q4'): del self.flip_Q4 def add_main(self): # Reinsert the main plot if self.axis_state > 0: self.remove_subplots() self.axes = self.fig.add_subplot(111) self.axis_state = 0 def create_menu(self): exitAction = QAction('Quit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(self.close) menubar = self.menuBar() fileMenu = menubar.addMenu('&File') save_plot_action = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save image to file") import_data_action = self.create_action("&Import data", shortcut="Ctrl+I", slot=self.import_data, tip="Import data from file") fileMenu.addAction(save_plot_action) fileMenu.addAction(import_data_action) fileMenu.addAction(exitAction) helpMenu = self.menuBar().addMenu("&Help") about_action = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About CutePlot') helpMenu.addAction(about_action) def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def save_plot(self): file_choices = "PNG (*.png)" path = unicode( QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def import_data(self): file_choices = "*.csv;;*.txt;;*.tab;;*.dat;;*.*" self.path = QFileDialog.getOpenFileName(self, 'Import data', '', file_choices) if self.path: datafile = open(self.path[0], 'r') if datafile: self.is_data = True delimiter = ',' input_xy = [ map(float, line.strip().split(delimiter)) for line in datafile ] self.input_x, self.y = [[row[col] for row in input_xy] for col in [0, 1]] datafile.close() self.statusBar().showMessage('Imported data', 2000) self.on_draw(self.is_data) def on_about(self): msg = """<center><b>CutePlot v. 0.1</b></center> <center>Free, open-source plotting program,<br /> written in Python (PySide/Qt + matplotlib).</center> <center>(c) Jack Peterson, 2012</center> """ QMessageBox.about(self, "About", msg.strip()) def quad_check(self): # Q = quadrant Q1 = False Q2 = False Q3 = False Q4 = False # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1 = True elif self.input_x[j] < 0 and self.y[j] > 0: Q2 = True elif self.input_x[j] < 0 and self.y[j] < 0: Q3 = True elif self.input_x[j] > 0 and self.y[j] < 0: Q4 = True if (Q3 or (Q2 and Q4) or ((Q2 or Q4) and self.axis_state == 3) ) and self.logx_cb.isChecked() and self.logy_cb.isChecked(): new_state = 3 elif (Q2 and self.logx_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 1): new_state = 1 elif (Q4 and self.logy_cb.isChecked()) or (self.hold_cb.isChecked() and self.axis_state == 2): new_state = 2 else: new_state = 0 return new_state def trunc_logx(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) # If only Q1 is populated, then use an ordinary semilogx plot if Q2_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogx(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 2 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 1 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(122) self.axes_Q2 = self.fig.add_subplot(121) self.axes_Q1.autoscale(enable=True) self.axes_Q2.autoscale(enable=True) self.axes_Q1.semilogx(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.semilogx(Q2_x, Q2_y, self.plot_symbol) # Reverse Q2 x-axis if not hasattr(self, 'flip_Q2'): self.flip_Q2 = True self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) except: x_UB_Q1 = 2 x_LB_Q1 = -1 Q1_xlabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.xaxis.tick_bottom() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) except: x_UB_Q2 = 2 x_LB_Q2 = -1 Q2_xlabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.xaxis.tick_bottom() self.axes_Q2.yaxis.tick_left() def trunc_logy(self): # Q = quadrant Q1_x = [] Q1_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary semilogy plot if Q4_x == [] and not self.hold_cb.isChecked(): self.add_main() self.axes.semilogy(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) if self.axis_state == 1 or self.axis_state == 3: self.axis_state = 3 else: self.axis_state = 2 # Create 2 subplots self.axes_Q1 = self.fig.add_subplot(211) self.axes_Q4 = self.fig.add_subplot(212) self.axes_Q1.autoscale(enable=True) self.axes_Q4.autoscale(enable=True) self.axes_Q1.semilogy(Q1_x, Q1_y, self.plot_symbol) self.axes_Q4.semilogy(Q4_x, Q4_y, self.plot_symbol) # Reverse Q4 y-axis if not hasattr(self, 'flip_Q4'): self.flip_Q4 = True self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_yticklabels([]) else: try: y_UB_Q1 = int(ceil(log10(max(Q1_y)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: y_UB_Q1 = 2 y_LB_Q1 = -1 Q1_ylabels = [] for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_yticklabels([]) else: try: y_UB_Q4 = int(ceil(log10(max(Q4_y)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: y_UB_Q4 = 2 y_LB_Q4 = -1 Q4_ylabels = [] for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def trunc_loglog(self): # Q = quadrant Q1_x = [] Q1_y = [] Q2_x = [] Q2_y = [] Q3_x = [] Q3_y = [] Q4_x = [] Q4_y = [] # Split the x and y values by sign for j in range(0, len(self.input_x)): if self.input_x[j] > 0 and self.y[j] > 0: Q1_x.append(self.input_x[j]) Q1_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] > 0: Q2_x.append(-self.input_x[j]) Q2_y.append(self.y[j]) elif self.input_x[j] < 0 and self.y[j] < 0: Q3_x.append(-self.input_x[j]) Q3_y.append(-self.y[j]) elif self.input_x[j] > 0 and self.y[j] < 0: Q4_x.append(self.input_x[j]) Q4_y.append(-self.y[j]) # If only Q1 is populated, then use an ordinary loglog plot if Q2_x == [] and Q3_x == [] and Q4_x == [] and not self.hold_cb.isChecked( ): self.add_main() self.axes.loglog(self.input_x, self.y, self.plot_symbol) # Otherwise, create a truncated plot else: # Remove main axes if self.axis_state == 0: self.fig.delaxes(self.axes) self.axis_state = 3 # Create 4 subplots self.axes_Q1 = self.fig.add_subplot(222) self.axes_Q2 = self.fig.add_subplot(221) self.axes_Q3 = self.fig.add_subplot(223) self.axes_Q4 = self.fig.add_subplot(224) self.axes_Q1.autoscale(enable=True) self.axes_Q2.autoscale(enable=True) self.axes_Q3.autoscale(enable=True) self.axes_Q4.autoscale(enable=True) self.axes_Q1.loglog(Q1_x, Q1_y, self.plot_symbol) self.axes_Q2.loglog(Q2_x, Q2_y, self.plot_symbol) self.axes_Q3.loglog(Q3_x, Q3_y, self.plot_symbol) self.axes_Q4.loglog(Q4_x, Q4_y, self.plot_symbol) if not hasattr(self, 'flip_Q3'): self.flip_Q3 = True # Reverse Q2 x-axis self.axes_Q2.set_xlim(self.axes_Q2.get_xlim()[::-1]) # Reverse Q3 x- and y-axes self.axes_Q3.set_xlim(self.axes_Q3.get_xlim()[::-1]) self.axes_Q3.set_ylim(self.axes_Q3.get_ylim()[::-1]) # Reverse Q4 y-axis self.axes_Q4.set_ylim(self.axes_Q4.get_ylim()[::-1]) # Set axis tickmarks at powers of 10 # Q1 axes if Q1_x == [] and not self.hold_cb.isChecked(): self.axes_Q1.set_xticklabels([]) self.axes_Q1.set_yticklabels([]) else: try: x_UB_Q1 = int(ceil(log10(max(Q1_x)))) y_UB_Q1 = int(ceil(log10(max(Q1_y)))) x_LB_Q1 = int(floor(log10(min(Q1_x)))) y_LB_Q1 = int(floor(log10(min(Q1_y)))) except: x_UB_Q1 = 2 y_UB_Q1 = 2 x_LB_Q1 = -1 y_LB_Q1 = -1 Q1_xlabels = [] Q1_ylabels = [] for i in range(x_LB_Q1, x_UB_Q1 + 1): Q1_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q1, y_UB_Q1 + 1): Q1_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q1.set_xticklabels(Q1_xlabels) self.axes_Q1.set_yticklabels(Q1_ylabels) self.axes_Q1.xaxis.tick_top() self.axes_Q1.yaxis.tick_right() # Q2 axes if Q2_x == [] and not self.hold_cb.isChecked(): self.axes_Q2.set_xticklabels([]) self.axes_Q2.set_yticklabels([]) else: try: x_UB_Q2 = int(ceil(log10(max(Q2_x)))) y_UB_Q2 = int(ceil(log10(max(Q2_y)))) x_LB_Q2 = int(floor(log10(min(Q2_x)))) y_LB_Q2 = int(floor(log10(min(Q2_y)))) except: x_UB_Q2 = 2 y_UB_Q2 = 2 x_LB_Q2 = -1 y_LB_Q2 = -1 Q2_xlabels = [] Q2_ylabels = [] for i in range(x_LB_Q2, x_UB_Q2 + 1): Q2_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q2, y_UB_Q2 + 1): Q2_ylabels.append('$10^{%s}$' % str(i)) self.axes_Q2.set_xticklabels(Q2_xlabels) self.axes_Q2.set_yticklabels(Q2_ylabels) self.axes_Q2.xaxis.tick_top() self.axes_Q2.yaxis.tick_left() # Q3 axes if Q3_x == [] and not self.hold_cb.isChecked(): self.axes_Q3.set_xticklabels([]) self.axes_Q3.set_yticklabels([]) else: try: x_UB_Q3 = int(ceil(log10(max(Q3_x)))) y_UB_Q3 = int(ceil(log10(max(Q3_y)))) x_LB_Q3 = int(floor(log10(min(Q3_x)))) y_LB_Q3 = int(floor(log10(min(Q3_y)))) except: x_UB_Q3 = 2 y_UB_Q3 = 2 x_LB_Q3 = -1 y_LB_Q3 = -1 Q3_xlabels = [] Q3_ylabels = [] for i in range(x_LB_Q3, x_UB_Q3 + 1): Q3_xlabels.append('$-10^{%s}$' % str(i)) for i in range(y_LB_Q3, y_UB_Q3 + 1): Q3_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q3.set_xticklabels(Q3_xlabels) self.axes_Q3.set_yticklabels(Q3_ylabels) self.axes_Q3.xaxis.tick_bottom() self.axes_Q3.yaxis.tick_left() # Q4 axes if Q4_x == [] and not self.hold_cb.isChecked(): self.axes_Q4.set_xticklabels([]) self.axes_Q4.set_yticklabels([]) else: try: x_UB_Q4 = int(ceil(log10(max(Q4_x)))) y_UB_Q4 = int(ceil(log10(max(Q4_y)))) x_LB_Q4 = int(floor(log10(min(Q4_x)))) y_LB_Q4 = int(floor(log10(min(Q4_y)))) except: x_UB_Q4 = 2 y_UB_Q4 = 2 x_LB_Q4 = -1 y_LB_Q4 = -1 Q4_xlabels = [] Q4_ylabels = [] for i in range(x_LB_Q4, x_UB_Q4 + 1): Q4_xlabels.append('$10^{%s}$' % str(i)) for i in range(y_LB_Q4, y_UB_Q4 + 1): Q4_ylabels.append('$-10^{%s}$' % str(i)) self.axes_Q4.set_xticklabels(Q4_xlabels) self.axes_Q4.set_yticklabels(Q4_ylabels) self.axes_Q4.xaxis.tick_bottom() self.axes_Q4.yaxis.tick_right() def guardarImagen(self): path = os.path.abspath("untitled.png") self.canvas.resize(460, 261) self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) self.canvas.resize(560, 361)
class PlotView(QFrame): """PlotView presents a matplotlib canvas with interaction possibilities. (picking and tooltip)""" def __init__(self): """Create a PlotView instance""" QFrame.__init__(self) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # setup some default data values self.data = PlotData() self.data.x_data_type = "number" self.data.setValid(False) self.plot_figure = PlotFigure() self.plot_settings = PlotSettings() self.canvas = FigureCanvas(self.plot_figure.getFigure()) self.canvas.setParent(self) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mouse_handler = MouseHandler(self) def toggleMember(self, line): gid = int(line.get_gid()) if gid in self.plot_settings.getSelectedMembers(): self.plot_settings.unselectMember(gid) else: self.plot_settings.selectMember(gid) @ert_gui.widgets.util.may_take_a_long_time def drawPlot(self): self.plot_figure.drawPlot(self.data, self.plot_settings) self.canvas.draw() def resizeEvent(self, event): QFrame.resizeEvent(self, event) self.canvas.resize(event.size().width(), event.size().height()) def loadSettings(self, name): if self.data.isValid(): plot_config_loader = PlotSettingsLoader() if not plot_config_loader.load(name, self.plot_settings): self.drawPlot() def saveSettings(self): if self.data.isValid(): plot_config_saver = PlotSettingsSaver() plot_config_saver.save(self.data.getSaveName(), self.plot_settings) def setData(self, data): self.saveSettings() self.data = data self.loadSettings(self.data.getSaveName()) def setXZoomFactors(self, xminf, xmaxf): self.plot_settings.setMinXZoom(xminf) self.plot_settings.setMaxXZoom(xmaxf) def setYZoomFactors(self, yminf, ymaxf): self.plot_settings.setMinYZoom(yminf) self.plot_settings.setMaxYZoom(ymaxf) def save(self): self.saveSettings() plot_generator = PlotGenerator(self.plot_settings.getPlotPath(), self.plot_settings.getPlotConfigPath()) plot_generator.save(self.data) def saveAll(self): self.saveSettings() plot_generator = PlotGenerator(self.plot_settings.getPlotPath(), self.plot_settings.getPlotConfigPath()) plot_generator.saveAll() def copyPlotSettings(self): plot_config_loader = PlotSettingsLoader() plot_config_loader.copy(self.plot_settings) def setPlotPath(self, plot_path): self.plot_settings.setPlotPath(plot_path) def setPlotConfigPath(self, path): self.plot_settings.setPlotConfigPath(path) def _selectedMemberIdentifier(self, artist): return artist.get_gid() in self.plot_settings.getSelectedMembers() def clearSelection(self): selected_lines = self.plot_figure.fig.findobj( self._selectedMemberIdentifier) for line in selected_lines: self.plot_settings.unselectMember(line.get_gid()) def displayToolTip(self, event): if not self.data is None and not event.xdata is None and not event.ydata is None: if self.data.getXDataType() == "time": date = matplotlib.dates.num2date(event.xdata) self.setToolTip("x: %s y: %04f" % (date.strftime("%d/%m-%Y"), event.ydata)) else: self.setToolTip("x: %04f y: %04f" % (event.xdata, event.ydata)) else: self.setToolTip("") def annotate(self, label, x, y, xt=None, yt=None): self.plot_settings.addAnnotation(label, x, y, xt, yt) def removeAnnotation(self, annotation_artist): annotations = self.plot_settings.getAnnotations() for annotation in annotations: if annotation.getUserData() == annotation_artist: self.plot_settings.removeAnnotation(annotation) def moveAnnotation(self, annotation_artist, xt, yt): annotations = self.plot_settings.getAnnotations() for annotation in annotations: if annotation.getUserData() == annotation_artist: annotation.xt = xt annotation.yt = yt annotation_artist.xytext = (xt, yt) def draw(self): self.canvas.draw() def setMinYLimit(self, value): self.plot_settings.setMinYLimit(value) def setMaxYLimit(self, value): self.plot_settings.setMaxYLimit(value) def setMinXLimit(self, value): self.plot_settings.setMinXLimit(value) def setMaxXLimit(self, value): self.plot_settings.setMaxXLimit(value) def getPlotConfigList(self): return self.plot_settings.getPlotConfigList()
class miVentana(QtGui.QWidget): def __init__(self): super(miVentana, self).__init__() self.titulo = 'Toda Informacion' self.ejeX = 'Todos' self.ejeY = 'Tiempo' self.miFilaAuxiliar = Queue() self.miTarjetaAdquisicion = TarjetaAdquisicion(self.miFilaAuxiliar) self.puertoConectadoExitoso = self.miTarjetaAdquisicion.reconociPuerto time.sleep(2) self.initUI() self.estadoAuxiliar = False self.indices = { 'Todos': [0, '$[V] [A] [C] [rpm]$'], 'Tiempo': [0, '$t [s]$'], 'Voltaje': [1, 'v [V]'], 'Corriente': [2, '$I [A]$'], 'Temperatura': [3, '$T [C]$'], 'rpm': [4, '$\omega [rpm]$'] } def initUI(self): ## PARAMETROS DEL MOTOR self.miTituloIzquierdo = QtGui.QLabel('Datos de Placa') self.parametrosOrganizacionales = QtGui.QHBoxLayout() self.parametrosDePlaca1 = QtGui.QHBoxLayout() self.parametrosDePlaca2 = QtGui.QHBoxLayout() self.parametrosDePlaca3 = QtGui.QHBoxLayout() self.miMotor = QtGui.QLabel('Motor:') self.miDataMotor = QtGui.QLineEdit('Motor-001') self.parametrosOrganizacionales.addWidget(self.miMotor) self.parametrosOrganizacionales.addWidget(self.miDataMotor) self.miPotenciaPlaca = QtGui.QLabel('Potencia Nominal [HP]:') self.miDatoPotenciaPlaca = QtGui.QLineEdit(str(85)) self.parametrosDePlaca1.addWidget(self.miPotenciaPlaca) self.parametrosDePlaca1.addWidget(self.miDatoPotenciaPlaca) self.miTensionNominal = QtGui.QLabel('Voltaje Nominal [V]:') self.miDataTensionNominal = QtGui.QLineEdit(str(350)) self.parametrosDePlaca2.addWidget(self.miTensionNominal) self.parametrosDePlaca2.addWidget(self.miDataTensionNominal) self.miVelocidadNominal = QtGui.QLabel('Velocidad Nominal [rpm]:') self.miDataVelocidadNominal = QtGui.QLineEdit(str(3500)) self.parametrosDePlaca3.addWidget(self.miVelocidadNominal) self.parametrosDePlaca3.addWidget(self.miDataVelocidadNominal) # MENU SUPERIOS extractAction = QtGui.QAction('&SALIR', self) extractAction.setShortcut('Ctrl+Q') extractAction.setStatusTip('Salir de la aplicacion') extractAction.triggered.connect(self.closeApplication) ## CONTROL EJECUCIÓN self.botonDeInicio = QtGui.QPushButton("Importar Datos") self.botonDeCancel = QtGui.QPushButton("Detener") self.botonDeInicializacion = QtGui.QPushButton("Descartar Prueba") self.botonParaPDF = QtGui.QPushButton("Exportar Info") #self.botonParaPDF.setStyleSheet("background-color: green") self.botonDeInicializacion.setEnabled(False) self.botonParaPDF.setEnabled(False) self.botonDeCancel.setEnabled(False) self.botonDeInicio.resize(100, 100) self.botonDeCancel.resize(100, 100) #self.botonDeInicio.setMinimumHeight(60) #self.botonDeCancel.setMinimumHeight(60) self.barraControlPrograma = QtGui.QHBoxLayout() self.barraControlPrograma.addWidget(self.botonDeInicio) self.barraControlPrograma.addWidget(self.botonDeCancel) self.barraControlPostPrograma = QtGui.QHBoxLayout() self.barraControlPostPrograma.addWidget(self.botonDeInicializacion) self.barraControlPostPrograma.addWidget(self.botonParaPDF) ## Control de Botones self.botonDeInicio.clicked.connect(self.importarDatos) self.botonDeCancel.clicked.connect(self.detenerDatos) self.botonDeInicializacion.clicked.connect(self.inicializarTodo) self.botonParaPDF.clicked.connect(self.exportarInformacion) # a figure instance to plot on #self.figure = Figure() self.figure = graficaActual.figure(figsize=(15, 5)) # this is the Canvas Widget that displays the `figure` # it takes the `figure` instance as a parameter to __init__ self.canvas = FigureCanvas(self.figure) self.canvas.resize(500, 800) self.canvas.setMinimumWidth(450) self.canvas.setMaximumWidth(500) self.canvas.setMinimumHeight(350) # this is the Navigation widget # it takes the Canvas widget and a parent self.toolbar = NavigationToolbar(self.canvas, self) ### CONTROL DE GRAFICA: self.miTituloControlGrafica = QtGui.QLabel( 'Graficar: (Inicio: ' + self.miTarjetaAdquisicion.horaInicioLiteral + ')') self.miLegendaVS = QtGui.QLabel('vs') self.seleccionDeOrdenada = QtGui.QComboBox(self) self.seleccionDeOrdenada.addItem('Todos') self.seleccionDeOrdenada.addItem('Voltaje') self.seleccionDeOrdenada.addItem('Corriente') self.seleccionDeOrdenada.addItem('Temperatura') self.seleccionDeOrdenada.addItem('rpm') self.seleccionDeAbsisa = QtGui.QComboBox(self) self.seleccionDeAbsisa.addItem('Tiempo') self.seleccionDeAbsisa.addItem('Voltaje') self.seleccionDeAbsisa.addItem('Corriente') self.seleccionDeAbsisa.addItem('Temperatura') self.seleccionDeAbsisa.addItem('rpm') self.seleccionDeOrdenada.activated[str].connect( self.actualizarOrdenada) self.seleccionDeAbsisa.activated[str].connect(self.actualizarAbsisa) self.barraControlGrafica = QtGui.QHBoxLayout() self.barraControlGrafica.addWidget(self.seleccionDeOrdenada) self.barraControlGrafica.addWidget(self.miLegendaVS) self.barraControlGrafica.addWidget(self.seleccionDeAbsisa) ## SECCION COMENTARIOS: self.miTituloComentarios = QtGui.QLabel('Comentarios:') self.miComentario = QtGui.QTextEdit() #self.miComentario.setMinimumHeight(50) ## Control de puerto self.miEtiquetaPuerto = QtGui.QLabel('Conectar a:') self.miIntroduccionPuerto = QtGui.QLineEdit('ttyAMA0') ## Adquisicion de datos: self.miDataMotor.editingFinished.connect(self.actualizarIngresoDatos) self.miDatoPotenciaPlaca.editingFinished.connect( self.actualizarIngresoDatos) self.miDataTensionNominal.editingFinished.connect( self.actualizarIngresoDatos) self.miDataVelocidadNominal.editingFinished.connect( self.actualizarIngresoDatos) self.miIntroduccionPuerto.returnPressed.connect( self.intentoIntroducirPuerto) #self.miComentario.textChanged.connect(self.actualizarIngresoDatos) ## GRAFICA IZQUIERDA capaVerticalAuxiliar = QtGui.QVBoxLayout() if not self.puertoConectadoExitoso: self.miEtiquetaPuerto.setText('No conectado, Conectar a:') self.botonDeInicio.setEnabled(False) capaVerticalAuxiliar.addWidget(self.miEtiquetaPuerto) capaVerticalAuxiliar.addWidget(self.miIntroduccionPuerto) capaVerticalAuxiliar.addWidget(self.miTituloIzquierdo) capaVerticalAuxiliar.addLayout(self.parametrosOrganizacionales) capaVerticalAuxiliar.addLayout(self.parametrosDePlaca1) capaVerticalAuxiliar.addLayout(self.parametrosDePlaca2) capaVerticalAuxiliar.addLayout(self.parametrosDePlaca3) capaVerticalAuxiliar.addWidget(self.miTituloControlGrafica) capaVerticalAuxiliar.addLayout(self.barraControlGrafica) capaVerticalAuxiliar.addWidget(self.miTituloComentarios) capaVerticalAuxiliar.addWidget(self.miComentario) capaVerticalAuxiliar.addStretch(1) capaVerticalAuxiliar.addLayout(self.barraControlPostPrograma) capaVerticalAuxiliar.addLayout(self.barraControlPrograma) graficaV = QtGui.QVBoxLayout() graficaV.addWidget(self.toolbar) graficaV.addWidget(self.canvas) layoutHorizontalPrincipal = QtGui.QHBoxLayout() #layoutHorizontalPrincipal.addStretch(1) layoutHorizontalPrincipal.addLayout(capaVerticalAuxiliar) layoutHorizontalPrincipal.addLayout(graficaV) self.setMinimumHeight(430) self.setLayout(layoutHorizontalPrincipal) self.setGeometry(300, 300, 300, 150) self.setWindowTitle('Adquisición de datos de Motor DC') self.setWindowIcon(QtGui.QIcon('./pictures/logo.png')) try: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create( sys.argv[1])) except: QtGui.QApplication.setStyle( QtGui.QStyleFactory.create('Cleanlooks')) self.show() def closeApplication(self): choice = QtGui.QMessageBox.question( self, 'Leaving so soon?', 'Are you sure you want to exit the program?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.Yes: self.miTarjetaAdquisicion.salir() sys.exit() else: pass def intentoIntroducirPuerto(self): exitoConexion = self.miTarjetaAdquisicion.iniciarPuerto( puerto='/dev/' + self.miIntroduccionPuerto.text()) if exitoConexion: self.miEtiquetaPuerto.setText('Exito, conectado a ' + self.miIntroduccionPuerto.text()) self.miIntroduccionPuerto.clear() #setVisible(False) self.botonDeInicio.setEnabled(True) else: self.miEtiquetaPuerto.setText('Aun no puedo conectarme a ' + self.miIntroduccionPuerto.text()) self.miIntroduccionPuerto.clear() def actualizarIngresoDatos(self): campos = [ self.miDataMotor.text(), self.miDatoPotenciaPlaca.text(), self.miDataTensionNominal.text(), self.miDataVelocidadNominal.text(), self.miComentario.toPlainText() ] if (self.miTarjetaAdquisicion.actualizarCampos(campos)): self.botonParaPDF.setEnabled(True) self.botonDeInicializacion.setEnabled(True) else: self.botonParaPDF.setEnabled(False) self.botonDeInicializacion.setEnabled(False) def actualizarOrdenada(self, text): self.refrescarGrafica(self.seleccionDeOrdenada.currentText(), self.seleccionDeAbsisa.currentText(), self.miTarjetaAdquisicion.obtenerUltimosDatos()) def actualizarAbsisa(self, text): self.refrescarGrafica(self.seleccionDeOrdenada.currentText(), self.seleccionDeAbsisa.currentText(), self.miTarjetaAdquisicion.obtenerUltimosDatos()) def importarDatos(self): self.estadoAuxiliar = True self.miTarjetaAdquisicion.recibirInformacion() self.miTituloControlGrafica.setText( 'Graficar : Inicio (' + self.miTarjetaAdquisicion.horaInicioLiteral + ')') self.botonDeCancel.setEnabled(True) self.botonDeInicio.setEnabled(False) self.miTareaGraficaParalela = threading.Thread( target=self.actualizacionAutomaticaGrafica, args=("task", )) self.miTareaGraficaParalela.start() def inicializarTodo(self): # Se hace un refresco previo para borrar los valores que puedan estar en etapa intermedia self.refrescarGrafica('Todos', 'Tiempo', self.miTarjetaAdquisicion.actualizarDatos()) self.miTarjetaAdquisicion.inicializar() self.refrescarGrafica('Todos', 'Tiempo', self.miTarjetaAdquisicion.actualizarDatos()) self.actualizarIngresoDatos() self.botonDeInicio.setEnabled(True) def exportarInformacion(self): self.actualizarIngresoDatos() self.miTarjetaAdquisicion.exportarDatosACSV() self.refrescarGrafica(self.seleccionDeOrdenada.currentText(), self.seleccionDeAbsisa.currentText(), self.miTarjetaAdquisicion.obtenerUltimosDatos(), True) def detenerDatos(self): self.estadoAuxiliar = False self.miTarjetaAdquisicion.detenerInformacion() self.botonDeInicio.setText("Importar Datos") self.botonDeCancel.setEnabled(False) self.miTareaGraficaParalela.do_run = False self.miTareaGraficaParalela.join() self.actualizarIngresoDatos() def actualizacionAutomaticaGrafica(self, argumento): self.miTareaGraficaParalela = threading.currentThread() while getattr(self.miTareaGraficaParalela, "do_run", True): self.refrescarGrafica(self.seleccionDeOrdenada.currentText(), self.seleccionDeAbsisa.currentText(), self.miTarjetaAdquisicion.actualizarDatos()) #print(len(self.miTarjetaAdquisicion.actualizarDatos()[0])) #print('Finalizando grafica desde adentro') def refrescarGrafica(self, argumentoOrdenada, argumentoAbsisa, listaDatos, guardarEnPDF=False): if listaDatos == None: #print('Nada que actualizar') pass else: self.titulo = self.miDataMotor.text() if argumentoOrdenada == 'Todos': graficaActual.cla() ax1 = self.figure.add_subplot(111) t = listaDatos[0] #range(len(listaDatos[0])) v = listaDatos[1] c = listaDatos[2] T = listaDatos[3] r = listaDatos[4] graficaActual.title(self.titulo) graficaActual.ylabel('$[V] [A] [C] [rpm]$') graficaActual.xlabel( self.miTarjetaAdquisicion.horaInicioLiteral + '+ $t [s]$') graficaActual.grid() graficaActual.plot(t, v, label='v') graficaActual.plot(t, c, label='i') graficaActual.plot(t, T, label='T') graficaActual.plot(t, r, label='rpm') #graficaActual.plot(t,v,'b.-',label='v',t,c,'y.-',label='i',t,T,'r.-',label='T',t,r,'g.-',label='rpm') graficaActual.legend(loc='upper center', bbox_to_anchor=(0.5, 1.05), ncol=4, fancybox=True, shadow=True) if guardarEnPDF: graficaActual.savefig(self.titulo + '_' + self.miTarjetaAdquisicion.fechaHora + '.pdf', bbox_inches='tight') self.canvas.draw() graficaActual.gcf().clear() else: graficaActual.cla() ax1 = self.figure.add_subplot(111) a = listaDatos[self.indices[argumentoAbsisa][0]] o = listaDatos[self.indices[argumentoOrdenada][0]] self.titulo += '_' + argumentoOrdenada + '_vs_' + argumentoAbsisa graficaActual.title(self.titulo) if self.indices[argumentoAbsisa][0] == 0: graficaActual.xlabel( self.miTarjetaAdquisicion.horaInicioLiteral + ' + ' + self.indices[argumentoAbsisa][1]) else: graficaActual.xlabel(self.indices[argumentoAbsisa][1]) graficaActual.ylabel(self.indices[argumentoOrdenada][1]) graficaActual.grid() graficaActual.plot(a, o) if guardarEnPDF: graficaActual.savefig(self.titulo + '_' + self.miTarjetaAdquisicion.fechaHora + '.pdf', bbox_inches='tight') self.canvas.draw()
class MainWindow(QMainWindow): analyzed_data_received_event = QtCore.Signal(object) def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self._ui = Ui_MainWindow() self._ui.setupUi(self) self.device = None self.analyzer = None self.in_error = False self.current_expected_pcm_clock_rate = None self.error_ticks = 0 self.error_counts = 0 self.audio = pyaudio.PyAudio() self.event_listener = SaleaeEventListener() self.event_listener.saleae_event_received.connect(self.on_saleae_event) self.play_sound_thread = SoundOutputRenderer(self.audio) self.analyzed_data_received_event.connect(self.play_sound_thread.on_data_received) PyDevicesManager.register_listener(self.event_listener, EVENT_ID_ONCONNECT) PyDevicesManager.register_listener(self.event_listener, EVENT_ID_ONDISCONNECT) PyDevicesManager.register_listener(self.event_listener, EVENT_ID_ONERROR) PyDevicesManager.register_listener(self.event_listener, EVENT_ID_ONREADDATA) PyDevicesManager.register_listener(self.event_listener, EVENT_ID_ONANALYZERDATA) self.audio_output_devices = [] for i in range(self.audio.get_device_count()): info = self.audio.get_device_info_by_index(i) if info['maxOutputChannels'] > 0: self.audio_output_devices.append(info) self.initialize_ui_items() self.recording_state = STATE_IDLE self.last_record_start = time.clock() self.realtime_timer = QtCore.QTimer() self.realtime_timer.timeout.connect(self.realtime_timer_timeout) self.plot_timer = QtCore.QTimer() self.plot_timer.timeout.connect(self.plot_timer_timeout) self.figure = Figure(dpi=100) self.plotCanvas = FigureCanvas(self.figure) self.plotCanvas.setParent(self._ui.plotWidget) # Hook this up so we can resize the plot canvas dynamically self._ui.plotWidget.installEventFilter(self) self.fft_axis = self.figure.add_subplot(111) self.fft_line = None ytick_values = range(-140, -6, 6) self.fft_axis.set_yticks(ytick_values) self.fft_axis.set_yticklabels(["%d" % w for w in ytick_values], size='xx-small') self.fft_axis.set_xlabel("Frequency (kHz)", size='small') self.fft_axis.set_ylabel("dBFS", size='small') self.fft_axis.grid(True) self.fft_axis.autoscale(enable=False, axis='both') self.plot_background = None self.update_controls() self.show_message("Waiting for a Logic device to connect...") self.event_listener.start() PyDevicesManager.begin_connect() def initialize_ui_items(self,): self._ui.onDecodeErrorComboBox.addItems(ON_DECODE_ERROR_OPTS) self._ui.onDecodeErrorComboBox.setCurrentIndex(HALT) self._ui.frameAlignmentComboBox.addItems(FRAME_ALIGNMENTS) self._ui.frameAlignmentComboBox.setCurrentIndex(FRAME_ALIGN_LAST_BIT) self._ui.clockEdgeComboBox.addItems(EDGES) self._ui.clockEdgeComboBox.setCurrentIndex(FALLING_EDGE) self._ui.outputLocationLineEdit.setText(RUN_PATH) for item in self.audio_output_devices: self._ui.comboOutputDeviceSelection.addItem(item['name'], item) # Select the default audio output default = self.audio.get_default_output_device_info() index = self._ui.comboOutputDeviceSelection.findData(default) if index < 0: index = 0 self._ui.comboOutputDeviceSelection.setCurrentIndex(index) num_channels = self._ui.channelsPerFrameSpinBox.value() self._ui.comboPCMChannelToListenTo.addItems(['%d' % w for w in range(1, num_channels + 1)]) self._ui.comboPCMChannelToListenTo.setCurrentIndex(0) def show_message(self, msg): self._ui.messagesLabel.setText(msg) def start_recording(self, mode): # Create an analyzer channels_per_frame = self._ui.channelsPerFrameSpinBox.value() sampling_rate = int(self._ui.samplingRateLineEdit.text()) bits_per_channel = self._ui.bitsPerChannelSpinBox.value() clock_channel = self._ui.clockChannelSpinBox.value() frame_channel = self._ui.frameChannelSpinBox.value() data_channel = self._ui.dataChannelSpinBox.value() clock_edge = self._ui.clockEdgeComboBox.currentIndex() frame_edge = LEADING_EDGE self.current_expected_pcm_clock_rate = \ channels_per_frame * sampling_rate * bits_per_channel if clock_edge == LEADING_EDGE: frame_edge = FALLING_EDGE decode_error = self._ui.onDecodeErrorComboBox.currentIndex() frame_transition = self._ui.frameAlignmentComboBox.currentIndex() output_dir = None if mode == MODE_WRITE_TO_FILE: output_dir = self._ui.outputLocationLineEdit.text() plot_spectrum = self._ui.checkboxShowSpectrum.isChecked() self.analyzer = PCMAnalyzer( output_folder = output_dir, audio_channels_per_frame = channels_per_frame, audio_sampling_rate_hz = sampling_rate, bits_per_channel = bits_per_channel, clock_channel = clock_channel, frame_channel = frame_channel, data_channel = data_channel, frame_align = frame_edge, frame_transition = frame_transition, clock_edge = clock_edge, on_decode_error = decode_error, calculate_ffts = plot_spectrum, logging = False) # Do not enable this unless you have a HUUUGE hard drive! self.device.set_analyzer(self.analyzer) self.device.set_active_channels(list(range(4))) rate = self.device.get_analyzer().get_minimum_acquisition_rate() self.device.set_sampling_rate_hz(rate) self.device.set_use_5_volts(False) self.recording_state = STATE_WAITING_FOR_FRAME self.last_record_start = time.clock() self.realtime_timer.start(100) if plot_spectrum: self.plot_timer.start(150) if mode == MODE_LISTEN: # Configure the audio player data = self._ui.comboOutputDeviceSelection.itemData( self._ui.comboOutputDeviceSelection.currentIndex()) format = pyaudio.paInt16 if bits_per_channel > 16: format = pyaudio.paInt32 self.play_sound_thread.configure(data['index'], format=format, channels=1, rate = sampling_rate, channel_to_play=self._ui.comboPCMChannelToListenTo.currentIndex()) self.play_sound_thread.start() self.show_message("Waiting for valid frame...") self.device.read_start() self.update_controls() def stop_recording(self,): self.reset() self.recording_state = STATE_IDLE self.show_message("Recording stopped.") self.update_controls() self.validate() def eventFilter(self, object, event): if event.type() == QtCore.QEvent.Resize: if object == self._ui.plotWidget: self.update_plot_canvas() return QWidget.eventFilter(self, object, event) @QtCore.Slot() def on_recordButton_clicked(self,): if self._ui.recordButton.text() == 'Record': self.start_recording(MODE_WRITE_TO_FILE) elif self._ui.recordButton.text() == 'Listen': self.start_recording(MODE_LISTEN) else: self.stop_recording() def realtime_timer_timeout(self,): elapsed = time.clock() - self.last_record_start time_text = "%d:%02.1f" % (elapsed / 60, elapsed % 60) self._ui.recordTimeLabel.setText(time_text) if self.in_error: if self.error_ticks > 15: self._ui.messagesLabel.setStyleSheet("background-color: none;") self.show_message("Continuing recording (last error: %s)..." % time_text) self.error_ticks = 0 self.error_counts = 0 self.in_error = False else: self._ui.messagesLabel.setStyleSheet("background-color: red;") self.error_ticks += 1 def plot_timer_timeout(self, d=None): if self.device is not None and self.device.get_analyzer() is not None: analyzer = self.device.get_analyzer() data = analyzer.get_latest_fft_data(purge=True) if data is not None: self.update_plot(data) @QtCore.Slot() def on_outputLocationBrowseButton_clicked(self,): # Prompt for file name dirname = QFileDialog.getExistingDirectory(self, "Select Output Folder", self._ui.outputLocationLineEdit.text(), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) # Returns a tuple of (filename, filetype) if dirname is not None and len(dirname) > 0: self._ui.outputLocationLineEdit.setText(dirname) @QtCore.Slot(int) def on_channelsPerFrameSpinBox_valueChanged(self, i): self._ui.comboPCMChannelToListenTo.clear() num_channels = self._ui.channelsPerFrameSpinBox.value() self._ui.comboPCMChannelToListenTo.addItems(['%d' % w for w in range(1, num_channels + 1)]) self._ui.comboPCMChannelToListenTo.setCurrentIndex(0) self.validate() @QtCore.Slot(int) def on_clockChannelSpinBox_valueChanged(self, i): self.validate() @QtCore.Slot(int) def on_frameChannelSpinBox_valueChanged(self, i): self.validate() @QtCore.Slot(int) def on_dataChannelSpinBox_valueChanged(self, i): self.validate() @QtCore.Slot(int) def on_comboPCMChannelToListenTo_currentIndexChanged(self, i): if self.play_sound_thread is not None: self.play_sound_thread.channel_to_play = self._ui.comboPCMChannelToListenTo.currentIndex() @QtCore.Slot(int) def on_comboOutputDeviceSelection_currentIndexChanged(self, i): pass @QtCore.Slot() def on_tabOutputRenderer_currentChanged(self,): if self.recording_state != STATE_IDLE: self.stop_recording() self.update_controls() def update_controls(self,): is_recording = (self.recording_state != STATE_IDLE) if not is_recording: current_tab = self._ui.tabOutputRenderer.currentWidget() if current_tab == self._ui.recordToFile: self._ui.recordButton.setText("Record") elif current_tab == self._ui.outputToSoundCard: self._ui.recordButton.setText("Listen") else: self._ui.recordButton.setText("Stop") self._ui.comboOutputDeviceSelection.setEnabled(not is_recording) self._ui.outputLocationBrowseButton.setEnabled(not is_recording) self._ui.logicGroupBox.setEnabled(not is_recording) self._ui.pcmGroupBox.setEnabled(not is_recording) self._ui.outputGroupBox.setEnabled(not is_recording) self._ui.checkboxShowSpectrum.setEnabled(not is_recording) self.update_plot_canvas() def update_plot_canvas(self,): self.fft_axis.set_xlim(0, int(self._ui.samplingRateLineEdit.text()) / 2) freq_values = range(0, int(self._ui.samplingRateLineEdit.text()) / 2, 1000) + \ [int(self._ui.samplingRateLineEdit.text()) / 2] self.fft_axis.set_xticks(freq_values) self.fft_axis.set_xticklabels(["%d" % (w / 1000) for w in freq_values], size='xx-small') self.plotCanvas.resize(self._ui.plotWidget.size().width(), self._ui.plotWidget.size().height()) self.plotCanvas.draw() self.plot_background = None def validate(self,): valid = False if self.device is not None: if (self._ui.clockChannelSpinBox.value() != self._ui.frameChannelSpinBox.value()) and \ (self._ui.frameChannelSpinBox.value() != self._ui.dataChannelSpinBox.value()) and \ (self._ui.clockChannelSpinBox.value() != self._ui.dataChannelSpinBox.value()): dirname = self._ui.outputLocationLineEdit.text() if dirname is not None and len(dirname) > 0: valid = True self.set_valid(valid) def set_valid(self, is_valid): self._ui.recordButton.setEnabled(is_valid) def on_saleae_event(self, event, device): analyzer = None if device is not None and device == self.device and \ self.device.get_analyzer() is not None: analyzer = self.device.get_analyzer() if event.id == EVENT_ID_ONCONNECT: self.show_message("Device connected with id %d" % device.get_id()) self.device = device self.update_controls() self.validate() elif event.id == EVENT_ID_ONERROR: if self._ui.onDecodeErrorComboBox.currentIndex() == HALT: self.stop_recording() self.show_message("ERROR: %s" % event.data) else: if not self.in_error: self.in_error = True self.show_message("ERROR: %s" % event.data) else: self.error_counts += 1 if self.error_counts > 5: self.stop_recording() self.show_message("Too many errors! %s" % event.data) elif event.id == EVENT_ID_ONDISCONNECT: self.recording_state = STATE_IDLE self.show_message("Device id %d disconnected." % device.get_id()) self.shutdown() elif event.id == EVENT_ID_ONREADDATA: if analyzer is not None: if self.recording_state == STATE_WAITING_FOR_FRAME: if self.device.get_analyzer().first_valid_frame_received(): self.show_message("Recording. Press 'Stop' to stop recording.") self.recording_state = STATE_RECORDING elif event.id == EVENT_ID_ONANALYZERDATA: if self.recording_state == STATE_RECORDING and \ self.current_expected_pcm_clock_rate is not None: # Sanity check the sampling rate with the detected clock frequency if analyzer is not None: clock_period_samples = self.device.get_analyzer().get_average_clock_period_in_samples() meas_clock_freq = self.device.get_sampling_rate_hz() / clock_period_samples if (1.2 * self.current_expected_pcm_clock_rate) < meas_clock_freq or \ (0.8 * self.current_expected_pcm_clock_rate) > meas_clock_freq: # The user's setup is probably wrong, so bail immediately self.stop_recording() self.show_message("Detected a PCM clock of ~%d Hz. Check your settings!" % meas_clock_freq) self.analyzed_data_received_event.emit(event.data) def update_plot(self, data): if self.plot_background is None: self.plot_background = self.plotCanvas.copy_from_bbox(self.fft_axis.bbox) channel = self._ui.comboPCMChannelToListenTo.currentIndex() channel_data = data[channel] numpoints = len(channel_data) if self.fft_line is None: self.fft_line, = self.fft_axis.plot(numpy.zeros(numpoints), animated=True) sampling_rate = int(self._ui.samplingRateLineEdit.text()) freqs = numpy.fft.fftfreq(numpoints * 2, d=1.0 / float(sampling_rate)) # Restore the clean slate background (this is the 'blit' method, which # is much faster to render) self.plotCanvas.restore_region(self.plot_background, bbox=self.fft_axis.bbox) self.fft_line.set_ydata(channel_data) self.fft_line.set_xdata(freqs[:numpoints]) # Draw the line self.fft_axis.draw_artist(self.fft_line) # Blit the canvas self.plotCanvas.blit(self.fft_axis.bbox) def reset(self,): self.current_expected_pcm_clock_rate = None if self.device is not None: self.device.stop() self.analyzer = None self.realtime_timer.stop() self.plot_timer.stop() if self.play_sound_thread.isRunning(): self.play_sound_thread.quit() self.play_sound_thread.wait() self._ui.messagesLabel.setStyleSheet("background-color: none;") self.error_ticks = 0 self.error_counts = 0 self.in_error = False def shutdown(self,): self.recording_state = STATE_IDLE self.reset() self.device = None try: self.figure.close() except: pass def closeEvent(self, event): """Intercepts the close event of the MainWindow.""" self.show_message("Closing device...") try: self.shutdown() self.event_listener.quit() self.event_listener.wait() self.audio.terminate() finally: super(MainWindow, self).closeEvent(event)
class MatPlot(object): def __init__(self, x, y, parent, color='blue', second_color='black'): self.color = color self.second_color = second_color self.fig, self.ax1 = plt.subplots() self.ax1.format_coord = lambda val1, val2: '' self.canvas = FigureCanvas(self.fig) self.canvas.setParent(parent) self.canvas.move(x, y) self.canvas.resize(WIND_HEIGHT - x, WIND_WIDTH - BUTTON_MENU_WIDTH - 5) self.toolbar = NavigationToolbarCustom(self.canvas, parent) self.toolbar.move(x + 100, 0) self.toolbar.resize(450, 40) def plot_bar(self, tickets, values, title): y_pos = range(len(tickets)) self.ax1.clear() self.ax1.grid(True) self.ax1.barh(y_pos, values, align='center', alpha=0.5, color=self.color) if values: pad = max(values) / 100 else: pad = 0 for i, tick in enumerate(tickets): self.ax1.annotate(tick, xy=(pad, i), va='center') self.ax1.set_yticks([]) self.ax1.set_title(title) self.canvas.draw() def plot_map(self, cities, vac_t, title): self.ax1.clear() m = Basemap(llcrnrlon=25., llcrnrlat=40., urcrnrlon=105., urcrnrlat=65., rsphere=(6378137.00, 6356752.3142), resolution='l', projection='merc') m.fillcontinents(color=self.color) m.drawcountries() for i, city in enumerate(cities): if city in AREA_COORD: coord = list(reversed(AREA_COORD[city])) self.ax1.annotate(work_with_data.decode_param(city), xy=m(*coord)) circle = plt.Circle(xy=m(*coord), radius=(m.ymax - m.ymin) / 540 * vac_t[i], fill=True, color=self.second_color) self.fig.gca().add_patch(circle) self.ax1.set_title(title) self.canvas.draw()