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