Is it possible to prevent an uitable popup menu from popping up? Or: How to get a callback by clicking a cell, returning the row & column index?

删除回忆录丶 提交于 2019-11-28 11:19:43

I would not use a uitable; it's just not suited for this sort of thing.

Here's how I would do it:

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

Output in the MATLAB command window when pressing "OK":

data =
    A.1
    B.1

The layout is of course still crude, but you get the idea. Of course, the radio buttons can also be replaced by a popup (more compact), three pushbuttons, or whatever else you like.

The contents of the popups are not related to each other, which is exactly the problem with the uitable approach. In this GUI, changes in the popup's contents can be instantaneous when changing a radio button, simply because you have better control over how to deal with changes.

A programming note: I personally don't like it when handles of individual components in what I treat as a "block" are floating around in the top-level function, which is why I use doubly-nested functions -- it's kind of like encapsulation. Now, when used outside of classes, this is not everyone's cup of tea, so you might want to convert them. Of course, all nested functions are trivially converted to subfunctions; you just have to manually pass a lot more information around.

With this approach, you lose some functionality (the ability to re-size your UI elements), but you gain intuitive behavior of the GUI controls. When these are the choices, I've been trained to develop towards the latter option. The nice bells and whistles will only impress the end-user the first few times round, but the program's basic functionality will become more and more important with increased usage. As you noted yourself, this buggy behavior gets annoying when you have to use the tool a lot; I'd say, drop the resizability in favor of improved control behavior.

Though I highly appreciate the effort and the solution of Rody Oldenhuis and he definetely deserved the award, his solution would have required a lot of changes in my code, so I kept trying to find a simpler solution. Here it is, finally 99% bug-free.

(all code parts within on function script)

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:

The solution is to use the Cell Selection Callback with two UITables in your GUI. Make the first table hold the data of {'a','b','c'} then in the cell selection callback, make the second UITable visible and set its data based on the cell selection properties of the first UITable. This link should have everything you need if you look down at the line 'A little hack not needing findjobj'

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!