Request a DuplicateGraph(...) Function
jjweimer
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:
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
July 1, 2019 at 08:46 am - Permalink
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.
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
July 1, 2019 at 09:36 am - Permalink
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.
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).
July 1, 2019 at 10:47 am - Permalink
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:
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.
July 1, 2019 at 05:16 pm - Permalink
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.
July 2, 2019 at 08:12 am - Permalink
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!"
July 2, 2019 at 09:23 am - Permalink
:-) :-) :-)
July 2, 2019 at 10:12 am - Permalink
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:
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
March 24, 2020 at 10:43 am - Permalink
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:
newpanel;Display/HOST=# junk
Then
DuplicateGraphSubwindow("Panel0#G0")
worked as expected.
March 24, 2020 at 11:19 am - Permalink
OK,
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):
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 ;-)
March 24, 2020 at 12:20 pm - Permalink
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".
March 24, 2020 at 03:27 pm - Permalink
One solution is to change the line that detects the Display command to this:
Yes, Jeff- a grep expression could do it with a single command!
March 24, 2020 at 03:33 pm - Permalink
John: Spring Break is next week. I'll see what I might be able to cobble together.
March 24, 2020 at 06:06 pm - Permalink
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...
March 25, 2020 at 10:26 am - Permalink
I'm sure that this group does not include those who have to jump fully into doing on-line teaching. :-)
March 25, 2020 at 10:53 am - Permalink
No doubt. Personally, I have taken a bit of time away from work (at home) to get my wife going teaching piano via Skype.
March 25, 2020 at 12:19 pm - Permalink
So, try this function in place of the for ... endfor loop. I have set it to remove the /W and /HOST flags.
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
April 1, 2020 at 07:02 pm - Permalink
Nice!
You need to change "PWR" to "WRstr" to make this compile. Then the complete code would be:
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!
April 2, 2020 at 11:02 am - Permalink
Oh. Yes. I was doing the testing directly with a global PWR. Thanks for the fix.
April 2, 2020 at 01:31 pm - Permalink
Ah, so you have provided us with an illustration of the evils of global variables :)
These things happen.
April 2, 2020 at 03:05 pm - Permalink
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!
June 21, 2022 at 07:56 am - Permalink