matlab - 带输入参数的 GUIDE - uimenu 自动更新其子菜单

matlab - GUIDE with input arguments - uimenu autoupdates its submenus

我有一个包含一些调试选项的结构,即:dbs = struct('db1', 0, 'db2', 1)。并且用户可以稍后向该结构添加更多调试选项(db3db4 等),以及更改现有字段的值 db1db2.

我想用 uimenu Debug options 创建一个 GUI dbGUIdbGUI 应该将 dbs 作为输入参数,所以我将 varargin 放到我的 GUI 的 OpeningFcn 中,这样我就可以从命令 window 调用 GUI,例如 dbGUI(dbs):

function dbGUI_OpeningFcn(hObject, eventdata, handles, varargin)
handles.dbNames = fieldnames(varargin{1});
handles.dbVal = cellfun(@(i) varargin{1}.(i), handles.dbNames);

uimenu "Debug options" 应显示与 dbs 的字段对应的子菜单。我想要的是:当用户从 GUI 外部修改结构 dbs 时如何自动更新子菜单(意味着在 Matlab 命令中 window)?

正如下面@marco wassmer 所建议的,结构 dbs 应该被声明为句柄 class 继承自 dynamicprops 并使用 addlistener。问题是:当用户在命令Window中更改dbs时,函数updateFoo无法获取dbGUI的句柄,因此无法更新句柄的值,最后uimenu_CallbackFcn 无法更新新字段和值。

注意:我尝试以编程方式使用 GUI,但我更喜欢 GUIDE,因为它更容易布置组件。

您有两个选择:

  • 每次用户点击时更新菜单,像这样:

    handles.dbs_menue = uimenu(f,'Label','Debug options','Callback','@foo')
    
    .
    .
    .
    
    function foo(hObj)
        dbs=getVariable('base','dbs')
        handles.dbO = fieldnames(dbs.dbOptions)
        % and replace all menue items...
    

根据 matworks,这是 推荐的, 来源:http://ch.mathworks.com/help/matlab/ref/uimenu-properties.html#prop_Callback

  • 其他方法是将 dbs 变成一个句柄对象并在 GUI 中添加一个侦听器。可能比您想象的要复杂一些,但就是这样。 别担心,它的工作方式几乎与结构相同...... 首先,您需要制作一个句柄对象,因此需要制作一个 class:

        classdef dbs_class < dynamicprops
    properties (Access='private')
        lhs=[];
    end
    events
        StateChange
    end
    methods
        function obj=dbs_class(a)
            addlistener(obj,'PropertyAdded',@eventPRadd);
            %addlistener(obj,'StateChange',@(varargin) disp('change'));
        end
    
        function eventPRadd(obj,evnt)
            names=fieldnames(obj);
            meta_p=obj.findprop(names{end});
            meta_p.SetObservable=true;
            obj.lhs.(names{end})=addlistener(obj,names{end},'PostSet',@(varargin) notify(obj,'StateChange'))
            obj.(names{end})=0;
            notify(obj,'StateChange')
        end
    
        function removeprop(obj,name)
            meta_p=obj.findprop(name);
            delete(meta_p)
            delete(obj.lhs.(name))
            notify(obj,'StateChange')
        end    
    end
    

    结束

然后制作其中之一:

dbs=dbs_class(1);

这个变量dbs也就是'behaves'现在几乎就像一个结构体,当有人改变其中一个属性时执行,事件StateChange将被触发。

在打开函数中,您需要做的就是将侦听器和回调添加到对象事件'StateChange':

function gui_test(dbs_in)
handles.fig=figure('Tag','fig');
addlistener(dbs_in,'something_changed',@updateFoo);
handles.dbO = fieldnames(dbs_in);
handles.dbmenu= uimenu('Label','Debug');
for n=1:numel(handles.dbO)
    if dbs_in.(handles.dbO{n})==1
        uimenu(handles.dbmenu,'Label',handles.dbO{n},'Callback',@(varargin) disp(['Debugg mode :' handles.dbO{n}]));
    end
end
guidata(handles.fig, handles);

function updateFoo(src,~)
handles=guidata(findobj('Type','Figure','Tag','fig'))
handles.dbO = fieldnames(src);
delete(get(handles.dbmenu,'Children'))
for n=1:numel(handles.dbO)
    if src.(handles.dbO{n})==1
        uimenu(handles.dbmenu,'Label',handles.dbO{n},'Callback',['disp([''DebuggingMode :'  handles.dbO(n) '])']);
    end
end
guidata(handles.fig, handles);

来源:http://ch.mathworks.com/help/matlab/matlab_oop/learning-to-use-events-and-listeners.html

我刚刚发现它可以更简单:

我只是将 dbs 声明为句柄 class。然后在GUI的OpeningFcn中,给dbs赋一个字段:handles.myvar = varargin{1};。由于 dbs 现在是句柄 class,我在 GUI 之外(即在命令 Window 中)使用 dbs 所做的一切也将更新为 GUI 的句柄。

function dbGUI_OpeningFcn(hObject, eventdata, handles, varargin)
handles.var = varargin{1};
% other fields in handles
guidata(hObject, handles);

function db_menu_Callback(hObject, eventdata, handles)
disp('Updated variable: ');
disp(handles.var);
% here do whatever i want ....
guidata(hObject, handles);