https://hal.archives-ouvertes.fr/hal-01897934
Tip revision: f370e484d8579b4ccc863d1c2a2f10decad21914 authored by Software Heritage on 27 July 2018, 00:00:00 UTC
hal: Deposit 299 in collection hal
hal: Deposit 299 in collection hal
Tip revision: f370e48
slalomWindow.py
# -*- coding: utf-8 -*-
# ======================================================================================================
# SLALOM - Open-Source Solar Cell Multivariate Optimizer
# Copyright(C) 2012-2019 Sidi OULD SAAD HAMADY (1,2,*), Nicolas FRESSENGEAS (1,2). All rights reserved.
# (1) Université de Lorraine, Laboratoire Matériaux Optiques, Photonique et Systèmes, Metz, F-57070, France
# (2) Laboratoire Matériaux Optiques, Photonique et Systèmes, CentraleSupélec, Université Paris-Saclay, Metz, F-57070, France
# (*) sidi.hamady@univ-lorraine.fr
# SLALOM source code is available to download from:
# https://github.com/sidihamady/SLALOM
# https://hal.archives-ouvertes.fr/hal-01897934
# http://www.hamady.org/photovoltaics/slalom_source.zip
# Cite as: S Ould Saad Hamady and N Fressengeas, EPJ Photovoltaics, 9:13, 2018.
# See Copyright Notice in COPYRIGHT
# ======================================================================================================
# ------------------------------------------------------------------------------------------------------
# File: slalomWindow.py
# Type: Class
# Use: The slalomWindow class and related classes are used by the slalomMonitor module.
# slalomWindow provides visualisation and control functionality...
# either in client/server configuration using ssh...
# ... or locally if it runs on the same machine than the optimizer.
# slalomWindow uses tkinter that is already installed on the client...
# (this is generally the case, except for some CentOS or RedHat machines)
# ------------------------------------------------------------------------------------------------------
from slalomCore import *
import threading
import re
# tkinter is not always installed by default (e.g. in RedHat Enterprise Linux 7+ or CentOS 6.x)...
# ...if not installed, the graphic part of the optimizer cannot be started...
# ...(just (re)install it or install a more recent python/numpy/scipy/matplotlib/tk version...
# ....and restart slalomMonitor).
TkFound = False
import matplotlib
matplotlib.use('Agg')
try:
matplotlib.use('TkAgg')
import matplotlib.backends.backend_tkagg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
if sys.version_info[0] < 3:
import Tkinter as Tk
import ttk
else:
import tkinter as Tk
import tkinter.ttk as ttk
# end if
from ttk import *
import tkMessageBox
import tkFileDialog
import matplotlib.pyplot as pl
from matplotlib.backends.backend_pdf import PdfPages
from matplotlib.ticker import MaxNLocator
TkFound = True
except ImportError as ierr:
# tkinter related modules not found. Just warn and skip this part.
# just install or update python/numpy/scipy/matplotlib/tk modules
dispError("{0}".format(ierr), doExit = False)
# catch only Exception (since sys.exit raise BaseException)
pass
except:
pass
try:
class NavigationToolbar(NavigationToolbar2TkAgg):
""" custom toolbar including an Autoscale and PDF save buttons """
def __init__(self, chart):
NavigationToolbar2TkAgg.__init__(self, chart.canvas, chart.root)
self.chart = chart
# end __init__
try:
toolitems = [tt for tt in NavigationToolbar2TkAgg.toolitems if tt[0] in ('Home', 'Back', 'Forward', 'Pan', 'Zoom')]
toolitems.append(('AutoScale', 'Auto scale the plot', 'hand', 'onAutoScale'))
toolitems.append(('Save', 'Save the plot as PDF', 'filesave', 'onSave'))
except:
pass
# end try
def onAutoScale(self):
self.chart.onAutoScale()
# end onAutoScale
def onSave(self):
self.chart.onSave()
# end onAutoScale
# end NavigationToolbar
except:
pass
import tkFont
# :REV:1:20181115: suppress a nonrelevant warning from matplotlib
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
class OptimizerViewer(Tk.Frame):
""" view the optimization data file in realtime """
def __init__(self, parent, *args, **kwargs):
Tk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
xscrollbar = ttk.Scrollbar(self, orient=Tk.HORIZONTAL)
xscrollbar.pack(side=Tk.BOTTOM, fill=Tk.X)
yscrollbar = ttk.Scrollbar(self, orient=Tk.VERTICAL)
yscrollbar.pack(side=Tk.RIGHT, fill=Tk.Y)
# colorize some optimizer specific keywords
self.text = Tk.Text(self, spacing1=2, spacing3=2, wrap='none', selectbackground="#B7DCFF", selectforeground="#000000", inactiveselectbackground="#B7DCFF")
self.boldFont = tkFont.Font(self.text, self.text.cget("font"))
self.boldFont.configure(weight="bold")
self.text.tag_configure("keyword", foreground="#414AF3")
self.text.tag_configure("keyword2", foreground="#41A30B")
self.text.tag_configure("bold", font=self.boldFont)
self.text.tag_configure("selectedline", background="#B7DCFF")
self.text.pack(side=Tk.LEFT, fill="both", expand=1)
self.text['xscrollcommand'] = xscrollbar.set
self.text['yscrollcommand'] = yscrollbar.set
xscrollbar.config(command=self.text.xview)
yscrollbar.config(command=self.text.yview)
self.selectedline = -1
self.count = Tk.IntVar()
self.listKeyword = None
self.listKeyword2 = None
self.text.bind("<1>", lambda event: self.text.focus_set())
self.text.bind('<Button-3>', self.onRightClick, add='')
self.text.bind("<Control-c>", lambda event: self.onCopy())
self.text.bind("<Control-a>", lambda event: self.onSelectAll())
self.text.bind("<Control-s>", lambda event: self.onSave())
# end __init__
def setText(self, strText, listKeyword, listKeyword2):
""" update the viewer colorize the optimizer specific keywords """
try:
self.config(cursor="wait")
self.update()
except:
pass
wfocus = self.text.focus_get()
self.text.config(state=Tk.NORMAL)
try:
self.text.delete("1.0", "end")
except:
pass
# end try
try:
self.text.insert("end", strText)
self.listKeyword = list(listKeyword)
self.listKeyword2 = list(listKeyword2)
if wfocus == self.text:
self.text.focus()
# end if
for strK in self.listKeyword:
self.colorize(strK, "keyword", "bold")
# end for
for strK in self.listKeyword2:
self.colorize(strK, "keyword2", "bold")
# end for
except:
pass
# end try
self.text.config(state=Tk.DISABLED)
self.config(cursor="")
# end setText
def colorize(self, keyword, taga, tagb):
start = self.text.index("1.0")
end = self.text.index("end")
self.text.mark_set("matchStart", start)
self.text.mark_set("matchEnd", start)
self.text.mark_set("searchEnd", end)
while True:
index = self.text.search(keyword, "matchEnd","searchEnd", count=self.count)
if (index == "") or (self.count.get() == 0):
break
# end while
self.text.mark_set("matchStart", index)
self.text.mark_set("matchEnd", "%s+%dc" % (index, self.count.get()))
self.text.tag_add(taga, "matchStart", "matchEnd")
self.text.tag_add(tagb, "matchStart", "matchEnd")
# end while
# end colorize
def onSelectAll(self):
self.text.tag_add(Tk.SEL, "1.0", Tk.END)
self.text.mark_set(Tk.INSERT, "1.0")
self.text.see(Tk.INSERT)
return 'break'
# end onCopy
# offset corresponds to the data file header and non-numeric part
def selectLine(self, index, offset = 12):
index += offset
if self.selectedline >= 0:
self.text.tag_remove("selectedline", "%d.0" % (1 + self.selectedline), "%d.end" % (1 + self.selectedline))
# end if
self.text.tag_add("selectedline", "%d.0" % (1 + index), "%d.end" % (1 + index))
self.selectedline = index
# end onCopy
def onCopy(self):
self.text.clipboard_clear()
strC = self.text.get("sel.first", "sel.last")
self.text.clipboard_append(strC)
return 'break'
# end onCopy
def onRightClick(self, event):
popmenu = Tk.Menu(None, tearoff=0)
popmenu.add_command(label='Select All', command=self.onSelectAll)
if self.text.tag_ranges("sel"):
popmenu.add_command(label='Copy', command=self.onCopy)
# end if
popmenu.add_separator()
popmenu.add_command(label='Save', command=self.onSave)
popmenu.add_separator()
popmenu.add_command(label='Close', command=self.onClose)
popmenu.tk_popup(event.x_root, event.y_root, 0)
return "break"
# end onRightClick
def onSave(self):
fileopt = {}
fileopt['defaultextension'] = '.txt'
fileopt['filetypes'] = [('Text files', '.txt')]
fileopt['initialfile'] = ''
fileopt['parent'] = self.root
fileopt['title'] = 'Save data'
dataFilename = tkFileDialog.asksaveasfilename(**fileopt)
if dataFilename:
try:
fileT = open(dataFilename, "w")
fileT.write(self.text.get(1.0, Tk.END))
fileT.close()
except:
pass
# end try
# end if
# end onRestart
# end OptimizerViewer
class OptimizerThread(threading.Thread):
def __init__(self, id, func):
threading.Thread.__init__(self)
self.id = id
self.func = func
# end __init__
def run(self):
self.func()
# end run
# end OptimizerThread
class OptimizerReport(Tk.Frame):
def __init__(self, owner, index, report, *args, **kwargs):
Tk.Frame.__init__(self, report, *args, **kwargs)
self.listParam = list()
self.listOptim = list()
self.listOptimout = list()
self.count = 0
self.fontsize = 10
self.index = index
self.owner = owner
self.root = report
self.figure = matplotlib.figure.Figure(figsize=(8,5), dpi=100, facecolor='#F1F1F1', linewidth=1.0, frameon=True)
self.figure.subplots_adjust(top = 0.95, bottom = 0.12, left = 0.09, right = 0.95, wspace = 0.0, hspace = 0.1)
self.plot = self.figure.add_subplot(111)
self.plot.set_xlabel("$\mathregular{Voltage\ (V)}$" if (self.index == 0) else "$\mathregular{Wavelength\ (\mu m)}$", fontsize=self.fontsize)
self.plot.set_ylabel("$\mathregular{Current\ (mA/cm^{2})}$" if (self.index == 0) else "$\mathregular{EQE}$", fontsize=self.fontsize)
self.plot.format_coord = self.formatCoord
try:
self.plot.tick_params(axis='x', labelsize=self.fontsize)
self.plot.tick_params(axis='y', labelsize=self.fontsize)
except:
[tx.label.set_fontsize(self.fontsize) for tx in self.plot.xaxis.get_major_ticks()]
[ty.label.set_fontsize(self.fontsize) for ty in self.plot.yaxis.get_major_ticks()]
pass
try:
if self.index == 0:
self.plot.axhline(0.0, linestyle='-', linewidth=2, color='g', zorder=3)
self.plot.axvline(0.0, linestyle='-', linewidth=2, color='g', zorder=3)
else:
self.plot.axhline(1.0, linestyle='-', linewidth=2, color='g', zorder=3)
# end if
self.plot.xaxis.major.locator.set_params(nbins=10)
self.plot.yaxis.major.locator.set_params(nbins=5)
self.plot.grid(True)
except:
pass
self.plot.mlinex = None
self.plot.mliney = None
self.indexm = 0
self.currentsign = 1.0
paramFrame = Tk.Frame(self.root)
paramFrame.pack(fill=Tk.X, padx=5, pady=5)
self.paramlistBox = ttk.Combobox(paramFrame, state='readonly')
self.paramlistBox.pack(side=Tk.LEFT, padx=(5, 5), fill=Tk.X, expand=1)
self.paramlistBox['state'] = 'readonly'
self.paramlistBox.bind('<<ComboboxSelected>>', self.onParamlistBox)
self.paramlistCurrent = 0
self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
self.canvas._tkcanvas.config(highlightthickness=0)
try:
self.toolbar = NavigationToolbar(self)
except:
self.toolbar = None
pass
self.toolbar.pack(side=Tk.BOTTOM, fill=Tk.X)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=Tk.LEFT, fill=Tk.BOTH, expand=1)
self.canvas.show()
self.linecolor = 'b'
self.linecolorm = '#FF7F00'
self.linestyle = '-'
self.linesize = 2.0
self.markersm = 'o'
self.markerhg = 'None'
self.markersizesm = 6.0
self.markersizehg = 3.0
self.line = None
self.dataSep = ' '
self.datax = list()
self.datay = list()
self.root.bind('<Button-3>', self.onRightClick, add='')
self.root.bind("<Control-s>", lambda event: self.onSave())
self.root.bind("<Prior>", self.onPageDown)
self.root.bind("<Next>", self.onPageUp)
# end __init__
def reset(self, listParam, listOptim, listOptimout, listFileContent):
if (listParam is None) or (listOptim is None) or (listOptimout is None):
return False
# end if
del self.listParam[:]
self.listParam = list(listParam)
del self.listOptim[:]
self.listOptim = list(listOptim)
del self.listOptimout[:]
self.listOptimout = list(listOptimout)
if self.count > 0:
for ida in range(0, self.count):
self.datax[ida] = np.delete(self.datax[ida], np.s_[:])
self.datay[ida] = np.delete(self.datay[ida], np.s_[:])
# end for
del self.datax[:]
self.datax = list()
del self.datay[:]
self.datay = list()
# end if
self.count = 0
dataValid = self.isDataValid(listFileContent)
if dataValid:
self.paramlistBox['values'] = self.listParam
self.paramlistBox.set(self.listParam[self.count - 1])
self.paramlistCurrent = self.paramlistBox.current()
# end if
return dataValid
# end reset
def isDataValid(self, listFileContent = None):
try:
iParamLen = len(self.listParam)
if iParamLen < 1:
return False
# end if
if listFileContent is not None:
iFcontentLen = len(listFileContent)
if (iFcontentLen < 1) or (iParamLen != iFcontentLen):
return False
# end if
# end if
self.count = iParamLen
except:
return False
# end try
return True
# end isDataValid
def formatCoord(self, x, y):
return ('Voltage: %08.5f V ; Current: %09.5f mA/cm²' if (self.index == 0) else 'Wavelength: %8.5f µm ; EQE: %8.5f') % (x, y)
# end formatCoord
def onPageMove(self, event, idir):
if self.owner.isRunning():
return
# end if
try:
indexT = self.paramlistBox.current() + idir
indexMax = len(self.paramlistBox["values"])
if (indexT < 0) or (indexT >= indexMax):
return
# end if
self.updatePlot(index = indexT)
self.paramlistBox.current(newindex = indexT)
if (self.owner is not None):
self.owner.updateSel(index = indexT)
self.owner.paramlistBox.current(newindex = indexT)
# end if
self.paramlistCurrent = self.paramlistBox.current()
except:
pass
# end onPageDown
def onPageDown(self, event):
return self.onPageMove(event, -1)
# end onPageDown
def onPageUp(self, event):
return self.onPageMove(event, 1)
# end onPageUp
def updatePlot(self, index = None, autoscale = False):
if (index is None) or (index < 0) or (index >= self.count):
index = self.count - 1
# end if
try:
if self.line is None:
self.line, = self.plot.plot(self.datax[index], self.datay[index], self.linestyle, linewidth=self.linesize, zorder=4)
self.line.set_markerfacecolor(self.linecolor)
self.line.set_markeredgecolor(self.linecolor)
else:
self.line.set_xdata(self.datax[index])
self.line.set_ydata(self.datay[index])
# end if
strTX = ""
try:
strTX = self.listOptim[index]
except:
pass
self.line.set_color(self.linecolorm if (self.indexm == index) else self.linecolor)
self.plot.set_title(strTX, fontsize=self.fontsize)
self.line.set_marker(self.markerhg if (len(self.datax[index]) > 20) else self.markersm)
self.line.set_markersize(self.markersizehg if (len(self.datax[index]) > 20) else self.markersizesm)
if (self.index == 0):
(Jm, Vm, FF, Jsc, Voc, Eff) = self.listOptimout[index]
if (self.plot.mlinex is not None):
self.plot.mlinex.remove()
# end if
if (self.plot.mliney is not None):
self.plot.mliney.remove()
# end if
self.plot.mlinex = self.plot.hlines(y=Jm * self.currentsign, xmin=0.0, xmax=Vm, linewidth=1, colors='g', linestyles='dashed', zorder=4)
self.plot.mliney = self.plot.vlines(x=Vm, ymin=Jm * self.currentsign, ymax=0.0, linewidth=1, colors='g', linestyles='dashed', zorder=4)
# end if
# end if
if autoscale:
self.plot.relim()
self.plot.autoscale()
if (self.index == 1):
ylim = self.plot.get_ylim()
self.plot.set_ylim([0.0, ylim[1]])
# end if
# end if
self.canvas.draw()
except:
pass
# end try
# end updatePlot
def setParam(self, listParam, listOptim, listOptimout, listFileContent):
if not self.reset(listParam, listOptim, listOptimout, listFileContent):
tkMessageBox.showwarning("SLALOM", "Report data not valid", parent=self.root)
return
# end if
try:
self.config(cursor="wait")
self.update()
except:
pass
arrLine = list()
try:
(Jmr, Vmr, FFr, Jscr, Vocr, Effr) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
self.indexm = self.count - 1
linestoskip = 8
prevPoints = 0
for ida in range(0, self.count):
(Jm, Vm, FF, Jsc, Voc, Eff) = self.listOptimout[ida]
if (Eff > Effr):
(Jmr, Vmr, FFr, Jscr, Vocr, Effr) = (Jm, Vm, FF, Jsc, Voc, Eff)
self.indexm = ida
# end if
arrLine = listFileContent[ida].split("\n")
arrlen = len(arrLine)
if (arrlen <= linestoskip):
break
# end if
self.datax.append(np.array([]))
self.datay.append(np.array([]))
xprev = None
xval = 0.0
yval = 0.0
curPoints = 0
for ii in range(linestoskip, arrlen):
tLine = arrLine[ii].replace("\r", "").replace("\n", "").split(self.dataSep)
dlen = len(tLine)
if dlen != 2:
break
# end if
try:
xval = float(tLine[0])
yval = float(tLine[1])
except:
pass
if xprev is None:
xprev = xval
elif xval > xprev:
# :REV:1:20181115: keep only (voltage or wavelength) that are monotically increasing
# but do not break (sometimes the simulator saves the same point)
self.datax[ida] = np.append(self.datax[ida], xval)
self.datay[ida] = np.append(self.datay[ida], yval)
xprev = xval
curPoints += 1
# end if
# end for
if ida == 0:
prevPoints = curPoints
elif (curPoints < 3):
tkMessageBox.showwarning("SLALOM", "Report data not valid (number of points: %d)" % curPoints, parent=self.root)
return
# end if
self.currentsign = 1.0 if (self.datay[ida][0] > 0.0) else -1.0
del arrLine[:]
arrLine = list()
# end for
self.updatePlot(index = self.indexm, autoscale = True)
except:
tkMessageBox.showwarning("SLALOM", "Report data not valid", parent=self.root)
return
self.config(cursor="")
# end setParam
def onParamlistBox(self, event):
if self.owner.isRunning():
self.paramlistBox.current(newindex = self.paramlistCurrent)
return
# end if
try:
self.paramlistBox.selection_clear()
indexT = self.paramlistBox.current()
self.updateSel(index = indexT)
if (self.owner is not None):
self.owner.updateSel(index = indexT)
self.owner.paramlistBox.current(newindex = indexT)
for ii in range(0, len(self.owner.report)):
if (self.owner.report[ii] is not None) and (ii != self.index):
self.owner.report[ii].frame.paramlistBox.current(newindex = indexT)
self.owner.report[ii].frame.updateSel(index = self.owner.report[ii].frame.paramlistBox.current())
# end if
# end for
if (self.owner.viewer is not None):
self.owner.viewer.frame.selectLine(index = indexT)
# end if
# end if
self.paramlistCurrent = indexT
except:
pass
# end try
# end onParamlistBox
def updateSel(self, index=None):
if not self.isDataValid():
tkMessageBox.showwarning("SLALOM", "Report data not valid", parent=self.root)
return
# end if
if (index is None) or (index < 0) or (index >= self.count):
index = self.count - 1
# end if
self.updatePlot(index)
# end updateSel
def onRightClick(self, event):
if self.owner.isRunning():
return
# end if
popmenu = Tk.Menu(None, tearoff=0)
popmenu.add_command(label='Auto scale', command=self.onAutoScale)
popmenu.add_command(label='Save as PDF', command=self.onSave)
popmenu.add_separator()
popmenu.add_command(label='Close', command=self.onClose)
popmenu.tk_popup(event.x_root, event.y_root, 0)
return "break"
# end onRightClick
def onAutoScale(self):
if self.owner.isRunning():
return
# end if
for ida in range(0, self.count):
curPoints = len(self.datax[ida])
if (curPoints < 4):
tkMessageBox.showwarning("SLALOM", "Report data not valid (number of points: %d)" % curPoints, parent=self.root)
return
# end if
# end for
if (self.plot is not None) and (self.count > 0):
self.plot.relim()
self.plot.autoscale()
if (self.index == 1):
ylim = self.plot.get_ylim()
self.plot.set_ylim([0.0, ylim[1]])
# end if
self.canvas.draw()
# end if
# end onAutoScale
def onSave(self):
if self.owner.isRunning():
return
# end if
fileopt = {}
fileopt['defaultextension'] = '.pdf'
fileopt['filetypes'] = [('PDF files', '.pdf')]
fileopt['initialfile'] = ''
fileopt['parent'] = self.root
fileopt['title'] = 'Save figure'
dataFilename = tkFileDialog.asksaveasfilename(**fileopt)
if dataFilename:
try:
pdfT = PdfPages(dataFilename)
pdfT.savefig(self.figure)
pdfT.close()
except:
pass
# end try
# end if
# end onSave
# end OptimizerReport
# slalomWindow class, the main monitor window
class slalomWindow(object):
""" main monitor window """
def __init__(self, dataFilename, remoteHost = None, simulator = "atlas"):
self.__version__ = slalomVersion
self.deviceSimulator = simulator
xIndex = 0
y1Index = 0
y2Index = 0
y3Index = 0
xLabel = None
y1Label = None
y2Label = None
y3Label = None
xType = 'int'
dataSep = '\t'
updateDelay = 30
# in seconds
self.updateDelay = 30
self.delayMin = 30
self.delayMax = 7200
self.startTime = 0
self.updateTime = 0
self.updateCount = 0
self.updateDelayMin = 0
self.updateDelayMax = 0
self.updateDelayMean = 0
self.filemtime = 0
self.fontsize = 10
self.dataSep = dataSep
self.count = 3
self.datax = np.array([])
self.dataxSel = np.array([])
self.datay = {}
self.dataySel = {}
for idy in range(0, self.count):
self.datay[idy] = np.array([])
self.dataySel[idy] = np.array([])
# end for
self.xLabel = xLabel
self.yLabel = {}
self.yLabel[0] = y1Label if (y1Label is not None) else '$\mathregular{J_{SC}\ (mA/cm^{2})}$'
self.yLabel[1] = y2Label if (y2Label is not None) else '$\mathregular{V_{OC}\ (V)}$'
self.yLabel[2] = y3Label if (y3Label is not None) else '$\mathregular{Efficiency\ (\%)}$'
self.xType = xType
self.xIndex = xIndex if ((xIndex >= -1) and (xIndex <= 16)) else 0
self.yIndex = [0, 0, 0]
self.yIndex[0] = y1Index if ((y1Index >= 0) and (y1Index <= 16)) else 0
self.yIndex[1] = y2Index if ((y2Index >= 0) and (y2Index <= 16)) else 0
self.yIndex[2] = y3Index if ((y3Index >= 0) and (y3Index <= 16)) else 0
self.root = Tk.Tk()
self.root.bind_class("Entry","<Control-a>", self.onEntrySelectAll)
self.root.bind_class("Entry","<Control-z>", self.onEntryUndo)
self.root.bind_class("Entry","<Control-y>", self.onEntryRedo)
self.root.withdraw()
self.root.wm_title("SLALOM")
self.figure = matplotlib.figure.Figure(figsize=(8,6), dpi=100, facecolor='#F1F1F1', linewidth=1.0, frameon=True)
self.figure.subplots_adjust(top = 0.9, bottom = 0.1, left = 0.09, right = 0.95, wspace = 0.0, hspace = 0.25)
self.plot = {}
self.plot[0] = self.figure.add_subplot(311)
self.plot[1] = self.figure.add_subplot(312, sharex=self.plot[0])
self.plot[2] = self.figure.add_subplot(313, sharex=self.plot[0])
for idy in range(0, self.count):
self.plot[idy].format_coord = self.formatCoord
# end for
self.dataFilename = None
self.dataDir = None
self.dataZip = None
self.dataFilenameLocal = None
self.dataDirLocal = None
self.dataZipLocal = None
self.dataShortPath = None
self.remoteHost = None
self.threadrunning = False
self.threadfinish = None
self.thread = None
self.killproc = 0
self.iPoints = 0
self.remoteHostEnabled = True
self.updateDelayAuto = True
# load the last used data filename, ssh host and update delay...
# ... only loaded if no parameter is given as a command line argument
if dataFilename is None:
try:
fileT = open("Settings/slalomMonitor.params", "r")
# maximum number of line to read
iMaxLine = 12
iMaxParam = 5
iLine = 0
iParam = 0
for lineT in fileT:
lineT = lineT.rstrip("\r\n")
if lineT.startswith("dataFilename = "):
dataFilename = lineT[len("dataFilename = "):]
if not dataFilename:
dataFilename = None
iParam += 1
elif lineT.startswith("remoteHost = ") and (remoteHost is None):
remoteHost = lineT[len("remoteHost = "):]
if not remoteHost:
remoteHost = None
iParam += 1
elif lineT.startswith("remoteHostEnabled = "):
try:
self.remoteHostEnabled = int(lineT[len("remoteHostEnabled = "):]) == 1
except:
self.remoteHostEnabled = False
iParam += 1
elif lineT.startswith("updateDelay = "):
updateDelay = lineT[len("updateDelay = "):]
if not updateDelay:
updateDelay = None
iParam += 1
elif lineT.startswith("updateDelayAuto = "):
try:
self.updateDelayAuto = int(lineT[len("updateDelayAuto = "):]) == 1
except:
self.updateDelayAuto = False
iParam += 1
# end if
iLine += 1
if (iLine >= iMaxLine) or (iParam >= iMaxParam):
break
# end for
fileT.close()
except:
pass
# end if
if remoteHost is None:
self.remoteHostEnabled = False
# end if
topFrame = Tk.Frame(self.root)
topFrame.pack(fill=Tk.X, padx=5, pady=5)
self.dataFilenameLabel = Tk.Label(topFrame, width=10, text="Filename: ")
self.dataFilenameLabel.pack(side=Tk.LEFT)
dataFilenameValidate = (topFrame.register(self.onDataFilenameValidate), '%P')
self.dataFilenameEdit = Tk.Entry(topFrame, validate="key", vcmd=dataFilenameValidate)
self.dataFilenameEdit.pack(side=Tk.LEFT, fill=Tk.X, expand=1)
self.dataFilenameEdit.insert(0, dataFilename if (dataFilename is not None) else "")
self.dataFilenameEdit.prev = None
self.dataFilenameEdit.next = None
self.dataFilenameBrowse = ttk.Button(topFrame, width=4, text="...", command=self.onBrowse)
self.dataFilenameBrowse.pack(side=Tk.LEFT, padx=(2, 2))
self.sepLabel1 = Tk.Label(topFrame, text="|")
self.sepLabel1.pack(side=Tk.LEFT)
self.remoteHostLabel = Tk.Label(topFrame, text="SSH: ")
self.remoteHostLabel.pack(side=Tk.LEFT)
remoteHostValidate = (topFrame.register(self.onRemoteHostValidate), '%P')
self.remoteHostEdit = Tk.Entry(topFrame, width=22, validate="key", vcmd=remoteHostValidate)
self.remoteHostEdit.pack(side=Tk.LEFT)
self.remoteHostEdit.insert(0, remoteHost if (remoteHost is not None) else "")
self.remoteHostEdit.prev = None
self.remoteHostEdit.next = None
self.remoteHostVar = Tk.BooleanVar()
self.remoteHostOpt = Tk.Checkbutton(topFrame, text="", variable=self.remoteHostVar, command=self.onRemoteHostOpt)
self.remoteHostOpt.pack(side=Tk.LEFT)
if self.remoteHostEnabled:
self.remoteHostOpt.select()
# end if
self.buttonbar = Tk.Frame(self.root)
self.buttonbar.pack(fill=Tk.X, padx=5, pady=5)
self.updateDelayLabel = Tk.Label(self.buttonbar, width=10, text="Delay(s): ")
self.updateDelayLabel.pack(side=Tk.LEFT)
updateDelayValidate = (self.buttonbar.register(self.onDelayValidate), '%P')
self.updateDelayEdit = Tk.Entry(self.buttonbar, width=6, validate="key", vcmd=updateDelayValidate)
self.updateDelayEdit.pack(side=Tk.LEFT)
self.updateDelayEdit.insert(0, updateDelay if (updateDelay is not None) else "30")
self.updateDelayEdit.prev = None
self.updateDelayEdit.next = None
self.updateDelayVar = Tk.BooleanVar()
self.updateDelayOpt = Tk.Checkbutton(self.buttonbar, text="Auto", variable=self.updateDelayVar, command=self.onUpdateDelayOpt)
self.updateDelayOpt.pack(side=Tk.LEFT, padx=(0, 5))
if self.updateDelayAuto:
self.updateDelayOpt.select()
# end if
self.updateProgressVar = Tk.IntVar()
self.updateProgress = ttk.Progressbar(self.buttonbar, length=120, orient='horizontal', mode='determinate', variable=self.updateProgressVar, maximum=(int(updateDelay) + 1) if (updateDelay is not None) else 31)
self.updateProgress.pack(side=Tk.LEFT, padx=(0, 5))
self.updateProgress.stopping = 0
self.updateProgress.stoppingtime = None
self.updateProgress.stopped = 0
self.updateProgress.stoppedtime = None
self.updateProgress.waiting = 0
self.sepLabel2 = Tk.Label(self.buttonbar, text="|")
self.sepLabel2.pack(side=Tk.LEFT)
self.btnstyle_red = ttk.Style()
self.btnstyle_red.configure("Red.TButton", foreground="#DE0015")
self.btnstyle_black = ttk.Style()
self.btnstyle_black.configure("Black.TButton", foreground="black")
iconStart = Tk.PhotoImage(file='Images/iconstart.gif')
self.btnStart = ttk.Button(self.buttonbar, width=10, text="Start", image=iconStart, compound=Tk.LEFT, command=self.onRestart)
self.btnStart.pack(side=Tk.LEFT, padx=(5, 5))
self.btnStart.configure(style="Black.TButton")
iconStop = Tk.PhotoImage(file='Images/iconstop.gif')
self.btnStop = ttk.Button(self.buttonbar, width=10, text="Stop", image=iconStop, compound=Tk.LEFT, command=self.onStop)
self.btnStop.pack(side=Tk.LEFT, padx=(2, 5))
self.btnStop.configure(style="Black.TButton")
iconView = Tk.PhotoImage(file='Images/iconview.gif')
self.btnView = ttk.Button(self.buttonbar, width=10, text="View data", image=iconView, compound=Tk.LEFT, command=self.onView)
self.btnView.pack(side=Tk.LEFT, padx=(2, 5))
self.btnView.configure(style="Black.TButton")
iconReport = Tk.PhotoImage(file='Images/iconreport.gif')
self.btnReport = ttk.Button(self.buttonbar, width=10, text="View JV", image=iconReport, compound=Tk.LEFT, command=self.onShowReport)
self.btnReport.pack(side=Tk.LEFT, padx=(2, 5))
self.btnReport.configure(style="Black.TButton")
iconReportb = Tk.PhotoImage(file='Images/iconreportb.gif')
self.btnReportb = ttk.Button(self.buttonbar, width=10, text="View SR", image=iconReportb, compound=Tk.LEFT, command=self.onShowReportb)
self.btnReportb.pack(side=Tk.LEFT, padx=(2, 5))
self.btnReportb.configure(style="Black.TButton")
self.actionbutton = None
self.timercount = 0.0
self.timerdelay = 500 # milliseconds
self.timerProgressVar = Tk.StringVar()
self.timerProgress = Tk.Label(self.buttonbar, text=" ", textvariable=self.timerProgressVar)
self.timerProgress.pack(side=Tk.LEFT, padx=(2, 0))
self.labelFill = Tk.Label(self.buttonbar, text=" ")
self.labelFill.pack(side=Tk.LEFT, fill=Tk.X, expand=1)
self.btnClose = ttk.Button(self.buttonbar, width=10, text="Close", image=None, command=self.onClose)
self.btnClose.pack(side=Tk.LEFT, padx=(0, 5))
self.btnClose.configure(style="Black.TButton")
iconAbout = Tk.PhotoImage(file='Images/iconabout.gif')
self.btnAbout = ttk.Button(self.buttonbar, width=3, text="", image=iconAbout, command=self.onAbout)
self.btnAbout.pack(side=Tk.LEFT, padx=(0, 5))
self.btnAbout.configure(style="Black.TButton")
paramFrame = Tk.Frame(self.root)
paramFrame.pack(fill=Tk.X, padx=5, pady=5)
self.paramSep = " ; "
self.paramIndexFormat = '{0:05d}'
self.paramName = None
self.paramCount = 0
self.paramlist = list()
self.paramlistThread = list()
self.paramlistBox = ttk.Combobox(paramFrame, state='readonly')
self.paramlistBox.pack(side=Tk.LEFT, padx=(5, 5), fill=Tk.X, expand=1)
self.paramlistBox['state'] = 'readonly'
self.paramlistBox.bind('<<ComboboxSelected>>', self.onParamlistBox)
self.paramlistCurrent = 0
self.optimlist = list()
self.optimout = list()
self.efficiencyMin = 0.0
self.efficiencyMax = 0.0
self.efficiencySel = 0.0
self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
self.canvas._tkcanvas.config(highlightthickness=0)
self.canvas.mpl_connect('button_press_event', self.onCanvasClick)
try:
self.toolbar = NavigationToolbar(self)
except:
self.toolbar = None
pass
self.toolbar.pack(side=Tk.BOTTOM, fill=Tk.X)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=Tk.LEFT, fill=Tk.BOTH, expand=1)
self.canvas.show()
self.root.protocol('WM_DELETE_WINDOW', self.onClose)
self.scattercolor = ['#8184FF', '#FF7D7D', '#3FFF3F']
self.scattercolorborder = ['#00178C', '#BC0000', '#007806']
self.scattermarkersize = [10.0, 10.0, 10.0]
self.linecolor = ['b', 'r', 'g']
self.linestyle = ['-', '-', '-']
self.linesize = [1.0, 1.0, 1.0]
self.markersm = ['o', 'o', 'o']
self.markersizesm = [6.0, 6.0, 6.0]
self.markerhg = ['.', '.', '.']
self.markersizehg = [3.0, 3.0, 3.0]
self.line = {}
self.scatter = {}
for idy in range(0, self.count):
self.line[idy] = None
self.scatter[idy] = None
# end if
# center the main window
iw = self.root.winfo_screenwidth()
ih = self.root.winfo_screenheight()
isize = (940, 600)
ix = (iw - isize[0]) / 2
iy = (ih - isize[1]) / 2
self.root.geometry("%dx%d+%d+%d" % (isize + (ix, iy)))
self.root.deiconify()
self.root.minsize(800, 600)
for idy in range(0, self.count):
try:
self.plot[idy].tick_params(axis='x', labelsize=self.fontsize)
self.plot[idy].tick_params(axis='y', labelsize=self.fontsize)
except:
[tx.label.set_fontsize(self.fontsize) for tx in self.plot[idy].xaxis.get_major_ticks()]
[ty.label.set_fontsize(self.fontsize) for ty in self.plot[idy].yaxis.get_major_ticks()]
pass
if self.yLabel[idy] is not None:
self.plot[idy].set_ylabel(self.yLabel[idy], fontsize=self.fontsize)
if (self.xType == 'int'):
self.plot[idy].xaxis.set_major_locator(MaxNLocator(integer=True))
self.plot[idy].yaxis.label.set_color(self.linecolor[idy])
try:
if idy < (self.count - 1):
pl.setp(self.plot[idy].get_xticklabels(), visible=False)
# end if
self.plot[idy].tick_params(axis='y', colors=self.linecolor[idy])
self.plot[idy].spines['left'].set_color(self.linecolor[idy])
self.plot[idy].spines['top'].set_color(self.linecolor[idy])
self.plot[idy].spines['right'].set_color(self.linecolor[idy])
self.plot[idy].spines['bottom'].set_color(self.linecolor[idy])
self.plot[idy].xaxis.major.locator.set_params(nbins=20)
self.plot[idy].yaxis.major.locator.set_params(nbins=5)
self.plot[idy].grid(True)
except:
[ty.set_color(self.linecolor[idy]) for ty in self.plot[idy].yaxis.get_ticklines()]
[ty.set_color(self.linecolor[idy]) for ty in self.plot[idy].yaxis.get_ticklabels()]
pass
# end for
self.strViewerFileContent = None
self.viewer = None
self.listKeyword = ['Optimization', 'Optim', 'Brute', 'Snap', 'tolerance', 'jaceps',
'Parameter', 'StartValue', 'EndValue', 'InitValue',
'NormValue', 'Points', 'L-BFGS-B', 'SQLP', 'Index', 'Time',
'Jm(mA/cm2)', 'Vm(V)', 'FF(%)', 'Jsc(mA/cm2)', 'Voc(V)',
'Efficiency']
self.listKeyword2 = None
self.strReportFileName = [list(), list()]
self.strReportFileContent = [list(), list()]
self.report = [None, None]
self.strReportFileNameLocal = [list(), list()]
self.popmenu = Tk.Menu(self.root, tearoff=0)
self.popmenu.add_command(label="Restart the optimizer", command=self.onRestart)
self.popmenu.add_command(label="Stop the optimizer", command=self.onStop)
self.popmenu.add_command(label="Kill the optimizer", command=self.onKill)
self.popmenu.add_separator()
self.popmenu.add_command(label="View data file", command=self.onView)
self.popmenu.add_command(label="View J-V characteristics", command=self.onShowReport)
self.popmenu.add_command(label="View Spectral Response", command=self.onShowReportb)
self.popmenu.add_separator()
self.popmenu.add_command(label="Auto scale", command=self.onAutoScale)
self.popmenu.add_separator()
self.popmenu.add_command(label="Save as PDF", command=self.onSave)
self.popmenu.add_separator()
self.popmenu.add_command(label="Close", command=self.onClose)
self.popmenu.add_separator()
self.popmenu.add_command(label="About...", command=self.onAbout)
self.root.bind("<Button-3>", self.onPopmenu)
self.root.bind("<Prior>", self.onPageDown)
self.root.bind("<Next>", self.onPageUp)
if (os.name == "nt"):
self.root.iconbitmap(r'Images/slalom.ico')
else:
iconSlalom = Tk.PhotoImage(file='Images/slalom.gif')
self.root.tk.call('wm', 'iconphoto', self.root._w, iconSlalom)
# end if
self.onUpdateData(force = True)
try:
self.plot[0].set_xlim([0.0, 1.0])
self.plot[0].set_ylim([0.0, 1.0])
self.plot[1].set_xlim([0.0, 1.0])
self.plot[1].set_ylim([0.0, 1.0])
self.plot[2].set_xlim([0.0, 1.0])
self.plot[2].set_ylim([0.0, 1.0])
except:
pass
# end try
self.root.mainloop()
return
# end __init__
def formatCoord(self, x, y):
fx = round(x)
if (x < (fx - 0.1)) or (x > (fx + 0.1)):
return ""
# end if
idx = int(fx)
return 'Index: %03d ; Value: %08.5f' % (idx, y)
# end formatCoord
def onPageMove(self, event, idir):
try:
if self.isRunning():
return
# end if
indexT = self.paramlistBox.current() + idir
indexMax = len(self.paramlistBox["values"])
if (indexT < 0) or (indexT >= indexMax):
return
# end if
# self.canvas.draw() called in updateSel
self.paramlistBox.current(newindex = indexT)
self.updateSel(index = indexT)
for report in self.report:
if (report is not None):
report.frame.paramlistBox.current(newindex = indexT)
report.frame.updateSel(index = report.frame.paramlistBox.current())
# end if
# end for
if (self.viewer is not None):
self.viewer.frame.selectLine(index = indexT)
# end if
self.paramlistCurrent = self.paramlistBox.current()
except:
pass
# end onPageDown
def onPageDown(self, event):
return self.onPageMove(event, -1)
# end onPageDown
def onPageUp(self, event):
return self.onPageMove(event, 1)
# end onPageUp
def onRestart(self):
self.onUpdateData(force = True)
# end onRestart
def onSave(self):
if self.isRunning():
return
# end if
fileopt = {}
fileopt['defaultextension'] = '.pdf'
fileopt['filetypes'] = [('PDF files', '.pdf')]
fileopt['initialfile'] = ''
fileopt['parent'] = self.root
fileopt['title'] = 'Save figure'
dataFilename = tkFileDialog.asksaveasfilename(**fileopt)
if dataFilename:
try:
pdfT = PdfPages(dataFilename)
pdfT.savefig(self.figure)
pdfT.close()
except:
pass
# end try
# end if
# end onSave
def onAutoScale(self):
if self.isRunning():
return
# end if
if (self.iPoints >= 1):
for idy in range(0, self.count):
self.plot[idy].relim()
self.plot[idy].autoscale()
# end for
self.canvas.draw()
return
# end if
# end onAutoScale
def checkFile(self, filename):
tFilename = self.dataDir + filename
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
try:
self.root.config(cursor="wait")
self.root.update()
except:
pass
bCheck = False
updateTime = None
if not remoteMon:
try:
if os.path.exists(tFilename):
statT = os.stat(tFilename)
updateTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(statT.st_mtime))
bCheck = True
# end if
except:
pass
# end try
else:
try:
STDDEVNULL = open(os.devnull, 'w')
strT = subprocess.check_output(['ssh', self.remoteHost, "stat", "--printf=%Y", tFilename], stderr=STDDEVNULL)
updateTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(strT)))
bCheck = True
except:
pass
# end try
# end if
self.root.config(cursor="")
return (bCheck, updateTime)
# end checkFile
def isStopped(self):
if (self.updateProgress.stopped >= 1):
return True
# end if
(bStopped, updateTime) = self.checkFile("stopped.txt")
if bStopped:
self.updateProgress.stopped = 1
self.updateProgress.stoppedtime = updateTime
# end if
return bStopped
# end isStopped
def isStopping(self):
if (self.updateProgress.stopping >= 1):
return True
# end if
(bStopping, updateTime) = self.checkFile("stop.txt")
if bStopping:
self.updateProgress.stopping = 1
self.updateProgress.stoppingtime = updateTime
# end if
return bStopping
# end isStopping
def onKill(self):
if self.isRunning():
return False
# end if
if (self.updateProgress.stopped >= 1):
return True
# end if
# first, try to kindly stop the optimizer... if it doesn't work, kill it (! the last data will then be lost)
if self.killproc < 1:
self.killproc += 1
return self.onStop()
# end if
self.killproc = 0
bStopped = self.isStopped()
if bStopped:
tkMessageBox.showinfo("SLALOM", "The optimization was stopped @ "
+ (self.updateProgress.stoppedtime if (self.updateProgress.stoppedtime is not None)
else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
return True
# end if
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
procFilename = self.dataDir + "proc.txt"
stoppedFilename = self.dataDir + "stopped.txt"
if not tkMessageBox.askyesno("SLALOM", "Kill the optimization process?\n", default=tkMessageBox.NO, parent=self.root):
return False
# end if
try:
self.root.config(cursor="wait")
self.root.update()
except:
pass
STDDEVNULL = open(os.devnull, 'w')
fileT = None
try:
if remoteMon:
procId = subprocess.check_output(['ssh', self.remoteHost, 'cat', procFilename], stderr=STDDEVNULL)
else:
fileT = open(procFilename, "r")
procId = str(fileT.read())
fileT.close()
# end if
except:
self.root.config(cursor="")
tkMessageBox.showwarning("SLALOM", "Optimization process not found", parent=self.root)
return False
# end try
lenId = len(procId)
if (lenId < 1) or (lenId > 64):
self.root.config(cursor="")
tkMessageBox.showwarning("SLALOM", "Optimization process not found", parent=self.root)
return False
# end try
try:
cmdT = ['ssh', self.remoteHost, 'kill', procId] if remoteMon else ['kill', procId]
try:
subprocess.Popen(cmdT, stderr=STDDEVNULL, stdout=STDDEVNULL)
except:
pass
# end try
iT = 2 if remoteMon else 0
cmdT[iT] = 'pkill'
arSim = ['deckbuild.exe', 'deckbld.exe', 'atlas.exe', 'atlas.exe2', 'atlas.exe3', 'atlas2.exe', 'atlas3.exe']
for tSim in arSim:
try:
cmdT[iT + 1] = tSim
subprocess.Popen(cmdT, stderr=STDDEVNULL, stdout=STDDEVNULL)
except:
pass
# end try
# end for
except:
pass
# end try
try:
if remoteMon:
STDDEVNULL = open(os.devnull, 'w')
subprocess.Popen(['ssh', self.remoteHost, 'touch', stoppedFilename], stdout=STDDEVNULL)
self.updateProgress.stopped = 1
else:
fileT = open(stoppedFilename, "w")
fileT.write("SLALOM\nStopped from the monitor\n")
fileT.close()
self.updateProgress.stopped = 1 if os.path.exists(stoppedFilename) else 0
# end if
except:
pass
# end try
if self.updateProgress.stopped == 1:
self.updateProgressVar.set(0)
self.updateProgress.stop()
tkMessageBox.showinfo("SLALOM", "The optimization process was killed", parent=self.root)
# end if
self.root.config(cursor="")
def onStop(self):
if self.isRunning():
return False
# end if
bStopped = self.isStopped()
if bStopped:
self.updateProgressVar.set(0)
self.updateProgress.stop()
tkMessageBox.showinfo("SLALOM", "The optimization was stopped @ "
+ (self.updateProgress.stoppedtime if (self.updateProgress.stoppedtime is not None)
else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
return True
# end if
bStopping = self.isStopping()
if bStopping:
tkMessageBox.showinfo("SLALOM", "The optimization is being stopped since "
+ (self.updateProgress.stoppingtime if (self.updateProgress.stoppingtime is not None)
else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
parent=self.root)
return True
# end if
stopFilename = self.dataDir + "stop.txt"
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
if not tkMessageBox.askyesno("SLALOM", "Stop the optimization?", default=tkMessageBox.NO, parent=self.root):
return False
# end if
try:
self.root.config(cursor="wait")
self.root.update()
except:
pass
try:
self.updateProgress.stopping = 0
if remoteMon:
STDDEVNULL = open(os.devnull, 'w')
subprocess.Popen(['ssh', self.remoteHost, 'touch', stopFilename], stdout=STDDEVNULL)
self.updateProgress.stopping = 1
else:
fileT = open(stopFilename, "w")
fileT.write("SLALOM\nStopped from the monitor\n")
fileT.close()
self.updateProgress.stopping = 1 if os.path.exists(stopFilename) else 0
# end if
if self.updateProgress.stopping == 1:
tkMessageBox.showinfo("SLALOM", "The optimization will stop at the end of the running iteration", parent=self.root)
# end if
except:
pass
# end try
self.root.config(cursor="")
return True
# end onStop
def onEntryUndo(self, event):
try:
if event.widget.prev is not None:
event.widget.next = event.widget.get()
strT = event.widget.prev
idx = event.widget.index(Tk.INSERT)
event.widget.delete(0, Tk.END)
event.widget.insert(0, strT)
event.widget.prev = strT
event.widget.icursor(idx + 1)
except:
pass
# end onEntryUndo
def onEntryRedo(self, event):
try:
if event.widget.next is not None:
idx = event.widget.index(Tk.INSERT)
strT = event.widget.prev
event.widget.delete(0, Tk.END)
event.widget.insert(0, event.widget.next)
event.widget.prev = strT
event.widget.icursor(idx + 1)
except:
pass
# end onEntryRedo
def onEntrySelectAll(self, event):
try:
event.widget.select_range(0, Tk.END)
except:
pass
# end onEntrySelectAll
def onDataFilenameValidate(self, sp):
try:
if (not sp) or (len(sp) <= 255):
self.dataFilenameEdit.prev = sp
return True
# end if
return False
except:
return True
# end try
# end onDataFilenameValidate
def onDelayValidate(self, sp):
try:
if (not sp) or ((len(sp) <= 4) and (re.match("^[0-9]*$", sp))):
self.updateDelayEdit.prev = sp
return True
# end if
return False
except:
return True
# end try
# end onDelayValidate
def onRemoteHostValidate(self, sp):
try:
if (not sp) or (len(sp) <= 63):
self.remoteHostEdit.prev = sp
return True
# end if
return False
except Exception as excT:
return True
# end try
# end onRemoteHostValidate
def onRemoteHostOpt(self):
try:
self.remoteHostEnabled = self.remoteHostVar.get()
except:
pass
# end onRemoteHostOpt
def onUpdateDelayOpt(self):
try:
self.updateDelayAuto = self.updateDelayVar.get()
except:
pass
# end onUpdateDelayOpt
def onParamlistBox(self, event):
if self.isRunning():
self.paramlistBox.current(newindex = self.paramlistCurrent)
return
# end if
try:
self.paramlistBox.selection_clear()
indexT = self.paramlistBox.current()
self.updateSel(index=indexT)
for report in self.report:
if (report is not None):
report.frame.paramlistBox.current(newindex = indexT)
report.frame.updateSel(index = report.frame.paramlistBox.current())
# end if
# end for
if (self.viewer is not None):
self.viewer.frame.selectLine(index = indexT)
# end if
self.paramlistCurrent = indexT
except:
pass
# end onParamlistBox
def onPopmenu(self, event):
try:
self.popmenu.post(event.x_root, event.y_root)
except:
pass
# end onPopmenu
def onCanvasClick(self, event):
try:
if self.isRunning():
return
# end if
if (event.button != 1) or (self.iPoints < 1):
return
# end if
subplotbox = self.plot[0].get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
iWidth, iHeight = subplotbox.width * self.figure.dpi, subplotbox.height * self.figure.dpi
fDeltaX = 2.0 * float(self.scattermarkersize[0]) / float(iWidth)
fDeltaY = 2.0 * float(self.scattermarkersize[0]) / float(iHeight)
indexX = (np.abs(self.datax - event.xdata)).argmin()
if (indexX < 0) or (indexX >= self.iPoints):
return
# end if
fx = self.datax[indexX]
fDeltaX *= math.fabs(fx)
bXfound = ((fx > (event.xdata - fDeltaX)) and (fx < (event.xdata + fDeltaX)))
if bXfound:
idy = -1
for ii in range(0, self.count):
if self.plot[ii] == event.inaxes:
idy = ii
break
# end if
# end for
if idy >= 0:
indexY = (np.abs(self.datay[idy] - event.ydata)).argmin()
if (indexY < 0) or (indexY >= self.iPoints):
return
# end if
fy = self.datay[idy][indexY]
fDeltaY *= math.fabs(fy)
bYfound = (fy > (event.ydata - fDeltaY)) and (fy < (event.ydata + fDeltaY))
if bYfound:
self.paramlistBox.current(newindex = indexX)
# self.canvas.draw() called in updateSel
self.updateSel(index = indexX)
for report in self.report:
if (report is not None):
report.frame.paramlistBox.current(newindex = indexX)
report.frame.updateSel(index = report.frame.paramlistBox.current())
# end if
# end for
if (self.viewer is not None):
self.viewer.frame.selectLine(index = indexX)
# end if
self.paramlistCurrent = self.paramlistBox.current()
# end if
# end if
# end if
except:
pass
# end onCanvasClick
def onBrowse(self):
if self.isRunning():
return
# end if
if self.remoteHostEnabled:
if not tkMessageBox.askyesno("SLALOM", "SSH monitoring enabled. Switch to local monitoring?",
default=tkMessageBox.NO, parent=self.root):
return
# end if
self.remoteHostVar = 0
self.remoteHostEnabled = False
# end if
fileopt = {}
fileopt['defaultextension'] = ''
fileopt['filetypes'] = [('SLALOM efficiency output file', 'simuloutput_optimized.txt')]
fileopt['initialfile'] = 'simuloutput_optimized.txt'
fileopt['parent'] = self.root
fileopt['title'] = 'Open the SLALOM efficiency output filename'
dataFilename = tkFileDialog.askopenfilename(**fileopt)
if dataFilename:
try:
self.dataFilenameEdit.delete(0, Tk.END)
self.dataFilenameEdit.insert(0, dataFilename)
self.updateParam()
self.onUpdateData(force = True)
except:
pass
# end if
# end onBrowse
def onView(self):
if self.isRunning():
return
# end if
if self.viewer is not None:
self.viewer.focus()
return
# end if
if self.dataFilename is None:
return
# end if
try:
dataFilename = self.dataFilename
self.updateParam()
if dataFilename != self.dataFilename:
if not tkMessageBox.askyesno("SLALOM", "The data filename has changed.\nSynchonize data?", default=tkMessageBox.NO, parent=self.root):
return
# end if
self.onUpdateData(force = True)
return
# end try
if (self.strViewerFileContent is None) or (self.strViewerFileContent == ""):
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
try:
self.root.config(cursor="wait")
self.root.update()
except:
pass
# get the file content locally (as it was already copied from the remote server).
# the ssh connexion should use auth keys, not password, for obvious security reasons.
fileT = None
try:
fileT = open(self.dataFilenameLocal, "r")
self.strViewerFileContent = fileT.read()
fileT.close()
except:
self.root.config(cursor="")
tkMessageBox.showwarning("SLALOM", "Optimization data file not found:\n%s" % self.dataFilenameLocal, parent=self.root)
return False
# end try
self.root.config(cursor="")
# end if
if (self.strViewerFileContent is None) or (len(self.strViewerFileContent) < 32):
tkMessageBox.showwarning("SLALOM", "Optimization data file not found", parent=self.root)
return False
# end if
self.viewer = Tk.Toplevel(self.root)
self.viewer.protocol("WM_DELETE_WINDOW", self.onViewerClose)
if (os.name == "nt"):
self.viewer.iconbitmap('Images/slalomd.ico')
else:
iconSlalom = Tk.PhotoImage(file='Images/slalomd.gif')
self.viewer.tk.call('wm', 'iconphoto', self.viewer._w, iconSlalom)
# end if
self.viewer.frame = OptimizerViewer(self.viewer)
self.viewer.frame.onClose = self.onViewerClose
self.viewer.frame.pack(expand=1, fill="both")
self.viewer.frame.setText(self.strViewerFileContent, self.listKeyword, self.listKeyword2)
self.root.wait_window(self.viewer)
except:
pass
# end onView
def onViewerClose(self):
self.viewer.destroy()
self.viewer = None
# end onViewerClose
def isRunning(self):
if (self.thread is None):
self.threadrunning = False
return self.threadrunning
# end if
if (not self.thread.isAlive()):
self.thread = None
self.threadrunning = False
# end if
return self.threadrunning
# end isRunning
def setRunning(self, threadrunning = True, fromthread = False):
self.threadrunning = threadrunning
if fromthread:
if not self.threadrunning:
self.thread = None
# end if
# do not update gui from the working thread
return
# end if
try:
if self.threadrunning:
if self.actionbutton is not None:
self.actionbutton.configure(style='Red.TButton')
# end if
self.timercount = 0.0
self.timerProgress.configure(foreground='#DE0015')
self.timerProgressVar.set('Running...')
else:
if self.actionbutton is not None:
self.actionbutton.configure(style='Black.TButton')
# end if
self.timerProgress.configure(foreground='black')
self.timerProgressVar.set(' ')
self.timercount = 0.0
self.actionbutton = None
# end if
except:
pass
# end setRunning
def onThreadMonitor(self):
threadrunning = self.isRunning()
try:
if not threadrunning:
self.setRunning(threadrunning = False, fromthread = False)
if self.threadfinish is not None:
self.threadfinish()
self.threadfinish = None
# end if
return
# end if
self.timerProgressVar.set('Running... %04.1f' % self.timercount)
timerdelay = float(self.timerdelay) / 1000.0
self.timercount += timerdelay
self.root.after(self.timerdelay, self.onThreadMonitor)
except:
pass
# end onThreadMonitor
def updateReport(self):
for ii in range(0, len(self.report)):
if (self.report[ii] is not None):
try:
self.report[ii].frame.setParam(self.paramlist, self.optimlist, self.optimout, self.strReportFileContent[ii])
except:
pass
# end try
# end if
# end for
# end updateReport
def showReport(self, index = 0):
if self.isRunning():
return
# end if
if (index < 0) or (index >= len(self.report)):
return
# end if
if self.report[index] is not None:
self.report[index].focus()
return
# end if
dataFilename = self.dataFilename
self.updateParam()
if dataFilename != self.dataFilename:
if not tkMessageBox.askyesno("SLALOM", "The data filename has changed.\nSynchonize data?", default=tkMessageBox.NO, parent=self.root):
return
# end if
self.onUpdateData(force = True)
return
# end try
strReportFileName = self.strReportFileName[index]
strReportFileContent = self.strReportFileContent[index]
dataNV = (self.dataFilename is None) or (strReportFileName is None) or (strReportFileContent is None)
if dataNV == False:
iFnameLen = len(strReportFileName)
iFcontentLen = len(strReportFileContent)
dataNV = (iFnameLen < 1) or (iFcontentLen < 1) or (iFcontentLen != iFnameLen) or (iFcontentLen != self.iPoints)
# end if
if dataNV:
tkMessageBox.showwarning("SLALOM", "Report data files not found", parent=self.root)
return
# end if
try:
self.report[index] = Tk.Toplevel(self.root)
self.report[index].withdraw()
self.report[index].protocol("WM_DELETE_WINDOW", self.onReportClose if (index == 0) else self.onReportCloseb)
if (os.name == "nt"):
self.report[index].iconbitmap(r'Images/slalomp.ico')
else:
iconSlalom = Tk.PhotoImage(file='Images/slalomp.gif')
self.report[index].tk.call('wm', 'iconphoto', self.report._w, iconSlalom)
# end if
self.report[index].frame = OptimizerReport(self, index, self.report[index])
self.report[index].frame.onClose = self.onReportClose if (index == 0) else self.onReportCloseb
self.report[index].frame.pack(expand=1, fill="both")
self.report[index].frame.setParam(self.paramlist, self.optimlist, self.optimout, strReportFileContent)
# center the main window
iw = self.report[index].winfo_screenwidth()
ih = self.report[index].winfo_screenheight()
isize = (800, 600)
ix = (iw - isize[0]) / 2
iy = (ih - isize[1]) / 2
self.report[index].geometry("%dx%d+%d+%d" % (isize + (ix, iy)))
self.report[index].deiconify()
self.report[index].minsize(600, 400)
self.root.wait_window(self.report[index])
except:
pass
# end try
# end showReport
def onShowReport(self):
self.showReport(index = 0)
# end onShowReport
def onShowReportb(self):
self.showReport(index = 1)
# end onShowReportb
def onReportClose(self):
if self.report[0] is not None:
self.report[0].destroy()
self.report[0] = None
# end if
# end onReportClose
def onReportCloseb(self):
if self.report[1] is not None:
self.report[1].destroy()
self.report[1] = None
# end if
# end onReportClose
def updateParam(self):
try:
self.remoteHost = self.remoteHostEdit.get().strip("\t ")
if not self.remoteHost:
self.remoteHost = None
# end if
except:
pass
# end try
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
try:
updateDelay = int(self.updateDelayEdit.get())
if (updateDelay >= self.delayMin) and (updateDelay <= self.delayMax):
self.updateDelay = updateDelay
self.updateProgress.configure(maximum=str(self.updateDelay + 3))
# end if
except:
pass
dataFilename = self.dataFilename
sepFrom = '/' if (os.name == "nt") else '\\'
sepTo = '\\' if (os.name == "nt") else '/'
try:
self.dataFilename = self.dataFilenameEdit.get().strip("\r\n\t ")
if not self.dataFilename:
self.dataFilename = None
self.dataDir = None
self.dataZip = None
self.dataShortPath = None
self.dataFilenameLocal = None
self.dataDirLocal = None
self.dataZipLocal = None
elif dataFilename != self.dataFilename:
if remoteMon:
# always normalize the server-side (always Linux) filenames
self.dataFilename = self.dataFilename.replace('\\', '/')
else:
self.dataFilename = self.dataFilename.replace(sepFrom, sepTo)
# end if
dirSepChar = '/' if remoteMon else ('\\' if (os.name == "nt") else '/')
iSep = self.dataFilename.rfind(dirSepChar)
if iSep > 0:
self.dataDir = self.dataFilename[:iSep + 1]
# end if
iii = self.dataFilename.find(dirSepChar + 'output' + dirSepChar)
if (iii >= 0):
self.dataShortPath = self.dataFilename[iii+8:]
iSep = self.dataFilename.rfind(dirSepChar)
if iSep > 0:
self.dataZip = self.dataFilename[:iSep] + '.zip'
# end if
if remoteMon:
# remote server
self.dataDirLocal = os.path.dirname(os.path.realpath(__file__))
if not self.dataDirLocal.endswith(dirSepChar):
self.dataDirLocal += dirSepChar
# end if
strDN = 'Device' + dirSepChar
strDN += ('Silvaco' + dirSepChar) if (self.deviceSimulator == "atlas") else ('TiberCAD' + dirSepChar)
self.dataDirLocal += (strDN + 'output' + dirSepChar) + self.dataShortPath
iSep = self.dataDirLocal.rfind(dirSepChar)
if iSep > 0:
self.dataDirLocal = self.dataDirLocal[:iSep + 1]
self.dataZipLocal = self.dataDirLocal[:iSep] + '.zip'
self.dataFilenameLocal = self.dataDirLocal
iSep = self.dataFilename.rfind(dirSepChar)
if iSep > 0:
self.dataFilenameLocal += self.dataFilename[iSep + 1:]
# end if
# end if
if not os.path.exists(self.dataDirLocal):
os.makedirs(self.dataDirLocal)
# end if
else:
# local server
self.dataFilenameLocal = self.dataFilename
self.dataDirLocal = self.dataDir
self.dataZipLocal = self.dataZip
# end if
# end if
# end if
except:
pass
# end try
if self.dataFilename is None:
return
# end if
try:
if remoteMon:
# always normalize the server-side (always Linux) filenames
self.dataDir = self.dataDir.replace('\\', '/')
self.dataZip = self.dataZip.replace('\\', '/')
self.dataShortPath = self.dataShortPath.replace('\\', '/')
else:
self.dataDir = self.dataDir.replace(sepFrom, sepTo)
self.dataZip = self.dataZip.replace(sepFrom, sepTo)
self.dataShortPath = self.dataShortPath.replace(sepFrom, sepTo)
# end if
self.dataFilenameLocal = self.dataFilenameLocal.replace(sepFrom, sepTo)
self.dataDirLocal = self.dataDirLocal.replace(sepFrom, sepTo)
self.dataZipLocal = self.dataZipLocal.replace(sepFrom, sepTo)
if dataFilename != self.dataFilename:
self.dataFilenameEdit.delete(0, Tk.END)
self.dataFilenameEdit.insert(0, self.dataFilename)
self.remoteHostEdit.delete(0, Tk.END)
self.remoteHostEdit.insert(0, self.remoteHost if (self.remoteHost is not None) else '')
# end if
except:
pass
# end try
# end updateParam
def resetData(self):
self.datax = np.delete(self.datax, np.s_[:])
for idy in range(0, self.count):
self.datay[idy] = np.delete(self.datay[idy], np.s_[:])
# end for
self.iPoints = 0
del self.paramlistThread[:]
self.paramlistThread = list()
self.paramName = None
self.paramCount = 0
self.strViewerFileContent = None
for strReportFileName in self.strReportFileName:
del strReportFileName[:]
strReportFileName = list()
# end for
for strReportFileContent in self.strReportFileContent:
del strReportFileContent[:]
strReportFileContent = list()
# end for
for strReportFileNameLocal in self.strReportFileNameLocal:
del strReportFileNameLocal[:]
strReportFileNameLocal = list()
# end for
del self.optimlist[:]
self.optimlist = list()
del self.optimout[:]
self.optimout = list()
# end resetData
def updateDataThread(self):
if self.dataFilename is None:
self.setRunning(threadrunning = False, fromthread = True)
return
# end if
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
filemtime = 0
STDDEVNULL = open(os.devnull, 'w')
if not remoteMon:
try:
if os.path.isfile(self.dataFilename) == False:
self.resetData()
self.setRunning(threadrunning = False, fromthread = True)
return
# end if
filemtime = os.path.getmtime(self.dataFilename)
except:
self.resetData()
self.setRunning(threadrunning = False, fromthread = True)
return
# end try
else:
try:
strT = subprocess.check_output(['ssh', self.remoteHost, "stat", "--printf=%Y", self.dataFilename], stderr=STDDEVNULL)
filemtime = int(strT)
except:
self.resetData()
self.setRunning(threadrunning = False, fromthread = True)
return
# end try
# end if
if (self.filemtime > 0) and (filemtime <= self.filemtime):
self.setRunning(threadrunning = False, fromthread = True)
return True
# end if
if (self.filemtime > 0):
iDelta = filemtime - self.filemtime
else:
iDelta = 0
# end if
self.filemtime = filemtime
self.updateDelayMin = self.updateDelayMax = self.updateDelayMean = 0
arrT = list()
try:
iMaxLine = 12
iMaxParam = 3
iLine = 0
iParam = 0
pathDelay = self.dataDir + "delay.txt"
if remoteMon:
# always normalize the server-side filename
pathDelay = pathDelay.replace("\\", "/")
strT = subprocess.check_output(['ssh', self.remoteHost, 'cat', pathDelay], stderr=STDDEVNULL)
arrT = strT.split('\n')
else:
fileT = open(pathDelay, "r")
strT = fileT.read()
fileT.close()
arrT = strT.split('\n')
# end if
for lineT in arrT:
if (not lineT):
break
# end if
lineT = lineT.rstrip("\r\n")
if lineT.startswith("DelayMin = "):
self.updateDelayMin = int(float(lineT[len("DelayMin = "):]))
iParam += 1
# end if
elif lineT.startswith("DelayMax = "):
self.updateDelayMax = int(float(lineT[len("DelayMax = "):]))
iParam += 1
# end if
elif lineT.startswith("DelayMean = "):
self.updateDelayMean = int(float(lineT[len("DelayMean = "):]))
iParam += 1
# end if
iLine += 1
if (iLine >= iMaxLine) or (iParam >= iMaxParam):
break
# end if
# end for
except Exception as excT:
pass
# end try
iLine = 0
try:
del arrT[:]
arrT = list()
except:
pass
# end try
# get the file content from remote server or locally.
# the ssh connexion should use auth keys, not password, for obvious security reasons.
fileT = None
try:
if remoteMon:
subprocess.check_call(['scp', self.remoteHost + ':' + self.dataFilename, self.dataFilenameLocal], stderr=STDDEVNULL, stdout=STDDEVNULL)
# end if
except:
# check if the data file is locally store
if not os.path.isfile(self.dataFilenameLocal):
self.resetData()
self.setRunning(threadrunning = False, fromthread = True)
return False
# end if
pass
# end try
self.resetData()
bAuto = (self.xIndex == self.yIndex[0])
paramlistItem = ""
self.strViewerFileContent = ""
self.efficiencyMin = 0.0
self.efficiencyMax = 0.0
self.efficiencySel = 0.0
try:
fileT = open(self.dataFilenameLocal, "r")
for lineT in fileT:
self.strViewerFileContent += lineT
if (self.paramName is None) and lineT.startswith("# Parameter:"):
try:
self.paramName = lineT[len("# Parameter:"):].lstrip("\t").rstrip("\r\n").replace("\t", self.paramSep)
self.paramCount = len(self.paramName.split(self.paramSep)) + 1
except:
self.paramName = ""
self.paramCount = 0
# end if
iLine += 1
continue
# end if
if lineT.startswith("#") or ((lineT[0].isdigit() == False) and (lineT[0].isspace() == False)):
iLine += 1
continue
# end if
if bAuto == True:
iSep = lineT.count(self.dataSep)
if iSep > (self.count - 1):
self.xIndex = 0
for idy in range(0, self.count):
self.yIndex[idy] = iSep - (self.count - 1) + idy
# end for
bAuto = False
else:
iLine += 1
continue
# end if
# end if
arrLine = []
try:
arrLine = lineT.split(self.dataSep)
except:
continue
# end try
arrlen = len(arrLine)
if (arrlen < (self.xIndex + 1)) or (arrlen < (self.yIndex[0] + 1)) or (arrlen < (self.yIndex[1] + 1)) or (arrlen < (self.yIndex[2] + 1)):
continue
# end if
if (self.xIndex >= 0):
self.datax = np.append(self.datax, float(arrLine[self.xIndex]))
else:
self.datax = np.append(self.datax, float(self.iPoints))
# end if
for idy in range(0, self.count):
self.datay[idy] = np.append(self.datay[idy], float(arrLine[self.yIndex[idy]]))
# end for
# optimized input parameters
paramlistItem = self.paramIndexFormat.format(self.iPoints + 1)
paramlistItem += " [ " + self.paramName + " ] = [ "
for ll in range(2, 1 + self.paramCount):
paramlistItem += '{0}'.format(float(arrLine[ll]))
if ll < self.paramCount:
paramlistItem += self.paramSep
else:
paramlistItem += " ]"
# end if
# end for
self.paramlistThread.append(paramlistItem)
# optimized output values (FF, Jsc, Voc, Eff)
try:
iopt = self.yIndex[0] - 1
if iopt >= 2:
optimlistItem = (("$\mathregular{FF\ =\ %6.3f\ \%%}$" % float(arrLine[iopt])) + self.paramSep
+ ("$\mathregular{J_{SC}\ =\ %6.3f\ mA/cm^{2}}$" % float(arrLine[iopt+1])) + self.paramSep
+ ("$\mathregular{V_{OC}\ =\ %6.3f\ V}$" % float(arrLine[iopt+2])) + self.paramSep
+ ("$\mathregular{Eff\ =\ %6.3f\ \%%}$" % float(arrLine[iopt+3])))
self.optimlist.append(optimlistItem)
self.optimout.append((math.fabs(float(arrLine[iopt-2])), float(arrLine[iopt-1]), float(arrLine[iopt]), float(arrLine[iopt+1]), float(arrLine[iopt+2]), float(arrLine[iopt+3])))
# end if
except:
pass
# report file names (J-V characteristics)
self.strReportFileName[0].append(self.dataDir + "simuloutput_jvp_" + arrLine[0] + "_" + arrLine[1] + ".log")
self.strReportFileNameLocal[0].append(self.dataDirLocal + "simuloutput_jvp_" + arrLine[0] + "_" + arrLine[1] + ".log")
# report file names (External quantum efficiency)
self.strReportFileName[1].append(self.dataDir + "simuloutput_spectralresponse_eqe_" + arrLine[0] + "_" + arrLine[1] + ".log")
self.strReportFileNameLocal[1].append(self.dataDirLocal + "simuloutput_spectralresponse_eqe_" + arrLine[0] + "_" + arrLine[1] + ".log")
self.iPoints += 1
iLine += 1
# end for
fileT.close()
self.updateCount += 1
except:
pass
try:
if self.listKeyword2 is None:
self.listKeyword2 = list()
for strK in self.paramName.split(self.paramSep):
self.listKeyword2.append(strK)
# end for
# end if
except:
pass
if self.iPoints >= 1:
self.efficiencyMin = np.min(self.datay[self.count - 1])
self.efficiencyMax = np.max(self.datay[self.count - 1])
self.efficiencySel = self.datay[self.count - 1][self.iPoints - 1]
# end if
for rr in range(0, len(self.strReportFileName)):
if self.strReportFileName[rr] is None:
continue
# end if
iFnameLen = len(self.strReportFileName[rr])
iFcontentLen = len(self.strReportFileContent[rr])
if (iFnameLen != self.iPoints) or (iFcontentLen >= iFnameLen):
self.setRunning(threadrunning = False, fromthread = True)
return
# end if
for ii in range(iFcontentLen, iFnameLen):
# get the files from the remote server and store them locally.
# the ssh connexion should use auth keys, not password, for obvious security reasons.
try:
if remoteMon and (not os.path.exists(self.strReportFileNameLocal[rr][ii])):
subprocess.check_call(['scp', self.remoteHost + ':' + self.strReportFileName[rr][ii], self.strReportFileNameLocal[rr][ii]], stderr=STDDEVNULL, stdout=STDDEVNULL)
# end if
fileT = open(self.strReportFileNameLocal[rr][ii], "r")
self.strReportFileContent[rr].append(fileT.read())
fileT.close()
except:
self.setRunning(threadrunning = False, fromthread = True)
return
# end try
if (self.isRunning() == False):
break
# end if
# end for
# end for
# At the optimization end, get the zipped files from remote server.
# the ssh connexion should use auth keys, not password, for obvious security reasons.
if remoteMon:
isStopped = False
stoppedFilename = self.dataDir + "stopped.txt"
stoppedFilename = stoppedFilename.replace("\\", "/")
try:
strT = subprocess.check_output(['ssh', self.remoteHost, "stat", "--printf=%Y", stoppedFilename], stderr=STDDEVNULL)
updateTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(strT)))
isStopped = True
except:
pass
# end try
if isStopped:
try:
subprocess.check_call(['scp', self.remoteHost + ':' + self.dataZip, self.dataZipLocal], stderr=STDDEVNULL, stdout=STDDEVNULL)
with zipfile.ZipFile(self.dataZipLocal, 'r') as zipT:
zipT.extractall(self.dataDirLocal)
# end if
except:
pass
# end try
# end if
# end if
self.setRunning(threadrunning = False, fromthread = True)
# end updateDataThread
def updateTitleEfficiency(self):
try:
strTX = ""
if ((self.iPoints >= 1) and (self.efficiencyMin <= self.efficiencyMax)
and (self.efficiencyMin <= self.efficiencyMax)):
strTX = ("Efficiency: min = " + ("%06.3f %%" % self.efficiencyMin)
+ " ; max = " + ("%06.3f %%" % self.efficiencyMax)
+ " ; selected = " + ("%06.3f %%" % self.efficiencySel))
# end if
self.plot[self.count - 1].set_title(strTX, fontsize=self.fontsize, color=self.linecolor[self.count - 1])
except:
pass
# end try
# end updateTitleEfficiency
def updateSel(self, index=None):
if self.isRunning():
return
# end if
self.dataxSel = np.delete(self.dataxSel, np.s_[:])
for idy in range(0, self.count):
self.dataySel[idy] = np.delete(self.dataySel[idy], np.s_[:])
# end for
if (self.iPoints < 1):
for idy in range(0, self.count):
if self.scatter[idy] is not None:
self.scatter[idy].set_xdata(self.dataxSel)
self.scatter[idy].set_ydata(self.dataySel[idy])
self.plot[idy].relim()
self.plot[idy].autoscale()
# end if
# end for
self.updateTitleEfficiency()
self.canvas.draw()
return
# end if
if (index is None) or (index < 0) or (index >= self.iPoints):
index = self.iPoints - 1
# end if
self.dataxSel = np.append(self.dataxSel, self.datax[index])
for idy in range(0, self.count):
self.dataySel[idy] = np.append(self.dataySel[idy], self.datay[idy][index])
# end if
self.efficiencySel = self.datay[self.count - 1][index]
self.updateTitleEfficiency()
for idy in range(0, self.count):
if self.scatter[idy] is None:
self.scatter[idy], = self.plot[idy].plot(self.dataxSel, self.dataySel[idy], self.markersm[idy], zorder=4)
self.scatter[idy].set_color(self.scattercolor[idy])
self.scatter[idy].set_markerfacecolor(self.scattercolor[idy])
self.scatter[idy].set_markeredgecolor(self.scattercolorborder[idy])
self.scatter[idy].set_markersize(self.scattermarkersize[idy])
# end if
self.scatter[idy].set_xdata(self.dataxSel)
self.scatter[idy].set_ydata(self.dataySel[idy])
self.plot[idy].relim()
self.plot[idy].autoscale()
# end for
self.canvas.draw()
# end updateSel
def updateData(self, force=False):
if self.isRunning():
return
# end if
dataRead = False
if self.updateProgress.stopping <= self.paramCount:
dataRead = True
self.paramlist = list(self.paramlistThread)
self.paramlistBox['values'] = self.paramlist
try:
self.paramlistBox.set(self.paramlist[self.iPoints - 1])
except:
pass
# end if
for idy in range(0, self.count):
if self.line[idy] is None:
self.line[idy], = self.plot[idy].plot(self.datax, self.datay[idy], self.linestyle[idy], linewidth=self.linesize[idy], zorder=4)
self.line[idy].set_color(self.linecolor[idy])
self.line[idy].set_markerfacecolor(self.linecolor[idy])
self.line[idy].set_markeredgecolor(self.linecolor[idy])
# end if
self.line[idy].set_marker(self.markerhg[idy] if (len(self.datax) > 20) else self.markersm[idy])
self.line[idy].set_markersize(self.markersizehg[idy] if (len(self.datax) > 20) else self.markersizesm[idy])
self.line[idy].set_xdata(self.datax)
self.line[idy].set_ydata(self.datay[idy])
self.plot[idy].relim()
self.plot[idy].autoscale()
# end for
isStopped = self.updateTitle(dataUpdated=dataRead)
# self.canvas.draw() called in updateSel
self.updateSel()
if dataRead:
if self.updateDelayAuto:
if (self.updateDelayMin >= self.delayMin) and (self.updateDelayMin <= self.delayMax):
self.updateDelayEdit.delete(0, Tk.END)
self.updateDelayEdit.insert(0, str(self.updateDelayMin))
self.updateDelay = self.updateDelayMin
# end if
# end if
# end if
if (self.viewer is not None):
self.viewer.frame.setText(self.strViewerFileContent, self.listKeyword, self.listKeyword2)
# end if
self.updateReport()
if (isStopped == False):
self.updateProgressVar.set(0)
self.updateProgress.start(1000)
# delay in milliseconds
self.root.after(1000 * (self.updateDelay + 2), self.onUpdateData)
else:
self.updateProgressVar.set(0)
self.updateProgress.stop()
# end if
# end updateData
def onUpdateData(self, force=False):
if self.isRunning():
return
# end if
nowT = time.time()
if force:
self.updateProgress.waiting = 0
self.updateProgress.stopping = 0
self.updateProgress.stopped = 0
self.killproc = 0
self.startTime = int(nowT)
self.filemtime = 0
self.updateTime = 0
self.updateCount = 0
self.updateDelayMin = 0
self.updateDelayMax = 0
try:
self.paramlistBox.selection_clear()
self.paramlistBox.set("")
except:
pass
# end if
self.updateProgressVar.set(0)
self.updateProgress.stop()
self.updateTime = int(nowT)
self.updateParam()
strTitle = "SLALOM - updating..."
self.plot[0].set_title(strTitle, fontsize=self.fontsize)
self.actionbutton = self.btnStart
self.setRunning(threadrunning = True, fromthread = False)
self.thread = OptimizerThread(id=1, func=self.updateDataThread)
self.thread.start()
self.threadfinish = self.updateData
self.onThreadMonitor()
# end if
# end onUpdateData
def updateTitle(self, dataUpdated=False):
strTitle = "SLALOM"
remoteMon = self.remoteHostEnabled and (self.remoteHost is not None) and ("@" in self.remoteHost)
strTitleM = strTitle + " - "
if self.startTime > 0:
strTitleM += "started @ " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(self.startTime)))
# end if
if self.dataFilename is None:
self.plot[0].set_title(strTitle, fontsize=self.fontsize)
self.root.wm_title(strTitleM)
return
# end if
try:
iii = self.dataFilename.find("/output/")
if (iii == -1):
iii = self.dataFilename.find("\\output\\")
# end if
if (iii != -1):
strTitleM += " - " + self.dataFilename[iii+8:]
# end if
except:
pass
self.root.wm_title(strTitleM)
isStopped = False
bStopped = self.isStopped()
if bStopped:
strTitle += " - stopped @ "
strTitle += self.updateProgress.stoppedtime if (self.updateProgress.stoppedtime is not None) else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
isStopped = True
else:
bStopping = self.isStopping()
if bStopping:
self.updateProgress.stopping += 1
strTitle += " - being stopped since "
strTitle += self.updateProgress.stoppingtime if (self.updateProgress.stoppingtime is not None) else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
isStopped = (self.updateProgress.stopping > self.paramCount)
# end if
# end if
if (isStopped == False) and (bStopping == False):
updateTime = None
errMessage = None
try:
if remoteMon:
STDDEVNULL = open(os.devnull, 'w')
sshT = subprocess.Popen(['ssh', self.remoteHost, "stat", "--printf=%Y", self.dataFilename], stderr=STDDEVNULL, stdout=subprocess.PIPE)
fileT = sshT.stdout
updateTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(fileT.readline())))
# end for
else:
updateTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(self.dataFilename)))
# end if
strTitle += " - updated @ "
strTitle += updateTime if (updateTime is not None) else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
except Exception as excT:
strTitle += " - file not found"
self.updateProgress.waiting += 1
isStopped = (self.updateProgress.waiting >= 3)
pass
# end try
# end if
self.plot[0].set_title(strTitle, fontsize=self.fontsize)
try:
strLabelX = ""
if ((self.updateCount >= 1) and (self.updateDelayMin >= self.delayMin) and (self.updateDelayMin <= self.delayMax)
and (self.updateDelayMax >= self.updateDelayMin)):
strLabelX = ("Delay: min = " + slalomCore.printTime(float(self.updateDelayMin), short=True)
+ " ; max = " + slalomCore.printTime(float(self.updateDelayMax), short=True)
+ " ; mean = " + slalomCore.printTime(float(self.updateDelayMean), short=True))
# end if
self.plot[self.count - 1].set_xlabel(strLabelX, fontsize=self.fontsize)
except:
pass
# end try
return isStopped
# end updateTitle
def onAbout(self):
tkMessageBox.showinfo("SLALOM",
("SLALOM - Open-Source Solar Cell Multivariate Optimizer\n" +
"Copyright(C) 2012-2019 Sidi OULD SAAD HAMADY (1,2,*), Nicolas FRESSENGEAS (1,2).\n" +
"All rights reserved.\n" +
"(1) Université de Lorraine, LMOPS, Metz, F-57070, France\n" +
"(2) LMOPS, CentraleSupélec, Université Paris-Saclay, Metz, F-57070, France\n" +
"(*) sidi.hamady@univ-lorraine.fr\n" +
slalomVersion + "\n" +
"The user manual is in the Guide directory\n" +
"Cite as: S Ould Saad Hamady and N Fressengeas, EPJ Photovoltaics, 9:13, 2018\n" +
"See Copyright Notice in COPYRIGHT"),
parent=self.root)
# end onAbout
def onClose(self):
if not tkMessageBox.askyesno("SLALOM", "Close Monitor?", default=tkMessageBox.NO, parent=self.root):
return
# end if
# save the last used data filename and ssh host
try:
self.updateParam()
if self.dataFilename:
fileT = open("Settings/slalomMonitor.params", "w")
fileT.write("dataFilename = " + self.dataFilename)
if (self.remoteHost is not None) and ("@" in self.remoteHost):
fileT.write("\nremoteHost = " + self.remoteHost)
fileT.write("\nremoteHostEnabled = 1" if self.remoteHostEnabled else "\nremoteHostEnabled = 0")
# end if
if self.updateDelay and (self.updateDelay >= self.delayMin) and (self.updateDelay <= self.delayMax):
fileT.write("\nupdateDelay = " + str(self.updateDelay))
fileT.write("\nupdateDelayAuto = 1" if self.updateDelayAuto else "\nupdateDelayAuto = 0")
# end if
fileT.close()
# end if
except:
pass
try:
if self.viewer is not None:
self.viewer.destroy()
self.viewer = None
# end if
for report in self.report:
if report is not None:
report.destroy()
report = None
# end if
# end for
self.root.quit()
self.root.destroy()
except:
pass
# end try
# end onClose
# end slalomWindow class