Renaming tracenames?
I just discovered the /TN flag in Display and AppendToGraph, and it makes some issues much much easier for me. My question is, is it possible to rename a tracename? When a wave is renamed, its associated tracenames are renamed automatically in every graph according to the new wavename, provided they are the default "#" style. But if I specify the tracename myself using the /TN flag with Display or AppendToGraph, how can I modify the tracename?
This arises because I am writing an interface for users to trace structures on images. The interface shows multiple images on one graph, and the user has the option to show or hide different colour channels, as well as the tracings they have already done. The same tracing can therefore appear on multiple images. I have implemented the tracings as x-y waves (e.g. "cell1_y vs cell1_x"), with the tracename being the y wave with a suffix (e.g. "cell1_y_1" if it is displayed on top of image #1). This method allows me to specifically modify traces that occupy known locations.
The problem is that the user needs to be able to name and rename the tracings. When the x-y waves are renamed, the tracenames do not change (they do with the default #-style tracenames). Is there a way to force a name change? ReplaceWave doesn't address this (side note: ReplaceWave acts a little strangely with these non-default tracenames). Right now, it seems I have to RemoveFromGraph the old tracename and then AppendToGraph with the new tracename. Is that my only option? BTW, I'm using Igor 7. Maybe this works differently in Igor 8.
I'm grateful for anyone's insights into this.
-Matthew
To my knowledge, trace renaming is not available (unfortunately).
One small piece of advice is that you might reduce the number of waves that have to be dealt with by combining the x-y waves into one wave. (You might already be doing this -- but if so it wasn't totally clear from your post.) Instead of a 1D wave cell1_y of length n rows (for n traced locations) and a second 1D wave cell1_x of length n rows, you would have a single wave, called say cell1, with two columns, one column for x locations and one column for y locations (and n rows). For example:
cell1=nan
setdimlabel 1,0,xLocations,cell1
setdimlabel 1,1,yLocations,cell1
cell1[0][%xLocations]=5 //store location (5,10) in row 0
cell1[0][%yLocations]=10
cell1[1][%xLocations]=10 //store location (10,15) in row 1
cell1[1][%yLocations]=15
//...and so on
display/k=1
appendtograph cell1[][%yLocations]/tn=cell1_1 vs cell1[][%xLocations]
June 26, 2019 at 09:53 am - Permalink
You are correct- at present there is no way to rename a user-named trace. As you say, if you rename the wave that provides a trace name in the old way, the trace name changes because the trace name comes from the wave's name.
The missing feature (how about ModifyGraph rename(something)=somethingelse) has been noticed here, we just haven't tackled it yet. It would have to be for a future version, maybe 9 but no promises.
June 26, 2019 at 02:49 pm - Permalink
> Right now, it seems I have to RemoveFromGraph the old tracename and then AppendToGraph with the new tracename. Is that my only option? BTW, I'm using Igor 7. Maybe this works differently in Igor 8.
This is the only way to hack a rename of a trace.
Perhaps an alternative of use to you is to set the old trace wave to NaN and rename it to "..."_NaN, allow the empty traces to accumulate, and then offer the user a "Save This Set" button. When clicked, have a code to remove all "..."_NaN waves in one step. The advantage here may be that you can offer a "Restore Previous Set" button to re-invoke something.
June 26, 2019 at 05:25 pm - Permalink
No real need to set to NaNs if all you want is to hide a trace. These days, you can do ModifyGraph hideTrace(tracename)=1
June 27, 2019 at 09:34 am - Permalink
Oh. Yes. Excellent! Better to just hide the trace and then reshow a new one.
Then, use the two strings below to find all traces and all displayed traces.
string onlydisplayedtraces = TraceNameList("",";",4)
It seems here that an iteration loop is needed to find hidden traces. Perhaps a request ... Allow TraceNameList(...) with Bit 5 to return only hidden traces. Alternatively, perhaps also have TraceInfo(...) return a setting for whether a trace is or is not hidden.
June 28, 2019 at 01:03 pm - Permalink
TraceInfo returns the hideTrace setting in the RECREATION portion of the result:
•ModifyGraph hideTrace=1
•print traceInfo("","",0)
XWAVE:;YAXIS:left;XAXIS:bottom;AXISFLAGS:;AXISZ:NaN;XWAVEDF:;YRANGE:[*];XRANGE:;TYPE:0;ERRORBARS:;RECREATION:zColor(x)=0;zColorMax(x)=0;zColorMin(x)=0;zmrkSize(x)=0;zmrkNum(x)=0;zpatNum(x)=0;
textMarker(x)=0;arrowMarker(x)=0;mask(x)=0;patBkgColor(x)=0;useNegPat(x)=0;hBarNegFill(x)=0;usePlusRGB(x)=0;plusRGB(x)=(65535,0,0);useNegRGB(x)=0;negRGB(x)=(65535,0,0);toMode(x)=0;mode(x)=0;
marker(x)=0;lSize(x)=0.5;lStyle(x)=0;lSmooth(x)=0;rgb(x)=(65535,0,0);msize(x)=0;mrkThick(x)=0.5;opaque(x)=0;mstandoff(x)=0;hbFill(x)=0;gaps(x)=1;live(x)=0;quickdrag(x)=0;mskip(x)=0;cmplxMode(x)=0;
hideTrace(x)=1;useMrkStrokeRGB(x)=0;mrkStrokeRGB(x)=(0,0,0);useBarStrokeRGB(x)=0;barStrokeRGB(x)=(0,0,0);offset(x)={0,0};muloffset(x)={0,0};
See that "hideTrace(x)=1;" part?
June 28, 2019 at 05:23 pm - Permalink
Thanks Jim. While we can get the required information this way, it would be useful to be able to get it without needing to resort to a loop and an extraction of recreation macro content. So, the feature request for the equivalent of a BIT 5 list option to TraceNameList(...) remains.
June 29, 2019 at 05:53 am - Permalink
From the TraceNameList help:
Make/O jack,jill,joy;Display jack,jill,joy
ModifyGraph hideTrace(joy)=1 // hide joy
// (hidden + visible) - visible = hidden
String visibleTraces=TraceNameList("",";",1+4) // only visible normal traces
String allNormalTraces=TraceNameList("",";",1) // hidden + visible normal traces
String hiddenTraces= RemoveFromList(visibleTraces,allNormalTraces)
Print hiddenTraces
// Prints: joy;
July 1, 2019 at 02:39 pm - Permalink
Jim: I would swear that I posted exactly that suggestion just a few days ago. Thanks.
July 2, 2019 at 08:16 am - Permalink
Today I implemented ModifyGraph userName(tracename)=newname so that you can change a user-defined trace name to something else, or if you set the name to $"" you can revert to the default based on the wave's name.
Coming to an Igor Pro 9 near you, on some unspecified future date.
July 2, 2019 at 05:09 pm - Permalink
In reply to Today I implemented… by johnweeks
That sounds great.
It would also be very nice for the new ModifyGraph to create a string containing what the new name was set to. Presumably #[number] would be applied in the case of a conflict with the requested new name. I am imagining something analogous to the S_name string that is created after a display command.
I have always wished that such a string was created after use of the /TN flag with display or appendtograph, and it would be great to have it added for that.
I suppose one issue is that multiple traces could be displayed/appended or renamed at once. Perhaps the created string (called something like S_traceNames) could be a keyed list e.g., "[requestedTraceName0]:[finalTraceName0];[requestedTraceName1]:[finalTraceName1];...". In the absence of the /TN flag, requested trace names would be the name of the wave, I suppose, and the order of the list would matter.
(Even more) peripherally related, I think it would be helpful if traceInfo reported whether a trace has a user-defined tracename (from the display/appendToGraph /TN flag) or an automatic trace name. Unless there is another way to tell? It would be helpful for making a user-defined graph duplication function that switches out the waves.
July 3, 2019 at 02:10 pm - Permalink
And later today I changed the keyword from userName to traceName.
I can see the utility in what you are suggesting; unfortunately it doesn't fit will into the mammoth reach of ModifyGraph. It might be that you could get what you need using TraceNameList. After an AppendToGraph command that adds a single wave, the newly added trace will be the last one in the list.
You are also correct that renaming a trace where there are multiple traces with the same name will change things. If you have three waves called "wave0" in your graph, then you have traces called "wave0", "wave0#1" and "wave0#2". Now if you execute ModifyGraph traceName(wave0#1)=betterName, then your list of traces will be wave0, betterName, wave0#1. So that third instance of wave0 has changed from wave0#2 to wave0#1.
One of the benefits of /TN is that you can avoid trace names with instance numbers!
July 3, 2019 at 04:50 pm - Permalink
In reply to And later today I changed… by johnweeks
Thanks -- good to know about the ordering of TraceNameList
July 5, 2019 at 07:08 am - Permalink
In reply to And later today I changed… by johnweeks
This sounds excellent! Now I'm counting the days till Igor 9.
-Matthew
July 8, 2019 at 09:59 am - Permalink
In reply to This sounds excellent! Now… by xufriedman
An approach like this might be useful in the interim. It seems to work for the cases I generally need to use it for.
function/S renameTrace(winN,oldName,newName)
String winN,oldName,newName
if (strlen(winN) < 1)
winN = winname(0,1)
endif
String traces = tracenamelist(winN,";",2^0+2^1+2^2+2^3+2^4)
Variable oldPosition = whichlistitem(oldName,traces)
if (oldPosition < 0)
print "renameTrace() in winN",winN,"oldName",oldName,"not found, aborting"
return ""
endif
//make sure new name does not conflict with anything except old name
if (stringmatch(newName,oldName)) //I guess could be useful if a difference in case is sought
print "renameTrace() newName requested matches oldName. Aborting"
return ""
endif
newName = uniquetraceName(winN,newName)
make/o/t/free axFlags = {"/L=","/B=","/T=","/R="}
setdimlabel 0,0,left,axFlags
setdimlabel 0,1,bottom,axFlags
setdimlabel 0,2,top,axFlags
setdimlabel 0,3,right,axFlags
String winFlag = "/W="+winN
String info = traceinfo(winN,oldName,0)
String xwave = Stringbykey("xwave",info)
Variable hasxwave = strlen(xwave) > 0
WAVE oldWv = $oldName
String ywave = nameofwave(tracenametowaveref(winN,oldName))
String xaxis = Stringbykey("xaxis",info)
String yaxis = Stringbykey("yaxis",info)
String xaxInfo = axisinfo(winN,xaxis)
String xaxType = stringbykey("axtype",xaxInfo)
String yaxInfo = axisinfo(winN,yaxis)
String yaxType = stringbykey("axtype",yaxInfo)
String yRange = Stringbykey("yRange",info)
String xRange = Stringbykey("xRange",info)
String errBars = Stringbykey("errorbars",info)
//trace info not handled:
//TYPE -- contour, box, violin may need different commands
//XWAVEDF -- problematic if X wave has a data folder that is not stored in xwave (same true of ywave perhaps, but there is no key for that in traceInfo)
String appendCmd = "appendtograph"+winFlag+axFlags[%$yaxType]+yaxis+axFlags[%$xaxType]+xaxis+" "+ywave+yRange+"/TN="+newName
if (hasxwave)
appendCmd += " vs " + xwave+xRange
endif
//before removing original, append this trace name (removing the original first would result in deletion of its axes if no other waves are plotted on them)
execute/q appendCmd
if (strlen(errBars) > 0)
WAVE/T errText = listtotextwave(errBars," ")
errText[0] = errText[0] + winFlag //held beginning of the command, ErrorBars + flags. Append /W=winN
errText[1] = newName //held original trace name, needs the new trace name
String errBarsCmd
wfprintf errBarsCmd,"%s ",errText
execute/q errBarsCmd
endif
//now remove original trace
removefromgraph/W=$winN $oldName
//now apply style settings
String easyInfo = "dummy~" + replacestring("RECREATION:",info,"|RECREATION~",1) + "|"
String recreation = Stringbykey("RECREATION",easyInfo,"~","|",1) //recreation is the last key, so put in a nonsense sepStr to get the entirety of the string from RECREATION: on
String styleStr
Variable i,numCmds = itemsinlist(recreation)
for (i=0;i<numCmds;i+=1)
styleStr = stringfromlist(i,recreation)
styleStr = replacestring("(x)",styleStr,"("+newName+")")
execute/Q "modifygraph/w="+winN+" " + styleStr
endfor
//now fix trace ordering
Variable numTraces = itemsinlist(traces)
if (oldPosition == 0) //bottom-most
reordertraces _back_, {$newName}
elseif (oldPosition < numTraces - 1) //if oldPosition == numTraces - 1, it was top-most and is still
Variable followingTracePos = oldPosition + 1
reordertraces $stringfromlist(followingTracePos,traces),{$newName} //put trace just before following trace, where it was originally
endif
return newName
end
function/S uniqueTraceName(winN,traceName) //(it would be nice if this were a built in function too, like uniqueName but for traces)
STring winN,traceName
Variable i=0,attemptLimit = 10^5
String traces = tracenamelist(winN,";",2^0+2^1+2^2+2^3+2^4)
String outName = traceName,numStr
do
if (whichListItem(outName,traces) < 0)
return outName
endif
sprintf numStr,"%i",i
outName = traceName + "#"+ numStr
i+=1
while (i < attemptLimit)
String justInCase = traceName + "_Unique_" + num2str(floor(10*(0.5+enoise(0.5)))) + num2str(floor(10*(0.5+enoise(0.5)))) + num2str(floor(10*(0.5+enoise(0.5)))) + num2str(floor(10*(0.5+enoise(0.5)))) + num2str(floor(10*(0.5+enoise(0.5))))
return uniqueTraceName(winN,justInCase)
end
July 8, 2019 at 11:13 am - Permalink
In reply to Today I implemented… by johnweeks
It would also be very nice if the Replace Wave dialog box provided a GUI-based way to use the new renameTrace option in Igor 9.
Replace Wave seems like the appropriate GUI dialog to use to me, since this dialog already changes the automatically assigned trace name in cases where /TN wasn't originally used.
I've attached the current ReplaceWave dialog box, along with the way I think it would look and work with the new renameTrace option.
It probably goes without saying, but the rename trace field I propose would ideally also be usable through the dialog even in the absence of a replace wave command.
August 28, 2019 at 05:30 am - Permalink
The fact that the Replace Wave dialog changes the automatic trace name is, of course, just a side effect of changing the displayed wave. I'm inclined to think it belongs in the Modify Trace Appearance dialog. That's where the ModifyGraph keywords are handled, so it already has a lot of the infrastructure needed. It's surprisingly complicated to do modify any dialog, and both the Replace Wave dialog and the Modify Trace dialog are especially difficult.
I'll see if I have time before Igor 9 ships.
August 28, 2019 at 01:39 pm - Permalink
In reply to Today I implemented… by johnweeks
Would it be possible to add an equivalent for renaming axes? This would be especially useful because of the special status of axes with the name "left" "bottom" "top" "right".
(This status is annoying to me because such axes behave differently (e.g., no freepos setting). At present, it is fairly laborious to recreate the graph with the axis under a different name.)
October 2, 2019 at 01:18 pm - Permalink
So you want to not just rename the axis, you want to convert it from Standard (left, right, bottom, top) to Free?
October 3, 2019 at 09:19 am - Permalink
In reply to So you want to not just… by johnweeks
Yes that would be great. Many times it's frustrating that the Standard axes can't be manipulated like Free axes. Freepos is the behavior I miss most often, but there might be others that I am forgetting.
Is there presently any work around for converting from a standard to free axis? The only way I know of is to remake the axis under a non-standard name (which means removing and then re-appending all the waves)
----
As a bit of an aside, I would personally be in favor of abolishing the differences between Standard and Free axis behaviors -- I don't see any benefit and they cause trouble and confusion. Why not just have left,right,bottom,top be default names for axes that all work the same?
The differences between them that I find continually troublesome: For standard axes, it's the lack of freepos. For free axes, it's that the default freepos and label pos are weird (-50 and 0) and I have to adjust them to make them look like standard axes. In my ideal world, all axes would default to looking like standard axes, but they would have all the functionality of free axes.
I presume this particular change is not an option on the grounds of backwards compatibility alone -- and probably also other people's preferences that differ from mine.
October 3, 2019 at 10:01 am - Permalink
One approach to converting a graph with standard axes into a graph with free axes would be to save a recreation macro (Windows->Window Control->Window Control, or Ctrl-Y). Then edit the macro, changing all instances of Left to, say, FreeLeft, and so forth. Then run the macro to create a new graph with free axes. Because of the freePos thing, some repair will be required :)
The reason for the differences between standard axes and free axes has to do with history and, as you suspect, backward compatibility. I'll have to ask around here to see if I can find out why it would be bad to make the default for a free action position be Fraction of Plot Area, which defaults to zero, which makes it like a standard axis.
October 3, 2019 at 04:26 pm - Permalink