Ошарашена попытка реализовать функциональность Сохранить / Загрузить объект в пользовательском интерфейсе MATLAB - PullRequest
1 голос
/ 19 марта 2009

Я пытаюсь реализовать функции сохранения / загрузки в пользовательском интерфейсе MATLAB (R2009a). Мой объект реализует функцию макета, которая генерирует пользовательский интерфейс для объекта. Я пытаюсь реализовать обратные вызовы для кнопок сохранения / загрузки. Кнопка сохранения работает и сохраняет объект в файл MAT, который можно загрузить позже.

Моя проблема заключается в реализации обратного вызова для кнопки загрузки. Кажется, я не могу загрузить данные из файла MAT и обновить свойства нового объекта. Будем очень благодарны за любые предложения о том, где я иду не так, как и за действиями, которые я могу предпринять.

Важный код - это мой файл определения класса. Конечно, мой фактический объект реализует гораздо больше свойств и методов, но вот скелет того, что у меня есть

classdef myObj<handle
   properties
       image % property holds a matlab image matrix
       objCount % number of objects in image
   end

   properties(Transient=true)
       parent
       children
   end

   methods
       function myObj
           % empty constructor
       end

       function load_object(self)
            % ask user for file
            [fileName, pathToFile]=uigetfile('*.mat','Select .mat file');
            tmp = load(fullfile(pathToFile,fileName);
            if isfield(tmp,'obj')
                self = tmp.obj;
            end
       end
       LayoutFcn(self) % UI layout function
   end
end

Макет пользовательского интерфейса определен в отдельном файле LayoutFcn.m , который в основном выглядит как

function LayoutFcn(self)
% create figure window and add various UI elements
...
% create load button
self.children(end+1) = uipushtool('Parent',hToolbar, ... % set parent to current toolbar
                   'CData',iconRead('open-document.png'), ... % read icon image from file
                   'Tag','uiLoad', ...
                    'ClickedCallback',@(hObj,event)loadingMyObject(self,hObj,event));

% create save button
self.children(end+1) = uipushtool('Parent',hToolbar, ... % set parent to current toolbar
                   'CData',iconRead('save-document.png'), ... % read icon image from file
                   'Tag','uiSave', ...
                    'ClickedCallback',@(hObj,event)savingMyObject(self,hObj,event));

...
end

function loadingMyObject(self,hObj,event)
     self.load_object; % call load_object method defined above in class definition
end

function savingMyObject(self,hObj,event)
     [fileName,pathName]=uiputfile('.mat','Save object to MAT file');
      obj = self;
      save(fullfile(pahtName,fileName),'obj')
end 

Примечание: Я использую MATLAB R2009a.

Код не выдает никаких ошибок. То, как я написал код, родительский объект (представленный self ) не обновляется после вызова LOAD в методе load_object . ТАК, это имеет желаемый эффект:

>> var = myObj;
>> var.load_object;

Однако, если я использую обратный вызов loadingMyObject , определенный в LayoutFcn.m таким образом

>> var = myObjl
>> var.LayoutFcn
-> click Load button to call _loadingMyObject_

не влияет на свойства var . То есть var по-прежнему будет иметь значения свойств по умолчанию после нажатия кнопки «Загрузить».

  1. При изменении методов загрузки для использования установить , как предложено gnovice , выдает следующую ошибку

    * * ??? тысяча сорок-семь Ошибка при использовании ==> set Преобразование в double из FujiCalibration невозможно.

    хотя я установил / получил методы для каждого свойства; как в

    method set.image(self,II)
       % ... some data validation code ...
       self.image = II
    end
    
  2. Использование цикла для установки каждого поля в соответствии с предложением Mr Fooz на самом деле не вариант, так как мой полный класс имеет открытую константу, которая выдает ошибку, когда они установлены.

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

Ответы [ 3 ]

1 голос
/ 19 марта 2009

Я верю Мистер Фуз прав. Переменная self, переданная load_object, является объектом типа "myObj", но строка:

self = tmp.obj;

просто перезаписывает переменную self структурой, хранящейся в tmp.obj. Выполнение:

self.image = tmp.obj.image;

должен вместо этого вызывать оператор set для свойства image объекта self. В документации MATLAB есть пример определения класса с методом с именем "set.OfficeNumber", который иллюстрирует это.

Кроме того, следующая строка в вашей функции saveMyObject может быть ненужной:

obj = self;

Я думаю, что это может иметь больше смысла (и сделать код немного более понятным), если вы используете имя «obj» вместо слова «self» в вашем коде класса (как это обычно бывает в документации). «self» не является каким-либо специальным ключевым словом в MATLAB (как это может быть в других языках). Насколько я могу судить, это просто еще одна переменная. =) * * 1016

РЕДАКТИРОВАТЬ # 1:

Если перспектива необходимости устанавливать каждое свойство по отдельности в вашем методе load_object не выглядит забавной, один из способов обойти это, если у вас есть метод SET для вашего объекта, который разработан как метод SET для дескриптора графика . Эта команда SET может принимать входные данные структуры, где каждое имя поля является именем свойства, а каждое значение поля является новым значением для этого свойства. Тогда у вас будет один звонок, как:

set(self,tmp.obj);

Немного короче, особенно если вам нужно установить много свойств. Конечно, тогда вам нужно будет написать новый метод SET для вашего объекта, но укороченный синтаксис может стоить дополнительной работы, если он пригодится и в других местах. =) * * Тысяча двадцать-семь

РЕДАКТИРОВАТЬ # 2:

Возможно, вы сможете использовать цикл Mr Fooz , предложенный в сочетании с try / catch block:

fn = fieldnames(tmp.obj);
for i = 1:numel(fn),
  try
    self.(fn{i}) = tmp.obj.(fn{i});
  catch
    % Throw a warning here, or potentially just do nothing.
  end
end
1 голос
/ 19 марта 2009

Объединив предложения мистера Фуза и gnovice , я добавил функцию SET со следующим определением

function set(self,varargin)
    if isa(varargin{1},'FujiCalibration')
        tmp = varargin{1};
        fns = fieldnames(self);
        for i = 1:length(fns)
            if strcmpi(fns{i}(1:2),'p_')
                self.(fns{i}) = tmp.(fns{i});
            end
        end
        self.calibImage = tmp.calibImage;
    else
        proplist=fields(self);
        for n=1:2:length(varargin)
            tmp = proplist(strcmpi(proplist,varargin{n}));
            value = varargin{n+1};
            switch length(tmp)
                case 0
                    msg = char(strcat('There is no ''', varargin{n}, '''property'));
                    error('FujiCalibration:setPropertyChk',msg)
                case 1
                    tmp = char(tmp);
                    self.(tmp) = value;
            end
        end  
    end
end

Затем я изменил load_object метод, предложенный gnovice, изменив

self = tmp.obj

до

set(self,tmp.obj).

Мне нужно убедиться, что свойства со значениями, которые я хочу сохранить, имеют префикс «p_».

Спасибо gnovice и мистеру Фузу за ответы.

1 голос
/ 19 марта 2009

Не присваивайте значения себе. Все, что нужно, это заменить привязку к переменной self в области вызова метода. Он не вызывает конструктор магических копий для замены ссылки на объект в вызывающей программе. Вместо этого скопируйте поля в себя. Попробуйте что-то вроде:

if isfield(tmp,'obj')
   self.image = tmp.obj.image;
   self.objCount = tmp.obj.objCount;
end
...