0

I'm having trouble with getting multiple dynamic inheritance to work. These examples make the most sense to me(here and here), but there's not enough code in one example for me to really understand what's going on and the other example doesn't seem to be working when I change it around for my needs (code below).

I'm creating a universal tool that works with multiple software packages. In one software, I need to inherit from 2 classes: 1 software specific API mixin, and 1 PySide class. In another software I only need to inherit from the 1 PySide class.

The least elegant solution that I can think of is to just create 2 separate classes (with all of the same methods) and call either one based on the software that's running. I have a feeling there's a better solution.

Here's what I'm working with:

## MainWindow.py

import os
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
    
# Build class
def build_main_window(*arg):
    
    class Build(arg):
        def __init__(self):
            super( Build, self ).__init__()

        # ----- a bunch of methods

# Get software
software = os.getenv('SOFTWARE')

# Run tool
if software == 'maya':
    build_main_window(maya_mixin_class, QtGui.QWidget)
if software == 'houdini':
    build_main_window(QtGui.QWidget)

I'm currently getting this error:

#     class Build(arg):
# TypeError: Error when calling the metaclass bases
#     tuple() takes at most 1 argument (3 given) # 

Thanks for any help!

EDIT:

## MainWindow.py

import os
    
# Build class 
class BuildMixin():
    def __init__(self):
        super( BuildMixin, self ).__init__()

    # ----- a bunch of methods

def build_main_window(*args):
    return type('Build', (BuildMixin, QtGui.QWidget) + args, {})

# Get software
software = os.getenv('SOFTWARE')

# Run tool
if software == 'maya':
    from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

    Build = build_main_window(MayaQWidgetDockableMixin)

if software == 'houdini':
    Build = build_main_window()
2
  • 1
    That's because it's trying to subclass the tuple of args itself. If you want dynamic multiple inheritance, use type(name, bases, dct). Commented Oct 5, 2016 at 16:10
  • To avoid the error you can add asterisk before *arg when you use it: class Build(*arg). Notice that the call to super inside the constructor might call different things depending on what you pass as arg. Commented Oct 5, 2016 at 17:08

2 Answers 2

3

The error in your original code is caused by failing to use tuple expansion in the class definition. I would suggest simplifying your code to this:

# Get software
software = os.getenv('SOFTWARE')

BaseClasses = [QtGui.QWidget]
if software == 'maya':
    from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
    BaseClasses.insert(0, MayaQWidgetDockableMixin)

class Build(*BaseClasses):
    def __init__(self, parent=None):
        super(Build, self).__init__(parent)

UPDATE:

The above code will only work with Python 3, so it looks like a solution using type() will be needed for Python 2. From the other comments, it appears that the MayaQWidgetDockableMixin class may be a old-style class, so a solution like this may be necessary:

def BaseClass():
    bases = [QtGui.QWidget]
    if software == 'maya':
        from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
        class Mixin(MayaQWidgetDockableMixin, object): pass
        bases.insert(0, Mixin)
    return type('BuildBase', tuple(bases), {})

class Build(BaseClass()):
    def __init__(self, parent=None):
        super(Build, self).__init__(parent)
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the reply, ekhumoro! I'm getting a syntax error at the asterisk at class Build(*BaseClasses):. I'm also getting an error in my IDE. Even directly copy and pasting your code causes errors to show up. I'm not familiar with this extension method, but it seems like the right one to go with if I can get it working. Any idea why I'm getting this error?
@MikeBourbeau. Damn - it looks like this works in Python 3, but not Python 2 (which I assume you must be using).
Yep, I am :/ well I have to move to Python 3 at some point because of Maya 2017... maybe this is the day I start migrating
@MikeBourbeau. I've added an alternative solution that should work for Python 2 (I can't fully test it myself).
That solution worked perfectly! Thank you very much :D
1

arg is a tuple, you can't use a tuple as a base class.

Use type() to create a new class instead; it takes a class name, a tuple of base classes (can be empty) and the class body (a dictionary).

I'd keep the methods for your class in a mix-in method:

class BuildMixin():
    def __init__(self):
        super(BuildMixin, self).__init__()

    # ----- a bunch of methods

def build_main_window(*arg):
    return type('Build', (BuildMixin, QtGui.QWidget) + args, {})

if software == 'maya':
    Build = build_main_window(maya_mixin_class)
if software == 'houdini':
    Build = build_main_window()

Here, args is used as an additional set of classes to inherit from. The BuildMixin class provides all the real methods, so the third argument to type() is left empty (the generated Build class has an empty class body).

Since QtGui.QWidget is common between the two classes, I just moved that into the type() call.

2 Comments

Thank you for the quick reply. I've set up my script, but I'm getting this error now: # return type('Build', (BuildMixin, QtGui.QWidget) + args, {}) # TypeError: Invalid base class used in type Shiboken.ObjectType. PySide only support multiple inheritance from python new style class. I'm not exactly sure how this class is 'old' style? See my post above for what code I'm using
@MikeBourbeau: sorry I was away; as ekhumoro pointed out, MayaQWidgetDockableMixin is an old-style class and you'd have to add object into the mix here.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.