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

Add node to instance in Maya #82

Open
tokejepsen opened this issue Nov 26, 2016 · 15 comments
Open

Add node to instance in Maya #82

tokejepsen opened this issue Nov 26, 2016 · 15 comments
Labels

Comments

@tokejepsen
Copy link
Member

Description

Getting an error when adding nodes to an instance and toggling in the UI;

# Traceback (most recent call last):
#   File "C:\Users\admin\Documents\conda-git-deployment\repositories\bumpybox-core\pyblish-lite\pyblish_lite\window.py", line 645, in <lambda>
#     "instance": index.data(model.Object)}))
# TypeError: object of type 'Transform' has no len()

Minimal steps to reproduce

import pyblish.api
import pymel.core
import pyblish_maya


# Create group
pymel.core.group(empty=True, name="test_group")


class Collect(pyblish.api.ContextPlugin):

    order = pyblish.api.CollectorOrder

    def process(self, context):

        default_cams = ["front", "persp", "side", "top"]
        for item in pymel.core.ls(type="transform"):
            if item.name() not in default_cams:
                instance = context.create_instance(name=str(item.name()))
                instance.add(item)


pyblish.api.register_plugin(Collect)
pyblish_maya.show()
@tokejepsen tokejepsen added the bug label Nov 26, 2016
@mottosso
Copy link
Member

I can't tell exactly why this is happening, but as a temporary workaround you could store the names of your nodes rather than the PyMEL objects.

@tokejepsen
Copy link
Member Author

I'm working around it for now, by just storing the PyMEL object in a data member 😄

@tokejepsen
Copy link
Member Author

Seems like this might be a deeper rooted problem.

index.data(model.Object) is not the instance object or plugin object we would expect, but a list.
The issue got flagged when implementing the callback feature, but I don't think it got addressed; #56 (comment)

@tokejepsen
Copy link
Member Author

tokejepsen commented Dec 9, 2016

Here is a small test that I'm working with to try and solve this;

import pyblish.api
import pyblish_lite


class Collect(pyblish.api.ContextPlugin):

    order = pyblish.api.CollectorOrder

    def process(self, context):

        context.create_instance(name="A")


pyblish.api.register_plugin(Collect)


def toggle_instance(instance, new_value, old_value):
    print type(instance)


pyblish.api.register_callback("instanceToggled", toggle_instance)

pyblish_lite.show()

This will print the type of the instance, which currently are a list but should be pyblish.plugin.Instance.

@tokejepsen
Copy link
Member Author

It seems between passing the instance object from the model to the window, it gets converted to a list.

Continuing the investigation...

@tokejepsen
Copy link
Member Author

tokejepsen commented Dec 9, 2016

Strangely the same code works with the plugins, and there shouldn't be a difference in fetching the data;

import pyblish.api
import pyblish_lite


class Collect(pyblish.api.ContextPlugin):

    order = pyblish.api.CollectorOrder

    def process(self, context):

        context.create_instance(name="A")


pyblish.api.register_plugin(Collect)


class Validate(pyblish.api.ContextPlugin):

    order = pyblish.api.ValidatorOrder
    optional = True

    def process(self, context):

        pass


pyblish.api.register_plugin(Validate)


def toggle_instance(instance, new_value, old_value):
    print type(instance)


pyblish.api.register_callback("instanceToggled", toggle_instance)


def toggle_plugin(plugin, new_value, old_value):
    print type(plugin)


pyblish.api.register_callback("pluginToggled", toggle_plugin)

pyblish_lite.settings.InitialTab = "overview"
pyblish_lite.show()

@mottosso
Copy link
Member

mottosso commented Dec 9, 2016

Context and Instance are subclassed from list.

Might be related.

@tokejepsen
Copy link
Member Author

So I've tried narrowing down where the problem is, but I don't understand why.
When executing the test code above, and printing the index from the view and model I get two different objects;

view
<PySide.QtCore.QModelIndex(0,0,0x0,Instance(0x4684b40) )   at 0x00000000045A7788>
model
<PySide.QtCore.QModelIndex(0,0,0x0,Instance(0x4684b40) )   at 0x0000000004597D08>

Is this expected?

@mottosso
Copy link
Member

Hm. Maybe try making a textual replication of the problem. Cut the GUI out of the equation.

For example, you can trigger a signal via pyblish.api.emit, which is used as-is by pyblish_lite.control.Controller.emit_. You can also add an instance to the model manually.

from pyblish import api
import pyblish_lite.model

item = api.Instance("MyInstance")
model = pyblish_lite.model.Instance()

model.append(item)
index = next(iter(model))
index.data(pyblish_lite.model.Object)

You could also emit the signal yourself.

from pyblish import api
item = api.Instance("MyInstance")
api.emit(signal="instanceToggled",
              kwargs={"new_value": True,
                      "old_value": False,
                      "instance": instance))

@mottosso
Copy link
Member

mottosso commented Dec 21, 2016

Ok, I stumbled upon this just now.

script.py

import sys
from Qt import QtWidgets


if __name__ == '__main__':
    from pyblish import api

    app = QtWidgets.QApplication(sys.argv)

    instance = api.Instance("camera")

    item = type("MyItem", (QtWidgets.QGraphicsItem,), {})()
    item.setData(0, instance)

    print(type(instance))
    print(type(item.data(0)))

    app.exit()

Returns:

$ python script.py
<class 'pyblish.plugin.Instance'>
<type 'list'>

As it turns out, Qt or the Qt binding alters the type from Instance to list when passing it through the binding. It may be some limitation in communicating with C++, or may simply be something overlooked. An optimisation on handling Python lists made by the author of the binding.

A solution is to simply not use the built-in data() member, but instead store it ourselves in a data[] dictionary, similar to how instances do things. There shouldn't be any side-effects.

@mottosso
Copy link
Member

It appears to only affect list and subclasses of it.

script2.py

import sys
from Qt import QtWidgets


class CustomClass(object):
    pass


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    instance = CustomClass()

    item = type("MyItem", (QtWidgets.QGraphicsItem,), {})()
    item.setData(0, instance)

    print(type(instance))
    print(type(item.data(0)))

    app.exit()

Returns:

$ python -u script2.py
<class '__main__.CustomClass'>
<class '__main__.CustomClass'>

@tokejepsen
Copy link
Member Author

Very interesting 😄

A solution is to simply not use the built-in data() member, but instead store it ourselves in a data[] dictionary, similar to how instances do things. There shouldn't be any side-effects.

Sorry, not entirely sure what you mean?

@mottosso
Copy link
Member

mottosso commented Jan 7, 2017

Missed this!

What I meant was, data() is a member of Qt's framework. It's part of the superclass for QAbstractListModel. For some reason, this member cannot take a subclassed list and pass it on, but instead chooses to demote it to a standard list.

So what we could do, is instead of subclassing data(), we make say a plain member, data = dict(), and use that.

There really is no reason to use the superclass data() except for familiarity - so that someone else looking at our code can go "ah, I know how data() works. So if it causes us trouble, best to not use it.

mottosso added a commit that referenced this issue Jan 14, 2017
@mottosso
Copy link
Member

This has been addressed on a shallow level with #83.

I'll keep this issue open as a reminder to go fix it proper.

@hannesdelbeke
Copy link
Contributor

hannesdelbeke commented Jan 30, 2022

has this been fixed?
i often use pymeshes in instances and seem to have no problem.
let me know if there are any problems to be aware of and i'll keep an eye out

if any thing more needs doing to adress this i'm happy to pick that up

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

No branches or pull requests

3 participants