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()
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()
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 © 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]