예제 #1
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):
        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)
예제 #2
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()
예제 #4
0
    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)
예제 #5
0
파일: plotview.py 프로젝트: JacobStoren/ert
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 ) 
예제 #8
0
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)
예제 #9
0
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()
예제 #10
0
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()
예제 #11
0
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)
예제 #12
0
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()