Create images of graphs and allow Igor to reopen the original image pxp when it opens the image
The following code must be compiled in Igor at the time that the image is opened by Igor. The image must have been saved with the included saveTopGraphs() function, which creates a text file in addition to the image file. The text file must be kept in the same folder as the image file. If these conditions are met, one can drag the image into Igor or double click on the image (if Igor is set as the default program) and the original pxp will open.
The ability to activate an existing pxp window that is already open requires that NirCmd.exe (download and documentation at http://nirsoft.net/utils/nircmd2.html) is available from command line (e.g., add it to your Windows folder).
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
//PART 1: save a graph with a text file containing "recovery" information (including waves in the graph,the pxp name,etc)
function saveTopGraphs(numGraphs, pathName, [h, w, res, nStr,type,nt])
Variable numGraphs; String pathName
Variable h, w //optionally set height. Uses inches by multiplying input value by 72
Variable res //see otions in switch statement below. 0 or 8 is 8x screen resolution, 1 is 1x, so on
String nStr //when passed, this is used as start of output name. If more than one graph being saved, naming is [nStr]0, [nStr]1, ...
Variable type //-2 for high res enhanced metafile, -5 for png, -7 for tiff, -8 pdf
Variable nt //supress transparency
Variable usedType
if (ParamIsDefault(type)) //no type speficiation, use default
usedType = -5 //-2 for high res enhanced metafile (good for illustrator), -5 for png (good for PPT or illustrator by ref), -7 for tiff
else
usedType = type
endif
make/o/t/free typeExtensions = {".emf",".png",".jpg",".tif",".pdf",".svg"}
dl_assignLblsFromList(typeExtensions,0,0,"-2;-5;-6;-7;-8;-9;","",0)
Variable spbt = (ParamIsDefault(nt) || !nt) ? 1 : 0 //Save PNG Background as Transparent (only affects PNGs)
PathInfo $pathName
if (!V_flag)
Newpath/O $pathName
if (V_flag)
Print "saveTopGraphs(): path set canceled; aborting."
endif
endif
PathInfo $pathName
String picSavePath = S_path
if (ParamIsDefault(res) || numtype(res) || (res <= 0))
res = 8
endif
Variable resVal = res*72 //1 to 8 are options for res
String graphWindows = WinList("*", ";", "WIN:1" )
Variable i, count,maxn=min(numGraphs,ItemsInList(graphWindows)); String picFileName,graphN,infoTxtFileName
make/o/t/free/n=(1,15) picInfo //put info along one row for easy writing with save/J (delimited text from wave columns)
for (i=0;i<maxn;i+=1)
graphN = StringFromList(i, graphWindows)
//check for height, width changes
if (!ParamIsDefault(h) || !ParamIsDefault(w))
if (!ParamIsDefault(h))
ModifyGraph/W=$graphN height = h*72
ModifyGraph/W=$graphN height = 0 //makes height adjustable again
endif
if (!ParamIsDefault(w))
ModifyGraph/W=$graphN width = w*72
ModifyGraph/W=$graphN width = 0 //makes height adjustable again
endif
doupdate
endif
//check for a custom name
if (ParamIsDefault(nStr)) //no custom name
SavePICT/Z/O/P=$pathName/EF=2/Q=1/B=(resVal)/E=(usedType)/TRAN=(spbt)/WIN=$graphN
picFileName = graphN + typeExtensions[%$num2str(usedType)]
else //custom name
if (numGraphs < 2) //just one input, don't append a number to give it a unique name
picFileName = nStr
else //multiple windows to save, append a number to make sure output name is unique for each one
picFileName = nStr + num2str(count)
endif
picFileName += typeExtensions[%$num2str(usedType)]
SavePICT/Z/O/P=$pathName/EF=2/E=(usedType)/Q=1/B=(resVal)/TRAN=(spbt)/WIN=$graphN as picFileName
endif
if (V_flag == 0)
Print "Saved: " + graphN,"target save name =", picFileName
//save text file with info about this file
Pathinfo home
String traces=tracenamelist(graphN,";",2^0+2^1+2^2+2^3+2^4+2^5),wavesForTraces="",xwavesForTraces=""
Variable j,numTraces=itemsinlist(traces)
for (j=0;j<numTraces;j+=1)
wavesForTraces+=nameofwave(tracenametowaveref(graphN,stringfromlist(j,traces)))
xWavesForTraces+=nameofwave(XWaveRefFromTrace(graphN,stringfromlist(j,traces)))
endfor
picInfo[0][0]="picFileName~"+picFileName
picInfo[0][1]="graphName~"+graphN
picInfo[0][2]="experimentName~"+igorinfo(1)+".pxp"
picInfo[0][3]="experimentFullPath~"+S_path //from pathinfo home
picInfo[0][4]="lastSaveDate~"+date()
picInfo[0][5]="lastSaveTime~"+time()
picInfo[0][6]="picSavePath~"+picSavePath
picInfo[0][7]="OS_user~"+igorinfo(7)
picInfo[0][8]="saveWidthInch~"+num2str(winDim(graphN,0))
picInfo[0][9]="saveHeightInch~"+num2str(winDim(graphN,1))
picInfo[0][10]="graphFullWaveList~"+wavelist("*",";","WIN:"+graphN)
picInfo[0][11]="graphTraceList~"+traces
picInfo[0][12]="wavesForTraces~"+wavesForTraces
picInfo[0][13]="xWavesForTraces~"+xWavesForTraces
picInfo[0][14]="winRecreation~"+winrecreation(graphN,0)
infoTxtFileName = picFileName +".txt"
save/J/P=$pathName/o/dlim="\r\n"/e=0 picInfo as infoTxtFileName
count+=1
else
Print "Error in saveTopGraphs() from SavePICT. Error code = " , num2str(V_flag) ," on graphN",graphN,"i=",i
endif
endfor
Print "total num saved = " + num2str(count) + " of target num = " + num2str(numGraphs)
end
//PART 2: what to do if that graph is passed to Igor as a file to open
//setup to call a function when any file open request is started
function BeforeFileOpenHook(refNum, fileNameStr, pathNameStr, fileTypeStr, fileCreatorStr, fileKind )
Variable refNum //is the file reference number. You can use this number with file I/O operations to read from the file. Igor closes the file when the user-defined function returns, and refNum becomes invalid. The file is opened for read-only; if you want to write to the file, you must close and reopen it with write access. refNum will be -1 for experiment files and XOPs. In this case, Igor has not opened the file for you.
String fileNameStr // contains the name of the file.
String pathNameStr // contains the name of the symbolic path. pathNameStr is not the value of the path. Use the PathInfo operation to determine the path's value.
String fileTypeStr // contains the Macintosh file type code, if applicable. File type codes are obsolete. Use the file name extension to determine if you want to handle the file. You can use ParseFilePath to obtain the extension from fileNameStr .
String fileCreatorStr // contains the Macintosh creator code, if applicable. Creator codes are obsolete so ignore this parameter.
Variable fileKind // is a number that identifies what kind of file Igor thinks it is. Values for fileKind are listed in the next section.
return fio_reopenImgPxp(refNum,fileNameStr,pathNameStr)
end
//handle reopening
static strconstant acceptedFileExtensions = "png;emf;" //don't include periods; semi-colon delimited list. Currently only png and emf will be handled. Others can be added
function fio_reopenImgPxp(refNum,fileNameStr,pathNameStr)
Variable refNum;string fileNameStr,pathNameStr
String fileExtension = fio_getFileNameOrExtension(fileNameStr,1)
Variable acceptedFileExtension = itemsinlist(ListMatch(acceptedFileExtensions,"*"+fileExtension)) > 0
if (!acceptedFileExtension)
return 0
endif
String txtFileName = fileNameStr + ".txt"
STring txtFiles = indexedfile($pathNameStr,-1,".txt")
Variable txtFileInd = whichlistitem(txtFileName,txtFiles)
if (txtFileInd < 0)
print "fio_reopenImgPxp() acceptable file type opened but no associated text file found, aborting"
return 0
endif
//make sure path to search for pxp files is set (once per experiment with drag)
//this could be altered to search more of the available drives
pathinfo fio_reopenImgPxpPath
if (!V_flag)
newpath/M="fio_reopenImgPxp() Choose a new path for retrieving PXP files on image drag"/o fio_reopenImgPxpPath//fio_reopenImgPxp
if (V_flag)
Print "fio_reopenImgPxp() failed to set path to retrieve pxp files on image drag, aborting. need to set symbolic path with name fio_reopenImgPxpPath"
endif
endif
String pxpPath = S_path
String info = fio_readTxtFile(pathNameStr,txtFileName)
String experimentName = stringbykey("experimentName",info,"~","\r")
String experimentName_noPrefix = removeending(experimentName,".pxp")
if (stringmatch(experimentName,igorinfo(1)))
print "fio_reopenImgPxp() This pxp created the image"
return 1
endif
WAVE/T openExpInfo = fio_getIgorExperiments("")
Variable expIndex = finddimlabel(openExpInfo,0,experimentName_noPrefix)
if (expIndex >= 0)
Variable is64 = str2num(openExpInfo[expIndex][%is64])
String winTitle=openExpInfo[expIndex][%$"Window Title"]
String activateCmd="nircmd win activate title \""+winTitle+"\""
String flashCmd="nircmd win flash title \""+winTitle+"\""
print "fio_reopenImgPxp() Pxp file that created this image,",experimentName," is already open in",selectstring(is64,"Igor32","Igor64"),"win="+winTitle,"attempting to activate that window (requires nircmd)"
executescripttext/B/Z/w=1 activateCmd
if (V_flag)
print "fio_reopenImgPxp() could not activate pxp that contained image ("+experimentName+"), appears nircd is not installed. Add nircmd.exe (http://www.nirsoft.net/utils/nircmd.html) to C:/Windows"
else
executescripttext/B/Z/w=1 flashCmd
endif
//doesnt seem like there is any easily available error handling if window is not found
return 1
endif
String pxpFiles = indexedfile(fio_reopenImgPxpPath,-1,".pxp")
Variable pxpFileInd = whichlistitem(experimentName,pxpFiles)
if (pxpFileInd<0)
print "fio_reopenImgPxp() pxp that generated image:",experimentName,". not found. need to set symbolic path with name fio_reopenImgPxpPath to proper path, or maybe it has been renamed since last image save"
return 1
endif
String sysPxpPath = parsefilepath(5,pxpPath,"*",0,100)
String fullPath = sysPxpPath+stringfromlist(pxpFileInd,pxpFiles)
print "fio_reopenImgPxp() pxp that generated image found at:",fullPath+". attempting open in new instance of Igor64"
executeIgorScript_doCmd(fullPath,64,newInstance=1,noCommand=1)
return 1
end
//get a (textwave) list of open Igor experiment windows (via ExecuteScriptText)
function/WAVE fio_getIgorExperiments(outRef)
String outRef //pass "" for free wave
String cmdExeKeys="Image Name;PID;Session Name;Session#;Mem Usage;Status;User Name;CPU Time;Window Title;" //output from the script below contains items with these keys. formatting is parsed by key in text_getCmdExeListInfo()
ExecuteScriptText/B "tasklist /v /FO LIST /fi \"IMAGENAME eq Igor*\""
WAVE/T instanceInfo = listtotextwave(S_value,"\r\n\r\n") //format is /r delimited list with key:<\t>itemStr<\r> it seems
duplicate/o/free/t instanceInfo,titleStrs,winTitles,imageNameStrs
Variable numWins = dimsize(instanceInfo,0),i,itemsInWindowTitleStr
Variable numCmdKeys=itemsinlist(cmdExeKeys),numParams=itemsinlist(cmdExeKeys)+3
if (strlen(outRef) == 0)
make/o/free/n=(numWins,numParams)/t out
else
make/o/n=(numWins,numParams)/t $outref/wave=out
endif
String key
for (i=0;i<numCmdKeys;i+=1)
key=stringfromlist(i,cmdExeKeys)
setdimlabel 1,i,$key,out
endfor
setdimlabel 1,numCmdKeys,is64,out
setdimlabel 1,numCmdKeys+1,isDefault,out
setdimlabel 1,numCmdKeys+2,winTitleNoIgorInfo,out //also stored in the dimension label
out[][0,numCmdKeys]=text_getCmdExeListInfo(stringfromlist(q,cmdExeKeys),instanceInfo[p])
out[][%is64] = num2str(stringmatch(out[p][%$"Image Name"],"*Igor64.exe*"))
out[][%isDefault] = num2str(itemsinlist(out[p][%$"Window Title"]," - ") < 2 )//for named windows, will be 2; for unnamed will be 1 --not a super safe test. Would be better to get Igor version and check a match to expected given 32-bit or 64-bit
String experimentName
for (i=0;i<numWins;i+=1)
experimentName = stringfromlist(0,out[i][%$"Window Title"]," - ")
experimentName = text_removeSurroundingSpaces(experimentName,2)
out[i][%winTitleNoIgorInfo] = experimentName
setdimlabel 0,i,$experimentName,out
endfor
return out
end
//parses output from ExecuteScriptText "tasklist..." as above
function/S text_getCmdExeListInfo(key,list)
String key,list
String match = stringfromlist(0,listmatch(list,key+":*","\r\n"),"\r\n")
match = replacestring(key+":",match,"",0,1)
return text_removeSurroundingSpaces(match,2)
end
//wrapper function for calling Igor from script
strconstant ksExecutable32="IgorBinaries_Win32\Igor.exe"
strconstant ksExecutable64="IgorBinaries_x64\Igor64.exe"
function executeIgorScript_doCmd(cmdStr,igorVers,[newInstance,noCommand])
String cmdStr //command to execute in other instance of igor
Variable igorVers //version of Igor to command
Variable newInstance //use the /I flag to start a new Igor instance. otherwise, a new instance should only be created if there are no instances of the version of Igor running
Variable noCommand //suppress the /X command flag. This is required to open a pre-existing pxp file
String windowsIgorPath = SpecialDirPath("Igor Application",0,1,0)
Variable doNewInstance = !ParamIsDefault(newInstance) && newInstance
Variable doCommand = ParamIsDefault(noCommand) || !noCommand
String newInstanceOption = selectstring(doNewInstance,"","/I ")
String commandOption = selectstring(doCommand,"","/X ")
string execPath
switch (igorVers)
case 32:
execPath = "\""+ windowsIgorPath + ksExecutable32 +"\""
break
case 64:
execPath = "\""+ windowsIgorPath + ksExecutable64 +"\""
break
default:
return 0 //not a valid igor version
endswitch
String scriptText = execPath + " " +commandOption + newInstanceOption + "\"" + cmdStr + "\""
ExecuteScriptText scriptText
end
//GENERALISH HELPER FUNCTIONS needed to run parts 1 and 2
function/S fio_getFileNameOrExtension(str,extensionNotName)
String str
Variable extensionNotName
Variable items = itemsinlist(str,".")
if (extensionNotName)
return stringfromlist(items-1,str,".")
endif
String noExt = removelistitem(items-1,str,".")
Variable len = strlen(noExt)
return noExt[0,len-2] //remove last "."
end
function dl_assignLblsFromList(wv,dim,startIndex,list,appendStr,appendBeforeLblNotAfter[reuseLast])
WAVE wv
Variable dim //dim to label
Variable startIndex //index to start at in dim to label (end index is based on length of list)
String list //list of labels to assign
String appendStr
Variable appendBeforeLblNotAfter
Variable reuseLast //optionally pass to coninue using the last in the list until the end of the dimension
variable i,num=itemsinlisT(list),maxIndex = dimsize(wv,dim),index,count=0
String lb=stringfromlist(0,list)
for (i=0;(i+startIndex)<maxIndex;i+=1) //loop through from startIndex til end of dimension ..
index = i+startIndex
if (i >= num)
if (ParamIsDefault(reuseLast) || !reuseLast)
break
endif
SetDimLabel dim,index,$lb,wv;count+=1
endif
if (appendBeforeLblNotAfter)
lb=appendStr + stringfromlist(i,list)
else
lb=stringfromlist(i,list) + appendStr
endif
SetDimLabel dim,index,$lb,wv;count+=1
endfor
return count
end
function winDim(winN,widthOrHeight)
String winN
Variable widthOrHeight //0 width 1 height, returns in inches
if (strlen(winN) < 1)
winN = winname(0,1)
endif
getwindow $winN,psize
Variable minCoord,maxCoord
if (widthOrHeight) //height
minCoord = V_top
maxCoord = v_bottom
else
minCoord = V_left
maxCoord = v_right
endif
//getwindow $winN,expand
return (maxCoord - minCoord) / 72// / v_value
end
Function/S fio_readTxtFile(pathNameStr,fileNameStr)
String pathNameStr //symbolic path for file
String fileNameStr //full name of file including extension
Variable refNum
Open/R/P=$pathNameStr refNum as fileNameStr
if (refNum == 0)
pathinfo $pathNameStr
print "fio_readTxtFile() failed to find pathNameStr",fileNameStr,"in symbolic path",pathNameStr,"("+S_path+")"
return "" // Failed
endif
Variable lineNumber, len
String buffer,out=""
lineNumber = 0
do
FReadLine refNum, buffer
len = strlen(buffer)
if (len == 0)
break // No more lines to be read
endif
out += buffer
if (CmpStr(buffer[len-1],"\r") != 0) // Last line has no CR ?
out+="\r"
endif
lineNumber += 1
while (1)
close refnum
return out
End
function/S text_removeSurroundingSpaces(str,beforeAfterOrBoth)
String str; Variable beforeAfterOrBoth //0 just before, 1 after, 2 both
Variable i,firstNonSpacePos,lastNonSpacePos,len=strlen(str)
if ( (beforeAfterOrBoth==0) || (beforeAfterOrBoth==2) )
for (i=0;i<len;i+=1)
if (!stringmatch(str[i], " "))
break
endif
endfor
firstNonSpacePos = i
if (firstNonSpacePos == len) //got through whole loop, string is empty or all blank
return ""
endif
if (beforeAfterOrBoth==0)
return str[firstNonSpacePos,inf]
endif
endif
//beforeAfterOrBoth is 1 or 2
for (i=len-1;i>=0;i-=1)
if (!stringmatch(str[i]," "))
break
endif
endfor
lastNonSpacePos = i
if (beforeAfterOrBoth==1) //just return without ending spaces
if (lastNonSpacePos == 0) //got through whole loop, string is empty or all blank
return ""
endif
return str[0,lastNonSpacePos]
endif
if (lastNonSpacePos == firstNonSpacePos) //got through whole loop, string is empty or all blank
return ""
endif
//return without ending and preceding spaces
return str[firstNonSpacePos,lastNonSpacePos]
end
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More