GUIDE listbox context menu callback fails to update handles using guidata

5 vues (au cours des 30 derniers jours)
Jian Zhou
Jian Zhou le 16 Août 2017
Commenté : Jian Zhou le 17 Août 2017
Hey everybody,
has anyone encountered the following problem when trying to make a GUI using GUIDE. I wanted to have a labeltool, which contains a pushbutton named "add category". When the user press this button, he/she will be prompted to enter a name for a new label category. The GUI also contains a listbox showing/listing all categories the user has ever entered. Now what i want is to have a context menu showing up when right-clicking on an item on the listbox. I achieved this with the help from Undocumented Matlab by modifying the java component underneath the Matlab uicontrol. On the emerging context menu, i have a submenu named "add new category", when selected, it will call the callback of pushbutton "new category". Now the problem is the following:
When i press the pushbutton, every thing works just fine. The pushbutton_Callback(hObject, eventdata, handles) gets called and executes. It also stores the new listitem (using guidata(hObject, handles)) in an handle to feed the listbox, which then shows the new added category. Now when I try to call the pushbutton_Callback from the context menu within the listbox, an error occurs, because the callback input arguments hObject and eventdata are now no longer Matlab UIcontrols, but Java objects. So guidata(hObject, handles) won't work as desired.
I uploaded the corresponding .fig and .m file for the gui. I really appreciate any kind of suggestion and help. The function findjobj.m will be needed for this code to work. This can be downloaded from hier. Just put them all in one folder and the code should run.
This is how the pushbutton callback looks like:
% --- Executes on button press in pushbutton_addCategory.
function pushbutton_addCategory_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton_addCategory (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% prompt user to enter name for new label category
prompt = {'Enter category name:'};
dlg_title = 'Add new label category';
num_lines = 1;
defaultans = {'MyLabelCategory'};
thisLabelCategory = inputdlg(prompt,dlg_title,num_lines,defaultans);
if ~isempty(thisLabelCategory)
% update list box by adding new category
handles.labelCategories{end+1} = char(thisLabelCategory);
set(handles.listbox_labelCategories,'Value',1);
set(handles.listbox_labelCategories,'String',handles.labelCategories);
end
guidata(hObject, handles);
And this is where it gets called from when using the context menu:
% --- Executes on selection change in listbox_labelCategories.
function listbox_labelCategories_Callback(hObject, eventdata, handles)
% Get the listbox's underlying Java control
jScrollPane = findjobj(handles.listbox_labelCategories);
% We got the scrollpane container - get its actual contained listbox control
jListbox = jScrollPane.getViewport.getComponent(0);
% Convert to a callback-able reference handle
jListbox = handle(jListbox, 'CallbackProperties');
% Prepare the context menu
menuItem1 = javax.swing.JMenuItem('Add new category');
% Set the menu items' callbacks
set(menuItem1,'ActionPerformedCallback',{@pushbutton_addCategory_Callback, handles}); % called here!!!!!!
% Add all menu items to the context menu (with internal separator)
jmenu = javax.swing.JPopupMenu;
jmenu.add(menuItem1);
jmenu.addSeparator;
% Set the mouse-click event callback
set(jListbox, 'MousePressedCallback', {@mousePressedCallback,handles.listbox_labelCategories,jmenu});
Thanks a lot in advance.

Réponse acceptée

Adam
Adam le 16 Août 2017
Modifié(e) : Adam le 16 Août 2017
You shouldn't really be calling a uicomponent's callback manually - it doesn't make sense. Factor out the actual functionality into a function that takes only those arguments it needs (detached from random irrelevant stuff like event data) and call that from both places.
The second point is that you should never pass 'handles' to a callback yourself like this:
set(menuItem1,'ActionPerformedCallback',{@pushbutton_addCategory_Callback, handles});
What this will do is take a copy of the handles struct as it is at the time you make this set call and it will assign that to the callback so you will always get that version of handles in the callback, not the latest. Instead you should pass
handles.figure1
That is the default tag for a GUIDE main figure. I never leave it as that unless I forget, I name it something sensible (in my case I always give it the name of the GUI file). That is a static handle and from it you can call
handles = guidata( hGUI )
where hGUI is the handle you passed in.
So create a new callback for your ActionPerformedCallback, pass in handles.figure1 and have it extract handles and whatever inputs you then need to call the functionality of the pushbutton callback that you earlier factored out into a function that takes sensible inputs.
If you want to be simple then that factored out function can just take the handles struct as an input argument from both places. I don't like this much myself as passing the whole handles structure around all over the place is ugly, but it does at least contain all the things you should need.
  3 commentaires
Adam
Adam le 17 Août 2017
"calling a uicomponent's callback manually" means you explicitly using that callback in your own code, in this case passing it to a different component as its callback. A GUIDE-created callback is specific to a component, it takes input arguments that GUIDE generates that are related to that component. Calling it yourself where you don't have those expected components just doesn't make sense. If two places want to do the same thing (e.g. often a slider and an edit box) then you should simply create your own function that takes just the arguments it needs to perform its task and you can then call that from the two independent callbacks - in your case the pushbutton and the listbox context menu.
In terms of storing guidata, if you are passing in handles.figure1 then in the same way you can get the handles from it you can also set the handles on that handle, not on hObject i.e.
handles = guidata( hGUI )
...
guidata( hGUI, handles )
where hGUI is the handles.figure1 that you passed in. If you had factored out your pushbutton callback you wouldn;t then have this spurious concept of 'hObject' that is getting passed in, but isn't what it is expected to be.
Jian Zhou
Jian Zhou le 17 Août 2017
Thank you for your explanation. I think i should start learning the basics in GUI programming before writing codes with twisted dependencies.

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur Interactive Control and Callbacks dans Help Center et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by