Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model Implementation #81

Closed
mottosso opened this issue Apr 14, 2015 · 1 comment
Closed

Model Implementation #81

mottosso opened this issue Apr 14, 2015 · 1 comment

Comments

@mottosso
Copy link
Member

Goal

Implementing new items and their corresponding data has become unwieldy. Simplify the implementation of the QAbstract Models.

Reference




# Implementation

As opposed to implementing disparate roles per data entry and passing roles onto QML, we instead pass the item itself (as a QObject) to QML who then accesses properties directly.

Technically, this means.

  • QML bindings happens per-item
  • Updates are made to items directly
  • Updates can be made from both Python and QML
  • Items can be used outside of model and maintain bindings

The consequence of this.

  • Ownership must be made explicit
  • dataChanged of model not emitted when data changes.
  • A decrease in performance
  • No roles

Tests




# Cons ### Ownership

If no owner is assigned to an item, it's owner is undefined. By explicitly assigning an owner, in this case the model then the model has final say about when to destroy each item. In this case, the model is part of Python and thus items are controlled by Python's garbage collection mechanism, as is the model itself.

If no owner is assigned, the runtime may throw errors such as

RuntimeError: wrapped C/C++ object of type Item has been deleted

dataChanged

This signal is used by users of the QAbstract models for various tasks. One of those tasks had a direct influence on the current operation of Pyblish QML; which was how proxy models invalidated their current state.

As a fix, each item emits a changed signal that is subscribed to by the model upon item creation.

Performance

Due to bindings and signals being emitted per-item, as opposed to for a single model, performance degrades exponentially with each item added, as opposed to the fixed hit it would normally have upon only having to listen and communicate with a single object (the model itself).

Whether this is relevant has yet to be seen, but a warning bell goes off and I have yet to do any benchmarks.

No Roles

Some objects are still dependent on the use of roles. One of those items is the default filter functionality QSortFilterProxyModel.

proxy = QSortFilterProxyModel()
proxy.setFilterRole(123)
proxy.setFilterFixedString("Value of role 123")

As a workaround, filterAcceptsRow is overridden to explicitly call upon the string at a pre-defined location.

def filterAcceptsRow(self, source_row, source_parent):
    regex = self.filterRegExp()
    return True if regex.indexIn(item.filter) else False



# Pros

As the goal of this implementation was to lessen complexity, I would be remiss if I were to not mention the added benefits.

  • Separation of concerns between items and model
  • Direct Access
  • QML Namespace

Separation of concerns

Models are no longer coupled to the data contained in it's items, which means we can perform some additional logic on item creation, such as generating properties to and from a JSON-dictionary.

json = {"name": "Marcus"}
item = Item(**json)
assert item.json == json

The model will serve the involved properties and adhere to changes by listening on each items dataChanged event.

The result of this simplification led to a large reduction of code and greater reuse.

Direct access

Data is updated uniformly across Python and QML, which means that data is updated similarly from Python as from QML.

current_implementation.py

    def update_current(self, pair):
        for index in range(self.rowCount()):
            self.setData(index, "isProcessing", False)

        for type in ("instance", "plugin"):
            name = pair[type]
            item = self.itemFromName(name)

            if not item:
                continue

            index = self.itemIndexFromItem(item)

            self.setData(index, "isProcessing", True)
            self.setData(index, "currentProgress", 1)

new_implementation.py

    def update_current(self, pair):
        for item in self.items:
            item.isProcessing = False

        for type in ("instance", "plugin"):
            name = pair[type]
            item = self.items.get(name)

            if not item:
                continue

            item.isProcessing = True
            item.currentProgress = 1

QML Namespace

An added benefit to dot-notation access is that data are accessed via a namespace, which makes it more clear where data is coming from.

current_implementation

    delegate: ListItem.StandardActions {
        text: name
        active: optional
        checked: isToggled

        height: 20
        width: parent.width

        status: {
            if (isProcessing)
                return "selected"
            if (hasError)
                return "error"
            if (succeeded)
                return "success"
            return "default"
        }

In this example, is name a variable of the this, a scope variable or part of our model?

new_implementation.qml

    delegate: ListItem.StandardActions {
        text: object.name
        active: object.optional
        checked: object.isToggled

        height: 20
        width: parent.width

        status: {
            if (object.isProcessing)
                return "selected"
            if (object.hasError)
                return "error"
            if (object.succeeded)
                return "success"
            return "default"
        }

Use outside of model

Because the GUI is bound to items directly, an item can be detached from it's model and used independently. The item may be altered and bound to whilst the model remains intact and GUI maintains bindings and updates in real-time.

item = model.item(index=1)
qml_function(item)
mottosso added a commit to mottosso/pyblish-qml that referenced this issue Apr 14, 2015
mottosso added a commit to mottosso/pyblish-qml that referenced this issue Apr 14, 2015
@mottosso mottosso mentioned this issue Apr 17, 2015
@mottosso
Copy link
Member Author

Implemented in 0.2.9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant