class SetTempVTICommand(): def __init__(self): self.regexp_str="Set VTI Loop (\d+) to "+Regexfloat+" K(?: @ "+Regexfloat+" K/min)?" self.label="Set VTI Loop X to X K (@ X K/min)" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() ##get the loop index and setpoint value loop=float(values[1]) setpoint=float(values[2]) #if a ramp rate was also provided, go to this setpoint at this rate VTI=main.instr_9 if values[3]!='': rate=float(values[3]) with main.reserved_access_to_instr: VTI.conf_ramp(loop,rate,'on') VTI.set_setpoint(loop,setpoint) else: with main.reserved_access_to_instr: VTI.switch_ramp(loop,'off') VTI.set_setpoint(loop,setpoint) #go to next line of macro after stdwtime (give some time to process other events) self.next_move=1 self.wait_time=500
class WaitForTPPMSStableCommand(): def __init__(self): self.regexp_str="Wait(?: at most "+Regexfloat+" secs)? for PPMS Temp to reach "+Regexfloat+" K" self.label="Wait(at most X secs) for PPMS Temp to reach X K" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() #Wait for the temperature measurement to be stable and #within 5% of specified temperature #(and lock only when accessing the instrument) with main.reserved_access_to_instr: Terror, T, status = main.ppms.get_temperature() if self.waiting==False: self.waiting=True self.waiting_start=time.clock() if status == 'Stable' and abs(T-float(values[2]))<abs(float(values[2])*0.05): #Temperature is stable, go to next line of macro self.waiting=False self.next_move=1 self.wait_time=500 elif values[1]!='' and time.clock()-self.waiting_start>float(values[1]): #time limit is reached, go to next line of macro self.waiting=False self.next_move=1 self.wait_time=500 else: #wait 10s and check again self.next_move=0 self.wait_time=10000
def __autoIncFilename(self): """ Private method to auto-increment the file name. """ # Extract the file name name = os.path.basename(self.__filename) # If the name contains a number, then increment it. numSearch = QRegExp("(^|[^\\d])(\\d+)") # We want to match as far left as possible, and when the number is # at the start of the name. # Does it have a number? start = numSearch.lastIndexIn(name) if start != -1: # It has a number, increment it. start = numSearch.pos(2) # Only the second group is of interest. numAsStr = numSearch.capturedTexts()[2] number = "{0:0{width}d}".format( int(numAsStr) + 1, width=len(numAsStr)) name = name[:start] + number + name[start + len(numAsStr):] else: # no number start = name.rfind('.') if start != -1: # has a '.' somewhere, e.g. it has an extension name = name[:start] + '1' + name[start:] else: # no extension, just tack it on to the end name += '1' self.__filename = os.path.join(os.path.dirname(self.__filename), name) self.__updateCaption()
class SetPPMSFieldCommand(): def __init__(self): self.regexp_str="Set PPMS Field to "+Regexfloat+" Oe @ "+Regexfloat+" Oe/s (Linear|NoOvershoot|Oscillate)? (Persistent|Driven)?" self.label="Set PPMS Field to X Oe @ X Oe/s (Linear|NoOvershoot|Oscillate) (Persistent|Driven)" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() ##get the loop index and setpoint value setpoint=float(values[1]) rate=float(values[2]) #if an approach was also provided, go to this setpoint at this rate approach = 'Linear' mode = 'Persistent' if values[3]!='':approach = values[3] if values[4]!='':mode = values[4] with main.reserved_access_to_instr: main.ppms.set_field(setpoint,rate,approach,mode) #go to next line of macro after stdwtime (give some time to process other events) self.next_move=1 self.wait_time=100
class WaitForHStableCommand(): def __init__(self): self.regexp_str="Wait(?: at most "+Regexfloat+" secs)? for magnet (X|Y|Z) to finish ramping" self.label="Wait (at most X secs) for magnet (X|Y|Z) to finish ramping" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() #wait for field to stabilize (and lock only when accessing the instrument) with main.reserved_access_to_instr: magnet=eval("main.magnet_"+values[2]) stat=magnet.query_status() if self.waiting==False: self.waiting=True self.waiting_start=time.clock() if not(stat=='RAMPING to programmed current/field') or (values[1]!='' and time.clock()-self.waiting_start>float(values[1])): #ramping is finished or time limit is reached, go to next line of macro self.waiting=False self.next_move=1 self.wait_time=500 else: #wait 5s and check again self.next_move=0 self.wait_time=5000
def __autoIncFilename(self): """ Private method to auto-increment the file name. """ # Extract the file name name = os.path.basename(self.__filename) # If the name contains a number, then increment it. numSearch = QRegExp("(^|[^\\d])(\\d+)") # We want to match as far left as possible, and when the number is # at the start of the name. # Does it have a number? start = numSearch.lastIndexIn(name) if start != -1: # It has a number, increment it. start = numSearch.pos(2) # Only the second group is of interest. numAsStr = numSearch.capturedTexts()[2] number = "{0:0{width}d}".format(int(numAsStr) + 1, width=len(numAsStr)) name = name[:start] + number + name[start + len(numAsStr):] else: # no number start = name.rfind('.') if start != -1: # has a '.' somewhere, e.g. it has an extension name = name[:start] + '1' + name[start:] else: # no extension, just tack it on to the end name += '1' self.__filename = os.path.join(os.path.dirname(self.__filename), name) self.__updateCaption()
class AngleCommand(): def __init__(self): #Text that will appear in the list of commands on the right side of the Macro editor self.label="Set start|stop|step angle to FLOAT" #Regular expression which may or may not catch parameters self.regexp_str="Set (start|stop|step) angle to "+Regexfloat #Add this to the beginning and end of the regular expression #so that whitespaces before and after will not prevent the regex from matching self.regexp_str="^ *"+self.regexp_str+" *$" #instantiate regex self.regexp=QRegExp(self.regexp_str) def run(self,main): #what to do when the regex defined just above in __init__ #has matched the current line of the Macro #get the captured parameters values=self.regexp.capturedTexts() #set the corresponding angle box if values[1] in ['stop','step','start']: anglebox=eval("main.ui.angle"+values[1]) anglebox.setValue(float(values[2])) #Finally go to next line of macro... self.next_move=1 #...after 10 milliseconds self.wait_time=10
class WaitCommand(): def __init__(self): self.regexp_str="Wait "+Regexfloat+" secs" self.label="Wait X secs" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #self.values[1]!='' #go to next line of macro after 'values[1]' seconds self.next_move=1 self.wait_time=float(values[1])*1000
class SetSaveFileCommand(): def __init__(self): self.regexp_str="Set Save file to "+Regexsimplefile+"" self.label="Set Save file to X" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() main.ui.savefile_txt_input.setText(values[1]) #new file name is set, go to next line of macro self.next_move=1 self.wait_time=500
class SetVTIHeaterCommand(): def __init__(self): self.regexp_str="Set VTI heater range to (\d)" self.label="Set VTI heater range to DIGIT" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the heater range with main.reserved_access_to_instr: main.instr_9.set_heater_range(int(values[1])) #go to next line of macro self.next_move=1 self.wait_time=500
class SetLR700VCommand(): def __init__(self): self.regexp_str="Set LR700 excitation to "+Regexfloat+" V" self.label="Set LR700 excitation to FLOAT V" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the voltage V_source_setpoint=eval("main.ui.V_LR700") V_source_setpoint.setValue(float(values[1])*1e6) #go to next line of macro self.next_move=1 self.wait_time=500
class SetLR700IntegRateCommand(): def __init__(self): self.regexp_str="Set LR700 integration rate to "+Regexfloat+" secs" self.label="Set LR700 integration rate to FLOAT secs" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the voltage setpoint=eval("main.ui.integ_rate_LR700") setpoint.setValue(float(values[1])) #go to next line of macro self.next_move=1 self.wait_time=500
class WaitForEpoch(): def __init__(self): self.regexp_str="Wait for Epoch \+ "+Regexfloat+" secs" self.label="Wait for Epoch + X secs" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #test if the current time is greater than the time provided in the macro (in Epoch seconds) if float(values[1])>time.time(): self.next_move=0 self.wait_time=5000 else: self.next_move=1 self.wait_time=100
class StartMeasureCommand(): def __init__(self): #type name_of_program() to start it self.regexp_str="Start "+Regexprogramfile+"\((.*)\)" self.label="Start PROGRAM()" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() main.ui.measMode.setCurrentIndex(main.ui.measMode.findText(values[1])) #start measurements main.start_measurements() #go to next line of macro self.next_move=1 self.wait_time=500
class SetVCommand(): def __init__(self): self.regexp_str="Set voltage (\d) to "+Regexfloat+" V" self.label="Set voltage DIGIT to FLOAT V" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the voltage if values[1] in ['1','2','3']: V_source_setpoint=eval("main.ui.V_setpoint_"+values[1]) V_source_setpoint.setValue(float(values[2])) #go to next line of macro self.next_move=1 self.wait_time=500
class SetUICommand(): def __init__(self,main): # print "|".join(dir(main.ui)) self.regexp_str="Set UI ("+"|".join(dir(main.ui))+") to "+Regexfloat self.label="Set UI PROPERTY to FLOAT" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the property prop = eval("main.ui."+values[1]) prop.setValue(float(values[2])) #go to next line of macro self.next_move=1 self.wait_time=10
class SetICommand(): def __init__(self): self.regexp_str="Set current (\d) to "+Regexfloat+" A" self.label="Set current DIGIT to FLOAT A" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() #set the current if values[1]=='1': main.ui.I_source_setpoint.setValue(float(values[2])*1e6) elif values[1] in ['2','3']: I_source_setpoint=eval("main.ui.I_source_setpoint_"+values[1]) I_source_setpoint.setValue(float(values[2])*1e6) #go to next line of macro self.next_move=1 self.wait_time=10
class EmailCommand(): def __init__(self): self.regexp_str="E-mail message: "+Regexsimplefile self.label="E-mail message: MESSAGE" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() msg=values[1] try: email_alert=Email_alert(message=msg,address=main.ui.email_address.text(),subject="Message from PyGMI",smtpadd=main.mainconf['smtpadd'],login=main.mainconf['login'],mdp=main.mainconf['mdp'],smtpport=main.mainconf['smtpport']) print("message successfully sent by e-mail") except: print("Exception: message could not be sent by e-mail") #go to next line of macro self.next_move=1 self.wait_time=500
class SetPersistFieldCommand(): def __init__(self): self.regexp_str="Set persistent field in magnet (X|Y|Z) to "+Regexfloat+" T" self.label="Set persistent field in magnet X|Y|Z to x T" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() ##set the loop index and setpoint value magnet_setpoint=eval("main.ui.B_"+values[1]+"_setpoint") magnet_setpoint.setValue(float(values[2])) time.sleep(1) main.ui.measMode.setCurrentIndex(main.ui.measMode.findText("Change_persistent_"+values[1]+"_field")) #start measurements main.start_measurements() #go to next line of macro after stdwtime (give some time to process other events) self.next_move=1 self.wait_time=500
class EmailDirCommand(): def __init__(self): self.regexp_str="E-mail directory: "+Regexsimplefile self.label="E-mail directory: DIRECTORY" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() dir_path=values[1] try: message="Hi,\n\nat your request, here is the directory: "+os.path.normpath(os.path.abspath(dir_path.strip()))+"\n\n PyGMI" email_alert=Email_directory(directory=dir_path.strip(),address=main.ui.email_address.text(),message=message,subject="Data directory from PyGMI",smtpadd=main.mainconf['smtpadd'],login=main.mainconf['login'],mdp=main.mainconf['mdp'],smtpport=main.mainconf['smtpport']) print("directory successfully sent by e-mail") except: print("Exception: directory could not be sent by e-mail") #go to next line of macro self.next_move=1 self.wait_time=500
class WaitForTStableCommand(): def __init__(self): self.regexp_str="Wait(?: at most "+Regexfloat+" secs)? for channel (\w) to reach "+Regexfloat+" \+/\- "+Regexfloat+" K" self.label="Wait(at most X secs) for channel X to reach X +/- X K" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() #wait for temperature to stabilize (and lock only when accessing the instrument) with main.reserved_access_to_instr: T=main.temp_controller.query_temp(values[2]) if self.waiting==False: self.waitTcounter=0 self.waiting=True self.waiting_start=time.clock() if values[1]!='' and time.clock()-self.waiting_start>float(values[1]): #time limit is reached, go to next line of macro self.waiting=False self.next_move=1 self.wait_time=500 elif abs(T-float(values[3]))<float(values[4]): #Wait for the temperature measurement to be ten times #within the specified limits, in a row, to consider it stable if self.waitTcounter<10: #count one, wait 0.5 secs and measure T again self.waitTcounter+=1 self.next_move=0 self.wait_time=500 else: #Temperature is stable, go to next line of macro self.waitTcounter=0 self.waiting=False self.next_move=1 self.wait_time=500 else: #wait 10s and check again, but reset the stable temperature counter self.waitTcounter=0 self.next_move=0 self.wait_time=10000
class SetFieldCommand(): def __init__(self): self.regexp_str="Set Field of magnet (X|Y|Z) to "+Regexfloat+" G(?: @ "+Regexfloat+" G/s)?" self.label="Set Field of magnet X|Y|Z to X G (@ X G/s)" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) def run(self,main): values=self.regexp.capturedTexts() with main.reserved_access_to_instr: magnet=eval("main.magnet_"+values[1]) if values[3]!='': #if a ramp rate was also provided, go to this setpoint at this rate magnet.program_ramp_rate_in_Gauss_per_second(float(values[3])) #print "Setting Field to ", magnet.program_field_in_kG(float(values[2])*1e-3) magnet.ramp_to_programmed_field() #go to next line of macro self.next_move=1 self.wait_time=500
class WaitForMeasureCommand(): def __init__(self): self.regexp_str="Wait(?: at most "+Regexfloat+" secs)? for measurements completion" self.label="Wait(at most X secs) for measurements completion" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() if self.waiting==False: self.waiting=True self.waiting_start=time.clock() if not(main.measurements_thread.isAlive()) or (values[1]!='' and time.clock()-self.waiting_start>float(values[1])): #Measurements are complete or time limit was reached, go to next line of macro self.waiting=False self.next_move=1 self.wait_time=500 else: #wait 1s and check again if measurements are complete self.next_move=0 self.wait_time=1000
class SetPPMSTempCommand(): def __init__(self): self.regexp_str="Set PPMS Temperature to "+Regexfloat+" K @ "+Regexfloat+" K/min (FastSettle|NoOvershoot)?" self.label="Set PPMS Temperature to X K @ X K/min (FastSettle/NoOvershoot)" self.regexp_str="^ *"+self.regexp_str+" *$" #so that the same string with heading and trailing whitespaces also matches self.regexp=QRegExp(self.regexp_str) self.waiting=False self.waiting_start=0 def run(self,main): values=self.regexp.capturedTexts() ##get the loop index and setpoint value setpoint=float(values[1]) rate=float(values[2]) #if an approach was also provided, go to this setpoint at this rate approach = 'FastSettle' if values[3]!='':approach = values[3] with main.reserved_access_to_instr: main.ppms.set_temperature(setpoint,rate,approach) #go to next line of macro after stdwtime (give some time to process other events) self.next_move=1 self.wait_time=100
class CallTraceViewer(QWidget, Ui_CallTraceViewer): """ Class implementing the Call Trace viewer widget. @signal sourceFile(str, int) emitted to show the source of a call/return point """ sourceFile = pyqtSignal(str, int) def __init__(self, debugServer, parent=None): """ Constructor @param debugServer reference to the debug server object (DebugServer) @param parent reference to the parent widget (QWidget) """ super(CallTraceViewer, self).__init__(parent) self.setupUi(self) self.__dbs = debugServer self.startTraceButton.setIcon( UI.PixmapCache.getIcon("callTraceStart.png")) self.stopTraceButton.setIcon( UI.PixmapCache.getIcon("callTraceStop.png")) self.resizeButton.setIcon(UI.PixmapCache.getIcon("resizeColumns.png")) self.clearButton.setIcon(UI.PixmapCache.getIcon("editDelete.png")) self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSave.png")) self.__headerItem = QTreeWidgetItem( ["", self.tr("From"), self.tr("To")]) self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("callReturn.png")) self.callTrace.setHeaderItem(self.__headerItem) self.__callStack = [] self.__entryFormat = "{0}:{1} ({2})" self.__entryRe = QRegExp(r"""(.+):(\d+)\s\((.*)\)""") self.__projectMode = False self.__project = None self.__callTraceEnabled = Preferences.toBool( Preferences.Prefs.settings.value("CallTrace/Enabled", False)) if self.__callTraceEnabled: self.startTraceButton.setEnabled(False) else: self.stopTraceButton.setEnabled(False) self.__dbs.callTraceInfo.connect(self.__addCallTraceInfo) def __setCallTraceEnabled(self, enabled): """ Private slot to set the call trace enabled status. @param enabled flag indicating the new state (boolean) """ self.__dbs.setCallTraceEnabled(enabled) self.stopTraceButton.setEnabled(enabled) self.startTraceButton.setEnabled(not enabled) self.__callTraceEnabled = enabled Preferences.Prefs.settings.setValue("CallTrace/Enabled", enabled) @pyqtSlot() def on_startTraceButton_clicked(self): """ Private slot to start call tracing. """ self.__setCallTraceEnabled(True) @pyqtSlot() def on_stopTraceButton_clicked(self): """ Private slot to start call tracing. """ self.__setCallTraceEnabled(False) @pyqtSlot() def on_resizeButton_clicked(self): """ Private slot to resize the columns of the call trace to their contents. """ for column in range(self.callTrace.columnCount()): self.callTrace.resizeColumnToContents(column) @pyqtSlot() def on_clearButton_clicked(self): """ Private slot to clear the call trace. """ self.clear() @pyqtSlot() def on_saveButton_clicked(self): """ Private slot to save the call trace info to a file. """ if self.callTrace.topLevelItemCount() > 0: fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.tr("Save Call Trace Info"), "", self.tr("Text Files (*.txt);;All Files (*)"), None, E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if QFileInfo(fname).exists(): res = E5MessageBox.yesNo( self, self.tr("Save Call Trace Info"), self.tr("<p>The file <b>{0}</b> already exists." " Overwrite it?</p>").format(fname), icon=E5MessageBox.Warning) if not res: return fname = Utilities.toNativeSeparators(fname) try: f = open(fname, "w", encoding="utf-8") itm = self.callTrace.topLevelItem(0) while itm is not None: isCall = itm.data(0, Qt.UserRole) if isCall: call = "->" else: call = "<-" f.write("{0} {1} || {2}\n".format( call, itm.text(1), itm.text(2))) itm = self.callTrace.itemBelow(itm) f.close() except IOError as err: E5MessageBox.critical( self, self.tr("Error saving Call Trace Info"), self.tr("""<p>The call trace info could not""" """ be written to <b>{0}</b></p>""" """<p>Reason: {1}</p>""") .format(fname, str(err))) @pyqtSlot(QTreeWidgetItem, int) def on_callTrace_itemDoubleClicked(self, item, column): """ Private slot to open the double clicked file in an editor. @param item reference to the double clicked item (QTreeWidgetItem) @param column column that was double clicked (integer) """ if item is not None and column > 0: columnStr = item.text(column) if self.__entryRe.exactMatch(columnStr.strip()): filename, lineno, func = self.__entryRe.capturedTexts()[1:] try: lineno = int(lineno) except ValueError: # do nothing, if the line info is not an integer return if self.__projectMode: filename = self.__project.getAbsolutePath(filename) self.sourceFile.emit(filename, lineno) def clear(self): """ Public slot to clear the call trace info. """ self.callTrace.clear() self.__callStack = [] def setProjectMode(self, enabled): """ Public slot to set the call trace viewer to project mode. In project mode the call trace info is shown with project relative path names. @param enabled flag indicating to enable the project mode (boolean) """ self.__projectMode = enabled if enabled and self.__project is None: self.__project = e5App().getObject("Project") def __addCallTraceInfo(self, isCall, fromFile, fromLine, fromFunction, toFile, toLine, toFunction): """ Private method to add an entry to the call trace viewer. @param isCall flag indicating a 'call' (boolean) @param fromFile name of the originating file (string) @param fromLine line number in the originating file (string) @param fromFunction name of the originating function (string) @param toFile name of the target file (string) @param toLine line number in the target file (string) @param toFunction name of the target function (string) """ if isCall: icon = UI.PixmapCache.getIcon("forward.png") else: icon = UI.PixmapCache.getIcon("back.png") parentItem = \ self.__callStack[-1] if self.__callStack else self.callTrace if self.__projectMode: fromFile = self.__project.getRelativePath(fromFile) toFile = self.__project.getRelativePath(toFile) itm = QTreeWidgetItem( parentItem, ["", self.__entryFormat.format(fromFile, fromLine, fromFunction), self.__entryFormat.format(toFile, toLine, toFunction)]) itm.setIcon(0, icon) itm.setData(0, Qt.UserRole, isCall) itm.setExpanded(True) if isCall: self.__callStack.append(itm) else: if self.__callStack: self.__callStack.pop(-1) def isCallTraceEnabled(self): """ Public method to get the state of the call trace function. @return flag indicating the state of the call trace function (boolean) """ return self.__callTraceEnabled
class Highlighter(QSyntaxHighlighter): """ Syntax Highlighting This class defines rules, a rule consists of a QRegExp pattern and a QTextCharFormat instance. """ # Keywords KEYWORDS = [ "select", "project", "rename", "product", "njoin", "louter", "router", "fouter", "difference", "intersect", "union", "and", "or" ] def __init__(self, editor): super(Highlighter, self).__init__(editor) # Keywords format keyword_format = QTextCharFormat() keyword_format.setForeground(QColor("#222")) keyword_format.setFontWeight(QFont.Bold) # Rules self._rules = [(QRegExp("\\b" + pattern + "\\b"), keyword_format) for pattern in Highlighter.KEYWORDS] # vars var_format = QTextCharFormat() var_pattern = QRegExp("\w+\s*\:\=") var_format.setFontWeight(QFont.Bold) var_format.setForeground(QColor("#dd1144")) self._rules.append((var_pattern, var_format)) op_format = QTextCharFormat() op_pattern = QRegExp("(\\:=|\\(|\\))|=|<|>") op_format.setForeground(QColor("#222")) op_format.setFontWeight(QFont.Bold) self._rules.append((op_pattern, op_format)) # Number format number_format = QTextCharFormat() number_pattern = QRegExp(r"\b([A-Z0-9]+)(?:[ _-](\d+))?\b") number_pattern.setMinimal(True) number_format.setForeground(QColor("orange")) self._rules.append((number_pattern, number_format)) # String format string_format = QTextCharFormat() string_pattern = QRegExp("\'.*\'") string_pattern.setMinimal(True) string_format.setForeground(Qt.darkGreen) self._rules.append((string_pattern, string_format)) # Comment format comment_format = QTextCharFormat() comment_pattern = QRegExp("%[^\n]*") comment_format.setForeground(QColor("#999988")) comment_format.setFontItalic(True) self._rules.append((comment_pattern, comment_format)) # Paren self.paren = QRegExp('\(|\)') def highlightBlock(self, text): """ Reimplementation """ block_data = TextBlockData() # Paren index = self.paren.indexIn(text, 0) while index >= 0: matched_paren = str(self.paren.capturedTexts()[0]) info = ParenInfo(matched_paren, index) block_data.insert_paren_info(info) index = self.paren.indexIn(text, index + 1) self.setCurrentBlockUserData(block_data) for pattern, _format in self._rules: expression = QRegExp(pattern) index = expression.indexIn(text) while index >= 0: length = expression.matchedLength() self.setFormat(index, length, _format) index = expression.indexIn(text, index + length) self.setCurrentBlockState(0)
class CallTraceViewer(QWidget, Ui_CallTraceViewer): """ Class implementing the Call Trace viewer widget. @signal sourceFile(str, int) emitted to show the source of a call/return point """ sourceFile = pyqtSignal(str, int) def __init__(self, debugServer, parent=None): """ Constructor @param debugServer reference to the debug server object (DebugServer) @param parent reference to the parent widget (QWidget) """ super(CallTraceViewer, self).__init__(parent) self.setupUi(self) self.__dbs = debugServer self.startTraceButton.setIcon( UI.PixmapCache.getIcon("callTraceStart.png")) self.stopTraceButton.setIcon( UI.PixmapCache.getIcon("callTraceStop.png")) self.resizeButton.setIcon(UI.PixmapCache.getIcon("resizeColumns.png")) self.clearButton.setIcon(UI.PixmapCache.getIcon("editDelete.png")) self.saveButton.setIcon(UI.PixmapCache.getIcon("fileSave.png")) self.__headerItem = QTreeWidgetItem( ["", self.tr("From"), self.tr("To")]) self.__headerItem.setIcon(0, UI.PixmapCache.getIcon("callReturn.png")) self.callTrace.setHeaderItem(self.__headerItem) self.__callStack = [] self.__entryFormat = "{0}:{1} ({2})" self.__entryRe = QRegExp(r"""(.+):(\d+)\s\((.*)\)""") self.__projectMode = False self.__project = None self.__callTraceEnabled = Preferences.toBool( Preferences.Prefs.settings.value("CallTrace/Enabled", False)) if self.__callTraceEnabled: self.startTraceButton.setEnabled(False) else: self.stopTraceButton.setEnabled(False) self.__dbs.callTraceInfo.connect(self.__addCallTraceInfo) def __setCallTraceEnabled(self, enabled): """ Private slot to set the call trace enabled status. @param enabled flag indicating the new state (boolean) """ self.__dbs.setCallTraceEnabled(enabled) self.stopTraceButton.setEnabled(enabled) self.startTraceButton.setEnabled(not enabled) self.__callTraceEnabled = enabled Preferences.Prefs.settings.setValue("CallTrace/Enabled", enabled) @pyqtSlot() def on_startTraceButton_clicked(self): """ Private slot to start call tracing. """ self.__setCallTraceEnabled(True) @pyqtSlot() def on_stopTraceButton_clicked(self): """ Private slot to start call tracing. """ self.__setCallTraceEnabled(False) @pyqtSlot() def on_resizeButton_clicked(self): """ Private slot to resize the columns of the call trace to their contents. """ for column in range(self.callTrace.columnCount()): self.callTrace.resizeColumnToContents(column) @pyqtSlot() def on_clearButton_clicked(self): """ Private slot to clear the call trace. """ self.clear() @pyqtSlot() def on_saveButton_clicked(self): """ Private slot to save the call trace info to a file. """ if self.callTrace.topLevelItemCount() > 0: fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.tr("Save Call Trace Info"), "", self.tr("Text Files (*.txt);;All Files (*)"), None, E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if QFileInfo(fname).exists(): res = E5MessageBox.yesNo( self, self.tr("Save Call Trace Info"), self.tr("<p>The file <b>{0}</b> already exists." " Overwrite it?</p>").format(fname), icon=E5MessageBox.Warning) if not res: return fname = Utilities.toNativeSeparators(fname) try: f = open(fname, "w", encoding="utf-8") itm = self.callTrace.topLevelItem(0) while itm is not None: isCall = itm.data(0, Qt.UserRole) if isCall: call = "->" else: call = "<-" f.write("{0} {1} || {2}\n".format( call, itm.text(1), itm.text(2))) itm = self.callTrace.itemBelow(itm) f.close() except IOError as err: E5MessageBox.critical( self, self.tr("Error saving Call Trace Info"), self.tr("""<p>The call trace info could not""" """ be written to <b>{0}</b></p>""" """<p>Reason: {1}</p>""").format( fname, str(err))) @pyqtSlot(QTreeWidgetItem, int) def on_callTrace_itemDoubleClicked(self, item, column): """ Private slot to open the double clicked file in an editor. @param item reference to the double clicked item (QTreeWidgetItem) @param column column that was double clicked (integer) """ if item is not None and column > 0: columnStr = item.text(column) if self.__entryRe.exactMatch(columnStr.strip()): filename, lineno, func = self.__entryRe.capturedTexts()[1:] try: lineno = int(lineno) except ValueError: # do nothing, if the line info is not an integer return if self.__projectMode: filename = self.__project.getAbsolutePath(filename) self.sourceFile.emit(filename, lineno) def clear(self): """ Public slot to clear the call trace info. """ self.callTrace.clear() self.__callStack = [] def setProjectMode(self, enabled): """ Public slot to set the call trace viewer to project mode. In project mode the call trace info is shown with project relative path names. @param enabled flag indicating to enable the project mode (boolean) """ self.__projectMode = enabled if enabled and self.__project is None: self.__project = e5App().getObject("Project") def __addCallTraceInfo(self, isCall, fromFile, fromLine, fromFunction, toFile, toLine, toFunction): """ Private method to add an entry to the call trace viewer. @param isCall flag indicating a 'call' (boolean) @param fromFile name of the originating file (string) @param fromLine line number in the originating file (string) @param fromFunction name of the originating function (string) @param toFile name of the target file (string) @param toLine line number in the target file (string) @param toFunction name of the target function (string) """ if isCall: icon = UI.PixmapCache.getIcon("forward.png") else: icon = UI.PixmapCache.getIcon("back.png") parentItem = \ self.__callStack[-1] if self.__callStack else self.callTrace if self.__projectMode: fromFile = self.__project.getRelativePath(fromFile) toFile = self.__project.getRelativePath(toFile) itm = QTreeWidgetItem(parentItem, [ "", self.__entryFormat.format(fromFile, fromLine, fromFunction), self.__entryFormat.format(toFile, toLine, toFunction) ]) itm.setIcon(0, icon) itm.setData(0, Qt.UserRole, isCall) itm.setExpanded(True) if isCall: self.__callStack.append(itm) else: if self.__callStack: self.__callStack.pop(-1) def isCallTraceEnabled(self): """ Public method to get the state of the call trace function. @return flag indicating the state of the call trace function (boolean) """ return self.__callTraceEnabled