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:
Property | Data Type | Description |
---|---|---|
AccessFlags | Matlab structure | Controls which objects can access (read/modify) the property |
CaseSensitive | on/off | Determines if the exact case is required to access the property (i.e., can we use ‘casesensitive’ instead of ‘CaseSensitive’) |
DataType | string | The underlying object’s property data type, set by the constructor |
Description | string | This can hold a description of the property (normally empty) |
FactoryValue | As specified by DataType | This is used to provide an initial or default property value |
GetFunction | Function handle | A function handle that is called whenever the property value is read |
Name | string | The name of the property, also set by the constructor |
SetFunction | Function handle | A function handle that is called whenever the properties value is changed |
Visible | on/off | Determines 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:
Category | Data Type |
---|---|
Universal | MATLAB array, mxArray |
Numeric Scalars | bool, byte, short, int, long, float, double |
Numeric Vectors | Nints, NReals |
Specialized Numeric | color, point, real point, real point3, rect, real rect |
Enumeration | on/off |
Strings | char, string, NStrings, string vector |
Handle | handle, handle vector, MATLAB callback, GetFunction, SetFunction |
Java | Any 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:
Fieldname | Description |
---|---|
PublicSet | Controls setting the property from code external to the class |
PublicGet | Controls reading the property value from code external to the class |
PrivateSet | Controls setting the property from internal class methods |
PrivateGet | Controls reading the property value from internal class methods |
Init | Controls initializing the property using FactoryValue in the class definition file |
Default | ??? (Undocumented, no examples exist) |
Reset | Controls initializing the property using FactoryValue when executing the built-in reset function |
Serialize | Controls whether this object can be serialized |
Copy | Controls whether to pass the property’s current value to a copy |
Listener | Controls whether property access events are generated or not |
AbortSet | Controls 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:
- getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
- Plot LimInclude properties The plot objects' XLimInclude, YLimInclude, ZLimInclude, ALimInclude and CLimInclude properties are an important feature, that has both functional and performance implications....
- Performance: accessing handle properties Handle object property access (get/set) performance can be significantly improved using dot-notation. ...
- Displaying hidden handle properties I present two ways of checking undocumented hidden properties in Matlab Handle Graphics (HG) handles...