Quantcast
Channel: Hidden property – Undocumented Matlab
Viewing all articles
Browse latest Browse all 30

UDD Properties

$
0
0

Donn Shull continues his series of articles on Matlab’s undocumented UDD mechanism. Today, Donn explains how to use and customize UDD properties.

Properties meta-data

The UDD system is a class system. UDD packages, classes, events, and properties are all classes. In this section we will take a closer look at property classes.

As we have already shown, properties are added to a UDD class by adding schema.prop calls to the schema.m class definition file. What this really means is that each property of a UDD class is itself a class object (schema.prop) with its own properties and methods. The methods of schema.prop are loadobj() and saveobj(), which are used to serialize objects of this class (i.e., storing them in a file or sending them elsewhere).

It is schema.prop‘s properties (so-called meta-properties) that interest us most:

PropertyData TypeDescription
AccessFlagsMatlab structureControls which objects can access (read/modify) the property
CaseSensitiveon/offDetermines if the exact case is required to access the property (i.e., can we use ‘casesensitive’ instead of ‘CaseSensitive’)
DataTypestringThe underlying object’s property data type, set by the constructor
DescriptionstringThis can hold a description of the property (normally empty)
FactoryValueAs specified by DataTypeThis is used to provide an initial or default property value
GetFunctionFunction handleA function handle that is called whenever the property value is read
NamestringThe name of the property, also set by the constructor
SetFunctionFunction handleA function handle that is called whenever the properties value is changed
Visibleon/offDetermines if a property will be displayed by the get method for a UDD object

We can manipulate the values of these meta-properties to control various aspects of our property:

% Create instance of simple.object
>> a = simple.object('a');
 
% Find the Value property and list its meta-properties
% We can manipulate these meta-properties within limits
>> a.findprop('Value').get
            Name: 'Value'
     Description: ''
        DataType: 'single'
    FactoryValue: 7.3891
     AccessFlags: [1x1 struct]
         Visible: 'on'
     GetFunction: []
     SetFunction: []
 
>> prop.Visible = 'off';  % i.e. hidden property (see below)
>> prop.AccessFlags.PublicSet = 'off';   % i.e. read-only
>> prop.AccessFlags.PublicGet = 'on';
 
% Find the DataType meta-property of the Value property
% This meta-property and all other schema.prop base class properties are fixed
>> a.findprop('Value').findprop('DataType').get
            Name: 'DataType'
     Description: ''
        DataType: 'string'
    FactoryValue: ''
           ...

Adding properties to existing objects in run-time

schema.prop is a very useful tool – it can be used to add new properties to existing object handles, even after these objects have been created. For example, let’s add a new property (MyFavoriteBlog) to a standard figure handle:

>> p=schema.prop(handle(gcf), 'MyFavoriteBlog','string')
p =
	schema.prop
 
>> set(gcf,'MyFavoriteBlog','UndocumentedMatlab.com')
 
>> get(gcf,'MyFavoriteBlog')
ans =
UndocumentedMatlab.com

Using this simple mechanism, we can add meaningful typed user data to any handle object. A similar functionality can be achieved via the setappdata/getappdata functions. However, the property-based approach above is much “cleaner” and more powerful, since we have built-in type checks, property-change event listeners and other useful goodies.

Property data types

In the article on creating UDD objects we saw that the Name and DataType meta-properties are set by the schema.prop constructor. Name must be a valid Matlab variable name (see isvarname).

DataType is more interesting: There are two equivalent universal data types, 'mxArray', and 'MATLAB array'. With either of these two data types a property can be set to a any Matlab type. If we use a more specific data type (e.g., ‘string’, ‘double’ or ‘handle’), Matlab automatically ensures the type validity whenever the property value is modified. In our simple.object we use ‘double’ and ‘string’. You can experiment with these and see that the Value property will only allow scalar numeric values and the Name property will only allow character values:

>> set(obj, 'Value', 'abcd')
??? Parameter must be scalar.
 
>> obj.Value='abcd'
??? Parameter must be scalar.
 
>> obj.Name=123
??? Parameter must be a string.

The following table lists the basic UDD data types:

CategoryData Type
UniversalMATLAB array, mxArray
Numeric Scalarsbool, byte, short, int, long, float, double
Numeric VectorsNints, NReals
Specialized Numericcolor, point, real point, real point3, rect, real rect
Enumerationon/off
Stringschar, string, NStrings, string vector
Handlehandle, handle vector, MATLAB callback, GetFunction, SetFunction
JavaAny java class recognized by Matlab

User-defined data types

While this is an extensive list, there are some obvious types missing. For example there are no unsigned integer types. To handle this UDD provides two facilities for creating your own data types. One is the schema.EnumType. As you can see, Matlab has had a form of enumerations for a really long time not just the last few releases. The other facility is schema.UserType.

With these two classes you can create any specialized data type you need. One word of caution: once you have created a new UDD data type it exists for the duration of that Matlab session. There is no equivalent of the clear classes mechanism for removing a data type. In addition once a new data type has been defined it cannot be redefined until Matlab is restarted.

Let’s use a problem discussed in the CSSM forum as example. The essence of the problem is the need to flag a graphic line object as either editable or not. The proposed proposed is to add a new Editable property to an existing line handle. We will use schema.EnumType to create a new type named 'yes/no' so that the new property could accept only ‘yes’ and ‘no’ values:

function tline = taggedLine(varargin)
%TAGGEDLINE create a line with Editable property
%
%   TLINE = TAGGEDLINE(VARARGIN) create a new handle graphics line
%   and add 'Ediatable' property to line. Default property value is 'yes'.
%
%   INPUTS:
%       VARARGIN  : property value pairs to pass to line
%
%   OUTPUTS:
%       TLINE     : hg line object with Editable property
 
    % If undefined define yes/no datatype</font>
    if isempty(findtype('yes/no'))
        schema.EnumType('yes/no', {'yes', 'no'});
    end
    tline = line(varargin{:});
    schema.prop(tline, 'Editable', 'yes/no');
end

It is necessary to test for the existence of a type before defining it, since trying to redefine a type will generate an error.

We can use this new taggedLine() function to create new line objects with the additional Editable property. Instead of adding a new property to the line class we could have defined a new class as a subclass of line:

function schema()
%SCHEMA  hg package definition function
    schema.package('hg');
end

We create our class definition as a subclass of the handle graphics line class:

function schema()
%SCHEMA  hg.taggedline class definition function
    % package definition
    superPackage = findpackage('hg');
    pkg = findpackage('hg');
 
    % class definition
    c = schema.class(pkg, 'taggedline', findclass(superPackage, 'line'));
 
    if isempty(findtype('yes/no'))
        schema.EnumType('yes/no', {'yes', 'no'});
    end
 
    % add properties to class
    schema.prop(c, 'Editable', 'yes/no');
end

And our constructor is:

function self = taggedline
%OBJECT constructor for the simple.object class
    self = hg.taggedline;
end

Here we have placed the schema.EnumType definition in the class definition function. It is usually better to place type definition code in the package definition function, which is executed prior to any of the package classes and available in all classes. But in this particular case we are extending the built-in hg package and because hg is already defined internally, our package definition code is never actually executed.

The schema.UserType has the following constructor syntax:

schema.UserType('newTypeName', 'baseTypeName', typeCheckFunctionHandle)

For example, to create a user-defined type for unsigned eight-bit integers we might use the following code:

schema.UserType('uint8', 'short', @check_uint8)
 
function check_uint8(value)
%CHECK_UINT8 Check function for uint8 type definition
    if isempty(value) || (value < 0) || (value > 255)
        error('Value must be a scalar between 0 and 255');
    end
end

Hidden properties

Visible is an 'on/off' meta-property that controls whether or not a property is displayed when using the get function without specifying the property name. Using this mechanism we can easily detect hidden undocumented properties. For example:

>> for prop = get(classhandle(handle(gcf)),'Properties')'
       if strcmpi(prop.Visible,'off'), disp(prop.Name); end
   end
 
BackingStore
CurrentKey
CurrentModifier
Dithermap
DithermapMode
DoubleBuffer
FixedColors
HelpFcn
HelpTopicMap
MinColormap
JavaFrame
OuterPosition
ActivePositionProperty
PrintTemplate
ExportTemplate
WaitStatus
UseHG2
PixelBounds
HelpTopicKey
Serializable
ApplicationData
Behavior
XLimInclude
YLimInclude
ZLimInclude
CLimInclude
ALimInclude
IncludeRenderer

Note that hidden properties such as these are accessible via get/set just as any other property. It is simply that they are not displayed when you run get(gcf) or set(gcf) – we need to specifically refer to them by their name: get(gcf,’UseHG2′). Many other similar hidden properties are described in this website.

You may have noticed that the CaseSensitive meta-property did not show up above when we used get to show the meta-properties of our Value property. This is because CaseSensitive has its own Visible meta-property set to 'off' (i.e., hidden).

Additional meta-properties

FactoryValue is used to set an initial value for the property whenever a new simple.object instance is created.

GetFunction and SetFunction were described in last week’s article, Creating a UDD Hierarchy.

AccessFlags is a Matlab structure of 'on/off' fields that control what happens when the property is accessed:

FieldnameDescription
PublicSetControls setting the property from code external to the class
PublicGetControls reading the property value from code external to the class
PrivateSetControls setting the property from internal class methods
PrivateGetControls reading the property value from internal class methods
InitControls initializing the property using FactoryValue in the class definition file
Default??? (Undocumented, no examples exist)
ResetControls initializing the property using FactoryValue when executing the built-in reset function
SerializeControls whether this object can be serialized
CopyControls whether to pass the property’s current value to a copy
ListenerControls whether property access events are generated or not
AbortSetControls whether property set events are generated when a set operation will not change the property’s value

The CaseSensitive meta-property has AccessFlag.Init = 'off'. This means that properties added to a class definition file are always case insensitive.

Another interesting fact is that properties can be abbreviated as long as the abbreviation is unambiguous. Using our simple.object as an example:

>> a = simple.object('a');
>> a.n  % abbreviation of Name
ans =
a
 
>> a.v  % abbreviation of Value
ans =
    0.0000

It is considered poor programming practice to use either improperly cased, or abbreviated names when writing code. It is difficult to read, debug and maintain. But show me a Matlab programmer who has never abbreviated Position as ‘pos’…

Note: for completeness’ sake, read yesterday’s post on MCOS properties on Loren’s blog, written by Dave Foti, author of the original UDD code. Dave’s post describes the fully-documented MCOS mechanism, which is newer than the undocumented UDD mechanism described here. As mentioned earlier, whereas UDD existed (and still exists) in all Matlab 7 releases, MCOS is only available since R2008a. UDD and MCOS co-exist in Matlab since R2008a. MCOS has definite advantages over UDD, but cannot be used on pre-2008 Matlab releases. Different development and deployment requirements may dictate using either UDD or MCOS (or both). Another pre-R2008a alternative is to use Matlab’s obsolete yet documented class system.

In the next installment of this series we will take a look at UDD events and listeners.

 
Related posts:
  1. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
  2. Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
  3. Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
  4. Displaying hidden handle properties I present two ways of checking undocumented hidden properties in Matlab Handle Graphics (HG) handles...
 

Viewing all articles
Browse latest Browse all 30

Trending Articles