How do I end a function that is using callbacks in Matlab? - matlab

I am trying to write some code that allow me to 'drag and drop' some lines over a figure so that I can obtain a particular coordinate: 'pt'. I am adapting some code from the internet that uses call backs, which is doing what I want it to do. However, I am unable to end the function or transfer the coordinate 'pt' to another function. I have attempted to use 'return', but it doesn't seem to work. I also tried to use 'while', but it caused Matlab to crash.
I am new to programming, so detailed suggestions would be appreciated.
function [pt] = movelines
global pt;
f=figure;
aH=axes('Xlim',[0, 1],'YLim', [0 1]);
key=0;
H=line([0 1], [0.5 0.5], ... %from [x1 x2] to [y1 y2]
'color', 'yellow',...
'linewidth', 3);
V=line([0.5 0.5], [0 1], ... %This line sets the length of the line. From [x1 x2] to [y1 y2]
'color', 'red', ...
'linewidth',3,...
'ButtonDownFcn', #startDragFcn);
set(f, 'WindowButtonUpFcn',#stopDragFcn);
mov_cancel = uicontrol(f,'Style','pushbutton',...
'Position',[30,130,110,30],...
'String','(cancel)',...
'BusyAction','cancel',...
'TooltipString','BusyAction = cancel',...
'Callback',#funct_cancel);
function startDragFcn (gcbo,eventdata,handles)
set(f,'WindowButtonMotionFcn',#draggingFcn)
end
function draggingFcn (gcbo,eventdata,handles)
pt=get(aH,'CurrentPoint');
set(V,'Xdata',pt(1)*[1 1]);
set(H,'Ydata',pt(1,2)*[1 1])
end
function stopDragFcn (gcbo,eventdata,handles)
set(f,'WindowButtonMotionFcn','');
end
function funct_cancel (gcbo,eventdata,handles)
key=key+1
return
end
end
To use the code, click and hold the vertical line. The function is supposed to end when the cancel button is clicked.

You can use the Matlab function waitfor to wait for a uicontrol object to close. I've also added functionality to stop allowing the user to drag the lines around after you cancel.
function [pt] = movelines()
f=figure;
enabled = 1;
aH=axes('Xlim',[0, 1],'YLim', [0 1]);
key=0;
H=line([0 1], [0.5 0.5], ... %from [x1 x2] to [y1 y2]
'color', 'yellow',...
'linewidth', 3);
V=line([0.5 0.5], [0 1], ... %This line sets the length of the line. From [x1 x2] to [y1 y2]
'color', 'red', ...
'linewidth',3,...
'ButtonDownFcn', #startDragFcn);
set(f, 'WindowButtonUpFcn',#stopDragFcn);
mov_cancel = uicontrol(f,'Style','pushbutton',...
'Position',[30,130,110,30],...
'String','(cancel)',...
'BusyAction','cancel',...
'TooltipString','BusyAction = cancel',...
'Callback',#funct_cancel);
function startDragFcn (gcbo,eventdata,handles)
if enabled
set(f,'WindowButtonMotionFcn',#draggingFcn)
end
end
function draggingFcn (gcbo,eventdata,handles)
pt=get(aH,'CurrentPoint');
set(V,'Xdata',pt(1)*[1 1]);
set(H,'Ydata',pt(1,2)*[1 1])
end
function stopDragFcn (gcbo,eventdata,handles)
set(f,'WindowButtonMotionFcn','');
end
function funct_cancel (gcbo,eventdata,handles)
key=key+1
enabled=0;
delete(mov_cancel);
end
waitfor(mov_cancel);
end

So I assume there is a reason that you aren't using ginput. If you haven't considered it then you should, see: http://uk.mathworks.com/help/matlab/ref/ginput.html
So assuming you want to proceed with your current approach:
Your code at the moment seems to terminate fine, do you mean that you want to close the figure. If so you can do this using delete(gcf) within your callback function.
In terms of returning the current point you could declare pt as a global variable. You can then use it anywhere you want. Remember to declare a global variable at the beginning of each function you use it in.
function draggingFcn (gcbo,eventdata,handles)
global pt;
pt=get(aH,'CurrentPoint');
set(V,'Xdata',pt(1)*[1 1]);
set(H,'Ydata',pt(1,2)*[1 1])
end
Hope I've answered your questions there. If you can though I would seriously consider ginput

Related

MATLAB show all 'active' plots, plt.show()?

hold on
ax = gca;
plot(ax, [1 2 3])
hold off
hold on
ax = gca;
plot(ax, [3 2 1])
hold off
Python's Matplotlib has plt.show() which can be used like
plt.plot([1, 2, 3])
plt.show()
plt.plot([3, 2, 1])
plt.show()
to display two separate figures. I've tried to recreate this behavior with the top block, with no success. Can it be done?
Context
I know this can be accomplished with explicit calls to figure and axes, but the idea is for this to happen after we finish plotting, not before we begin. That is, I define convenience plot functions
function plot1(varargin)
% do stuff
end
function plot2(varargin)
% do stuff
end
and each has a keyword argument show, which is to be used like
plot1(x, show=true)
plot2(x, show=true)
so plot2 should figure out that something's plotted before it without user input.
I tested below to work as intended with calls to variants of plot, scatter, and image. Also, as in Python, show=false for the last executed plot still acts same as show=true.
plot2([1 2 3], show=1)
plot2([3 2 1])
plot2([1 2 3])
plot2([3 2 1])
function plot2(x, C)
arguments
x;
C.show = false;
end
hold on
fig = gcf();
ax = gca();
plot(ax, x);
maybe_show(fig, ax, C.show)
end
function maybe_show(fig, ax, show_)
fig.Visible = true;
if show_
figure('Visible', 'off')
hold off
end
end

Multiple custom graphics object inheriting from ChartContainer

I am trying to write a custom chart (or graphical object, rather), and I would like to plot several of them in a single figure. I am following this guide, but I keep getting:
Error using matlab.graphics.chartcontainer.ChartContainer
Adding TestChart to axes is not supported. Turn hold off.
In my custom class I am combining a line and a surface, but this a minimal class works as an example:
classdef TestChart < matlab.graphics.chartcontainer.ChartContainer
properties (Access = private, Transient, NonCopyable)
% Chart axes.
HandleLine(1,1) % Handle of the line object
end
properties
Axes
XData double {mustBeReal}
YData double {mustBeReal}
ZData double {mustBeReal}
end
methods (Access = protected)
function setup(this)
this.Axes = getAxes(this);
% Create line handle
this.HandleLine = plot3(this.Axes, NaN, NaN, NaN, '-o');
end
function update(this)
this.HandleLine.XData = this.XData;
this.HandleLine.YData = this.YData;
this.HandleLine.ZData = this.ZData;
end
end
end
And then, if I try creating two of them:
a = TestChart('XData', [0 0], 'YData', [0 0], 'ZData', [1 2]);
b = TestChart('XData', [0 0], 'YData', [0 0], 'ZData', [5 8]);
I get the error saying "Adding TestChart to axes is not supported. Turn hold off.".
Is it possible to create custom graphics object that can be plotted several times in the same axex ?
Thanks in advance!

How to plot an array using a for loop?

I want to plot inside a for loop. I tried the following code also used hold on but the plot is still blank. I don't know where I am getting wrong.
M2 = true(21, 6);
M2(1:2, 3:5) = false;
R = [0.5:0.1:2.5];
H=[0:5:25];
m=21,
n=6,
for i=1:m
for j=1:n
if M(i,j)==0
plot(H(i),R(j), 'color', 'r')
drawnow();
end
end
end
As Ander Biguri already said, the plot function actually does not draw anything when you provide only one point and do not specify any markers.
But, if you want to create some animated plot, that one of the points is displayed at a time:
h = scatter(0, 0, 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);
for i=1:m
for j=1:n
if M(i,j)==0
set(h, 'xdata', H(i), 'ydata', R(j));
pause(0.5);
drawnow();
end
end
end
Or if you want to show all point together, you don't even need a loop:
[HH, RR] = meshgrid(H, R);
scatter(HH(~M), RR(~M), 'r');
xlim([min(H) max(H)]);
ylim([min(R) max(R)]);

Updating plot while maintaining axes rotations?

So far I have a 3d plot that updates in realtime during a Monto-Carlo simulation of a Kessel Run. I want the plot to be rotatable while updating, but with the rotate3d flag enabled, when I call drawnow after updating the plot data, it resets the axes rotation to the default orientation, canceling whatever the user changed it to. My relevant code is as follows:
s = scatter3(pg(:,1), pg(:,2), pg(:,3), 'O', 'filled');
set(s, 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'k');
hold on
p_x = curr_path(:,1);
p_y = curr_path(:,2);
p_z = curr_path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'k');
p_x = longest.path(:,1);
p_y = longest.path(:,2);
p_z = longest.path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'r');
p_x = shortest.path(:,1);
p_y = shortest.path(:,2);
p_z = shortest.path(:,3);
p = plot3(p_x, p_y, p_z);
set(p, 'LineWidth', 2, 'Color', 'b');
sample_str = sprintf('%d samples', sample);
short_str = sprintf('shortest: %g parsecs', shortest.dist);
long_str = sprintf('longest: %g parsecs', longest.dist);
title(sprintf('%s, %s, %s', sample_str, short_str, long_str));
xlim([-10 10]);
ylim([-10 10]);
zlim([-10 10]);
hold off
drawnow
This is executed every time I update the data being drawn on the plot. What can I add to ensure that the axes rotation is maintained during an update?
I hope I understood you problem right. Well, here goes!
I suppose the problem is related to using plot3, which apparently resets the view settings of the figure to their defaults.
I verified this with the following program:
figure;
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3));
drawnow
pause; %// do rotations in the figure GUI, press a button when done
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3)); %// rotations reset
drawnow;
pause
You can rotate the figure when the pause is active, but when the pause is released and the second call to plot3 made, the rotation settings are reset.
A solution which avoids the reset is to directly update the XData, YData, and ZData in the already drawn graphics object. This can be achieved as follows:
figure;
x = randn(10,3);
p = plot3(x(:,1), x(:,2), x(:,3));
drawnow
pause; %// do rotations in the figure GUI, press a button when done
x = randn(10,3);
set(p, 'XData', x(:,1), 'YData', x(:,2), 'ZData', x(:,3)); %// no reset!
drawnow;
pause
So whatever code you have, use the handle of the graphics object to directly update the line properties to avoid the reset.
I had the same problem, in Matlab 2015b you can solve it in this simple way.
[az,el] = view;
scatter3(x,y,z);
view(az,el);
drawnow

GUI Axes Handle Hold On/Off Not Working Within Callback Function - Matlab

Firstly, programatically, I created and axes object, then an empty scatter which is subsequently populated with data from within another callback function named guiList which works fine. However, as my title states, I cannot seem to be able to get the hold function to work when I pass my axes_h handle into my callback function 'guiHold`.
Here is my scripted code for axes and scatter:
%% create empty scatter plot with panel
e_panel_position = [0.1 0.3 0.5 0.6];
e_panel_h = uipanel('Parent', fig_h, 'Title','Emotion','FontSize',12,'FontWeight', 'bold','BackgroundColor','white','Position',e_panel_position);
axes_position = [0.15 0.12 0.7 0.8];
axes_h = axes('Parent', e_panel_h, 'Position', axes_position);
scatter_h = scatter(axes_h, [],[], 'MarkerEdgeColor',[0 .5 .5], 'MarkerFaceColor',[0 .7 .7],'LineWidth',1.5);
axis(axes_h, [-4 4 -4 4]);
xlabel(axes_h, 'Valence', 'FontSize', 12); % 'FontWeight', 'bold'
ylabel(axes_h, 'Arousal', 'FontSize', 12);
grid on
box on
Here is my guiHold function:
function guiHold(hold_toggle_h, evt, axes_h)
button_state = get(hold_toggle_h,'Value');
if button_state == 1
hold(axes_h, 'on')
%hold on
elseif button_state == 0
hold(axes_h, 'off')
%holf off
end
end
As you may see, I have tried alternative versions of hold to try and make this
happen.
guiHold is being invoked with a GUI toggle button.
EDIT:
Here is my guiList function that is invoked when you select an item from my list_h handle:
function guiList(list_h, evt, scatter_h)
global predict_valence
global predict_arousal
val = get(list_h, 'value');
a = 100;
x = predict_valence;
y = predict_arousal;
N = length(predict_valence);
for i=1:N
if i == val
set(scatter_h, 'XData', x(i), 'YData', y(i), 'SizeData', a);
end
end
end