1

I've made a context manager wrapper for PyQt, where __enter__ will setup the layout or widget, and __exit__ will apply it to the parent layout. Basically it reduces the lines of code needed to create a layout, and works well with indenting.

I'll use the wrapper on widgets if I want to have access to some of the methods (such as remove_border if it's a QTreeWidget), but I need to call the code like this:

with MyClass(QtWidgets.QTreeWidget, parent_layout) as widget:
    pass

Would it be possible to have it so I can do widget = MyClass(QtWidgets.QTreeWidget, parent_layout), but still have it run the enter and exit methods?

Edit: Based on code_onkel's answer, I've separated the functions but kept it quite simple, so a lot of the work is automatically done.

Here's an example with QPushButton:

class Example(object):
    ...

    def addQPushButton(self, *args, **kwargs):
        with QWidgetPushButton(self, *args, **kwargs) as widget:
            return widget

    @contextmanager
    def QPushButton(self, *args, **kwargs):
        with QWidgetPushButton(self, *args, **kwargs) as widget:
            yield widget


with Example(parent) as layout:
    button_1 = layout.addQPushButton('1')
    with layout.QPushButton('2') as button_2:
        pass
Peter
  • 3,186
  • 3
  • 26
  • 59
  • You could have a look at `__del__`, but see [this question](https://stackoverflow.com/questions/1481488/what-is-the-del-method-how-to-call-it) for possible problems. – Thierry Lathuille Aug 11 '18 at 09:24

1 Answers1

0

I think the most elegant way to work around this is to adjust you factory pattern a little bit and add wrapper functions for the interaction with your factory class. See the code below, there is a create_simple() method that is not a context manager. IMHO, this is cleaner than forcing the execution of a context manager by some trick when you do not need the context. If you insist on using a single factory function, you need an argument to tell the factory if you want a context manager or a plain widget (see create2()).

import contextlib

class WidgetFactory:
    def __init__(self, widget_cls, parent=None):
        self.widget_cls = widget_cls
        self.widget = None
        self.parent = parent

    def create_widget(self):
        # create widget
        self.widget = object()

    def setup_widget(self):
        # do stuff
        pass

    def apply_to_parent(self):
        # do suff
        pass

    @contextlib.contextmanager
    def create(self):
        self.create_widget()
        self.setup_widget()
        yield self.widget
        self.apply_to_parent()

    def create_simple(self):
        self.create_widget()
        self.setup_widget()
        self.apply_to_parent()
        return self.widget

def create(*args, **kwargs):
    return WidgetFactory(*args, **kwargs).create()

def create_simple(*args, **kwargs):
    return WidgetFactory(*args, **kwargs).create_simple()

def create2(*args, context=False, **kwargs):
    factory =  WidgetFactory(*args, **kwargs)
    if context:
        return factory.create()
    else:
        return factory.create_simple()

def main():
    root = create_simple('dummy for qt class')

    with create('dummy for qt class', root) as child:
        # do stuff with child
        print(child)

    simple_child = create_simple('dummy for qt class', root)
    print(simple_child)

if __name__ == '__main__':
    main()
code_onkel
  • 2,759
  • 1
  • 16
  • 31
  • Thanks, I didn't exactly copy this but did use it for inspiration (this way looks like it'd be hard to maintain if building a lot on it), also what you did in `create2` was pretty nice, wasn't aware it was possible to return a yielded value for the same effect. – Peter Aug 12 '18 at 12:24