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 TextEncoding = "UTF-8"
#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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More