class Loop(ArduinoErrorProofedRoutine): activeLoop = None #reference active loop so that if a protocol is cancelled while a loop is running, we can remove "iterations:" label. # Loop.__init__ # Input: # master - a Tkinter frame that the Loop will be nested in # Output: # None def __init__(self, master): # Loop frames hold the routine frame with steps and sub-loops, as well as a bar to specify # iterations self.box = LabelFrame(master, text="Loop") self.iterations = LabelEntry(self.box, 0, 0, "Number of iterations: ") self.currIter = Label( self.box, text="") #Displays current iteration while running self.currIter.grid(row=0, column=2) self.steptype = "Loop" #also alows Loops to be saved and loaded as if steps. super(Loop, self).__init__(self.box) # Loop.draw: # Inputs: # _row - the row of the Tkinter parent frame in which the loop will be drawn with the grid manager # _cos - the column of the Tkinter parent frame in which the loop will be drawn with the grid manager # Output: None def draw(self, _row, _col): self.box.grid(row=_row, column=_col, sticky=W) super(Loop, self).draw(1, 0) # Loop.saveEntries: Called before running or saving a protocol. Checks to make sure all entries are valid (recursively # for nested loops) and saves the values so the protocol will not crash even if the user changes values during a run. # Input: # iters - a tuple containing the number of iterations each outer loop will be iterated over. The immediate outer # loop is first, the second outer loop is second, and so on. This is used for recursive error checking. # Output: None def saveEntries(self, iters=None): saveIter = self.iterations.get() if saveIter == "": raise ValueError("Error: Unfilled number of iterations in loop.") try: self.saveIter = int(saveIter) except: raise ValueError(saveIter + " is not a valid n") if self.saveIter < 1: raise ValueError( "You must loop over a postitive integer number of iterations.") if self.steps == []: raise Exception("You cannot run a loop with no steps!") for item in self.steps: if iters: _iters = tuple([self.saveIter] + list(iters)) else: _iters = (self.saveIter, ) try: item.saveEntries(iters=_iters) if item.box.cget('bg') == "yellow": try: item.box.config(bg='SystemButtonFace') except: item.box.config(bg='gray') except Exception as E: if hasattr( item, 'activeLoop' ): # then item is a loop. Only turn yellow if iteration error. if E.message in ( "Error: Unfilled number of iterations in loop.", "You must loop over a postitive integer number of iterations.", "You cannot run a loop with no steps!" ) or " is not a valid n" in E.message: item.box.config(bg='yellow') # turn yellow else: # turn yellow item.box.config(bg='yellow') # in anycase, raise the error again. raise E # Loop.save: Called recursively when Protocol.save is called. Returns information necessary to reconstruct loop to calling object. # Inputs: # None # Outputs: # savedLoop - A list of information necessary to reconstruct the loop to be saved in a JSON file. def save(self): savedLoop = super(Loop, self).save() print(type(self.stepImplementation)) if hasattr( self.stepImplementation, '__iter__' ): # if more than one step can be used in the protocol (self.stepImplementation is an iterable) stepImp = [s.__name__ for s in self.stepImplementation] savedLoop = ["Loop", stepImp, self.iterations.get()] + savedLoop else: #Otherwise only one steptype is used in a protocol savedLoop = [ "Loop", self.stepImplementation.__name__, self.iterations.get() ] + savedLoop return savedLoop # Called recursively when Protocol.loadProtocol is called. Reconstructs loop saved by Loop.save # Inputs: # savedLoop - list of information to reconstruct saved loop object, generated by Loop.saved and retrieved from # a JSON file. # Outputs: # None def load(self, savedLoop): stepImp = savedLoop[0] self.stepImplementation = [] if type(stepImp) == list: for s in stepImp: self.checkIfHasIllegalCharacters(s) self.stepImplementation.append(eval(s)) else: self.checkIfHasIllegalCharacters(stepImp) self.stepImplementation = eval(stepImp) self.iterations.insert(0, savedLoop[1]) self.iterations.saved = savedLoop[1] super(Loop, self).load(savedLoop[2:]) # Loop.run - executes the loop # Inputs: # iter - a tuple containing the current iteration values of outer loops. The current loop is at bin 0, the first # outer Loop is at bin 1, ect. # Outputs: # Returns "Error" if there is an error while running. This propogates up through the recursive structure to # cancel the run. def run(self, iter=None): Loop.activeLoop = self #this marker allows steps to clean up iteration counter if the protocol is canceled for i in range(1, self.saveIter + 1): self.currIter.config(text="Iteration: " + str(i)) if not iter: iter0 = (i, ) else: iter0 = (i, ) + iter if super(Loop, self).run( iter=iter0 ) == "Error": #run through one iteration of the loop return "Error" self.currIter.config(text="") Loop.activeLoop = None