Esempio n. 1
0
class ResultWidget(get_ui_class("resultwidget.ui")):

    update_start = pyqtSignal()
    update_end = pyqtSignal()
    plot_changed = pyqtSignal('QString', 'QString')

    def __init__(self, parent, filename, settings):
        super(ResultWidget, self).__init__(parent)
        self.filename = unicode(filename)
        self.settings = settings.copy()
        self.dirty = True
        self.settings.OUTPUT = "-"

        self.results = ResultSet.load_file(self.filename)
        self.extra_results = []
        self.settings.update(self.results.meta())
        self.settings.load_test(informational=True)
        self.settings.compute_missing_results(self.results)

        try:
            self.formatter = PlotFormatter(self.settings)
        except Exception as e:
            traceback.print_exc()
            if isinstance(e, RuntimeError):
                err = "%s" % e
            else:
                typ, val, tra = sys.exc_info()
                err = "".join(traceback.format_exception_only(typ, val))
            QMessageBox.warning(
                self, "Error loading plot",
                "Error while loading plot:\n\n%s\nFalling back to default plot. Full traceback output to console."
                % err)

            self.settings.PLOT = self.settings.DEFAULTS['PLOT']
            self.formatter = PlotFormatter(self.settings)

        self.canvas = FigureCanvas(self.formatter.figure)
        self.canvas.setParent(self.graphDisplay)
        self.toolbar = NavigationToolbar(self.canvas, self.graphDisplay)

        vbl = QVBoxLayout()
        vbl.addWidget(self.canvas)
        vbl.addWidget(self.toolbar)
        self.graphDisplay.setLayout(vbl)

        self.plotModel = PlotModel(self, self.settings)
        self.plotSelectionModel = QItemSelectionModel(self.plotModel)
        self.plotSelectionModel.setCurrentIndex(
            self.plotModel.index_of(self.settings.PLOT),
            QItemSelectionModel.SelectCurrent)
        self.plotSelectionModel.currentChanged.connect(self.change_plot)

        self.metadataModel = MetadataModel(self, self.results.meta())
        self.metadataSelectionModel = QItemSelectionModel(self.metadataModel)

        if self.settings.TITLE:
            self.title = "%s - %s" % (self.settings.NAME, self.settings.TITLE)
            self.long_title = "%s - %s" % (
                self.title, self.settings.TIME.strftime("%Y-%m-%d %H:%M:%S"))
        else:
            self.title = "%s - %s" % (
                self.settings.NAME,
                self.settings.TIME.strftime("%Y-%m-%d %H:%M:%S"))
            self.long_title = self.title

        if self.settings.GUI_NO_DEFER:
            self.redraw()

    def disconnect_all(self):
        for s in (self.update_start, self.update_end, self.plot_changed):
            s.disconnect()

    def disable_cleanup(self):
        self.formatter.disable_cleanup = True

    def load_files(self, filenames):
        added = 0
        for f in filenames:
            if self.add_extra(ResultSet.load_file(unicode(f))):
                self.update(False)
                added += 1
        self.redraw()
        return added

    def add_extra(self, resultset):
        if resultset.meta('NAME') == self.settings.NAME:
            self.extra_results.append(resultset)
            self.update()
            return True
        return False

    def clear_extra(self):
        self.extra_results = []
        self.update()

    def can_save(self):
        # Check for attribute to not crash on a matplotlib version that does not
        # have the save action.
        return hasattr(self.toolbar, 'save_figure')

    def save_plot(self):
        if self.can_save():
            self.toolbar.save_figure()

    def zero_y(self, val=None):
        if val is not None and val != self.settings.ZERO_Y:
            self.settings.ZERO_Y = val
            self.update()
        return self.settings.ZERO_Y

    def invert_y(self, val=None):
        if val is not None and val != self.settings.INVERT_Y:
            self.settings.INVERT_Y = val
            self.update()
        return self.settings.INVERT_Y

    def disable_log(self, val=None):
        if val is not None and val == self.settings.LOG_SCALE:
            self.settings.LOG_SCALE = not val
            self.update()
        return not self.settings.LOG_SCALE

    def scale_mode(self, val=None):
        if val is not None and val != self.settings.SCALE_MODE:
            self.settings.SCALE_MODE = val
            self.update()
        return self.settings.SCALE_MODE

    def subplot_combine(self, val=None):
        if val is not None and val != self.settings.SUBPLOT_COMBINE:
            self.settings.SUBPLOT_COMBINE = val
            self.update()
        return self.settings.SUBPLOT_COMBINE

    def draw_annotation(self, val=None):
        if val is not None and val != self.settings.ANNOTATE:
            self.settings.ANNOTATE = val
            self.update()
        return self.settings.ANNOTATE

    def draw_legend(self, val=None):
        if val is not None and val != self.settings.PRINT_LEGEND:
            self.settings.PRINT_LEGEND = val
            self.update()
        return self.settings.PRINT_LEGEND

    def draw_title(self, val=None):
        if val is not None and val != self.settings.PRINT_TITLE:
            self.settings.PRINT_TITLE = val
            self.update()
        return self.settings.PRINT_TITLE

    def filter_legend(self, val=None):
        if val is not None and val != self.settings.FILTER_LEGEND:
            self.settings.FILTER_LEGEND = val
            self.update()
        return self.settings.FILTER_LEGEND

    def change_plot(self, plot_name):
        if isinstance(plot_name, QModelIndex):
            plot_name = self.plotModel.name_of(plot_name)
        plot_name = unicode(plot_name)
        if plot_name != self.settings.PLOT and plot_name in self.settings.PLOTS:
            self.settings.PLOT = plot_name
            self.plotSelectionModel.setCurrentIndex(
                self.plotModel.index_of(self.settings.PLOT),
                QItemSelectionModel.SelectCurrent)
            self.plot_changed.emit(self.settings.NAME, self.settings.PLOT)
            self.update()
            return True
        return False

    def current_plot(self):
        return self.settings.PLOT

    def updates_disabled(self):
        return UpdateDisabler(self)

    def update(self, redraw=True):
        self.dirty = True
        if redraw and ((self.isVisible() and self.updatesEnabled())
                       or self.settings.GUI_NO_DEFER):
            self.redraw()

    def redraw(self):
        if not self.dirty:
            return
        self.update_start.emit()
        try:
            self.formatter.init_plots()
            if self.settings.SCALE_MODE:
                self.settings.SCALE_DATA = self.extra_results
                self.formatter.format([self.results])
            else:
                self.settings.SCALE_DATA = []
                self.formatter.format([self.results] + self.extra_results)
            self.canvas.draw()
            self.dirty = False
        except Exception as e:
            traceback.print_exc()
            typ, val, tra = sys.exc_info()
            err = "".join(traceback.format_exception_only(typ, val))
            QMessageBox.warning(
                self, "Error plotting",
                "Unhandled exception while plotting:\n\n%s\nAborting. Full traceback output to console."
                % err)
        finally:
            self.update_end.emit()
Esempio n. 2
0
class ResultWidget(get_ui_class("resultwidget.ui")):

    update_start = pyqtSignal()
    update_end = pyqtSignal()
    plot_changed = pyqtSignal('QString', 'QString')
    default_title = "New tab"

    def __init__(self, parent, settings):
        super(ResultWidget, self).__init__(parent)
        self.results = None
        self.settings = settings.copy()
        self.dirty = True
        self.settings.OUTPUT = "-"

        self.extra_results = []
        self.title = self.default_title

        self.plotModel = None
        self.plotSelectionModel = None
        self.metadataModel = None
        self.metadataSelectionModel = None
        self.toolbar = None
        self.formatter = None

    @property
    def is_active(self):
        return self.results is not None

    def init_formatter(self):

        try:
            self.formatter = PlotFormatter(self.settings)
        except Exception as e:
            traceback.print_exc()
            if isinstance(e, RuntimeError):
                err = "%s" % e
            else:
                typ,val,tra = sys.exc_info()
                err = "".join(traceback.format_exception_only(typ,val))
            QMessageBox.warning(self, "Error loading plot",
                                "Error while loading plot:\n\n%s\nFalling back to default plot. Full traceback output to console." % err)

            self.settings.PLOT = self.settings.DEFAULTS['PLOT']
            self.formatter = PlotFormatter(self.settings)

        self.canvas = FigureCanvas(self.formatter.figure)
        self.canvas.setParent(self.graphDisplay)
        self.toolbar = NavigationToolbar(self.canvas, self.graphDisplay)

        vbl = QVBoxLayout()
        vbl.addWidget(self.canvas)
        vbl.addWidget(self.toolbar)
        self.graphDisplay.setLayout(vbl)

        self.plotModel = PlotModel(self, self.settings)
        self.plotSelectionModel = QItemSelectionModel(self.plotModel)
        self.plotSelectionModel.setCurrentIndex(self.plotModel.index_of(self.settings.PLOT),
                                                QItemSelectionModel.SelectCurrent)
        self.plotSelectionModel.currentChanged.connect(self.change_plot)

        self.metadataModel = MetadataModel(self, self.results.meta())
        self.metadataSelectionModel = QItemSelectionModel(self.metadataModel)

        if self.settings.TITLE:
            self.title = "%s - %s" % (self.settings.NAME, self.settings.TITLE)
            self.long_title = "%s - %s" % (self.title, self.settings.TIME.strftime("%Y-%m-%d %H:%M:%S"))
        else:
            self.title = "%s - %s" % (self.settings.NAME,
                                      self.settings.TIME.strftime("%Y-%m-%d %H:%M:%S"))
            self.long_title = self.title

        if self.settings.GUI_NO_DEFER:
            self.redraw()

    def has(self, resultset):
        return resultset in chain([self.results], self.extra_results)

    def load_results(self, results):
        if isinstance(results, ResultSet):
            self.results = results
        else:
            self.results = ResultSet.load_file(unicode(results))
        self.settings.update(self.results.meta())
        self.settings.load_test(informational=True)
        self.settings.compute_missing_results(self.results)
        self.init_formatter()
        return True

    def disconnect_all(self):
        for s in (self.update_start, self.update_end, self.plot_changed):
            s.disconnect()

    def disable_cleanup(self):
        if self.formatter is not None:
            self.formatter.disable_cleanup = True

    def load_files(self, filenames):
        added = 0
        for f in filenames:
            if self.add_extra(ResultSet.load_file(unicode(f))):
                self.update(False)
                added += 1
        self.redraw()
        return added

    def add_extra(self, resultset):
        if self.results is None:
            return self.load_results(resultset)
        if resultset in self.extra_results:
            return False
        if resultset.meta('NAME') == self.settings.NAME:
            self.extra_results.append(resultset)
            self.update()
            return True
        return False

    def remove_extra(self, resultset):
        if not resultset in self.extra_results:
            if resultset == self.results and self.extra_results:
                self.results = self.extra_results.pop(0)
                self.update()
                return True
            return False
        self.extra_results.remove(resultset)
        self.update()
        return True

    def clear_extra(self):
        self.extra_results = []
        self.update()

    @property
    def can_save(self):
        # Check for attribute to not crash on a matplotlib version that does not
        # have the save action.
        return hasattr(self.toolbar, 'save_figure')
    def save_plot(self):
        if self.can_save:
            self.toolbar.save_figure()

    def zero_y(self, val=None):
        if val is not None and val != self.settings.ZERO_Y:
            self.settings.ZERO_Y = val
            self.update()
        return self.settings.ZERO_Y

    def invert_y(self, val=None):
        if val is not None and val != self.settings.INVERT_Y:
            self.settings.INVERT_Y = val
            self.update()
        return self.settings.INVERT_Y

    def disable_log(self, val=None):
        if val is not None and val == self.settings.LOG_SCALE:
            self.settings.LOG_SCALE = not val
            self.update()
        return not self.settings.LOG_SCALE

    def scale_mode(self, val=None):
        if val is not None and val != self.settings.SCALE_MODE:
            self.settings.SCALE_MODE = val
            self.update()
        return self.settings.SCALE_MODE

    def subplot_combine(self, val=None):
        if val is not None and val != self.settings.SUBPLOT_COMBINE:
            self.settings.SUBPLOT_COMBINE = val
            self.update()
        return self.settings.SUBPLOT_COMBINE

    def draw_annotation(self, val=None):
        if val is not None and val != self.settings.ANNOTATE:
            self.settings.ANNOTATE = val
            self.update()
        return self.settings.ANNOTATE

    def draw_legend(self, val=None):
        if val is not None and val != self.settings.PRINT_LEGEND:
            self.settings.PRINT_LEGEND = val
            self.update()
        return self.settings.PRINT_LEGEND

    def draw_title(self, val=None):
        if val is not None and val != self.settings.PRINT_TITLE:
            self.settings.PRINT_TITLE = val
            self.update()
        return self.settings.PRINT_TITLE

    def filter_legend(self, val=None):
        if val is not None and val != self.settings.FILTER_LEGEND:
            self.settings.FILTER_LEGEND = val
            self.update()
        return self.settings.FILTER_LEGEND

    def change_plot(self, plot_name):
        if isinstance(plot_name, QModelIndex):
            plot_name = self.plotModel.name_of(plot_name)
        plot_name = unicode(plot_name)
        if plot_name != self.settings.PLOT and plot_name in self.settings.PLOTS:
            self.settings.PLOT = plot_name
            self.plotSelectionModel.setCurrentIndex(self.plotModel.index_of(self.settings.PLOT),
                                                    QItemSelectionModel.SelectCurrent)
            self.plot_changed.emit(self.settings.NAME, self.settings.PLOT)
            self.update()
            return True
        return False

    @property
    def current_plot(self):
        if not self.is_active:
            return None
        return self.settings.PLOT

    def updates_disabled(self):
        return UpdateDisabler(self)

    def update(self, redraw=True):
        self.dirty = True
        if redraw and ((self.isVisible() and self.updatesEnabled()) or self.settings.GUI_NO_DEFER):
            self.redraw()

    def redraw(self):
        if not self.dirty or not self.is_active:
            return
        self.update_start.emit()
        try:
            self.formatter.init_plots()
            if self.settings.SCALE_MODE:
                self.settings.SCALE_DATA = self.extra_results
                self.formatter.format([self.results])
            else:
                self.settings.SCALE_DATA = []
                self.formatter.format([self.results] + self.extra_results)
            self.canvas.draw()
            self.dirty = False
        except Exception as e:
            traceback.print_exc()
            typ,val,tra = sys.exc_info()
            err = "".join(traceback.format_exception_only(typ,val))
            QMessageBox.warning(self, "Error plotting",
                                "Unhandled exception while plotting:\n\n%s\nAborting. Full traceback output to console." % err)
        finally:
            self.update_end.emit()
Esempio n. 3
0
class MainWindow(QMainWindow):
    NextId = 1
    Instances = set()
    
    def __init__(self, filename=str(),  parent=None):
        super(MainWindow, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        MainWindow.Instances.add(self)
        
#        create the layout 
        self.table = QTableWidget()
         # instantiate a widget, it will be the main one
        self.graph_widget = QWidget()
        # create a vertical box layout widget
        graph_layout = QVBoxLayout(self.graph_widget)
        # instantiate our Matplotlib canvas widget
        self.plotiv = MplWidget()
        # instantiate the navigation toolbar
        self.navtoolbar = NavigationToolbar(self.plotiv.canvas, self.graph_widget)
        # pack these widget into the vertical box
        graph_layout.addWidget(self.plotiv)
        graph_layout.addWidget(self.navtoolbar)
        self.ivSplitter = QSplitter(Qt.Horizontal)
        self.ivSplitter.addWidget(self.table)
        self.ivSplitter.addWidget(self.graph_widget)
        self.setCentralWidget(self.ivSplitter)
        
#        main variables
        self.dirty = False
        self.setparam = False
        self.calculated = False
        self.filename = None
        self.loadedfile = False
        self.sampleparameters = ['', 0,100]
        self.calcparameters = [0, 0,  0,  0, 0, 0,  0] # [isc,  voc,  fillfactor,  maxscpower,  effic, rshunt,  rseries]
        self.normalArray = []
        self.ivdata = np.zeros((0, 2))
        self.plottype = 'None'
        self.isDark = None
        self.lastfile = ''

        self.printer = None

        fileOpenAction = self.createAction("&Open...", self.fileOpen,
                QKeySequence.Open, "fileopen",
                "Open an I-V data file")
        fileSaveAction = self.createAction("&Save", self.fileSave,
                QKeySequence.Save, "filesave", "Save results file")

        filePrintAction = self.createAction("&Print", self.filePrint,
                QKeySequence.Print, "fileprint", "Print the results")
        fileCloseAction = self.createAction("&Close", self.close, 
                                            QKeySequence.Close, "fileclose","Close this window")
        fileQuitAction = self.createAction("&Quit", self.close,
                                            "Ctrl+Q", "filequit", "Close the application")
                
        processGraphAction = self.createAction("Make &Graph",  self.processGraph,  "Ctrl+G",  "graph",  "Show I-V graph")
        
        processCalculateAction = self.createAction("Calculate P&arameters",  self.processCalculate,  
                "Ctrl+A",  "calculate",  "Calculate parameters from I-V data")

        helpAboutAction = self.createAction("&About this program", self.helpAbout)
        helpHelpAction = self.createAction("&Help", self.helpHelp, QKeySequence.HelpContents)

        fileMenu = self.menuBar().addMenu("&File")
        self.addActions(fileMenu,  (fileOpenAction, fileSaveAction, None, fileCloseAction,
                fileQuitAction))
        
        processMenu = self.menuBar().addMenu("&Process")
        self.addActions(processMenu,  (processGraphAction,  processCalculateAction))
        #mirrorMenu = processMenu.addMenu(QIcon(":/editmirror.png"), "&Mirror")
        
        self.windowMenu = self.menuBar().addMenu("&Window")
        self.connect(self.windowMenu, SIGNAL("aboutToShow()"), self.updateWindowMenu)

        helpMenu = self.menuBar().addMenu("&Help")
        self.addActions(helpMenu, (helpAboutAction, helpHelpAction))
        

        fileToolbar = self.addToolBar("File")
        fileToolbar.setObjectName("FileToolBar")
        self.addActions(fileToolbar, (fileOpenAction, fileSaveAction))
        processToolbar = self.addToolBar("Process")
        processToolbar.setObjectName("ProcessToolBar")
        self.addActions(processToolbar,  (processGraphAction,  processCalculateAction))
#        darkIvGraphModeToolbar = self.addToolbar("")

        self.connect(self, SIGNAL("destroyed(QObject*)"), MainWindow.updateInstances)
        
        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.showMessage("Ready", 5000)
        
        self.filename = filename
        if self.filename == "":
            self.filename = str("Unnamed-{0}".format(MainWindow.NextId))
            MainWindow.NextId += 1
            self.loadedfile = False
            self.setWindowTitle("Solar Cell I-V processing - {0}".format(self.filename))
        else:
            self.loadFile()
            
        settings = QSettings()
        #self.recentFiles = settings.value("RecentFiles").toStringList()
        if len(sys.argv) > 1:
            self.loadFiles()
#            self.updateFileMenu()


    @staticmethod
    def updateInstances(qobj):
        MainWindow.Instances = (set([window for window in MainWindow.Instances if isAlive(window)]))

    def createAction(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(":/{0}.png".format(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 addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)


    def closeEvent(self, event):
         if (self.dirty and
            QMessageBox.question(self,
                "I-V Processing - Unsaved Changes",
                "Save unsaved changes in {0}?".format(self.filename),
                QMessageBox.Yes|QMessageBox.No) ==
                QMessageBox.Yes):
            self.fileSave()
       


    def okToContinue(self):
        if self.dirty:
            reply = QMessageBox.question(self,
                    "I-V processing - Unsaved Changes",
                    "Save unsaved changes?",
                    QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return False
            elif reply == QMessageBox.Yes:
                return self.fileSave()
        return True


    def updateStatus(self, message):
        self.statusBar().showMessage(message, 5000)
#        self.listWidget.addItem(message)
        if self.filename is not None:
            self.setWindowTitle("I-V processing - {0}[*]".format(
                                os.path.basename(self.filename)))
        elif not self.loadedfile:
            self.setWindowTitle("I-V processing - Unnamed[*]")
        else:
            self.setWindowTitle("I-V processing[*]")
        self.setWindowModified(self.dirty)


    def fileOpen(self):
        dir = (os.path.dirname(self.lastfile)
               if self.filename is not None else ".")
        filename = QFileDialog.getOpenFileName(self, "Choose an Illuminated I-V data file", dir,
        "Text files(*.txt);;Data files(*.dat);;CSV files(*.csv);;IVK files(*.ivk);;All files(*.*)")
        if not filename == "":
            if (not self.loadedfile):
                self.filename = filename
                self.loadFile()
                self.lastfile = self.filename
            else:
                MainWindow(filename).show()
#            msg = self.loadFile(fname)
    
    def loadFile(self):
        ok, msg = self.loadData()
        self.table.show()
        self.initGraph()
#        self.addRecentFile(fname)
        self.updateTable()
#        self.filename = fname
#        settings = QSettings()
#        settings.setValue("LastFile", fname)
        self.loadedfile = True
        self.updateGraph()
        self.updateStatus(msg)

    def loadData(self):
        error = None
        fh = None
        self.ivdata=np.zeros((0, 2))
        try:
            fh = open(self.filename,  "r")
            for line in fh:
                if not line or line.startswith(("#", "\n", "\r")):
                    continue
                line = line.rstrip()
                fields = line.split("\t")
                self.ivdata = np.append(self.ivdata,  [[float(fields[0]), float(fields[1])]],  axis=0)
                
        except Exception:
            return "Failed to load file"
        finally:
            return True,  "File successfully loaded"


    def loadFiles(self):
        for filename in sys.argv[1:5]:
            filename = QString(filename)
            if QFileInfo(filename).isFile():
                self.filename = filename
                self.loadFile()
                

    def addRecentFile(self, fname):
        if fname is None:
            return
        if not self.recentFiles.contains(fname):
            self.recentFiles.prepend(QString(fname))
            while self.recentFiles.count() > 9:
                self.recentFiles.takeLast()


    def fileSave(self):
        if not self.dirty:
            return True
        filetypes = self.plotiv.canvas.get_supported_filetypes_grouped()
        sorted_filetypes = filetypes.items()
        sorted_filetypes.sort()
        default_filetype = self.plotiv.canvas.get_default_filetype()
        start = self.sampleparameters[0] + default_filetype
        filters = []
        selectedFilter = None
        for name, exts in sorted_filetypes:
            exts_list = " ".join(['*.%s' % ext for ext in exts])
            filter = '%s (%s)' % (name, exts_list)
            if default_filetype in exts:
                selectedFilter = filter
            filters.append(filter)
        filters = ';;'.join(filters)
        
        self.navtoolbar.save_figure()
        self.dirty = False
    

    def fileSaveAs(self):
        if not self.dirty:
            return True
        fname = self.filename if self.filename is not None else "."
        formats = (["*.{0}".format(format.lower())
                for format in QImageWriter.supportedImageFormats()])
        fname = QFileDialog.getSaveFileName(self,
                "Solar Cell I-V processing - Save Graph Image", fname,
                "Image files ({0})".format(" ".join(formats)))
        if fname:
            if "." not in fname:
                fname += ".png"
            self.addRecentFile(fname)
            self.filename = fname
            return self.fileSave()
        return False

    def updateWindowMenu(self):
        self.windowMenu.clear()
        for window in MainWindow.Instances:
            if isAlive(window):
                action = self.windowMenu.addAction(
                        window.windowTitle().mid(
                                len("Solar Cell I-V processing - ")),
                                self.raiseWindow)
                action.setData(QVariant(long(id(window))))
    
    def raiseWindow(self):
        action = self.sender()
        if not isinstance(action, QAction):
            return
        windowId = action.data().toLongLong()[0]
        for window in MainWindow.Instances:
            if isAlive(window) and id(window) == windowId:
                window.activateWindow()
                window.raise_()
                break

    def filePrint(self):
        if self.image.isNull():
            return
        if self.printer is None:
            self.printer = QPrinter(QPrinter.HighResolution)
            self.printer.setPageSize(QPrinter.Letter)
        form = QPrintDialog(self.printer, self)
        if form.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.image.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.drawImage(0, 0, self.image)

    def helpAbout(self):
        QMessageBox.about(self, "I-V processing",
                """<b>Solar Cell I-V processing</b> v {0}
                <p>Copyright &copy; 2012 Borlanghini  
                All rights reserved.
                <p>This application can be used to process illuminated I-V
                    measured data in order to obtain the main parameters from the device .
                <p>Python {1} - Qt {2} - PyQt {3} on {4}""".format(
                __version__, platform.python_version(),
                QT_VERSION_STR, PYQT_VERSION_STR,
                platform.system()))


    def helpHelp(self):
        form = helpform.HelpForm("index.html", self)
        form.show()

    def updateTable(self, current=None):
        self.table.clear()
        self.table.setRowCount(len(self.ivdata))
        self.table.setColumnCount(2)
        self.table.setHorizontalHeaderLabels(["Voltage (V)", "Current (A)"])
        self.table.setAlternatingRowColors(True)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setSelectionMode(QTableWidget.SingleSelection)
        selected = None
        for i, row in enumerate(self.ivdata.tolist()):
            for j,  col in enumerate(row):
                item = QTableWidgetItem(str(col))
                item.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter)
                self.table.setItem(i,  j,  item)
        self.table.resizeColumnsToContents()
        if selected is not None:
            selected.setSelected(True)
            self.table.setCurrentItem(selected)
            self.table.scrollToItem(selected)
            
    def processGraph(self):
        self.updateGraph()


    def initGraph(self):
        self.calculated = False
        jsc = ''
        voc = ''
        maxpower = ''
        fillfactor = ''
        effic = ''
        graphtitle = "Iluminated IV\nSample: "
        self.plotiv.canvas.ax.clear()
        self.plotiv.canvas.ax.grid(True)
        self.plotiv.canvas.ax.set_ylabel('Current (A)',  fontsize=16)
        self.plotiv.canvas.ax.set_xlabel('Voltage(V)',  fontsize=16)
        self.plotiv.canvas.draw()


    def updateGraph(self):
        if self.calculated:
            isc = " %.2f " % (self.calcparameters[0]*1000)
            jsc = " %.2f " % (self.calcparameters[0]*1000/ self.sampleparameters[1])
            voc = " %.2f " % (self.calcparameters[1]*1000)
            maxpower = " %.2f " %(self.calcparameters[3]*1000)
            fillfactor = " %.2f "%(self.calcparameters[2])
            effic = " %.2f "%(self.calcparameters[4]*100)
            rshunt = " %.2f "%(self.calcparameters[5])
            rseries = " %.2f "%(self.calcparameters[6])
        else:
            jsc = ''
            voc = ''
            maxpower = ''
            fillfactor = ''
            effic = ''
        if self.setparam:
            area = " %.2f " %(self.sampleparameters[1])
            irrad = " %.2f " %(self.sampleparameters[2])
        else:
            area = ''
            irrad = ''
            
        graphtitle = "Iluminated IV\nSample: "+ self.sampleparameters[0]
        self.plotiv.canvas.ax.clear()        
        self.plotiv.canvas.ax.grid(True)
        self.plotiv.canvas.ax.set_ylabel('Current (A)',  fontsize=16)
        self.plotiv.canvas.ax.set_xlabel('Voltage(V)',  fontsize=16)
        self.plotiv.canvas.ax.set_title(graphtitle,  fontsize=16)
        if self.isDark is None:
            if not self.calculated:
                xmin= np.amin(self.ivdata[:, 0])
                xmax=np.amax(self.ivdata[:, 0])
                ymin=np.amin(self.ivdata[:, 1])
                ymax=np.amax(self.ivdata[:, 1])
                self.plotiv.canvas.ax.set_xlim(xmin=xmin,  xmax=xmax)
                
                if abs(np.amin(self.ivdata[:, 1]))<0.5:
                    self.plotiv.canvas.ax.set_ylabel('Current (mA)',  fontsize=16)
                    self.plotiv.canvas.ax.set_ylim(ymin=ymin*1000,  ymax=ymax*1000)
                    self.plotiv.canvas.ax.plot(self.ivdata[:,0], self.ivdata[:,1]*1000, color='b', linestyle ='dashed', marker='o', lw=1.5, zorder=1)
                else:
                    self.plotiv.canvas.ax.set_ylim(ymin=ymin,  ymax=ymax)
                    self.plotiv.canvas.ax.plot(self.ivdata[:,0], self.ivdata[:,1], color='b', linestyle ='dashed', marker='o', lw=1.5, zorder=1)
            if self.calculated:
                if abs(self.calcparameters[0])<0.5:
                    self.plotiv.canvas.ax.set_ylabel('Current (mA)',  fontsize=16)
                    ymin = self.calcparameters[0]*1000
                    self.plotiv.canvas.ax.plot(self.ivdata[:,0], self.ivdata[:,1]*1000, color='b', linestyle ='dashed', marker='o', lw=1.5, zorder=1)
                else:
                    ymin = self.calcparameters[0]
                    self.plotiv.canvas.ax.plot(self.ivdata[:,0], self.ivdata[:,1], color='b', linestyle ='dashed', marker='o', lw=1.5, zorder=1)
                self.plotiv.canvas.ax.set_xlim(xmin= -0.01,  xmax= self.calcparameters[1]+ self.calcparameters[1]*0.1)
                self.plotiv.canvas.ax.set_ylim(ymin=ymin-abs(ymin*0.1),  ymax=abs(ymin*0.1))
                self.plotiv.canvas.ax.text(0.05, 0.95, r'Cell area $=$' +area+' $cm^{2}$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.87, r'$I_{SC} = $' + isc + ' $mA \quad J_{SC} = $' +jsc +' $mA/cm^{2}$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.80, r'$V_{OC} = $'+voc+' $mV$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.73, r'$FF = $'+fillfactor,  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.66, r'$P_{MAX} = $'+maxpower+' $mW$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.59, r'$\eta = $'+effic+' $\%$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.52, r'$R_{Shunt} \sim $' +rshunt+'$ \Omega$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
                self.plotiv.canvas.ax.text(0.05, 0.45, r'$R_{Series} \sim $' +rseries+'$ \Omega$',  fontsize = 14,  transform = self.plotiv.canvas.ax.transAxes)
        self.plotiv.canvas.draw()
        
    
    def processCalculate(self):
        form = sampleparamdlg.SampleParamDlg(self.sampleparameters, None)
        form.show()
        form.exec_()
        self.sampleparameters = form.paramset
        self.setparam = True
        self.calcparameters = self.findscparam()
        self.calculated = True
        self.dirty = True
        self.updateGraph()


    def findscparam(self):
        if  not self.setparam:
            return
        if self.ivdata[:, 0][0]>self.ivdata[:, 0][1]:
            volt = np.flipud(self.ivdata[:, 0])
            curr = np.flipud(self.ivdata[:, 1])
        else:
            volt = self.ivdata[:, 0]
            curr = self.ivdata[:, 1]
#        finding last data position before zero crossing
        zero_crossing=np.where(np.diff(np.sign(curr)))[0][0]
#        creating function for data interpolation
        data_interpld = interpolate.interp1d(volt, curr,  kind='cubic')
#        approximate Voc value by linear interpolation
        slope = (curr[zero_crossing +1] - curr[zero_crossing])/(volt[zero_crossing + 1]-volt[zero_crossing])
        intercept = curr[zero_crossing] - slope*volt[zero_crossing]
#        slope,  intercept,  r_value,  p_value,  std_err = stats.linregress(volt[zero_crossing:zero_crossing+1],  curr[zero_crossing:zero_crossing+1])
        voc = - intercept/slope
        isc = data_interpld(0)
#        finding max power point
        voltnew = np.arange(0, volt[zero_crossing+1],  0.001)
        maxscpower = max(np.abs(np.multiply(voltnew,  data_interpld(voltnew))))
        maxscpower_voltposition = np.argmax(np.abs(np.multiply(voltnew, data_interpld(voltnew))))
        fillfactor = np.abs(maxscpower/(voc*isc))
        effic = maxscpower*1000/(self.sampleparameters[2]*self.sampleparameters[1])
#        finding r_s and r_shunt graphically --- approximate method
        rsh_slope,  intercept,  r_value,  p_value,  std_err = stats.linregress(voltnew[0:int(maxscpower_voltposition*0.8)], data_interpld(voltnew[0:int(maxscpower_voltposition*0.8)]))
        rshunt = np.abs(1/rsh_slope)
        rs_slope,  intercept,  r_value,  p_value,  std_err = stats.linregress(voltnew[-50:-1], data_interpld(voltnew[-50:-1]))
        rseries = np.abs(1/rs_slope)
        return [isc,  voc,  fillfactor,  maxscpower,  effic, rshunt,  rseries]