尝试在 MATLAB UI 中实现保存/加载对象功能时遇到困难

发布于 2024-07-15 11:05:36 字数 3315 浏览 3 评论 0原文

我正在尝试在 MATLAB (R2009a) UI 中实现保存/加载功能。 我的对象实现了一个布局函数,该函数为该对象生成用户界面。 我正在尝试实现保存/加载按钮的回调。 保存按钮起作用并将对象保存到 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

UI 布局是在单独的文件 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。

该代码不会引发任何错误。 我编写代码的方式是,在方法 load_object 中调用 LOAD 后,父对象(由 self 表示)不会更新。 所以,这达到了预期的效果:

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

但是,如果我以这种方式使用 LayoutFcn.m 中定义的 loadingMyObject 回调,

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

不会影响 var特性。 也就是说,在单击“加载”按钮后,var 仍将具有其默认属性值。

  1. 按照gnovice 抛出以下错误

    <块引用>

    ??? 使用 ==> 时出错 放 无法从 FujiCalibration 转换为双精度。

    尽管我为每个属性都有设置/获取方法; 如

    方法 set.image(self,II) 
         % ...一些数据验证代码... 
         自我形象 = II 
      结尾 
      
  2. 按照 Mr Fooz 并不是一个真正的选择,因为我的整个类都有公共常量,在设置它们时会抛出错误。

我正在寻找一种解决方案,以避免我必须手动编写代码来单独设置每个字段。 尽管目前看来这是唯一的可能性。

I am trying to implement save/load functions in a MATLAB (R2009a) UI. My object implements a layout function that generates a user interface for the object. I am trying to implement the callbacks for the save/load buttons. The save button works and save the object out to a MAT file which can be loaded later.

My problem is implementing the callback for the load button. I cannot seem to get the load to load the data from the MAT file and update the properties of the new object. Any suggestions on where I am going wrong along with suggestions on how I might proceed is greatly appreciated.

The important code is my class definition file of course my actual object implements many more properties and methods but here is a skeleton of what I have

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

The UI layout is defined in a seperate file LayoutFcn.m which basically looks like

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 

Note: I am using MATLAB R2009a.

The code doesn't throw any errors. The way I wrote the code the parent object (represented by self) does not get updated after the call to LOAD in the method load_object. SO, this has the desired effect:

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

However, if I use the loadingMyObject callback defined in LayoutFcn.m in this fashion

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

doesn't affect var properties. That is var will still have its default property values after clicking the Load button.

  1. Changing the load methods to use set as suggested by gnovice throws the following error

    ??? Error using ==> set
    Conversion to double from FujiCalibration is not possible.

    even though I have set/get methods for each property; as in

    method set.image(self,II)
       % ... some data validation code ...
       self.image = II
    end
    
  2. Using a loop to set each field as suggested by Mr Fooz is not really an option as my full class has public constant that throw an error when they are set.

I am looking for a solution that would avoid me having to hand code setting each field individually. Although at this point it seems like the only possibility.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

っ左 2024-07-22 11:05:36

我相信福兹先生 是对的。 传递给 load_object 的 self 变量是“myObj”类型的对象,但行:

self = tmp.obj;

只是用 tmp.obj 中存储的结构覆盖 self 变量。 做法:

self.image = tmp.obj.image;

应该为对象 self 的图像属性调用集合运算符。 在 MATLAB 文档中,有一个示例类定义 使用名为“set.OfficeNumber”的方法来说明这一点。

此外,函数 savingMyObject 中的以下行可能是不必要的:

obj = self;

如果您在类代码中使用名称“obj”代替单词“self”,我认为这可能最有意义(并使代码更清晰) (正如文档往往所做的那样)。 “self”在 MATLAB 中似乎不是任何类型的特殊关键字(就像在其他语言中一样)。 据我所知,这只是另一个变量。 =)

编辑 #1:

如果必须在 load_object 方法中单独设置每个属性的前景听起来并不有趣,解决它的一种方法是,如果您的对象有一个 SET 方法,其设计类似于 处理图形的 SET 方法。 该 SET 命令可以接受结构输入,其中每个字段名称都是属性名称,每个字段值都是该属性的新值。 然后你会得到一个像这样的调用:

set(self,tmp.obj);

相当短,特别是当你有很多属性需要设置时。 当然,您必须为您的对象编写新的 SET 方法,但是如果缩短的语法在其他地方也能派上用场,那么它可能值得额外的工作。 =)

编辑#2:

您也许可以使用循环Mr Fooz 建议结合 try/catch 块:

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

I believe Mr Fooz is right. The self variable passed to load_object is an object of type "myObj", but the line:

self = tmp.obj;

is simply overwriting the self variable with the structure stored in tmp.obj. Doing:

self.image = tmp.obj.image;

should instead invoke a set operator for the image property of object self. In the MATLAB documentation there is a sample class definition with a method called "set.OfficeNumber" that illustrates this.

In addition, the following line in your function savingMyObject may be unnecessary:

obj = self;

I think it might make most sense (and make the code a little clearer) if you used the name "obj" in place of the word "self" within your class code (as the documentation tends to do). "self" doesn't appear to be any kind of special keyword in MATLAB (like it may be in other languages). It's just another variable as far as I can tell. =)

EDIT #1:

If the prospect of having to set each property individually in your load_object method doesn't sound like fun, one way around it is if you have a SET method for your object that is designed like the SET method for handle graphics. That SET command can accept a structure input where each field name is a property name and each field value is the new value for that property. Then you would have one call like:

set(self,tmp.obj);

Quite a bit shorter, especially if you have lots of properties to set. Of course, you'd then have to write the new SET method for your object, but the shortened syntax may be worth the extra work if it comes in handy elsewhere too. =)

EDIT #2:

You may be able to use the loop Mr Fooz suggested in conjunction with a 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
长不大的小祸害 2024-07-22 11:05:36

不要给自己赋值。 所做的只是替换方法调用范围内对 self 变量的绑定。 它不会调用神奇的复制构造函数来替换调用者中的对象引用。 相反,将字段复制到 self. 尝试类似的方法:

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

Don't assign values to self. All that does is replace the binding to the self variable in the scope of the method call. It does not call a magical copy constructor to replace the object reference in the caller. Instead, copy the fields into self. Try something like:

if isfield(tmp,'obj')
   self.image = tmp.obj.image;
   self.objCount = tmp.obj.objCount;
end
难理解 2024-07-22 11:05:36

结合福兹先生的< /a> 和 根据 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

,然后按照 gnovice 的建议修改了 load_object 方法,将其更改

self = tmp.obj

set(self,tmp.obj).

I need to makesure theproperty with value I Want to persist are前缀为“p_”。

感谢 gnovice 和 Mr Fooz 的回答。

Combining Mr Fooz's and gnovice's suggestions, I added a SET function with the following definition

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

I then modified the load_object method as suggested by gnovice by changing

self = tmp.obj

to

set(self,tmp.obj).

I need to make sure that properties with values I want to persist are prefixed with 'p_'.

Thanks to gnovice and Mr Fooz for their answers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文