Loading pyui files into the root level of a custom class
I have seen quite a few posts about loading subviews into an existing view. As has far as I have seen there is no way to load a pyui into the same layer as the parent view, it will become a subview. Would be great if I am wrong! But I can't find a way to do it. I have seen posts about reading the JSON file and parsing it. I guess slow and prone to errors.
I was looking through the JSON file of a pyui file that didn't contain any subviews. But I noticed the nodes attribute, In this case always an empty list. I am guessing nodes are subviews in the view.
But it would be so cool if @omz had a method like ui.load_into_parent(xx.pyui). I am guessing it would be just how he parses the nodes. Each ui element has a uuid, so I think no duplication issues. If he hoisted the node by -1 or something like that in his parser, I think then the imported view could be added to the root level of the parent view (in a way normalising the namespace for the imported view). If subviews are defined in the pyui file, they would still persist.
Maybe I have to go back to AA and start my 12 step program. But I think it would be great to have this functionality.
@dgelessus, I am not sure. But it looks promising. Off to test it.
Thanks for your help. However niether approach flattens out the view. It appears to graphically, but is still a sub_view of the parent . When it's a sub_view, the addressing of the objects are via the subview. I have tried to demonstrate with the code below.
Does not matter if a pyui file or multiple files.
You can't call v.superview.add_subview on your topmost view. Still does not matter, you can call it on a lower class, same results, you are adding a new view with its own addressing space
Look, again. Maybe I am missing something simple. But I think this would be valuable in building ui's.
# coding: utf-8 import ui class v1(ui.View): def __init__(self): self.name = 'aux_view' lb = ui.Label(name = 'lb') lb.text = 'some text' self.add_subview(lb) class MyClass(ui.View): def __init__(self, f): self.frame = f self.background_color = 'white' v = v1() # i would like to do something like.... # self.merge_this_view(v1) self.add_subview(v) # line below fails, even though the view is # merged visually, its not merged, its a still # a sub_view. #self['lb'].text = 'some text' # this line will work self['aux_view']['lb'].text = 'new text' # i know, i can save attributes etc to access # the subviews eaisier. i really just wanted it # flattened out into a single view in this case. if __name__ == '__main__': f = (0,0,540, 576) mc = MyClass(f) mc.present('sheet')
what exactly are you trying to achieve? You want to design the ui in the editor, but have it contained within your custom class, so that you can encapsulate callbacks, etc?
The is exactly what the Custom View field in the ui editor is for. The docs are a little vague about this, but the idea is you have a custom View class, which you refernce in the ui editor. When you call load_view, the class is instantiated, and the loaded pyui associated with the instance.
@JonB, yes I have done that before from the ui. I think @ccc helped me getting it going. For the moment, I am working on my cell class that is used by the VirtualView. A part of the flexibility I wanted for the cell is to be able to load a pyui file as its view. I can get that working. But the bigger idea in my mind is just composing cells/views from multiple views, memory or file based. But to have those elements be in the same addressing space.
I can also see many times you would not want this. When you want the container view and its subviews to be a subview.
But I think times when you would like view merged into views (hierarchy flatted out).
I am looking at ui.py now, will see what I can see from there
ok, played around a bit, here are a few thoughts:
You set the CustomView class in the ui editor, then you use load_view() which will instantiate your custom class. Button callbacks, if they are class instance variables, need to be set inside your custom class did_load, because you don't have access to the instantiated object yet.
Actually, this used to be true, but since in 1.6 the action attribute is simply eval'd, it is possible using some
inspecttrickery to gain access to the custom class instance!
def this(): import inspect fb=inspect.currentframe().f_back while fb.f_back.f_code.co_name != 'load_view_str': fb=fb.f_back return fb.f_locals['v']
allows one to use this().some_callback_method as the action attribute for buttons, etc inside the ui editor -- this() returns the top level custom class instance, which has already been built but simply is in the process of having subviews instantiated. This might make custom class code a lot cleaner (usually, button actions, various delegates, etc all have to be set inside of did_load, which makes for a big pile of initializations)
@JonB, I was also playing. I am sure not as good as you. But if use self = ui.load_view('somefile.pyui', bindings = self), I seem to get the view loaded into the root, but it does not display. I don't understand the stackframe param.
Is this possibly a more direct way?
what would "merging" a view really mean? You just want the subviews of the loaded view as subviews of your existing view?
for sv in otherview.subviews: myview.add_subview(sv)
But what to do about the loaded top Views attributes, like bg color, etc. What to do when subview names conflict? Complicated pyui's might have custom classes that depend on the top level attribs. You could copy these over of course, but this starts getting pretty fragile.
Your VirtualViewCell sounds more like a container -- much like a ScrollView or TableViewCell, for example. If you want to standardize addressing, consider placing all user content into a
contentattribute, similar to TableViewCells. You could also override the
subviewsmethod, so that named or indexed calls to other subviews are redirected to your
content... though i wouldnt recommend that approach.
@JonB , I get your point. But I would imagine any merged views attributes would not override the parents attributes. Duplicate subview names a little more tricky. I guess could just refuse to load, raise an error.
But I do see the problems with this approach.
Thanks for your help. I will keep trying. As you say, don't want anything that is fragile. Needs to be robust.
here is an example of a custom view defined by a pyui. not what you are looking for now, but thought i'd share this method of setting the action from within the custom view itself.
I can see @omz could do it in the future. It's a little funky how all the calls to _view_from_dict sort of cascade. I made a copy of ui to ui2, passed self all the way to _view_from_dict. Then Added the parsed items to self.add_subview.
It sort of works.
I just tried to do some minimal things without hardly changing the code.
However _view_from_dict returns a view, also so other things I don't understand. So a little rework would be required, but possible.
I know I could copy the code and roll my own.... But not worth it. Eventually it would break.