2015-05-06 2 views
2

В моем собственном графическом я создал множество элементов управления (с использованием uicontrol), чтобы пользователи могли настроить фильтр, используемый на более поздней стадии обработки.Создание пользовательского и многоразового управления пользовательским интерфейсом

Редактор фильтра состоит из выпадающего списка, чтобы выбрать тип фильтра, а также множество полей редактирования, которые обновляются по выбранному типу фильтра и многим обратным вызовам, чтобы реагировать на пользовательские входы.

Теперь мне нужно добавить этот фильтр в другой графический интерфейс, и, конечно же, я не хочу копировать всю логику, которую я уже сделал, и предпочел бы создать какой-то пользовательский элемент управления, который я могу повторно использовать как легко, как:

filterEditor = uifilter('Parent', gcf); 
set(filterEditor, 'FilterDescription', 'Cylinder (r = 45 cm, h = 1 m)'); 
set(filterEditor, 'Callback', @onFilterEditModified); 

есть ли стандартная процедура для создания пользовательских «uicontrol» объекты? Я искал в интернете и документации MatLab, но не нашел хороший указатель еще ...

В настоящее время я имею в виду, чтобы создать пользовательский класс, производный от hgsetget:

classdef uifilter < hgsetget 

    properties 
     % Local properties 
     FilterDescription; 
     Callback; 
    end 

    properties(SetAccess=private, GetAccess=private) 
     % Internal controls 
     globalContainer; 
     comboFilterType; 
     edit1; 
    end 

    methods 
     function [this] = uifilter(varargin) 

      % Create a global `uicontainer` to hold my controls 
      [localPVpairs, genericPVpairs] = separatePVpairs(varargin{:}); 
      this.container = uicontainer(genericPVpairs{:}); 

      % Create my own controls and logic 
      this.comboFilterType = uicontrol('Parent', this.container, ...); 
      this.edit1 = ... 

     end 
    end 

end 

для того, чтобы имитировать поведение uicontrol (set , get, и т. Д.), Но, возможно, существует более стандартный подход или какой-либо базовый класс, отличный от hgsetget, чтобы начать с (т.е. некоторый базовый класс с Visible, Enable, HitTest и т. Д. Уже определен с реализацией по умолчанию)?

ответ

2

Я думаю, что это был бы правильный подход.

Чтобы сделать это правильно, вам, вероятно, потребуется реализовать собственные методы set и get для каждого объекта uicontrol. Эти методы и get будут в основном просто проходить через значения до и от базового uicontrol. Вероятно, вам удастся избежать реализации некоторых из менее используемых свойств в вашем первом черновике (например, FontAngle), добавив их по мере необходимости и просто проживая с uicontrol по умолчанию до тех пор.

В некоторых случаях им нужно будет сделать больше, и вам потребуется проявить немного осторожности, когда вы реализуете такие вещи, как set, для свойства Parent (возможно, потребуется уничтожить оригинальный uicontrol и создать новый для нового родителя). Вам также необходимо проявлять осторожность при внедрении set для свойств Position и Units - для нормальных uicontrol s они взаимодействуют довольно сложным образом, и я думаю, что результат может иногда зависеть от того, какой из них задан первым.

Я бы также предположил, что для внутренних свойств, а также для их установки в private вы также можете установить их в Hidden, чтобы пользователи не пытались вмешиваться в них.

Последний вопрос - я думаю, из некоторых ваших других вопросов, что вы используете GUI Layout Toolbox. Я не очень много думал об этом, но вам, возможно, нужно подумать о том, нужно ли делать что-то особенное, чтобы это сделать.

+0

Благодарим за поддержку по поводу моих многочисленных вопросов по GUI :) ... Вы правы, мне нужно позаботиться о детях типа GUI-Layout (+ ошибки, введенные в версию для HG2 с видимыми/включенными свойствами, см. мои комментарии с 22 по 23 декабря 2014 года на [fileexchange] (http://www.mathworks.com/matlabcentral/fileexchange/47982-gui-layout-toolbox) ... Я начну разрабатывать некоторые базы данных 'uicontrolbase', класс и отправьте его здесь для справки ... Я надеюсь, что MathWorks также предоставит нам лучший способ разработки современного/современного графического интерфейса в следующих выпусках Matlab, потому что в настоящее время довольно сложно делать простые вещи :) – CitizenInsane

+2

Они делают именно это - ознакомьтесь с этим [Tech Preview] (http://www.mathworks.com/matlabcentral/fileexchange/48142-app-designer) планируемого «App Designer», который, как я предполагаю, является предстоящей заменой GUIDE. Он имеет множество новых элементов управления и генерирует приятный OO GUI-код, который ** бесконечно ** лучше, чем текущий код GUIDE. Это еще не закончено, поэтому у вас есть возможность предоставить свои отзывы о том, что вы хотели бы видеть. –

+0

** Звучит здорово! ** Я проверю это как можно :) – CitizenInsane

0

Возвращаясь по этому вопросу, очень простой подход (эквивалент определить пользовательский класс, унаследованный от hgsetget или, возможно, некоторых uicontrolbase класса имеет поведение по умолчанию для Enable, Position и т.д ...) - создать класс, наследующий от uiextras.Container в GUI Layout toolbox.

Действительно, этот класс полностью эквивалентен идее наличия класса uicontrolbase. Он предоставляет защищенное UIContainer свойство, которое является панелью, в которой поместить все дочерние элементы, так что очень легко построить многоразовый составной компонент из него:

classdef uimyfilter < uiextras.Container 

    %% --- Exposed properties 
    % NB: Can be accessed with set/get routines 
    properties(Dependent, Transient) 
     FilterDescription; 
     Callback; 
    end 
    methods 
     {% ... own custom set/get logic for exposed properties ... %} 
    end 

    %% --- Lifetime 
    methods 
     function [this] = uimyfilter(varargin) 

      % Consume or init local properties from varargin list 
      [c, otherPvPairs] = uimyfilter.extractOrInitPvPairs(varargin, { ... 
       'FilterDescription', @()'Cylinder (r = 10 cm, h = 42 cm)'; ... 
       'Callback', @()[]; ... 
      }); 

      % Call superclass with other pv pairs 
      [email protected](otherPvPairs{:}); 

      % Build interface 
      grid = uiextras.Grid('Parent', this.UIContainer, 'Spacing', 5, 'Padding', 5); 

       c.handles.cbFilterType = uicontrol('Parent', grid, 'Style', 'Popup', 'String', { 'Cylinder', 'Sphere' }, 'Callback', @(s,e)onFilterTypeChanged(this,s,e)); 
       uiextras.Empty('Parent', grid); 

       c.handles.cardFilterParams = uiextras.CardPanel('Parent', grid); 
       uiextras.Empty('Parent', grid); 

       set(grid, 'ColumnSizes', [90, -1]); 
       set(grid, 'RowSizes', [23, -1]); 

       uicontrol('Parent', c.handles.cardFilterParams, 'Style', 'Text', 'String', '... todo: params for cylinder ...', 'BackgroundColor', 'r'); 
       uicontrol('Parent', c.handles.cardFilterParams, 'Style', 'Text', 'String', '... todo: params for sphere ...', 'BackgroundColor', 'r'); 

      % Store local properties and handles for later calls 
      this.state = c; 

      % Init Gui 
      this.refresh(); 

     end 
    end 

    %% --- Internal logic 
    methods(Access=private) 
     function [] = refresh(this) 
      set(this.state.handles.cardFilterParams, 'SelectedChild', get(this.state.handles.cbFilterType, 'Value')); 
     end 
     function [] = onFilterTypeChanged(this, s, e) %#ok 
      this.refresh(); 
      if (~isempty(this.state.Callback)), 
       this.state.Callback(this); 
      end 
     end 
    end 
    methods(Access = protected) 
     function [] = redraw(this) %#ok    
     end 
    end 
    properties(GetAccess=private, SetAccess=private) 
     state;   
    end 

    %% --- Helpers 
    methods(Static, Access=protected) 
     function [c, otherPvPairs] = extractOrInitPvPairs(pvPairs, consumeDescriptor) 

      % Check arguments 
      if (nargin < 2), 
       error('Not enough input arguments.'); 
      end 
      if (~isempty(consumeDescriptor) && ... 
       (~iscell(consumeDescriptor) || ~ismatrix(consumeDescriptor) || ... 
       ~iscellstr(consumeDescriptor(:, 1)) || ~all(cell2mat(cellfun(@(x)isa(x, 'function_handle'), consumeDescriptor(:,2), 'UniformOutput', false))))) 
       error('Invalid descriptor for properties to consume.'); 
      end 
      if (~iscell(pvPairs) || (~isvector(pvPairs) && ~isempty(pvPairs)) || (length(pvPairs(1:2:end)) ~= length(pvPairs(2:2:end))) || ~iscellstr(pvPairs(1:2:end))) 
       error('Invalid list or property names/values pairs.'); 
      end 

      % Consume local properties 
      c = struct(); 
      otherNames = pvPairs(1:2:end); 
      otherValues = pvPairs(2:2:end); 
      for ki = 1:size(consumeDescriptor, 1), 
       pname = consumeDescriptor{ki,1}; 
       pinit = consumeDescriptor{ki,2}; 
       idx = strcmpi(otherNames, pname); 
       if (isempty(idx)), 
        c.(pname) = pinit();      
       elseif (isscalar(idx)), 
        c.(pname) = otherValues{idx}; 
        otherNames(idx) = []; otherValues(idx) = []; 
       else 
        error('Property `%s` appears more than once.', pname); 
       end     
      end 

      % Recompose other pv 
      otherPvPairs = cell(1, 2*length(otherNames)); 
      otherPvPairs(1:2:end) = otherNames(:); 
      otherPvPairs(2:2:end) = otherValues(:); 

     end   
    end 

end 

Открытых свойств и внутренняя логика, конечно, полностью привязаны к составной компонент в любом случае, интерфейс построения так же просто, как добавление uicontrol или uiextras.(...) объектов к this.UIContainer.

PS: Для R2014b и более поздних версий вы должны унаследовать от uix.Container в GUI Layout toolbox for HG2 в любом случае идея похожа.

Смежные вопросы