# -*- 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: slalomDeviceGui.py # Type: Class # Use: The slalomDeviceGui class is used by the SLALOM module. # slalomDeviceGui provides a useful interface to select one of the... # ...predefined devices. # slalomDeviceGui uses tkinter that is already installed on the client... # (this is generally the case, except for some CentOS or RedHat machines) # ------------------------------------------------------------------------------------------------------ from slalomCore import * from slalomDevice import * import re TkFound = False try: 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 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 # end try try: class slalomDeviceGui(object): def __init__(self, device, remoteDir, remoteHost, optimType, minimizeMethod, *args, **kwargs): self.__version__ = slalomVersion self.device = device self.device.deviceType = 'User' self.parcount = 6 self.rowcount = 8 self.remoteDir = remoteDir self.remoteHost = remoteHost self.optimType = optimType self.minimizeMethod = minimizeMethod self.validated = False # end __init__ def show(self): self.root = Tk.Tk() self.root.withdraw() self.root.wm_title("SLALOM - Device Parameters") self.root.protocol("WM_DELETE_WINDOW", self.onClose) self.frame = Tk.Frame(self.root) self.frame.pack(expand=1, fill="both", padx=10, pady=10) irow = 0 ipadx = 5 ipady = 5 validateDirname = (self.frame.register(self.onEntryValidateDirname), '%P') validateFilename = (self.frame.register(self.onEntryValidateFilename), '%P') validateHostname = (self.frame.register(self.onEntryValidateHostname), '%P') Tk.Label(self.frame, text='Current Dir:', justify=Tk.RIGHT).grid(row=irow, column=0, sticky=Tk.E, padx=ipadx, pady=ipady) self.currentDirWidget = Tk.Entry(self.frame, validate="key", vcmd=validateDirname) self.currentDirWidget.grid(row=irow, column=1, columnspan=4, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) Tk.Label(self.frame, text='Input file:', justify=Tk.RIGHT).grid(row=irow, column=5, sticky=Tk.E, padx=ipadx, pady=ipady) self.inputFilenameWidget = Tk.Entry(self.frame, validate="key", vcmd=validateFilename) self.inputFilenameWidget.grid(row=irow, column=6, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) irow += 1 Tk.Label(self.frame, text='Remote Dir:', justify=Tk.RIGHT).grid(row=irow, column=0, sticky=Tk.E, padx=ipadx, pady=ipady) self.remoteDirWidget = Tk.Entry(self.frame, validate="key", vcmd=validateDirname) self.remoteDirWidget.grid(row=irow, column=1, columnspan=4, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) Tk.Label(self.frame, text='SSH host:', justify=Tk.RIGHT).grid(row=irow, column=5, sticky=Tk.E, padx=ipadx, pady=ipady) self.remoteHostWidget = Tk.Entry(self.frame, validate="key", vcmd=validateHostname) self.remoteHostWidget.grid(row=irow, column=6, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) irow += 1 Tk.Label(self.frame, text='Device type:', justify=Tk.RIGHT).grid(row=irow, column=0, sticky=Tk.E, padx=ipadx, pady=ipady) validateParam = (self.frame.register(self.onEntryValidateParam), '%P') self.deviceTypeWidget = Tk.Entry(self.frame, validate="key", vcmd=validateParam) self.deviceTypeWidget.grid(row=irow, column=1, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) validateOptim = (self.frame.register(self.onEntryValidateOptim), '%P') validateMethod = (self.frame.register(self.onEntryValidateMethod), '%P') Tk.Label(self.frame, text='Optimization:', justify=Tk.RIGHT).grid(row=irow, column=3, sticky=Tk.E, padx=ipadx, pady=ipady) self.optimWidget = Tk.Entry(self.frame, validate="key", vcmd=validateOptim) self.optimWidget.grid(row=irow, column=4, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) Tk.Label(self.frame, text='Method:', justify=Tk.RIGHT).grid(row=irow, column=5, sticky=Tk.E, padx=ipadx, pady=ipady) self.methodWidget = Tk.Entry(self.frame, validate="key", vcmd=validateMethod) self.methodWidget.grid(row=irow, column=6, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) irow += 1 Tk.Label(self.frame, text='Models:', justify=Tk.RIGHT).grid(row=irow, column=0, sticky=Tk.E, padx=ipadx, pady=ipady) self.modelWidget = list() for ii in range(0, 2): for jj in range(0, self.parcount): self.modelWidget.append(Tk.Entry(self.frame, validate="key", vcmd=validateFilename)) self.modelWidget[(ii * self.parcount) + jj].grid(row=irow, column=1+jj, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) # end for irow += 1 # end for self.paramWidget = [ list(), list(), list(), list(), list(), list(), list(), list() ] self.label = ['Parameter:', 'Format:', 'Norm:', 'Start:', 'End:', 'Init:', 'Points:', 'LogScale:'] self.type = ['string', 'format', 'float', 'float', 'float', 'float', 'int', 'bool'] self.deviceparam = [ self.device.paramName, self.device.paramFormat, self.device.paramNorm, self.device.paramStart, self.device.paramEnd, self.device.paramInit, self.device.paramPoints, self.device.paramLogscale ] validateFormat = (self.frame.register(self.onEntryValidateFormat), '%P') validateFloat = (self.frame.register(self.onEntryValidateFloat), '%P') validateInteger = (self.frame.register(self.onEntryValidateInteger), '%P') validateBoolean = (self.frame.register(self.onEntryValidateBoolean), '%P') self.paramvalidator = [validateParam, validateFormat, validateFloat, validateFloat, validateFloat, validateFloat, validateInteger, validateBoolean] for ii in range(0, self.rowcount): Tk.Label(self.frame, text=self.label[ii], justify=Tk.RIGHT).grid(row=irow+ii, column=0, sticky=Tk.E, padx=ipadx, pady=ipady) for jj in range(0, self.parcount): self.paramWidget[ii].append(Tk.Entry(self.frame, validate="key", vcmd=self.paramvalidator[ii]) if (self.paramvalidator[ii] is not None) else Tk.Entry(self.frame)) self.paramWidget[ii][jj].grid(row=irow+ii, column=1+jj, padx=ipadx, pady=ipady) # end for # end for irow += self.rowcount ttk.Button(self.frame, text="Optimize", command=self.onOptimize).grid(row=irow, column=self.parcount-1, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) ttk.Button(self.frame, text="Cancel", command=self.onCancel).grid(row=irow, column=self.parcount, sticky=Tk.W+Tk.E, padx=ipadx, pady=ipady) self.root.deiconify() if (os.name == "nt"): self.root.iconbitmap(r'Images/slalomg.ico') else: iconSlalom = Tk.PhotoImage(file='Images/slalomg.gif') self.root.tk.call('wm', 'iconphoto', self.root._w, iconSlalom) # end if self.updateGui() self.root.mainloop() # end __init__ def onEntryValidateDirname(self, sp): try: if (not sp) or (len(sp) <= 255): return True # end if return False except: return True # end try # end onEntryValidateDirname def onEntryValidateFilename(self, sp): try: if (not sp) or (len(sp) <= 63): return True # end if return False except: return True # end try # end onEntryValidateFilename def onEntryValidateHostname(self, sp): try: if (not sp) or (len(sp) <= 63): return True # end if return False except: return True # end try # end onEntryValidateHostname def onEntryValidateParam(self, sp): try: if (not sp) or ((len(sp) <= 31) and (re.match(r'^\w+$', sp))): return True # end if return False except: return True # end try # end onEntryValidateParam def onEntryValidateFormat(self, sp): try: if (not sp) or ((len(sp) <= 7) and (re.match(r'^[0-9\%\.efgd]*$', sp))): return True # end if return False except: return True # end try # end onEntryValidateFormat def onEntryValidateFloat(self, sp): try: if (not sp) or ((len(sp) <= 15) and (re.match(r'[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?', sp))): return True # end if return False except: return True # end try # end onEntryValidateFloat def onEntryValidateInteger(self, sp): try: if (not sp) or ((len(sp) <= 3) and (re.match('^\d+$', sp))): return True # end if return False except: return True # end try # end onEntryValidateInteger def onEntryValidateBoolean(self, sp): try: if (not sp) or ((len(sp) <= 5) and ('True'.startswith(sp) or 'False'.startswith(sp))): return True # end if return False except: return True # end try # end onEntryValidateBoolean def onEntryValidateOptim(self, sp): try: if (not sp) or ((len(sp) <= 5) and ('Brute'.startswith(sp) or 'Snap'.startswith(sp) or 'Optim'.startswith(sp))): return True # end if return False except: return True # end try # end onEntryValidateOptim def onEntryValidateMethod(self, sp): try: if (not sp) or ((len(sp) <= 8) and ('L-BFGS-B'.startswith(sp) or 'SLSQP'.startswith(sp))): return True # end if return False except: return True # end try # end onEntryValidateOptim def updateGui(self): try: self.currentDirWidget.delete(0, Tk.END) self.currentDirWidget.insert(0, self.device.currentDir) self.inputFilenameWidget.delete(0, Tk.END) self.inputFilenameWidget.insert(0, self.device.inputFilename) self.remoteDirWidget.delete(0, Tk.END) self.remoteDirWidget.insert(0, self.remoteDir) self.remoteHostWidget.delete(0, Tk.END) if self.remoteHost is not None: self.remoteHostWidget.insert(0, self.remoteHost) # end if self.deviceTypeWidget.delete(0, Tk.END) self.deviceTypeWidget.insert(0, self.device.deviceType) self.optimWidget.delete(0, Tk.END) self.optimWidget.insert(0, self.optimType) self.methodWidget.delete(0, Tk.END) self.methodWidget.insert(0, self.minimizeMethod) if self.device.modelFilename: iCnt = len(self.device.modelFilename) for ii in range(0, 2): for jj in range(0, self.parcount): self.modelWidget[(ii * self.parcount) + jj].delete(0, Tk.END) if (((ii * self.parcount) + jj) < iCnt): self.modelWidget[(ii * self.parcount) + jj].insert(0, self.device.modelFilename[jj]) # end if # end for # end for # end if paramCount = len(self.deviceparam[0]) for ii in range(0, self.rowcount): for jj in range(0, self.parcount): self.paramWidget[ii][jj].delete(0, Tk.END) self.paramWidget[ii][jj].insert(0, str(self.deviceparam[ii][jj]) if (jj < paramCount) else '') # end for # end for except: pass # end try # end updateGui def updateDevice(self): try: self.device.currentDir = self.currentDirWidget.get() self.device.inputFilename = self.inputFilenameWidget.get() self.remoteDir = self.remoteDirWidget.get() self.remoteHost = self.remoteHostWidget.get() if len(self.remoteHost) < 3: self.remoteHost = None # end if self.device.deviceType = self.deviceTypeWidget.get() self.optimType = self.optimWidget.get() self.minimizeMethod = self.methodWidget.get() bFlag = False if self.device.modelFilename: iCnt = len(self.device.modelFilename) for ii in range(0, 2): for jj in range(0, self.parcount): modelT = self.modelWidget[(ii * self.parcount) + jj].get() if (not modelT) or (len(modelT) < 3): break # end if if (((ii * self.parcount) + jj) < iCnt): self.device.modelFilename[jj] = modelT else: if modelT not in self.device.modelFilename: self.device.modelFilename.append(modelT) # end if # end if # end for # end for # end if iCnt = len(self.deviceparam[0]) for ii in range(0, self.rowcount): for jj in range(0, self.parcount): paramT = self.paramWidget[ii][jj].get() if (not paramT) or (len(paramT) < 1): break # end if if self.type[ii] == 'string' or self.type[ii] == 'format': if (jj < iCnt): self.deviceparam[ii][jj] = paramT else: self.deviceparam[ii].append(paramT) # end if elif self.type[ii] == 'float': if (jj < iCnt): self.deviceparam[ii][jj] = float(paramT) else: self.deviceparam[ii] = np.append(self.deviceparam[ii], float(paramT)) # end if elif self.type[ii] == 'int': if (jj < iCnt): self.deviceparam[ii][jj] = int(paramT) else: self.deviceparam[ii].append(int(paramT)) # end if elif self.type[ii] == 'bool': if (jj < iCnt): self.deviceparam[ii][jj] = True if (paramT == 'True') else False else: self.deviceparam[ii].append(True if (paramT == 'True') else False) # end if # end if # end for # end for (self.validated, message) = self.device.validate() if not self.validated: tkMessageBox.showwarning("SLALOM", message, parent=self.root) # end if except Exception as excT: tkMessageBox.showwarning("SLALOM", "Device data not valid: " + str(excT), parent=self.root) pass # end try # end updateDevice def onOptimize(self): self.updateDevice() self.onClose() # end onOptimize def onCancel(self): self.validated = False self.onClose() # end onCancel def onClose(self): if self.root is not None: self.root.quit() self.root.destroy() self.root = None # end if # end onClose # end onDevicelistBox # end slalomDeviceGui except Exception as excT: excType, excObj, excTb = sys.exc_info() excFile = os.path.split(excTb.tb_frame.f_code.co_filename)[1] strErr = "\n! cannot initialize GUI:\n %s\n in %s (line %d)\n" % (str(excT), excFile, excTb.tb_lineno) print(strErr) os._exit(1) # never reached pass # end try