mardi 21 août 2018

Create a subclass whose superclass is known at runtime

Problem

I want a convenient way to create a widget that has a QHBoxLayout or QVBoxLayout as its layout. The type of the widget is not known initially and is only supplied later on.

Usage

As a class decorator

This is happens to be the part of the code that keeps getting repeated.

# write this
@BoxWidget(QDialog, Qt.Vertical)
class JukeBox:
    PLAYING, STOPPED, PAUSED = range(3)
    def __init__(self, *args, **kwargs):
        self.state = JukeBox.STOPPED
        self.playBtn = QButton("Play")
        self.layout().addWidget(self.playBtn)

# Insted of this
class JukeBox(QDialog):
    PLAYING, STOPPED, PAUSED = range(3)
    def __init__(self, *args, **kwargs):
        super(JukeBox, self).__init__(self, *args, **kwargs)
        self.state = STOPPED
        self.mainLayout = QVBoxLayout()
        self.playBtn = QPushbutton("Play")
        self.mainLayout.addWidget(self.playBtn)
        self.setLayout(self.mainLayout)

As a callable

If I can use it as a callable that returns an instance of a specific widget that will be a plus. Then I will not necessarily need to create a subclass each time I wish to use a convenient container widget.

# Conveniently create a button box
# without having to write a subclass
btnBox = BoxedWidget(QFrame, Qt.Horizontal)
btnBox.addtoLayout(QPushbutton("OK"))
btnBox.addtoLayout(QPushbutton("Cancel")

Possible Solutions

From the research that I've done it seems the following solutions may solve the problem.

Override __new__: I really do not understand the mechanism and behind the scenes work of the __new__ method despite all the blog posts I've read. I mean I do not understand enough to even ask the right questions.

Use a metaclass: I'm not really sure about this

Create a factory function*: Create a BoxedMainWindow, BoxedDialog, BoxedFrame subclasses of BoxedWidget. The factory function then decides from the parameters which type to use in the creation of the instance.

Create a decorator class/function: I understand decorators a bit. This is what I hacked together and needless to say it is failing.

def boxedWidget(Cls, orient=Qt.Horizontal):

    @functools.wraps
    class BoxedWidget:
        def __init__(self, *args, **kwargs):
            self.widgetInstance = Cls(*args, **kwargs)
            layout = QHBoxLayout()
            if orient == Qt.Vertical:
                layout = QVBoxLayout()
            self.widgetInstance.setLayout(layout)

        def layoutWidget(self, widget, *args, **kwargs):
            layout = self.widgetInstance.layout()
            layout.addWidget(widget, *args, **kwargs)

    return BoxedWidget

The error message: AttributeError: 'functools.partial' object has no attribute 'layoutWidget'

I am open to all solutions though I prefer the fourth one. Any help is appreciated.

Aucun commentaire:

Enregistrer un commentaire