Request a DuplicateGraph(...) Function

I'd like to petition for a DuplicateGraph(WinName,newWaves,[newWinName]) function to improve upon the methods that have to otherwise be used according to the discussion at this link.

https://www.wavemetrics.com/forum/general/duplicate-graph

WinName -- name of source window (following conventions)
newWaves -- 0 means duplicate just the graph keeping traces linked to original waves, 1 means generate new duplicates of waves used as well (functions as Save Graph Copy only internally)
[newWinName] -- optional new window name (default is to add suffix _N)

 

If you don't need to copy the waves in the graph, it's pretty simple to duplicate a graph programmatically. Here is an example:

Function test()
    Make/O ddd=sin(x)
    Display ddd
    DoIgorMenu "Edit", "Duplicate"
    // Get name of duplicate graph
    String duplicateName = WinName(0, 1)
    print "New graph is named ", duplicateName
End

 

It's possible to make such a function (see below), relying on WinRecreation, with info from DoWindow, and Execute. Nevertheless, I also think it would be useful to have a built in function, since functions like this will fail if the recreation macro syntax changes. (I'm also not sure the function below works for every possible recreation macro.)

EDIT: I hadn't realized that similar functions were linked in the earlier forum topic, so this is not much of an improvement.

function/S graph_duplicate(origWinName,appendStrForNewWaves,[newWinName,appendBefore])
    String origWinName      //original window name. Top graph is used in case of ""
    String appendStrForNewWaves //pass "" to make new plot with same waves, otherwise renames the waves as [origName]+appendStrForNewWaves and plots with those
    String newWinName       //optionally give the window a new name instead of its automatic name
    Variable appendBefore       //optionally specify to append appendStrForNewWaves before wave names
   
    Variable startSkipLines = 2     //skip "function ..." and "PauseUpdate ..." lines from beginning of recreation macro
    Variable endSkipLines = 1           //skip "PauseUpdate ..." line from recreation macro
   
    if (strlen(origWinName) < 1)    //default to top graph for ""
        origWinName = winname(0,1) 
    endif
   
    Variable i,newWaves = strlen(appendStrForNewWaves) > 0
    String recreationStr,createdWaves=""
    if (newWaves)       //use getWindow wavelist and winrecreation to replace unique ##(num)## in recreation macro with new wave names, after duplication
        recreationStr = winrecreation(origWinName,2)   
        getwindow $origWinName, waveList
        WAVE/T W_WaveList       //not sure what the difference betweens columns 0 and 1 is in this
        Variable numWaves = dimsize(W_WaveList,0)
        Variable doAppendBefore = !paramIsDefault(appendBefore) && appendBefore
       
        //set new wave names
        make/o/t/free/n=(numWaves) newWaveNames
        if (doAppendBefore)
            newWaveNames = appendStrForNewWaves + W_waveList[p][0]
        else
            newWaveNames = W_waveList[p][0] + appendStrForNewWaves
        endif
       
        //duplicate new waves and insert names into recreationStr
        String hashTagStr,newWaveName,origWaveName
        for (i=0;i<numWaves;i+=1)
            origWaveName = W_waveList[i][0]
            newWaveName = newWaveNames[i]
            hashTagStr = W_waveList[i][2]
            Duplicate/o $origWaveName,$newWaveName
            recreationStr=replacestring(hashTagStr,recreationStr,newWaveName)
            createdWaves += newWaveName + ";"
        endfor
    else
        recreationStr = winrecreation(origWinName,0)
    endif
   
    //window name handling
    String finalName
    if (ParamIsDefault(newWinName) || (strlen(newWinName) < 1) )        //use default window name
        finalName = stringfromlist(1,recreationStr," ") //get window name from recreation macro name.. (allows winStr="" for top window)
        finalName = replacestring("()",finalName,"")        //remove () from after recreation macro name
    else    //newWinName passed
        finalName = newWinName
    endif  
    recreationStr = replacestring("Display",recreationStr,"Display/N="+finalName,0,1)       //modify "Display..." line to include window name
   
    //run commands, line by line to avoid execute length limit
    Variable lines = itemsinlist(recreationStr,"\r"),lineLimit = lines - endSkipLines
    for (i=startSkipLines;i<lineLimit;i+=1)
        execute/q stringfromlist(i,recreationStr,"\r")
    endfor
   
    return createdWaves
end

 

Thanks Adam. I did not recognize DoIgorMenu in order to create what is needed. Unfortunately, that approach does not work for my main need, which is to duplicate sub-windows in panels to their own stand-alone window. So, what I want to achieve in one function call is this ...

DuplicateGraph("MainPanel#EmbeddedGraph",0,"StandAloneGraph")

@GSB ... I was not really concerned about changing the waves, just duplicating the graph entirely to go from an embedded graph in a panel to a stand alone graph. I ended up pulling the WinRec string, removing the Display/.../HOST= line, and executing the revised WinRec after my own Display statement.

In the meantime, I have decided to just run the entire set of commands to generate the graphics with a switch.

Function ShowGraphics(how)
     variable how

     switch(how)
          case 0: // embed in panel
          ....
          case 1: // stand alone
          ...
     endswitch
     ... // common changes to make to graph
return

So, my equivalent when needed to DuplicateGraph(...) is to call ShowGraphics(1).

As of Igor 7, WinRecreation() works on subwindows. But it doesn't excise the subwindow stuff like "/HOST", so here is some code to do that:

Function/S RemoveSlashW(String aLine)
    String newstr = ""
    if (StringMatch(aLine, "*/W=*"))
        Variable pos1 = StrSearch(aLine, "/W=", 0)
        Variable pos2 = StrSearch(aLine, ")", pos1)
        newstr = aLine[0,pos1-1]
        newstr += aLine[pos2+1,strlen(aLine)]
    endif
   
    return newstr
end

Function/S RemoveSlashHost(String aLine)
    String newstr = ""
    if (StringMatch(aLine, "*/HOST=*"))
        Variable pos1 = StrSearch(aLine, "/HOST=", 0)
        Variable pos2 = StrSearch(aLine, " ", pos1)     // This fails if there is another flag after /HOST=...
        newstr = aLine[0,pos1-1]
        newstr += aLine[pos2,strlen(aLine)]
    endif
   
    return newstr
end


Function DuplicateGraphSubwindow(String gname)
    String rec = WinRecreation(gname, 0)   
    Variable lines=ItemsInList(rec, "\r")
    Variable i
    String newrec=""
    for (i = 0; i < lines; i++)
        String oneline = StringFromList(i, rec, "\r")
        if (StringMatch(oneline, "*Display*"))
            oneline = RemoveSlashW(oneline)
            oneline = RemoveSlashHost(oneline)
        endif
        newrec += oneline+"\r"
    endfor
    Execute newrec
end

Caveats: no error checking! Feed it a proper string like "Graph0#G0". It worked on a really simple case with just one subwindow, not a bunch of nested subwindows.

Thanks. I have to suspect that grep could be used in a clever way to wipe out the /HOST=...  and /W=... flags regardless of whether they are or are not followed by another flag. This would eliminate the need for the iteration loop and two sub-functions.

 

Well, you're probably right about a cleverer way to deal with the subwindow cruft. But I'm not grep-clever. I subscribe to the rule of thumb: "I could solve this problem with grep. Oh, no- now I have two problems!"

I needed this function now and I believe, there is bug in it - at least for graph embedded in panel. 

If you need to duplicate graph embedded in panel: PanelName#EmbeddedGraphName, this seems to be necessary:

 

Function DuplicateGraphSubwindow(String gname)
    String rec = WinRecreation(gname, 0)    
    Variable lines=ItemsInList(rec, "\r")
    Variable i
    String newrec="Window Test1() : Graph"
    for (i = 0; i < lines; i++)
        String oneline = StringFromList(i, rec, "\r")
        if (StringMatch(oneline, "*Display*"))
            oneline = RemoveSlashW(oneline)
            oneline = RemoveSlashHost(oneline)
        endif
        newrec += oneline+"\r"
    endfor
    Execute newrec
end

Note the 

    String newrec="Window Test1() : Graph"
Then it is fine and works as advertised. 

Can you post an example recreation macro for a panel with graph that fails? I tried this ultimately simple example and my original code worked correctly:

make junk=x
newpanel;Display/HOST=# junk

Then 

DuplicateGraphSubwindow("Panel0#G0")

worked as expected.

OK,

 

Function TestPanel()
    PauseUpdate; Silent 1       // building window...
    NewPanel /K=1 /W=(2.25,43.25,1210,800) as "test"
    DoWIndow/C TestPanelWIndow
    TitleBox MainTitle title="My test",pos={140,2},frame=0,fstyle=3, fixedSize=1,font= "Times New Roman", size={360,30},fSize=22,fColor=(0,0,52224)
    Display /W=(521,10,1183,410) /HOST=# /N=DataDisplay
    SetActiveSubwindow ##

    Display /W=(521,420,1183,750) /HOST=# /N=PDFDisplay
    SetActiveSubwindow ##


end

 

Results in this output (I renamed my function now):

•DupGraphInPanel("TestPanelWIndow#PDFDisplay")
 
    PauseUpdate; Silent 1       // building window...
    Display
EndMacro
•DupGraphInPanel("TestPanelWIndow#DataDisplay")
 
    PauseUpdate; Silent 1       // building window...
    Display
EndMacro

I need to add the string "Window DupWindwFromPanel() : Graph" at the start of the newrec. 

or nothing will be created. Side advantage here: Now I know what the name of the graph is ;-)

 

 

Hah! The test for the Display command also matches the name of your graph subwindow. That's why it screws up. I'm sure that Jeff's grep suggestion would be able to handle that...

But a mildly annoying work-around would be to change the names of your graphs so that they don't contain "Display".

One solution is to change the line that detects the Display command to this:

        if (StringMatch(oneline, "*Display*") && StringMatch(oneline, "*HOST*"))

Yes, Jeff- a grep expression could do it with a single command!

John: Spring Break is next week. I'll see what I might be able to cobble together.

We have a few customers contacting us with bug reports and questions that are clearly using their extra at-home time to do things with Igor that they haven't had time for previously...

I'm sure that this group does not include those who have to jump fully into doing on-line teaching. :-)

No doubt. Personally, I have taken a bit of time away from work (at home) to get my wife going teaching piano via Skype.

So, try this function in place of the for ... endfor loop. I have set it to remove the /W and /HOST flags.

Function/S ReFactorWinRec(WRstr)
    string WRstr
   
    string gstr
    string fstr, istr, rstr, estr
   
    // remove the /W component
    gstr = "((?s).+)(Display/W=\(\\d+,\\d+,\\d+,\\d+\))((?s).+)"   
    SplitString/E=gstr PWR, istr, rstr, estr   
    fstr = istr + "Display" + estr
   
    // remove the /HOST component
    gstr = "((?s).+)(/HOST=#)((?s).+)" 
    SplitString/E=gstr fstr, istr, rstr, estr  

    fstr = istr + estr
    return (fstr)
end

 

Nice!

You need to change "PWR" to "WRstr" to make this compile. Then the complete code would be:

Function/S ReFactorWinRec(WRstr)
    string WRstr
   
    string gstr
    string fstr, istr, rstr, estr
   
    // remove the /W component
    gstr = "((?s).+)(Display/W=\(\\d+,\\d+,\\d+,\\d+\))((?s).+)"  
    SplitString/E=gstr WRstr, istr, rstr, estr  
    fstr = istr + "Display" + estr
   
    // remove the /HOST component
    gstr = "((?s).+)(/HOST=#)((?s).+)"
    SplitString/E=gstr fstr, istr, rstr, estr  

    fstr = istr + estr
    return (fstr)
end

Function DuplicateGraphSubwindow(String gname)
    String rec = WinRecreation(gname, 0)  
    Variable lines=ItemsInList(rec, "\r")
    Variable i
    String newrec = ReFactorWinRec(rec)
    Execute newrec
end

And tested on Jan's perverse case!

Oh. Yes. I was doing the testing directly with a global PWR. Thanks for the fix.

Ah, so you have provided us with an illustration of the evils of global variables :)

These things happen.

Hi I found that graph_duplicate above gives an error if the data in the window you want to duplicate are not in the root folder. This is in Igor 7, no idea about other versions. FldrSav0 is a global string it creates to keep tabs on data folders, but if you run it twice it gets confused the second time because there's already a global string there.

Fixed by adding an extra line after

recreationStr = replacestring("Display",recreationStr,"Display/N="+finalName,0,1)

The new line is:

recreationStr = replacestring("String FldrSav0",recreationStr,"String/G FldrSav0",0,1)  //Avoid an error if you run this function twice and data are not in the root folder

Cheers!