MATLAB: Permanently set "Text Update Function" for Data Cursor in figures - matlab

Problem: I use MATLAB for science, and I often need more than 4 significant digits. Every time I use Data Cursor in a figure's GUI, I need to manually right-click on the point, Select Text Update Function... or Edit Text Update Function..., and navigate to the folder where I saved the function whose callback prints more than 4 (e.g. 8) significant figures. This is annoying and there should be a way to automatically change this.
Ideal answer: I want this done permanently for all figures, e.g. in a function that changes default settings in my startup.m file.
Good enough answer: I want a wrapped function to which I give the figure handle and it fixes this for me.
I humbly await SO's infinite wisdom.

The permanent solution
would be, to edit the default_getDatatipText.m function.
You can find it in:
C:\...\MATLAB\R20xxx\toolbox\matlab\graphics\#graphics\#datacursor
There you will find the line:
DEFAULT_DIGITS = 4; % Display 4 digits of x,y position
Edit it as desired, you can't do much harm, but make a backup before if you want.
Alternative solution:
There is also the possibility of custom data tips: Tutorial at Matlab Central
It could finally look like this:
(additional text outside data-tips was post-processed)
And as you're talking about precision. The data-tip always snaps to the closest data-point. It doesn't show interpolated data at the clicked position.

The permanent answer given by thewaywewalk doesn't work anymore in R2015a, and probably later ones. So here I share my solution for both the temporary and permanent solution
Temporary solution (for a single figure):
The following function contains the update function as a nested function. Call datacursorextra to apply it to the current figure, or datacursorextra(fig) to apply it to some figure fig.
function datacursorextra(fig)
% Use current figure as default
if nargin<1
fig = gcf;
end
% Get the figure's datacursormode, and set the update function
h = datacursormode(fig);
set(h,'UpdateFcn',#myupdatefcn)
% The actual update function
function txt = myupdatefcn(~,event)
% Short-hand to write X, Y and if available Z, with 10 digit precision:
lbl = 'XYZ';
txt = arrayfun(#(s,g)sprintf('%s: %.10g',s,g), lbl(1:length(event.Position)), event.Position,'uniformoutput',false);
% If a DataIndex is available, show that also:
info = getCursorInfo(h);
if isfield(info,'DataIndex')
txt{end+1} = sprintf('Index: %d', info.DataIndex);
end
end
end
Permanent solution (apply to all figures by default):
I have not found a way to set a default UpdateFcn for the data cursor, but it is possible to add some code which will be called every time a new figure is created. Add the following line to your startup.m:
set(0,'defaultFigureCreateFcn',#(s,e)datacursorextra(s))
and make sure the datacursorextra function given above is available in your Matlab path.

#caspar's solution works very well.
You can also update the txt{} part of the solution with
if isfield(info,'DataIndex')
DataIndex = [info.DataIndex];
txt{end+1} = sprintf('Index: %d\n', DataIndex(1));
end
This will enable you to update the Index field when you have multiple pointers in the same figure.

Related

Is there any function for getpts() of MATLAB in Octave?

I load a data file and plot it in octave. But in the plot, I want to mark the periodic appearance of points on the plot. I used ginput() function for marking. But the problem I see is, if I mark a different point which was not supposed to mark and then immediately realise that i made mistake, now I want to delete my last marked point and then mark the correct point. I'm not able to do it. I found out that there is MATLAB function getpts() which does the same but octave version of getpts() is not there. Can anyone help me out please?
Example:
The sequence i want to mark is 1,2,3,4,5,6,7,8,9,10.
But accidently I mark 1,2,3,5 And realise that I did a mistake and then press delete button on the keyboard which deletes 5 and then I mark 4 and then 5.
While getpts is not implemented per se, producing a small function which gets inputs one by one via ginput and vets them to get the desired behaviour is fairly easy. E.g.
X = []; Y = [];
while true
[x, y, b] = ginput(1);
if b == 8 , X(end)=[]; Y(end)=[]; % backspace key pressed
elseif isempty(b), break; % enter key pressed
else , X(end+1)=x; Y(end+1)=y; % any other key
end
disp([X;Y]); fprintf('\n'); fflush(1); % Optional terminal output
end
This is a very flexible approach which allows you to modify and add functionality as you desire (e.g., add different markers based on specific key pressed, plot as you go, etc).

MATLAB Get a list of color map names

I am writing a GUI that would benefit from a user-selection of colormaps by name. However, I am at a bit of a quandary in that I can't seem to programmatically get a list of supported colormap names!
While I could hardcode the names; my code may be run on older versions of matlab which may have different colormaps. My primary concern is with the parula colormap which, if I recall correctly, was not present in MATLAB 2014.
Any thoughts?
Alternatively, you can hardcode them and have an if statement with graphicsversion(fhandle) on it.
It returns returns true if the default graphics system is the old
handle graphics one.
You could also try to get an extensive list, and then check if colormapname.m is a file in matlabroot\toolbox\matlab\graph3d. If the function is there, the colormap comes in that version. You'd still need to hardcode an extensive list though.
EDIT: as #thewaywewalk suggests, you could open Contents.min matlabroot\toolbox\matlab\graph3d and esarch for % Color maps. It has a list of the colormaps included in the version. In 2014b its on lines 29-48
I'm not 100% sure it works in MATLAB As #BillBokeey points out in the comments this does not work in MATLAB, but in Octave you can use:
CM = colormap('list');
It will return a cell array of strings containing all of the valid colormaps.
CM =
{
[1,1] = autumn
[1,2] = bone
[1,3] = cool
[1,4] = copper
[1,5] = flag
[1,6] = gmap40
[1,7] = gray
[1,8] = hot
[1,9] = hsv
[1,10] = jet
[1,11] = lines
[1,12] = ocean
[1,13] = pink
[1,14] = prism
[1,15] = rainbow
[1,16] = spring
[1,17] = summer
[1,18] = white
[1,19] = winter
}
A possibility to get an extensive list of available colormaps :
In matlabroot\help\matlab\ref, you can find previews of all available colormaps whose filenames are of the form colormap_colormapname.png
In order to get your list, you can use :
CurrFolder=pwd;
cd(strcat(matlabroot,'\help\matlab\ref'))
Colormaps=dir('*colormap_*.png');
TmpColormapsList={Colormaps.name};
TmpColormapsList=cellfun(#(S)strrep(S,'colormap_',''),TmpColormapsList,'UniformOutput',false);
ColormapsList=cellfun(#(S)strrep(S,'.png',''),TmpColormapsList,'UniformOutput',false);
cd(CurrFolder);
This will output a cell array of string containing the names of the available colormaps.
Pretty ugly hack, but at least it works on 2014b (Please check it for your version if you have another one)
Another way (hack) might be to extract the string out the the colormapeditor function:
colormapeditorString = fileread(strcat(matlabroot,'\toolbox\matlab\graph3d\colormapeditor.m'));
posStart = strfind(colormapeditorString,'stdcmap(maptype');
posEnd = strfind(colormapeditorString(posStart:end),'end') + posStart;
stdcmapString = colormapeditorString(posStart:posEnd);
split = strsplit(stdcmapString, '(mapsize)');
list = cellfun(#(x)x(find(x==' ', 1,'last'):end), split,'uni',0);
list(end) = [];
If everything fails you can try the callback routine:
function=ChangeCMap()
CMList=get(CMapList,'string'); %% Read colormap names
CMVal =get(CMapList,'value'); %% Get the index of desired colormap
try
colormap(Ax,CMList{CMVal}); %% Try to set the colormap...
catch Msg %% ... if it fails, then:
if strcmp(Msg.stack.name,'colormap') %% Check if error was caused by colormap function
set(Ax,'colormap`,'jet'); %% set "default" colormap (optional)
indices=1:length(CMList);
set(CMapList,'string',CMList{indices~=CMVal}) %% remove the colormap name thet caused error
else
disp(Msg) %% Print the error message in matlab shell
end
end
end
In this example shared variables CMapList - handle to the popup menu - and Ax - handle of the axes - are expected.
When the function is called it tries to set the colormap. If it fails it set the default colormap and remove the problematic name from the menu.
Be sure that first and last colormaps won't cause error, otherwise the CMapList update will have to handle those options.
You can also benefit from the fact, that each colormap has its own .m file so you don't need to wait until error occurs.
CMap='bone'; %% example
if exist(CMap,'file')
colormap(Ax,CMap) %% bone.m exist somewhere in the matlab paths
else
colormap(Ax,'jet') %% bone.m does not exist in the matlab paths
end
This raises one point - you can define your own colormaps and make the algorithm to generate missing .m files...
Given that any function that returns a 3-column matrix, or even a .MAT file containing a cmap variable with such a matrix, can be used by colormap, it doesn't make sense to ask for a list of “all” colormaps.
You might use exist (i.e., exist('parula', 'file')), or try clauses, to check for the existence of certain functions and offer them as colormaps in your GUI, though that might be problematic if the user has custom functions with the same name that don't yield colormaps.
I have users who love making totally custom colormaps, which they save in MAT files, so for them, I'd make the colormap a customizable textfield, and do some validation to ensure that it is indeed a valid colormap.
I've recently had the same issue building an app in app generator. After finding the above attempts overly cumbersome, I have decided to provide my approach.
Matlab stores it's colormaps as *.m files in a single directory, so select for the filenames in this directory.
coldir = dir([matlabroot '\toolbox\matlab\graphics\color']);
maps={};
for i=1:length(coldir)
if coldir(i).isdir, continue, end
maps{end+1}=replace(coldir(i).name,'.m','');
end

Graphic object handle array in embedded matlab function in Simulink in R2014a or newer

For debugging, I have some plots of vectors in an embedded matlab function in Simulink. Up to Matlab R2013b, everything works just fine with the following minimum example code:
function fcn
%#minimum example for plot within for-loop of embedded matlab function
coder.extrinsic('delete');
coder.extrinsic('quiver');
coder.extrinsic('gobjects');
numSteps=4;
persistent hFig;
persistent hVector;
if isempty(hFig)
hFig = figure;
hold on;
hVector=zeros(numSteps,1);
hVector=gobjects(numSteps,1);
for i=1:numSteps
hVector(i) = quiver(0,0,0,0);
end
end
set(0, 'CurrentFigure', hFig);
delete(hVector);
for i=1:numSteps
startInc=[1 1;1 1].*i;
endInc=[2 3;2 -3].*i;
hVector(i) = quiver(startInc(1,:),startInc(2,:),endInc(1,:),endInc(2,:));
end
For the handle array hVector, an initialization is necessary due to its use in the for-loop. However, for an Initialization of a graphics handle object, the function gobjects is needed and in turn the initialization as double with zeros(numSteps,1) becomes necessary, since matlab cannot correctly determine the data type of an output of an extrinsic function.
As I said, this code snippet works just fine if copied to an embedded matlab function block in simulink without anything else in the model.
My problem: Mathworks changed a lot of the plotting functions in R2014a, one of the changes being the datatype of the graphics handles which are no of type quiver for my quiver plot. Thus, the initialization with zeros(numSteps,1) initializes the wrong data type for the handle array. However leaving it out still does not work, because of the problem mentioned above. Neither does a init loop or anything similar compile without errors.
I'd greatly appreciate any help on that issue.
You can try to remove the gobject initialisation and use double() to wrap your call to any matlab graphic object. Ex:
hVector(i) = double( quiver(startInc(1,:),startInc(2,:),endInc(1,:),endInc(2,:)) ) ;
I suggest reading Loren's article about the compatibility issues which can arise when switching to HG2 versions of Matlab.
A quick quote from it which apply more to your issue:
Graphics Functions Return Objects, not Numeric Handles
Prior to R2014b, you could store a set of handles to graphics objects
in an array and then add some numeric data to that array. In R2014b,
that will cause an error.
[...]
If you find yourself really stuck, it is possible to cast object
handles to numeric handles using the double function. You can then
cast the number back to an object handle using the handle function. We
don't recommend this as a long term solution. Be aware that we may
choose to remove this feature in a future version of MATLAB. If we do,
we'll let you know in advance.
Now if you really have to use this solution, note that both functions works on single elements but also on arrays. So
hVector_HG = handle( hVector_Num ) ; %// works if "hVector_Num" is an array of numeric handles
%// and ...
hVector_Num = double( hVector_HG ) ; %// works if "hVector_HG" is an array of HG2 graphic handles
That may simplify the round trips between a format or another if they often become necessary.
Edit:
I put this at the bottom of the post for the moment because the beginning is already accepted answer but please try the following and let me know if it works. It may solve your problem in a better (more future-proof) way.
Another way to initialise an array of handle of a given graphic object is to create one (empty is good enough) and replicate it. For example:
hqNaN = quiver(NaN,NaN) ; %// create an empty quiver
hVector = repmat( hqNaN , numSteps , 1 ) ; %// replicate it in an array
will give you an array hVector containing numSteps HG2 graphic object handles. At this point they all point to the very same object but it is perfectly legal to overwrite each element with a handle of the same type. So a later :
hVector(i) = quiver( ... , etc ... ) ; %// overwrite handle "i" with a new quiver plot handle
will (should) work without problem.
A few things to take care with this approach:
where will the empty quiver be created ?
you may already have a "current" figure and you don't want it to be messed up. If not a new empty plot will be created. So to make sure the empty quiver do not cause problem (just a flicker on the screen), you could wrap it this way:
figure ; hqNaN = quiver(NaN,NaN) ; close(gcf) ;
or you can also park it in a figure (it will be invisible anyway) in case you need to re-use a handle of this type for other array initialisation. Just don't forget that once you close the figure it is on, or you delete the graphic object, the hqNaN variable is still there but it is not the same type of handle any more (so not useful to replicate if you want this type exactly).
What if you do not overwrite all you initial handle array ?
Remember all the initial handles of the array point to the same graphic object. So in case your array contains 12 elements but let's say by mistake you only overwrite 10 of them, then 2 elements will be handles pointing to the same object (which you may already have deleted)). This means that calling:
delete(hVector)
will throw you the annoying:
Error using handle.handle/delete. Invalid or deleted object.
gna gna gna ... luckily you can easily prepare for that by programming defensively and using instead:
delete( hVector(ishandle(hVector)) )
Matlab will do the check automatically and only delete the right handles.

MATLAB uitable row generation from user input

I've got a GUI in MATLAB which uses uitables for input. There are a fixed number columns, and each column has a very specific format which I have stored as a cell array, like so:
columnformat = {'text', 'numeric', {#doSomething, inputArg1}, {'Option1' 'Option2'}};
The number of rows is theoretically unlimited; the user could provide as many they like. The back-end is capable of handling arbitrarily many row inputs. Right now, I'm building a large uitable initially, and just assuming the user won't use it all.
Here's the question: I want to set up the table and associated code such that any time the user has selected the final row and presses enter, it creates a new row with the same format as the rest of the table.
I've tried many different approaches, including dynamically setting 'Data', and they all seem to break the custom formatting dictated by the cell array. I'm sure someone has done this before. Thanks for your help!
This solution works on GUI created using MATLAB GUIDE. I think it's true that MATLAB GUI shows odd behaviour, but I have seen most of the odd behaviour when debugging MATLAB callbacks using something like keyboard and not properly exiting from them using dbquit. So, my advice would be to stay away from using keyboard related commands for MATLAB GUIs created with GUIDE.
Now, back to solving your problem, follow these steps.
Step 1: Add this at the start of GUINAME__OpeningFcn:
handles.row_col_prev = [1 1];
Step 2: Click on the properties of the table in context and click on CellSelectionCallback. Thus, if the tag of the table is uitable1, it would create a function named - uitable1_CellSelectionCallback.
Assuming the figure of the GUI has the tag - addrows_figure
Add these in it:
%%// Detect the current key pressed
currentkey = get(handles.addrows_figure,'CurrentCharacter')
%%// Read in previous row-col combination
prev1 = handles.row_col_prev
%%// Read in current data. We need just the size of it though.
data1 = get(handles.uitable1,'Data');
%%// Main processing where a row is appended if return is pressed
if numel(prev1)~=0
if size(data1,1)==prev1(1) & currentkey==13 %%// currentkey==13 denotes carriage return in ascii
data1(end+1,:) = repmat({''},1,size(data1,2)); %%// Append empty row at the end
set(handles.uitable1,'Data',data1); %%// Save it back to GUI
end
end
%%// Save the current row-col combination for comparison in the next stage
%%// when selected cell changes because of pressing return
handles.row_col_prev = eventdata.Indices;
guidata(hObject, handles);
Hope this works out for you!
I couldn't think of a possibility to achieve what you want with a certain key, I think it would be possible with any key (KeyPressFcn). But I'd rather recommend to introduce a toolbar with a pushbutton:
h = figure(...
u = uitable(h, ...
set(u,'Tag','myTable')
tbar = uitoolbar(h);
uipushtool(tbar,'ClickedCallback',#addRow);
In your callback function then you need to get your data, add a row and write it back:
function addRow(~,~)
u = findobj(0,'Type','uitable','Tag','myTable');
data = get(u,'Data');
%// modify your data, add a row ...
set(src,'Data',data);
end
Sorry if everything is a little simple and untested, but a good answer would require a considerable effort, I don't have time for. The tag matlab-uitable can give you a lot of further ideas.

How to check if a figure is opened and how to close it?

My m-file opens figures depending on parameters. Sometimes is one figure, sometimes it opens 2 figures.
If the user call the function, the figures appear. If he calls the function again, with other parameters, I'm clearing figures with clf before the new plots.
If the second call is set to draw only one figure, the second one (opened by the previous call) remain gray (because of the clf).
Is there any way to check if it is opened and close it?
close all
Will close all open figures.
You can use findobj() to find objects that may exist by specifying search parameters. For example:
figure('name','banana')
Creates a figure with the name banana.
close(findobj('type','figure','name','orange'))
Does nothing because there are no figures open with the name orange.
close(findobj('type','figure','name','banana'))
Closes the figure.
You can specify search parameters to meet your needs.
I'm a little unclear about what you mean by "open". Figures don't really have "open" or "closed" states. They either exist or they don't. The FIGURE command will return a handle to the figure it makes:
hFig = figure(...your arguments here...);
You can also get a figure handle from the FINDOBJ function, which will find all graphics objects matching the property values you pass to it:
hFig = findobj(...your property/value pairs here...);
You can get rid of a figure with either of these commands:
close(hFig);
delete(hFig);
You can check if a figure has been closed/deleted using the function ISHANDLE:
ishandle(hFig) %# Returns 'true' if the figure exists, 'false' if it doesn't
Figures can also be "visible" or "invisible". They have a 'Visible' property that you can get or set the value of:
get(hFig,'Visible') %# Returns 'on' or 'off'
set(hFig,'Visible','off') %# Makes a figure invisible, but it still
%# exists (i.e. it's not closed)
If you're wanting to check if a figure is minimized, that may be a little more difficult. I believe there are some files that may help you with that on the MathWorks File Exchange: here's one to check out.
In MATLAB, you can GET information on the 'root'. Figures are children of 'root' (handle of root is 0) they are the only children of the root.
http://www.mathworks.com/help/techdoc/creating_plots/f7-41259.html
Knowing this, you can try this code that looks for the children of root, and gives you a list.
>> close all
>> get(0,'children')
ans =
Empty matrix: 0-by-1
>> figure(1)
>> get(0,'children')
ans =
1
>> figure(3)
>> get(0,'children')
ans =
3
1
I think you will find this the most direct way to query what figures are open.
isempty(findobj('name','Your_Figure_Name'))
if the answer is 0, then your figure is open
If inside your method, you create a figure without a 'name':
function [] = myMethod()
myFigure = figure()
end
you won't be able to access myFigure handle the next time through. So:
function [] = myMethod()
if ishandle(myFigure) % will fault, cant find variable myFigure
close(myFigure) % will fault
delete(myFigure) % will fault
end
myFigure = figure()
end
gnvoice wasn't 100% clear when he says:
You can check if a figure has been closed/deleted using the function
ISHANDLE:
He means you can only check AFTER you have recovered the handle:
function [] = createMyFigure()
recoveredHandle = findobj('type','figure', 'Name', 'myFigureName')
close(recoveredHandle)
delete(recoveredHandle)
ishandle(recoveredHandle)
myFigure = figure('Name','myFigureName') % now create figure
end
To close figure there is the "close" function. I'm still looking one to check if a figure is open.
for f=1:numel(findobj('type','figure'))
close(figure(f));
end
clear('f')