Add data tips to non-supported chart

12 次查看(过去 30 天)
I'm creating a 2D graph, element by element, using rectangles. This is my current code:
clc
fig=figure('Position', [600 300 1000 1000])
ax = axes
axis([-128 128 -128 128])
xticks([-128 -112 -96 -80 -64 -48 -32 -16 0 16 32 48 64 80 96 112 128]);
yticks([-128 -112 -96 -80 -64 -48 -32 -16 0 16 32 48 64 80 96 112 128]);
grid on
grid minor
OffsetX=0;
OffsetY=0;
Axe_A = transpose(linspace(0, 96, 24))
Axe_L = transpose(linspace(50, 50, 24))
Axe_B = transpose(linspace(0, 0, 24))
InGamut = transpose(linspace(0, 0, 24))
Lab = [Axe_L, Axe_A, Axe_B];
RGB = lab2rgb(Lab)
%Add 4th column to flag out of gamut colors
RGBx = [RGB InGamut];
for ROW = 1:row
if RGB(ROW,1) < 0
RGBx(ROW,1) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,1) > 1
RGBx(ROW,1) = 1;
RGBx(ROW,4) = 1;
end
if RGB(ROW,2) < 0
RGBx(ROW,2) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,2) > 1
RGBx(ROW,2) = 1;
RGBx(ROW,4) = 1;
end
if RGB(ROW,3) < 0
RGBx(ROW,3) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,3) > 1
RGBx(ROW,3) = 1;
RGBx(ROW,4) = 1;
end
end
T = RGBx
[row,col] = size(RGB); % 16 x 3 double
for ROW = 1:row
if RGBx(ROW,4) == 0
rectangle(ax,'Position',[OffsetX OffsetY 4 4],'FaceColor',[RGBx(ROW,1) RGBx(ROW,2) RGBx(ROW,3)], 'LineStyle', 'none');
else
rectangle(ax,'Position',[OffsetX OffsetY 4 4],'FaceColor',[RGBx(ROW,1) RGBx(ROW,2) RGBx(ROW,3)]');
end
OffsetX=OffsetX+4
end
This is what the result looks like, so far :
Trouble is, there does not seem to be a way to add data tips to this graphics?
I wish I could display the Lab values when the mouse hover the graphic.

回答(1 个)

Adam Danz
Adam Danz 2022-1-18
编辑:Adam Danz 2022-1-21
Demo 1: Click rectangle to show datatip using ButtonDownFcn
This demo is based on this answer using patches. A ButtonDownFcn is assigned to each rectangle. When clicked, a datatip appears. A 1x3 vector of RGB color values is added to the datatip.
% Define rectangle data
[xo,yo] = meshgrid(1:4,1:5); % rectangle (x,y) positions
rng('default') % for reproducibility of this demo
colors = rand(numel(xo),3) % mx3 color matrix for m rectangles (aka "Lab" in your code)
labels = compose('[%.2f %.2f %.2f]',colors) % Rectangle labels
% Draw rectangles
hFig = figure();
ax = axes(hFig);
hold(ax, 'on')
for i = 1:numel(xo)
rectangle(ax, 'Position', [xo(i), yo(i), .6, .6], ...
'FaceColor', colors(i,:), ...
'LineStyle','none', ...
'UserData', labels(i), ... % Assign label to each rectangle
'ButtonDownFcn', @rectButtonDownFcn); % Assign button-down-function
end
function rectButtonDownFcn(rectObj, hit)
% Responds to mouse clicks on rectangle objs.
% Add point at click location and adds a datatip representing
% the underlying patch.
% datatip() requires Matlab r2019b or later
% store mouse click coordinate
hitPoint = hit.IntersectionPoint;
% store original hold state and return at the end
ax = ancestor(rectObj,'axes');
holdStates = ["off","on"];
holdstate = ishold(ax);
cleanup = onCleanup(@()hold(holdStates(holdstate+1)));
hold(ax,'on')
% Search for and destroy previously existing datatips
% produced by this callback fuction.
preexisting = findobj(ax,'Tag','TempDataTipMarker');
delete(preexisting)
% detect 2D|3D axes
nAxes = numel(axis(ax))/2;
% Plot temp point at click location and add datatip
if nAxes==2 % 2D axes
hh=plot(ax,hitPoint(1),hitPoint(2),'k.','Tag','TempDataTipMarker');
dt = datatip(hh, hitPoint(1), hitPoint(2),'Tag','TempDataTipMarker');
else %3D axes
hh=plot(ax,hitPoint(1),hitPoint(2),hitPoint(3),'k.','Tag','TempDataTipMarker');
dt = datatip(hh, hitPoint(1), hitPoint(2), hitPoint(3),'Tag','TempDataTipMarker');
end
dt.DeleteFcn = @(~,~)delete(hh);
clear cleanup % return hold state
% Update datatip
% pos = hit.IntersectionPoint(1:2);
dtr = dataTipTextRow('Color:', repelem(rectObj.UserData,1,4));
hh.DataTipTemplate.DataTipRows(end+1) = dtr;
end
Demo 2: Move cursor over rectangles to show datatip using WindowButtonMotionFcn
A WindowButtonMotionFcn is assigned to the figure. When the mouse is within a rectangle, a datatip appears and follows the mouse until it leaves the rectangle. A 1x3 vector of RGB color values is added to the datatip. For simplicity, this demo assumes rectangles do not overlap and that their edges are parallel to the x and y axes. If any rectangle is deleted, the WindowButtonMotionFcn will be removed.
% Define rectangle data
[xo,yo] = meshgrid(1:4,1:5); % rectangle (x,y) positions
rng('default') % for reproducibility of this demo
colors = rand(numel(xo),3) % mx3 color matrix for m rectangles (aka "Lab" in your code)
labels = compose('[%.2f %.2f %.2f]',colors) % Rectangle labels
% Draw rectangles
hFig = figure();
ax = axes(hFig);
hold(ax, 'on') % required by WindowButtonMotionFcn
rectHandles = gobjects(size(xo));
for i = 1:numel(xo)
rectHandles(i) = rectangle(ax, ...
'Position', [xo(i), yo(i), .6, .6], ...
'FaceColor', colors(i,:), ...
'LineStyle','none', ...
'UserData', labels(i), ... % Assign label to each rectangle
'DeleteFc', @(h,~)set(ancestor(h,'figure'), 'WindowButtonMotionFcn',''));
% Remove WindowButtonMotionFcn if any rectangle is deleted
end
% Assign WindowButtonMotionFcn to figure
hFig.WindowButtonMotionFcn = {@rectButtonMoFcn, rectHandles(:), ax};
function rectButtonMoFcn(~, ~, rectObj, axHandle)
% Responds to mouse movement in figure (fig).
% Displays datatip if within rectangle (rectObj).
% This fcn should be as efficient and simple as possible.
% datatip() requires Matlab r2019b or later
persistent floatingMarkerHandle
% Create floating marker if it doesn't exist
if isempty(floatingMarkerHandle) || ~isvalid(floatingMarkerHandle)
floatingMarkerHandle = plot(axHandle,rectObj(1).Position(1),rectObj(1).Position(2),'k.','Visible','off');
datatip(floatingMarkerHandle, floatingMarkerHandle.XData, floatingMarkerHandle.YData);
dtr = dataTipTextRow('Color:', repelem({'[0 0 0]'},1,4));
floatingMarkerHandle.DataTipTemplate.DataTipRows(end+1) = dtr;
end
% get rectangle values
rectPos = cell2mat(get(rectObj,'position'));
rectX = [rectPos(:,1), rectPos(:,1)+rectPos(:,3)];
rectY = [rectPos(:,2), rectPos(:,2)+rectPos(:,4)];
% Determine which rectangle the mouse is in, if any.
% Assumes rectangle edges are vertical or horizontal (parallel to x,y axes)
cp = axHandle.CurrentPoint(1,1:2); % Current mouse point, data units, 2D axes.
rectIdx = cp(1) >= rectX(:,1) & ...
cp(1) <=rectX(:,2) & ...
cp(2) >= rectY(:,1) & ...
cp(2) <=rectY(:,2);
% Update floating marker and data tip if inside a rectangle; otherwise turnoff
if any(rectIdx)
set(floatingMarkerHandle,'XData',cp(1),'YData',cp(2),'Visible','on');
rectUserData = rectObj(rectIdx).UserData; % Assumes only 1 rectangle is selected (ie, no overlap)
floatingMarkerHandle.DataTipTemplate.DataTipRows(end).Value = rectUserData;
else
set(floatingMarkerHandle,'Visible','off');
end
end
  8 个评论
Roger Breton
Roger Breton 2022-1-20
I take your suggestions very well, Adam! I experimented at some point with passing the axis handle in the callback function parameters but it did not solve my problem then? But I'll 'revisit' it -- promised.
I was thinking about using a fixed text box in the figure margin that displays the value but it's not as "live" or "interactive" as having a datatip exactly where the mouse points, which is the 'point' I want to nail with students...
Adam Danz
Adam Danz 2022-1-21
编辑:Adam Danz 2022-1-31
I've added a 2nd demo to my answer showing how to apply the same method as my first demo to a WindowButtonMotionFcn so that datatips show automatically when hovering over rectangels.
I believe this is what you were describing in this comment.

请先登录,再进行评论。

类别

Help CenterFile Exchange 中查找有关 Line Plots 的更多信息

产品


版本

R2021b

Community Treasure Hunt

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

Start Hunting!

Translated by