Issue Integrating figure, uicontrol, and callback function

2 views (last 30 days)
Hello,
I am trying to build a figure with a clickable legend that allows the user to make any of the multiple data series on my plot visible or invisible. I am using Matlab R2017a.
Due to the >50 quantity of data series, I am using a uicontrol listbox as opposed to a conventional legend. My code works by creating the plot, adding the listbox, and controlling the listbox via a Callback function. I am relatively new to the concept of Callbacks, so I'd bet my issue lies there.
When the user clicks on a listbox item, the item's ID is grabbed by the Callback function. There it is paired with the data series matching that ID (using the original plot array), and is either made visible or invisible per the user's selection. I have passed the plot array from my workspace to the callback via the evalin function.
I have created something that works perfectly for my purposes...that is UNTIL I close the initial figure. If I save it and then reopen it, I lose the ability to interact with it, receiving the following error:
Invalid or deleted object.
Error in HitCallbackLB (line 23)
pltLB(i).LineStyle = 'none';
Error while evaluating UIControl Callback.
My guess is that the evalin is the wrong choice for executing the plot connection between workspace and Callback, but I'm at a loss regarding how to fix it - I am unsure how to get my Callback function to better integrate with my figure and the rest of my code.
Here's the main code:
% initialize fullscreen figure
figure('units', 'normalized', 'outerposition', [0 0 1 1])
% loop through each dataset
for i = 1:length(P)
hold on
% iteratively add each dataset to plot
plt(i) = plot(tP{i},P{i},'LineStyle','none');
hold off
end
% create cell array holding all trial names
listUI = {};
for i = 1:length(P)
listUI{length(listUI)+1} = <source of string names>
end
% create clickable data series selection functionality for 2D figure
% add listbox user interface control, which will call HitCallbackLB function when selection(s) made
uiCtrl = uicontrol('style','listbox','max',2,'min',0,'fontsize',6,'position',[1250 100 115 600],...
'string',listUI,'callback',@HitCallbackLB);
And here is my Callback code (note: the for loop may not be the cleanest solution, but doesn't come at a runtime cost so I'm ok with it for now):
% Callback Function
function HitCallbackLB(hObject, eventdata, handles)
% copy over variable 'plt' from main workspace (full collection of figure's data series)
pltLB = evalin('base','plt');
% get index and string value(s) of user selection(s) in listbox
itemsLB = get(hObject,'String');
indexLB = get(hObject,'Value');
% each time user makes selection, re-execute loop through each data series in plt variable
for i = 1:length(pltLB)
% if data series listbox entry discovered to be clicked:
if ismember(i,indexLB)
% turn off data series on figure
pltLB(i).LineStyle = 'none';
% turn on data series on figure
pltLB(i).LineStyle = '-';
% if data series listbox entry discovered to not be clicked:
else
% turn off data series on figure
pltLB(i).LineStyle = 'none';
end
end
Any further insight into how to properly use Callbacks would be much appreciated! The goal is to share the final figure with several other folks, so the inability for even me to reopen it is a dealbreaker at the moment. I think I'm close but I could use some advice.
Thanks a bunch, Charlie
  2 Comments
Greg
Greg on 18 Jan 2018
Ignoring pre-allocation discussions, at least replace
listUI{length(listUI)+1}
with
listUI{end+1}
Charles Arentzen
Charles Arentzen on 18 Jan 2018
Hey Greg, you're absolutely right - no reason not to preallocate that array.

Sign in to comment.

Accepted Answer

Greg
Greg on 18 Jan 2018
Store plt in the listbox's UserData property.
uiCtrl = uicontrol('style','listbox','max',2,'min',0,'fontsize',6,'position',[1250 100 115 600],...
'string',listUI,'callback',@HitCallbackLB,'UserData',plt);
Then, retrieve it from there inside the callback.
pltLB = hObject.UserData;
  2 Comments
Charles Arentzen
Charles Arentzen on 18 Jan 2018
Hey Greg, that works like a charm! Good to see that variables can be passed directly through a function argument. Really appreciate your help,
Charlie
Greg
Greg on 18 Jan 2018
It gets even easier in AppDesigner.
Happy to help.

Sign in to comment.

More Answers (0)

Categories

Find more on MATLAB in Help Center and File Exchange

Tags

Community Treasure Hunt

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

Start Hunting!