Understanding QML, prototypal inheritance, and generalization/specialization

This discusses QML in object oriented terms, for someone who is familiar with classes and unfamiliar with declarative programming and prototypal inheritance.

This was quickly written, and could be wrong.

Thinking of QML Components as classes

QML has no classes (which are first-class objects).  However, you can loosely think of component files as class definitions.

For example you have a directory:

foo
  Foo.qml

which defines a component.  (In QML, every .qml file must have only one root item, the component that is being defined.)  You can think of this as class “Foo”.  Note that this enforces the common programming practice ‘one class per file’ and it eliminates the tedium of naming a class AND the file that contains the class definition:  the filename IS the class name.

Like a class, you can instantiate it many times:

import "foo" as MyFoo

Item {
 id: item1
 MyFoo.Foo{}
 MyFoo.Foo{}
}

(Glossing over the details of how you might identify and reference the two separate instances.)  The two instances are of the same type (they have the same properties.)  The instance “item1” has a new type, an unnamed type.  (Making this code a named component (loosely speaking, class) only requires that you put it in a file whose name starts with a capital letter.)  You can say the two instances have the class Foo, but the class Foo is not a first-class object: you can’t pass it around, except as the string name of the file that defines it.

Likewise, you can think of the directory “foo” as a module, and the directory name and the module name are one-and-the-same.

Specializing generalizations in QML

Suppose you want Foo to be a generalization, and to specialize instances of it.

Note that specializing instances creates new types (derived types but not subtypes?).  It is NOT like creating subclasses?  (I should refresh my reading of Bertrand Meyer’s discussions of the orthogonality of generalization and inheritance.)

QML has two “kinds” of properties:

  • non-visual, sometimes called ‘resources’
  • visual, often items in the list value of a property named ‘children’ (which QML magically hides using default property, all of which is a digression which I won’t discuss in detail.)

Specializing non-visual properties of generalizations in QML

It is often relatively easy to specialize an instance’s non-visual properties.  First define a generalized component (class?):

//File: Foo.qml
Item {
  property var specializableProperty
}

Then create a specialized instance:

//File: Bar.qml
import "foo" as MyFoo

MyFoo.Foo { specializableProperty: "specialValue" }

This creates an instance of Foo whose specializableProperty is bound to string “specialValue.”  You might think of this a procedural parameter passing.  Except that QML is mostly declarative (except for Javascript functions.)  If “specialValue” was not a string literal, it would seem less like parameter passing.

A more illustrative example (and a common use case) would be if the name of the property were ‘model’  and you bound it to a more elaborate object (say of class Model or the QML predefined type: ListModel) than a simple string.  Then you are creating instances of Foo, specialized by having different models.  (Where the use case is: using the model-view-controller paradigm.)

Specializing visual properties of generalizations in QML

Summary: use a QML Loader.

Specializing with visual components is complicated by the fact that visual components are frequently trying to bind their properties to properties of their parent.  In other words, the object you are specializing with must be instantiated later, when the specialized component is instantiated, not at the time you are ‘passing’ the specializing component.  First create a component whose contents are loaded from a url at instantiation time:

//File: Foo.qml
// to be specialized
Item {
  property var specializedVisualPropertySourceURL
  Loader {
    id: specializedVisualProperty
    source: specializedVisualPropertySourceURL
  } 
}

Then create a specialized instance:

//File: Bar.qml
import "foo" as MyFoo
MyFoo.Foo {specializedVisualPropertySourceURL: "../baz/Baz.qml" }

where Baz.qml defines some visual component.  Here you are passing the name of the file that defines the specializing part (you are passing a class as a factory.)  You are passing it into the specialized component, to its Loader.  The Loader instantiates the specializing part in the context of the specialized component (said context having many properties such as ‘anchor’ that are needed by the specializing visual component part.)

If you try to use the same technique as for non-visual specialization, you might get errors such as “anchor not defined”.  These errors come when you are instantiating the specializing stuff so that you can pass it: at that time, the context may not be a visual component having the needed properties such as anchor.

(Here I should have example wrong code.)

Note there are many ways to skin a cat.  I think you could also use property aliases in the above code, for example to expose Loader.source directly to the specializing component.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s