In a GUI, I have several axes. How can I create differents custom data tips for each one?
This is just what I've tried so far: a function like this one right here. I've also tried two separates updatefunctions.
% --- Executes on mouse press over axes background.
function graph_5_ButtonDownFcn(hObject, eventdata, handles)
% hObject handle to graph_5 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
datacursormode(handles.figMainWindow, 'off')
VolDenPotVol = handles.VolDenPotVol;
VolRend = handles.VolRend;
Vol_x_mat = handles.Vol_x_mat;
VolPartNumberHS = handles.VolPartNumberHS;
VolConfigHS = handles.VolConfigHS;
VolCompHS = handles.VolCompHS;
% datacursormode(handles.graph_5, 'on')
% dcm = datacursormode(handles.graph_5);
datacursormode(handles.figMainWindow, 'on') %on
% dcm = datacursormode(gcf);
dcm = datacursormode(handles.figMainWindow);
set(dcm,'UpdateFcn',@(t,e) myupdatefcn_graph5(t,e,VolDenPotVol,VolRend,Vol_x_mat, VolPartNumberHS, VolConfigHS, VolCompHS, 5) );
% --- Executes on mouse press over axes background.
function graph_4_ButtonDownFcn(hObject, eventdata, handles)
% hObject handle to graph_4 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
datacursormode(handles.figMainWindow, 'off')
VolDenPotPeso = handles.VolDenPotPeso;
VolRend = handles.VolRend;
Vol_x_mat = handles.Vol_x_mat;
VolPartNumberHS = handles.VolPartNumberHS;
VolConfigHS = handles.VolConfigHS;
VolCompHS = handles.VolCompHS;
% datacursormode(handles.graph_5, 'on')
% dcm = datacursormode(handles.graph_5);
datacursormode(handles.figMainWindow, 'on') %on
% dcm = datacursormode(gcf);
dcm = datacursormode(handles.figMainWindow);
set(dcm,'UpdateFcn',@(t,e) myupdatefcn_graph5(t,e,VolDenPotPeso,VolRend,Vol_x_mat, VolPartNumberHS, VolConfigHS, VolCompHS, 4) );
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function txt = myupdatefcn(~,event,xdata,ydata,x_mat, PartNumberHS, ConfigHS, Comp, graph)
pos = get(event,'Position');
dts = get(event.Target,'Tag');
[~,j]= find( xdata==pos(1) & ydata==pos(2) );
% D = pdist2(pos(:)',[xdata(:) ydata(:)]);
% [~,j] = min(D);
% xdata(1,:) = xdata(1,:) / 1000;
% j=j(1);
switch graph
case 5
txt = {dts,...
['Densidade de Potência: ', num2str(pos(1)), ' [kW/dm³]'],...
['Rendimento: ', num2str(100 * pos(2)), ' [%]'],...
['Frequência de Chaveamento: ', num2str(xdata(1,j)), ' [Hz]' ],...
['Material do Indutor: ', x_mat{j}]...
['Dissipador: ', PartNumberHS{j}]...
['Comprimento do Dissipador: ', num2str(Comp{j}), '[dm]']...
['Configuração: ', ConfigHS{j}]};
case 4
txt = {dts,...
['Densidade de Potência: ', num2str(pos(1)), ' [kW/kg]'],...
['Rendimento: ', num2str(100 * pos(2)), ' [%]'],...
['Frequência de Chaveamento: ', num2str(xdata(1,j)), ' [Hz]' ],...
['Material do Indutor: ', x_mat{j}]...
['Dissipador: ', PartNumberHS{j}]...
['Comprimento do Dissipador: ', num2str(Comp{j}), '[dm]']...
['Configuração: ', ConfigHS{j}]};
end
end
However, I get this problem when I try to click on the second plot.

 Akzeptierte Antwort

Adam Danz
Adam Danz am 17 Apr. 2020
The datacursormode and its update function is applied to the entire figure but you can assign different update function responses to each axes by following these steps listed and demonstrated below.
  1. Assign a unique tag name to each axes so the update function can identify which axes its responding to.
  2. Define the update function after all axes are created and pass any relevant variables into the function.
  3. Within the update function, identify which axes contains the object that was clicked and create the text output according to the active axes.
See inline comments for detail.
% Create figure with 2 subplots
fig = figure();
sp(1) = subplot(2,1,1);
hold on
h(1) = plot(rand(1,20),'-o','DisplayName','A');
h(2) = plot(rand(1,20),'-o','DisplayName','B');
h(3) = plot(rand(1,20),'-o','DisplayName','C');
h(4) = plot(rand(1,20),'-o','DisplayName','D');
h(5) = plot(rand(1,20),'-o','DisplayName','E');
sp(1).Tag = 'subplot1'; % <--- Assign a unique tag to first axes
title('Click to reveal line name')
sp(2) = subplot(2,1,2);
x = linspace(0,3*pi,200);
y = cos(x) + rand(1,200);
index = 1:numel(x);
sz = 25;
c = linspace(1,10,length(x));
s = scatter(x,y,sz,c,'filled');
sp(2).Tag = 'subplot2'; % <--- Assign a unique tag to second axes
title('Click to reveal index value')
% After plotting, assign data cursor object to figure.
dcm = datacursormode(fig);
dcm.Enable = 'on';
% Create a structure that contains fields to any variable
% needed for the data cursor text for any axes.
S.index = index;
% Define the update function
dcm.UpdateFcn = @(empty,info,data)dcmUpdateFcn(empty,info,S);
function txt = dcmUpdateFcn(~, info, S)
% Identify subplot tag
subplotTag = info.Target.Parent.Tag;
% Create text output based on which subplot was identified
switch subplotTag
case 'subplot1'
% Display line name
txt = sprintf('Line Name: %s', info.Target.DisplayName);
case 'subplot2'
% Find closest point to mouse click
d = pdist2([info.Target.XData(:), info.Target.YData(:)], info.Position);
[~, minIdx] = min(d);
% Display the x value, y value, and index value
txt = sprintf('X = %.3f\nY = %.2f\ni = %d', ...
info.Target.XData(minIdx), info.Target.YData(minIdx), S.index(minIdx));
otherwise
% Define a default output for axes that aren't identified
txt = 'Undefined';
end
end
Example of result:

16 Kommentare

Thank you very much!!!
Hey, another quick question:
Is it possible to create custom data tips in a 3d plot, like a mesh?
And how do I know which curve is clicked inside the same axes?
Q1: Yes. Here's a demo.
Q2: My answer actually demonstrates this. In subplot 1, I give each line object a unique DisplayName. In the update function, the second input gives you the handle to the selected object where you can access the DisplayName property to identify the object.
Thanks again for your answer!
So this should work?
DispName = str2double(event.Target.DisplayName);
index = find(deltaI == DispName); % To find which index of the struct I'm working with
Where my DisplayName is given in a for loop like this:
txt = num2str(delta_i(k-1));
scatter(handles.graph_9, handles.PesoDenPotVol(k,1:end), handles.PesoRend(k,1:end), 'DisplayName',txt,'Tag',"Ripple: " +delta_i(k-1)*100+ " [%]");
When I try this, DispName returns NaN
I just realized that somehow the DisplayName is '30 [%]'. Why? I said it to be specificaly just the number, without the [%].
Even if I take out the [%] here, DisplayName keep having it.
..'Tag',"Ripple: " +delta_i(k-1)*100+ " [%]")
Actually, this part right here has no effect whatsoever:
, 'DisplayName',txt,
I removed this part and nothing has changed.
What can be defining the DisplayName of my plots?
Adam Danz
Adam Danz am 17 Apr. 2020
Bearbeitet: Adam Danz am 17 Apr. 2020
You have to define the DisplayName for each object you're plotting.
Look at the top of my answer.
h(1) = plot(rand(1,20),'-o','DisplayName','A');
% ^^^^^^^^^^^^^^^^^^
An alternative:
h(1) = plot(rand(1,20),'-o');
h(1).DisplayName = 'A';
Go through my answer line by line to understand what's happening. Then compare that to what you're doing to see the differences.
"You have to define the DisplayName for each object you're plotting."
I did that, inside a for loop. I also tried the alternative version (like the code below). But this isn't working.
Like a said, somehow, DisplayName doens't change, no matter what I do. It's always '30 [%]', '40 [%]' and '50 [%]', when I want '0.3', '0,4' and '0.5'. I tried searching through my code to find if it's being defined elsewhere, but I couldn't find anything.
delta_i = [0.3 0.4 0.5];
if ~isempty(handles.graph_7)
hold(handles.graph_7,'on');
ripple_leg = [];
for k = 2 : length(delta_i) + 1
%txt = ['Ripple = ',num2str(delta_i(k-1))];
txt = num2str(delta_i(k-1));
h(k) = scatter(handles.graph_7, handles.VolDenPotVol(k,1:end), handles.VolRend(k,1:end),'Tag',"Ripple: " +delta_i(k-1)*100+ " [%]");
h(k).DisplayName = num2str(delta_i(k-1));
ripple_leg = [ripple_leg ""+delta_i(k-1)*100+ " [%]"] ;
end
legend(handles.graph_7, ripple_leg);
xlabel(handles.graph_7, 'Densidade de Potência [kW/dm³]');
ylabel(handles.graph_7, 'Rendimento');
title(handles.graph_7, 'Rendimento x Densidade de Potência Volumétrica - Otimização do Volume e Diferentes Ripples');
handles.graph_7.Tag = 'ParetoV_Vol';
end
Adam Danz
Adam Danz am 17 Apr. 2020
Bearbeitet: Adam Danz am 17 Apr. 2020
Could you show me the update function? It looks like the x% value is coming from the Tag field rather than DisplayName field. If you add a legend to the plot, I bet the legend correctly shows the DisplayName values.
Here is the update function:
function txt = myupdatefcn(~, event, info)
% function txt = myupdatefcn(~, event, xdata, ydata, x_mat, PartNumberHS, ConfigHS, Comp)
plotTag = event.Target.Parent.Tag;
disp(plotTag)
assignin('base','event',event)
for k = 1 : length(info)
deltaI(k) = unique([info(k).Ripple.DeltaI]);
end
switch plotTag
% case 'Perdas_Vol'
%
% case 'Vol_Vol'
%
% case 'Peso_Vol'
%
% case 'Perdas_Peso'
%
% case 'Vol_Peso'
%
% case 'Peso_Peso'
%
case 'ParetoV_Vol'
d = pdist2([event.Target.XData(:), event.Target.YData(:)], event.Position);
[~, minIdx] = min(d);
DispName = str2double(event.Target.DisplayName);
assignin('base','DispName',DispName)
index = find(deltaI == DispName);
assignin('base','index',index)
% pos = get(event,'Position');
dts = get(event.Target,'Tag');
assignin('base','dts',dts)
% [~,j]= find( xdata==pos(1) & ydata==pos(2) );
txt = {dts,...
['Densidade de Potência: ', num2str(event.Target.XData(minIdx)), ' [kW/dm³]'],...
['Rendimento: ', num2str(100 * event.Target.YData(minIdx)), ' [%]'],...
%['Frequência de Chaveamento: ', num2str(info.VolResults(1).Fsw(minIdx)), ' [Hz]' ],...
['Frequência de Chaveamento: ', num2str(info(index).Ripple(minIdx).Fsw(minIdx)), ' [Hz]' ],...
['Material do Indutor: ', info(index).Ripple(minIdx).Material]...
['Dissipador: ', info(index).Ripple(minIdx).PartNumberHS]...
['Comprimento do Dissipador: ', num2str(info(index).Ripple(minIdx).CompHS), ' [dm]']...
['Configuração: ', info(index).Ripple(minIdx).ConfigHS]};
case 'ParetoM_Vol'
d = pdist2([event.Target.XData(:), event.Target.YData(:)], event.Position);
[~, minIdx] = min(d);
DispName = str2double(event.Target.DisplayName);
assignin('base','DispName',DispName)
index = find(deltaI == DispName);
assignin('base','index',index)
% pos = get(event,'Position');
dts = get(event.Target,'Tag');
assignin('base','dts',dts)
% [~,j]= find( xdata==pos(1) & ydata==pos(2) );
txt = {dts,...
['Densidade de Potência: ', num2str(event.Target.XData(minIdx)), ' [kW/kg]'],...
['Rendimento: ', num2str(100 * event.Target.YData(minIdx)), ' [%]'],...
['Frequência de Chaveamento: ', num2str(info(index).Ripple(minIdx).Fsw(minIdx)), ' [Hz]' ],...
['Material do Indutor: ', info(index).Ripple(minIdx).Material]...
['Dissipador: ', info(index).Ripple(minIdx).PartNumberHS]...
['Comprimento do Dissipador: ', num2str(info(index).Ripple(minIdx).CompHS), ' [dm]']...
['Configuração: ', info(index).Ripple(minIdx).ConfigHS]};
%
% case 'ParetoV_Peso'
%
% case 'ParetoM_Peso'
%
end
end
I tried removing the Tag field, and tha DisplayName didn't change. I've also added a legend to the plot in this line:
legend(handles.graph_7, ripple_leg);
The legend does indeed shows the DisplayName like x [%]. Is that the problem?
I do want the legend to be that way, while the DisplayName is like I explained before.
Thank you for your attention!
Yikes! What's with all of the assignin() functions? That's almost always a bad idea and it's highly recommended to stay away from that. That potentially causes lots of problems.
"I tried removing the Tag field, and tha DisplayName didn't change"
Nor should it. The tag property is unrelated to the DisplayName property.
The switch-case is confusing. In the code you posted earlier, the tags look something like this
. . ., 'Tag',"Ripple: " +delta_i(k-1)*100+ " [%]")
The switch-case is matching tag names,
plotTag = event.Target.Parent.Tag;
. . .
switch plotTag
but the case look nothing like that tags you're assigning.
case 'ParetoV_Vol'
. . .
case 'ParetoM_Vol'
. . .
so I have no idea how any of the cases are being matched.
In my answer, the switch-case within my update function references axes tags not line-object tags. You could change that to reference line object tags or DisplayNames instead but I get the sense that you haven't grasped what the update function is doing in my answer.
"The legend does indeed shows the DisplayName like x [%]. ... I do want the legend to be that way, while the DisplayName is like I explained before."
The DisplayName property defines the string used in the legend. If the DisplayName appears as x[%] then the displayName is doing exactly what you programmed it to do. You could use the tag instead of the DisplayName to identify the selected object but again, there's a misunderstanding of what's happening at a conceptual level.
For examle, in the block of code below, you seem to be correctly defining the DisplayName but then you override those values when you call legend().
ripple_leg = [];
for k = 2 : length(delta_i) + 1
. . .
h(k) = scatter(handles.graph_7, handles.VolDenPotVol(k,1:end), handles.VolRend(k,1:end),'Tag',"Ripple: " +delta_i(k-1)*100+ " [%]");
% Here you're assinging the TAG ------->------->------->------->------->------->-------> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
h(k).DisplayName = num2str(delta_i(k-1));
% ^^^^^^^^^^^
% Here you're assigning the DisplayName.
ripple_leg = [ripple_leg ""+delta_i(k-1)*100+ " [%]"] ;
end
legend(handles.graph_7, ripple_leg);
% ^^^^^^^^^^
% But you're overriding the Legend strings here
% which renames the DispalyName values!
When you define the DisplayName in advance, you don't need to provide legend strings. Just use,
legend(handles.graph_7);
"What's with all of the assignin() functions?"
They are just for testing. I'll delete them after everything is working fine.
That's the Tag of the plot, the switch-case is referencing the Tag of the axes, like this:
handles.graph_7.Tag = 'ParetoV_Vol';
Is that wrong or bad coding?
Hey, this worked:
legend(handles.graph_7);
Although, I want the legend to be shown like this: x * 100 [%], and the DisplayName to be x. Is that even possible? That's not really important though, just wondering.
Thank you for your help!!
Ok, glad to hear that the assignin lines will be removed. If you want to see the values of variables within the code, instead of assigning them to base, use Matlab's debugging feature by putting a break somewhere toward the top of you update function (or anywhere) and step through the code line by line. You can see the value of any variable from within the editor.
I now see that the switch case is accessing the axes tag - I must have misinterpretted that previously.
"I want the legend to be shown like this: x * 100 [%], and the DisplayName to be x. Is that even possible? "
No, for reasons I explained in my previous comment.
Sounds like you're getting closer.
Yes, I'll try later to make these data tips to the 3d plot, using the demo you provided.
Thanks again!
Glad I could help.
Hey man, I'm sorry to bother you again.
I can't make the 3d datatip to work like you said. I get the following error:
Error Message:
No appropriate method, property, or field 'DataTipTemplate' for class 'matlab.graphics.chart.primitive.Surface'.
This demo you showed only works for MatLab 2018b+. I use the 2017 version.
So, I think I have to use the myupdatefcn. However, this 3d plot is not on the GUI itself, but on another figure that is created when a button is pressed. The custom data tip is working on all the plots that are on axes of the GUI, but for this figure I can't make them work. When the user press a button, this code here will run:
handles.fig_plot3d_vol = figure;
handles.fig_plot3d_vol.Tag = 'Mesh_Pareto_Vol';
for k = 1 : length(handles.PesoResults(index_vol).Ripple)
x(k) = handles.VolResults(index_vol).Ripple(k).DensidadePotenciaPeso;
y(k) = handles.VolResults(index_vol).Ripple(k).DensidadePotenciaVol;
z(k) = handles.VolResults(index_vol).Ripple(k).Rendimento;
end
x = x';
y = y';
z = z';
x = x((all((~isnan(x)),2)));
y = y((all((~isnan(y)),2)));
z = z((all((~isnan(z)),2)));
tam = min(length(x), length(y));
tam = min(tam, length(z));
x = x(1:tam);
y = y(1:tam);
z = z(1:tam);
xlin = linspace(min(x), max(x), 145);
ylin = linspace(min(y), max(y), 145);
[X,Y]=meshgrid(xlin,ylin);
Z = griddata(x,y,z,X,Y, 'cubic');
s = mesh(X,Y,Z, 'DisplayName','Pareto - Otimização do Volume','Tag','Pareto - Otimização do Volume');
%mesh(x,y,z)
axis tight; hold on
%plot3(x,y,z, '.','MarkerSize', 15)
%plot3(x,y,z)
title('Pout / Vol x Pout / Peso x Rend - Otimização do Volume')
xlabel('Pout/Peso')
ylabel('Pout/Vol')
zlabel('Rend')
datacursormode(handles.fig_plot3d_vol, 'on') %on
handles.dcm = datacursormode(handles.fig_plot3d_vol);
set(handles.dcm,'UpdateFcn',@(t,e) myupdatefcn(t,e, handles.VolResults));
And the updatefcn is:
case 'Mesh_Pareto_Vol'
%d = norm([event.Target.XData(:) event.Target.YData(:) event.Target.ZData(:)] - event.Position)
d = pdist2([event.Target.XData(:), event.Target.YData(:), event.Target.ZData(:)], event.Position);
[~, minIdx] = min(d);
dts = get(event.Target,'Tag');
% [~,j]= find( xdata==pos(1) & ydata==pos(2) );
txt = {dts,...
['Densidade de Potência Mássica: ', num2str(event.Target.XData(minIdx)), ' [kW/kg]'],...
['Densidade de Potência Volumétrica: ', num2str(event.Target.YData(minIdx)), ' [kW/dm³]'],...
['Rendimento: ', num2str(100 * event.Target.ZData(minIdx)), ' [%]'],...
['Frequência de Chaveamento: ', num2str(info(index).Ripple(minIdx).Fsw), ' [Hz]' ]};
dataTipTextRow was released in r2019a, actually.
The datacursormode is set as a figure property. If you're applying it to a different figure, you need to set it for that figure.
Try setting the datacursormode for the new figure,
dcm2 = datacursormode(fig2);
dcm2.Enable = 'on';
and then assigning the existing update function,
dcm2.UpdateFcn = @(empty,info,data)dcmUpdateFcn(empty,info,S);
Yes, I could make it work using scatter3. My problem is doing this with a surface plot, because I have 4 vectors that I want to be displayed, the X Y Z and, for each point, a info from anothar vector, F.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (0)

Kategorien

Mehr zu Graphics Object Properties finden Sie in Hilfe-Center und File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by