有没有办法阻止uitable弹出菜单的弹出?或者:如何通过点击单元格获取回调,返回行和列索引?

7

我正在编写一个用户界面,其中包括一个 uitable。用户在第一列中选择选项 A、B 或 C,第二列的子选项取决于第一列选择了什么,可以是 A.1、A.2 或 A.3,也可以是 B.1、B.2 或 B.3,或者与 C 相同。

enter image description here

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

当用户首先定义主选项时,自动减少子选项以仅显示有效选择。这是通过评估第1列的CellEditCallback并重置第2列的ColumnFormat来实现的。(函数modifySelection附录B中) 如果用户现在意识到他犯了一个错误并需要再次编辑子选项,则ColumnFormat仍根据先前编辑的主选项设置,除非他再次选择主选项,否则有效选择不可用(请参见图片中的蓝色突出显示)。

为解决此问题,我还实现了CellSelectionCallback调用函数justifySelection(在附录B中),它通过选择检查选择了哪个选项来提供正确的第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”,但它是在单元格已经弹出并显示无效选择之后执行的。我需要一种类似于“pushbuttons”中的“ClickedCallback”的方法。
或者确保只选择一个主选项即可设置该行的子选项。这是不可能的,您无法为特定行设置子选项,因为您需要修改“ColumnFormat”,这会影响整个表格,而不仅仅是一行。

如何缓冲数据:将完整的数据保存在一个变量中,例如FullData,并使用另一个变量DisplayedData来显示,当选择某些内容时,它将被扩展 :) - Lucius II.
我也考虑过这个问题,但我想这是不可能的。因为弹出窗口只是通过 ColumnFormat 属性实现的,而这必须是一个行向量。 - Robert Seifert
想象以下步骤:1)您在第一排中选择主选项A。2)第一排中的子选项包含A.1、A.2和A.3的选择。3)您选择第二排中的主选项B,因此第二排的子选项的选择为B.1、B.2和B.3 5)但现在您想直接更改第一行中的子选项 - 您会期望获得A.1、A.2和A.3的选择 - 但您并没有。您会被提供B.1、B.2和B.3的选择 - 因为您最后选择的主选项是B(尽管在不同的行)。现在清楚了吗? - Robert Seifert
@DennisJaheruddin 我将您的评论包含在问题中,并回答了它们,以保持概览。 - Robert Seifert
与其改变事情发生的顺序,是否有可能在正确的值设置后再次强制打开下拉菜单?否则(假设更改操作不是很繁重,并且您不担心回退),您可以根据鼠标悬停状态更改集合。 - Dennis Jaheruddin
显示剩余10条评论
3个回答

4
尽管我非常赞赏Rody Oldenhuis的努力和解决方案,并且他绝对值得获奖,但他的解决方案需要对我的代码进行很多更改,所以我一直试图找到一种更简单的解决方案。最终,在这里,我找到了一个几乎没有错误的解决方案。(所有代码部分在一个函数脚本中)
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

brings up:

finaltable


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

enter image description here enter image description here

按下“确定”后在MATLAB命令窗口中的输出:

data =
    A.1
    B.1

布局当然还很粗糙,但你可以理解。当然,单选按钮也可以被弹出窗口(更紧凑),三个推按钮或其他您喜欢的东西所替代。
弹出窗口的内容彼此无关,这正是uitable方法的问题所在。在这个GUI中,更好地控制如何处理变化,因此更改单选按钮时,弹出窗口的内容可以瞬间改变。
编程注意事项:我个人不喜欢将我视为“块”的各个组件的句柄浮动在顶级函数中,这就是为什么我使用双重嵌套函数的原因——这有点像封装。现在,在类外使用它时,这并不是每个人都喜欢的,因此您可能需要进行转换。当然,所有嵌套函数都可以轻松转换为子函数;您只需要手动传递更多的信息即可。
采用这种方法,您会失去一些功能(调整UI元素大小的能力),但您会获得直观的GUI控件行为。当这些是选择时,我被训练为朝着后者的方向发展。漂亮的花哨功能只会在第一轮印象用户,但随着使用次数的增加,程序的基本功能将变得越来越重要。正如您自己所注意到的,当您不得不经常使用该工具时,这种错误行为变得非常令人烦恼;我会说,放弃可调整大小以改善控制行为。

非常感谢您的努力,并将在今后遵循您的建议。 (+1) 我开始同意我的所需功能只能通过多个 UI 对象来实现。但这可能会使我的代码变得异常复杂。您必须想象我有大约10个主选项和20个相关的子选项(因此肯定还必须有一个主选项的弹出窗口),此外还有两个具有弹出窗口的独立列。最后,我需要将所有列的信息合并在一起。因此,我想我的代码长度将是我的错误但有效的临时解决方案的5-10倍。 - Robert Seifert
...但非常感谢你花费的时间,我稍后会发布关于你的想法的最终解决方案! - Robert Seifert
@thewaywewalk:没错,这就是编写UI的问题所在;通常需要很多“低密度代码”——你需要大量的代码才能实现功能。但是,如果我理解你的意思正确,你需要一堆主要弹出窗口,每个窗口有10个条目,并且与此相关的还有两个带有子选项的弹出窗口,每个窗口有20个左右的条目。你已经可以使用我的代码来完成这个任务了;我的代码会迭代生成完全功能的整个“行”。你只需要设计其中一个“行”,然后就完成了。将结果连接起来也不应该太困难... - Rody Oldenhuis
@thewaywewalk:我很乐意提供帮助。也许我们可以交换电子邮件地址,通过电子邮件继续沟通?我的邮箱是<我的姓氏>(at)<gmail>(dot)<com>。 - Rody Oldenhuis
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/39986/discussion-between-rody-oldenhuis-and-thewaywewalk - Rody Oldenhuis
显示剩余2条评论

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

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


抱歉,我在你的链接中没有看到任何在我的代码中尚未实现的内容。我在第一列中选择了某个东西,第二列会更新。这不是问题所在,这已经完美地运作了。问题出现在当我想要修改第二列中的一个单元格时,而在修改另一个第二列单元格之后。你说的"两个表"是什么意思 - 它们是相邻的吗,也就是说每一列都有一个表,还是它们应该重叠? - Robert Seifert
我猜你的错误认识是,我想要更新第二列中的“Data”。但我需要更新“ColumnFormat”。它是一个行向量而不是矩阵。 - Robert Seifert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接