7

对于用户界面,我正在编写一个uitable. 用户在第一列中选​​择选项A、B 或 C,第二列中的子选项取决于第一列中选​​择的内容,A.1,A.2 或 A.3B.1,B.2或 B.3C相同

在此处输入图像描述

该表的代码可在附录 A中找到。

当用户首先定义主选项时,子选项会自动相应地减少为只有有效的选项。这是通过评估CellEditCallbackfor column 1并重置ColumnFormatfor column 2来实现的。(附录BmodifySelection中的功能)如果用户现在意识到他犯了一个错误并且需要再次编辑一个子选项,那么仍然根据之前编辑的设置主选项和有效选项不可用,除非他再次重新选择主选项。(请参见图片中的蓝色突出显示)。ColumnFormat

为了解决这个问题,我还实现了CellSelectionCallback调用函数justifySelection(在附录 B中),该函数通过选择进行检查,在第 1 列中选择了哪个选项以再次为第 2 列提供正确的子选项。但是当这个回调对选择做出反应时,我需要选择两次,一次触发CellSelectionCallback,另一次实际得到我的选择。对于大桌子,这可能很烦人!

所以我的问题是:

有没有办法阻止第 2 列中的弹出菜单弹出,直到它发现相应第 1 列的内容是什么,所以它立即提供有效的选择?

或者:

如何检测鼠标单击单元格并获取行和列索引?但是没有调用以下选择并弹出操作?

我已经在搜索所有可用的属性,但没有找到任何有用的东西。也许可以使用 做某事ButtonDownFcn,但如何获取单元格索引?财产呢BusyAction,如何用于我的目的?

有任何想法吗?

我很抱歉提前用这么多代码轰炸你,它已经是最小的例子,但完全可执行,所以你可以尝试一下。


附录 A/B

function fancyUitable 

selector_1 = { 'A'; 'B' ; 'C' };
selector_2 = { 'first select first row!' };

h = figure('Position',[200 100 268 120],'numbertitle','off','MenuBar','none');

defaultData =  repmat( {'select main option...', 'select suboption...'} ,5,1);
columnname =   {'Option                             ',...
                'Suboption                          '};
columnformat = { {selector_1{:}}, selector_2 };
columneditable =  [true true]; 
t = uitable(h,'Units','normalized','Position',[0 0 1 1],...
              'Data', defaultData,... 
              'ColumnName', columnname,...
              'ColumnEditable', columneditable,...
              'ColumnFormat', columnformat,...  
              'RowName',[],...
              'CellEditCallback',@modifySelection,...
              'CellSelectionCallback',@justifySelection);

set(h,'Tag','Config_figure')
set(t,'Tag','Config_table')
end

%   **Appendix B**
%   (part of the same function file)


function modifySelection(~,evt_edit)
if evt_edit.Indices(2) == 1
    modifyPopup( evt_edit.Indices(1) );
end
end

function justifySelection(~,evt_select)
try  %to surpress an unimportant error
    if evt_select.Indices(2) == 2
        modifyPopup( evt_select.Indices(1) );
    end
end
end

最后是modifyPopup重写的单个函数Columnformat

function  modifyPopup( row )
    id_group_1 = {'A.1';'A.2';'A.3'};
    id_group_2 = {'B.1';'B.2';'B.3'};
    id_group_3 = {'C.1';'C.2';'C.3'};
    id_default = {'select main option first'};

    myfigure = findobj('Tag','Config_figure');
    config_data = get(findobj(myfigure,'Tag','Config_table'),'Data');
    selector = config_data(row,1);
    selector = selector{1};

    config_format = get(findobj(myfigure,'Tag','Config_table'),'ColumnFormat');
    switch selector
        case 'A'
            config_format{2} = id_group_1';
        case 'B'
            config_format{2} = id_group_2';
        case 'C'
            config_format{2} = id_group_3';
        otherwise
            config_format{2} = id_default;
    end
    set(findobj(myfigure,'Tag','Config_table'),'ColumnFormat',config_format)
end

赏金:为什么只是+50?- 我想这是不可能的,或者答案很简单,一旦一个人有了正确的初步想法。我不是在寻找使用 java 对象属性等的复杂解决方法。提前谢谢你!


我在此处包含评论中的讨论以保持概述:

如果您想尝试一下,可以复制代码并按照以下步骤重现不良行为:

  1. 在第一行中选择主要选项 A。
  2. 第一行中的子选项则包含选项 A.1、A.2 和 A.3。
  3. 选择第二行的主选项B,因此第二行的子选项的选择是B.1、B.2和B.3
  4. 但是现在您想(直接)更改第一行中的子选项;你会期望得到选项 A.1、A.2 和 A.3;但你没有。您将获得 B.1、B.2 和 B.3;- 因为您选择的最后一个主要选项是 B(尽管在不同的行中)。

看来,您应该查看相关选项,而不是寻找最后一个选项。因此,要么确保单击子选项进行“查找”以查看存在哪个主选项,

这正是我正在寻找的!但我怎么能这样做呢?如何检测点击,获取列&行索引,设置正确 ColumnFormat,最后让单元格弹出。到目前为止,我看到的唯一可能性是CellSelectionCallback,但它是在单元格已经弹出无效选项后执行的。我需要一种ClickedCallback,就像有pushbuttons

或确保选择主选项仅设置该行的子选项。

这是不可能的,您不能根据需要为某行设置子选项 modify ColumnFormat,这会影响整个表而不仅仅是一行。

4

3 回答 3

4

尽管我非常感谢 Rody Oldenhuis 的努力和解决方案,并且他绝对应得的奖项,但他的解决方案需要对我的代码进行大量更改,因此我一直在尝试寻找更简单的解决方案。在这里,终于 99% 没有错误。

(函数脚本中的所有代码部分)

function fancyUitable 

close all

%basic properties
line_height = 21.32;
table_height = 6*line_height;
lh = line_height/table_height;
cw = 200; %columnwidth

h = figure('Position',[200 100 2*cw+2 table_height],...
           'numbertitle','off','MenuBar','none');

%header
uitable(h,'Units','normalized','Position',[0 1-lh 1 lh],...
              'ColumnWidth', {cw cw},...              
              'ColumnName', {'Option','Suboption'},...
              'RowName',[]);

%button (currently no icon) to store table
tbar = uitoolbar(h);
uipushtool(tbar,'ClickedCallback',@store);

% addrow(figurehandle,number of row, percentage lineheight)
% every function call creates a new row, later dynamically
addRow(h,1,lh);
addRow(h,2,lh);
addRow(h,3,lh);
addRow(h,4,lh);
addRow(h,5,lh);
end

function edit(src,evt)

if evt.Indices(2) == 1
    modifyPopup( src,evt.Indices(1) );
end

% disables cell selection highlighting, when one jumps to next table,
% a bit laggy though
fh = get(src,'parent');
copyobj(src,fh);
delete(src);

end

function  modifyPopup( src,row )
    id_group_1 = {'A.1';'A.2';'A.3'};
    id_group_2 = {'B.1';'B.2';'B.3'};
    id_group_3 = {'C.1';'C.2';'C.3'};
    id_default = {'select output file first'};

    config_data = get(src,'Data');
    selector = config_data(row,1);
    selector = selector{1};

    config_format = get(src,'ColumnFormat');
    switch selector
        case 'A'
            config_format{2} = id_group_1';
        case 'B'
            config_format{2} = id_group_2';
        case 'C'
            config_format{2} = id_group_3';
        otherwise
            config_format{2} = id_default;
    end
    config_data = { selector , 'select suboption...' };  %reset column 2
    set(src,'Data',config_data);
    set(src,'ColumnFormat',config_format);
end

function addRow(fh,k,lhp)
selector_1 = { 'A'; 'B' ; 'C' };
selector_2 = { 'first select first row!' };

defaultData =  {'select main option...', 'select suboption...'};
columnformat = { {selector_1{:}}, selector_2};
columneditable =  [true true];

th = uitable(fh,'Units','normalized','Position',[0 1-(k+1)*lhp 1 lhp],...
              'Data', defaultData,... 
              'ColumnName', [],...
              'ColumnWidth', {200 200},...
              'ColumnEditable', columneditable,...
              'ColumnFormat', columnformat,...  
              'RowName',[],...
              'Tag','value',...
              'UserData',k,...
              'SelectionHighlight','off',...
              'CellEditCallback',@edit);
end

function store(~,~)
ui = findobj(0,'Type','uitable','Tag','value');
L = numel(ui);
output = cell(L,2);
order = zeros(L,1);
for ii=1:L;
    output(ii,:) = get(ui(ii),'Data');
    order(ii)    = get(ui(ii),'UserData');
end
[~,idx] = sort(order);    %as order of handles unequals displayed order
assignin('base','output',output(idx,:));
end

提出:

决赛桌

于 2013-10-24T15:18:46.893 回答
4

我不会使用uitable; 它只是不适合这种事情。

这是我的做法:

function GUIdemo

    %%// Construct GUI

    %// Main figure
    mainFig = figure;
    set(mainFig, 'Color', get(0, 'DefaultUicontrolBackgroundColor'));

    %// Create as many blocks as needed. The only thing you have to do is
    %// figure out the "right" positions for each block
    popupHandles = create_ui_blocks([
        0.00  0.50 1.00  0.35
        0.00  0.15 1.00  0.35]);

    %// This OK button gathers all selected options, and just prints them.
    uicontrol(...
        'style'   , 'pushbutton',...
        'units'   , 'normalized',...
        'parent'  , mainFig,...
        'position', [0.4 0.01 0.2 0.1],...
        'callback', @(~,~)getData(popupHandles),...
        'string'  , 'OK'...
        );


    %%// Helper functions

    %// Create control blocks. Each block is composed of:
    %// - a uipanel as container
    %// - three radio buttons for the main selection
    %// - a corresponding popup or the secondary selection
    function popupHandles = create_ui_blocks(positions)

        %// initialize
        numBlocks = size(positions,1);

        panels = zeros(numBlocks,1);
        groups = zeros(numBlocks,1);
        radios = zeros(numBlocks,3);
        popups = zeros(numBlocks,1);

        %// Build each block
        for ii = 1:numBlocks

            %// The container
            panels(ii) = uipanel(...
                'parent'  , mainFig,...
                'position', positions(ii,:)...
                );

            %// The radio buttons
            groups(ii) = uibuttongroup(...
                'parent'  , panels(ii),...
                'position', [0.05 0.05 0.45 0.9]...
                );
            radios(ii,1) = uicontrol(...
                'style'   , 'radio',...
                'units'   , 'normalized',...
                'string'  , 'A',...
                'parent'  , groups(ii),...
                'position', [0.05 0.66 0.9 0.25]...
                );
            radios(ii,2) = uicontrol(...
                'style'   , 'radio',...
                'units'   , 'normalized',...
                'string'  , 'B',...
                'parent'  , groups(ii),...
                'position', [0.05 0.33 0.9 0.25]...
                );
            radios(ii,3) = uicontrol(...
                'style'   , 'radio',...
                'units'   , 'normalized',...
                'string'  , 'C',...
                'parent'  , groups(ii),...
                'position', [0.05 0.0 0.9 0.25]...
                );

            %// Initially, nothing's selected
            set(groups(ii), 'SelectedObject',[]);

            %// The popups
            popups(ii) = uicontrol(...
                'style'   , 'popup',...
                'units'   , 'normalized',...
                'parent'  , panels(ii),...
                'position', [0.55 0.4 0.4 0.2],...
                'string'  , 'Select main option',...
                'enable'  , 'off'...
                );

            %// On changing radiobutton, correct the string list of the popups
            set(groups(ii),'SelectionChangeFcn', @(~,~)selectionChangeCallback(ii));

            %// This is needed by the OK button callback
            popupHandles = popups;

        end

        %// What happens when clicking a radio button?
        %// NOTE: this is a doubly-nested function
        function selectionChangeCallback(num)
            switch get(groups(num), 'SelectedObject')
                case radios(num,1)
                    set(popups(num), 'string', {'A.1', 'A.2', 'A.3'}, 'enable', 'on');
                case radios(num,2)
                    set(popups(num), 'string', {'B.1', 'B.2', 'B.3'}, 'enable', 'on');
                case radios(num,3)
                    set(popups(num), 'string', {'C.1', 'C.2', 'C.3'}, 'enable', 'on');
                otherwise
                    %// ...
            end
        end

    end

    %// What happens when pressing the OK button?
    function data = getData(popupHandles)
        data = char(cellfun(@(x,y)x{y}, ...
            get(popupHandles, 'String'),...
            get(popupHandles, 'Value'),...
            'UniformOutput', false))         %#ok<NOPRT> //
    end

end

在此处输入图像描述 在此处输入图像描述

按“确定”时在 MATLAB 命令窗口中输出:

data =
    A.1
    B.1

布局当然仍然很粗糙,但你明白了。当然,单选按钮也可以替换为弹出窗口(更紧凑)、三个按钮或任何您喜欢的其他按钮。

弹出窗口的内容彼此不相关,这正是该uitable方法的问题。在此 GUI 中,更改单选按钮时弹出窗口内容的更改可能是即时的,这仅仅是因为您可以更好地控制如何处理更改。

编程说明:我个人不喜欢我视为“块”的单个组件的句柄在顶级函数中浮动,这就是我使用双重嵌套函数的原因——有点像封装。现在,当在课外使用时,这不是每个人都喜欢的,所以你可能想要转换它们。当然,所有嵌套函数都可以简单地转换为子函数;您只需要手动传递更多信息。

使用这种方法,您会失去一些功能(重新调整 UI 元素大小的能力),但您会获得GUI 控件的直观行为。当这些是选择时,我已经接受过培训,可以朝着后一种选择发展。漂亮的花里胡哨只会在最初几次给最终用户留下深刻印象,但随着使用量的增加,程序的基本功能将变得越来越重要。正如您自己指出的那样,当您必须大量使用该工具时,这种错误行为会变得很烦人;我会说,放弃可调整性以支持改进的控制行为。

于 2013-10-25T08:49:20.767 回答
-2

解决方案是在您的 GUI 中使用带有两个 UITable 的单元选择回调。让第一个表格保存 {'a','b','c'} 的数据,然后在单元格选择回调中,使第二个 UITable 可见并根据第一个 UITable 的单元格选择属性设置其数据。如果您低头查看“不需要 findjobj 的小技巧”这一行,此链接应该包含您需要的一切

http://www.mathworks.com/matlabcentral/newsreader/view_thread/306392

于 2013-10-21T20:09:00.997 回答