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 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More