MATLAB: Best way to pass uicontrols/data to callback function.

callback functionguidatahandlesMATLAB

Notes:
plotRaceTrack is a function in which I use a couple of plot- and fill-commands.
plotSimulationPredictionInteractable is the main function. Here I plot a racetrack (using plotRaceTrack), then plot 3 trajectories & add 3 buttons and an editbox. The idea is that everytime I push a button, these 3 trajectory-plots change as a result of some parameter changes.
I will add a corresponding code extract below (only first 2 functions, others are completely similar). My problems with it are twofold:
  • Currently, I clear the figure made in the main function in order to redraw the racetrack (which isn't actually necessary, but I don't know how to separately clear plots from a figure, and it isn't computationally heavy to redraw, so this does not concern me too much) and update the 3 trajectory plots. Unfortunately, clearing the figure also clears my buttons (uicontrols), which I currently solved by copy pasting the code of the main function. I feel like there should be a more efficient way to do this, but I can't seem to figure out how to store the uicontrols & call them again…
  • Minor problem: I currently use the callback function with a lot of input arguments. I thought I could avoid this storing the needed data: guidata(f, data) (in which f = figure in main loop) and unwrapping it in the callback function as data = guidata(hObject) as is described here (fourth option): https://nl.mathworks.com/help/matlab/creating_guis/share-data-among-callbacks.html#bt9p4xi, but I get an error 'Object must be a figure or one of its child objects.', which does not allow me to do so. This is a minor issue, but I feel like it is correlated to the first one, as if I know how to correctly pass the uicontrols to the callback functions, I should also be able to figure out an efficient way to pass the needed data.
Thank you!
function plotSimulationPredictionInteractable(track, start, N, X, Y, predX, predY, refX, refY, colors)
w = 0.47;
figure;
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
plotRaceTrack(track);
axis equal;
% Start at given starting position
% End at start+1 for now
eind = start+1;
plot(refX(start:eind),refY(start:eind), colors(1), 'LineWidth', 2)
plot(X(start:eind),Y(start:eind), colors(2), 'LineWidth', 2)
plot(predX((eind-1)*N+1:(eind-1)*N+N), predY((eind-1)*N+1:(eind-1)*N+N), '*g', 'MarkerSize', 1);
title('Race Track Simulation With Predictions');
xlabel('X (m)');
ylabel('Y (m)');
L(1) = plot(nan, nan, 'color', colors(1));
L(2) = plot(nan, nan, 'color', colors(2));
L(3) = plot(nan, nan, '*g');
legend(L, {'Reference track', 'NonLinear MPC', 'State Predictions'});
% Create a pushbutton to go to next state:
pb1 = uicontrol('style','push',...
'units','normalized',...
'position',[0.3 0.03 0.15 0.05],...
'fontsize',14,...
'string','Next State',...
'callback',{@(src,evnt)pb_next(track, start, eind, N, X, Y, predX, predY, refX, refY, colors, w)});
pb2 = uicontrol('style','push',...
'units','normalized',...
'position',[0.5 0.03 0.15 0.05],...
'fontsize',14,...
'string','Previous State',...
'callback',{@(src,evnt)pb_prev(track, start, eind, N, X, Y, predX, predY, refX, refY, colors, w)});
editBox = uicontrol('Style', 'Edit',...
'units','normalized',...
'String', '',...
'HorizontalAlignment', 'Center',...
'Position', [0.04 0.5 0.05 0.05]);
pb3 = uicontrol('style','push',...
'units','normalized',...
'position',[0.04 0.45 0.05 0.05],...
'fontsize',10,...
'string','Change Start',...
'callback',{@(src,evnt)pb_start(track, N, X, Y, predX, predY, refX, refY, colors, w, editBox)});
end
% Callback function for the pushbutton to go to the next state.
function pb_next(track, start, eind, N, X, Y, predX, predY, refX, refY, colors, w)
clf;
plotRaceTrack(track);
eind = min(length(X)-1,eind+1);
plot(refX(start:eind),refY(start:eind), colors(1), 'LineWidth', 2)
plot(X(start:eind),Y(start:eind), colors(2), 'LineWidth', 2)
plot(predX((eind-1)*N+1:(eind-1)*N+N), predY((eind-1)*N+1:(eind-1)*N+N), '*g', 'MarkerSize', 1);
axis equal;
title('Race Track Simulation With Predictions');
xlabel('X (m)');
ylabel('Y (m)');
xlim([min(min(X(start:eind)), min(predX((eind-1)*N+1:(eind-1)*N+N)))-w, max(max(X(start:eind)), max(predX((eind-1)*N+1:(eind-1)*N+N)))+w]);
ylim([min(min(Y(start:eind)), min(predY((eind-1)*N+1:(eind-1)*N+N)))-w, max(max(Y(start:eind)), max(predY((eind-1)*N+1:(eind-1)*N+N)))+w]);
L(1) = plot(nan, nan, 'color', colors(1));
L(2) = plot(nan, nan, 'color', colors(2));
L(3) = plot(nan, nan, '*g');
legend(L, {'Reference track', 'NonLinear MPC', 'State Predictions'});
pb1 = uicontrol('style','push',...
'units','normalized',...
'position',[0.3 0.03 0.15 0.05],...
'fontsize',14,...
'string','Next State',...
'callback',{@(src,evnt)pb_next(track, start, eind, N, X, Y, predX, predY, refX, refY, colors, w)});
pb2 = uicontrol('style','push',...
'units','normalized',...
'position',[0.5 0.03 0.15 0.05],...
'fontsize',14,...
'string','Previous State',...
'callback',{@(src,evnt)pb_prev(track, start, eind, N, X, Y, predX, predY, refX, refY, colors, w)});
editBox = uicontrol('Style', 'Edit',...
'units','normalized',...
'String', '',...
'HorizontalAlignment', 'Center',...
'Position', [0.04 0.5 0.05 0.05]);
pb3 = uicontrol('style','push',...
'units','normalized',...
'position',[0.04 0.45 0.05 0.05],...
'fontsize',10,...
'string','Change Start',...
'callback',{@(src,evnt)pb_start(track, N, X, Y, predX, predY, refX, refY, colors, w, editBox)});
end

Best Answer

I would suggest splitting everything into smaller functions. If you have a function that changes the content of buttons: write it once and call it from both the main function and the callback. I would strongly suggest that you only use the two standard input arguments for callbacks and retrieve all settings and data from the guidata struct.
You never store the handles to your objects in guidata, so it is difficult to use the best practice: don't recreate objects, but change the properties instead.
If this is not clear to you, feel free to comment.
Related Question