Best way to pass uicontrols/data to callback function.

8 vues (au cours des 30 derniers jours)
Matteo Janssen
Matteo Janssen le 3 Déc 2020
Commenté : Rik le 4 Déc 2020
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
  1 commentaire
Matteo Janssen
Matteo Janssen le 3 Déc 2020
Update: I found out why the second option did not work. Apparently it does work, however, you cannot check the content of guidata if you use the debugger. If you don't debug, writing 'data = guidata(hObject)' does not result in an error. When debugging on this line, it does however.
So my only question remaining is how to reuse the same code of the uicontrols made in the main function in the callback functions.

Connectez-vous pour commenter.

Réponse acceptée

Rik
Rik le 3 Déc 2020
Modifié(e) : Rik le 3 Déc 2020
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.
  2 commentaires
Matteo Janssen
Matteo Janssen le 3 Déc 2020
I agree, but if I store all the variables in a struct and give it to guidata, I find it really cumbersome to write:
data = guidata(hObject);
and then everytime write:
data.whatever_I_need
or add for every input_argument:
input1 = data.input1
as I have a lot of input arguments to use.
Anyway, thank you so much, I made a draw_function which does the trick! I'm not sure if updating the objects can be done, as I need to clear the figure in order to redraw. But I certainly agree that it would be much better practice otherwise.
Rik
Rik le 4 Déc 2020
I use deal to put everything on one line at the start of a function:
[a,b]=deal(data.a,data.b);
I don't see any indication why you would need to clear the figure instead of adjusting the properties of the objects you created. All graphics functions return an explicit handle, so you should adjust the plotRacetrack function so it returns the handles to the objects it creates. I'm also not sure if the data in guidata survives clf, but as you don't report an issue I suspect it does.

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur App Building dans Help Center et File Exchange

Produits

Community Treasure Hunt

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

Start Hunting!

Translated by