Forum Archive

[suggestion] ui element instantiation, add_subview

Phuket2

@omz, It would be great if all ui elements could be instantiationed with all attributes as named params as well as a parent param. If the parent param is a valid view then add_subview(parent) is called by ui.
I think this would clean up a lot of code when making views in code. Just a idea

ccc

How is parent different from superview?

Phuket2

@ccc, yes parent is the wrong word. Maybe just 'view' is a better name. But the idea being you can in one statement define a ui element and have it added as a subview. But could be a subview of the parent. My meaning is you would not assume that you wanted it added to the superview, rather explicitly state what view it would be added to as a subview

omz

Not a bad idea. Because of the way ui.View used to work (fixed set of attributes), it would require quite a few changes to support this directly, but it's fairly easy to write a factory function that instantiates views with arbitrary attributes, and (optionally) adds them to an existing parent view:

def make_view(cls=ui.View, **kwargs):
    if not issubclass(cls, ui.View):
        raise TypeError('cls must be a subclass of ui.View')
    v = cls()
    for name in sorted(kwargs.keys()):
        value = kwargs[name]
        if name in ('parent', 'superview') and isinstance(value, ui.View):
            value.add_subview(v)
        else:
            name = name.strip('_')
            setattr(v, name, value)
    return v

Usage example:

main_view = ui.View(frame=(0, 0, 400, 400))
make_view(ui.SegmentedControl,
          parent=main_view,
          frame=(10, 10, 380, 32),
          tint_color='green', 
          _segments=['foo', 'bar', 'baz'],
          selected_index=1)

main_view.present('sheet')

Now, you may be wondering why I prefixed the segments attribute with an underscore, and then stripped that underscore in make_view. This is basically just a trick to force that the attributes are set in a specific order, specifically, that the segments attribute is set before the selected_index because setting the selected index doesn't work if there are no segments yet. There aren't a lot of cases where the order matters, but I happened to come across one of them when I wrote this example, and this was the easiest workaround I could think of.

Phuket2

@omz , ok, that is cool also.i was thinking another way.
Like
self.btn = ui.Button(any_named_attr = x, and so on, etc..., parent = self)
So that one statement you could define a ui.Button entirely (of course all params would have default values) and if the parent or view param (whatever to call it) is not None, then the add_subview would be called by ui.
So in my meaning each ui element could possibly only need one statement to have 100% of its attrs set and be added to a view. I say could possibly, because a lot of the time you are going to calculate and set the frame in the layout method.

Phuket2

Oh, I thought I was going to get it straight between the eyes. 2 seconds after I wrote the above post, I was thinking.....
Is it possible to do self.lb = ui.Label(name = 'xxx').add_subview(self)
But, I just tried it and you can't. Maybe a better way to go

Phuket2

@omz I did this and is very nice. Of course, better if in ui module.

# coding: utf-8

import ui

import MakeView

v = ui.View(frame= (0,0,540,576))
x = MakeView.make_view(ui.Label, frame = (0,0,v.width, 44), text = 'hello', background_color = 'purple', text_color = 'white', alignment = ui.ALIGN_CENTER, font = ('<system-bold>', 22) , parent = v)
v.present('sheet')
Phuket2

And sorry, I didn't get your first comment about changing it easily. I get it now. As long as It does not create more legacy code for you, Then would be nice to have it in the std API.