omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Loading pyui files into the root level of a custom class

    Pythonista
    1.6 pyui
    4
    15
    9938
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Phuket2
      Phuket2 last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • Webmaster4o
        Webmaster4o last edited by Webmaster4o

        Here's working code:

        import ui
        
        def load_into_parent(parent, filename):
        	for subview in ui.load_view(filename).subviews:
        		parent.add_subview(subview)
             return parent
        

        The function load_into_parent will place the contents of the file named filename into the existing view parent. For example, this code will load the contents of Untitled.pyui into view1:

        view1 = ui.View()
        load_into_parent(view1, 'Untitled.pyui')
        view1.present()
        

        It can also be used to merge two pyui files together:

        # Present a merged view of a.pyui and b.pyui
        a = ui.load_view('a.pyui')
        load_into_parent(a, 'b.pyui')
        a.present()
        

        View a:

        View b:

        Combined:

        both the button and the segmented control are in the same container.

        1 Reply Last reply Reply Quote 1
        • dgelessus
          dgelessus last edited by

          ui.Views have a superview attribute that points to their parent view. If I'm understanding you correctly this would do what you want:

          import ui
          
          def load_into_parent(view, filename):
              view.superview.add_subview(ui.load_view(filename))
          
          Phuket2 1 Reply Last reply Reply Quote 0
          • Webmaster4o
            Webmaster4o last edited by Webmaster4o

            This post is deleted!
            1 Reply Last reply Reply Quote 0
            • Phuket2
              Phuket2 @dgelessus last edited by

              @dgelessus, I am not sure. But it looks promising. Off to test it.

              1 Reply Last reply Reply Quote 0
              • Phuket2
                Phuket2 last edited by

                Guys,
                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')
                	
                
                1 Reply Last reply Reply Quote 0
                • JonB
                  JonB last edited by

                  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.

                  Phuket2 1 Reply Last reply Reply Quote 0
                  • Phuket2
                    Phuket2 @JonB last edited by

                    @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).

                    1 Reply Last reply Reply Quote 0
                    • Phuket2
                      Phuket2 last edited by

                      I am looking at ui.py now, will see what I can see from there

                      1 Reply Last reply Reply Quote 0
                      • JonB
                        JonB last edited by

                        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 inspect trickery 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)

                        Phuket2 1 Reply Last reply Reply Quote 0
                        • Phuket2
                          Phuket2 @JonB last edited by

                          @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?

                          1 Reply Last reply Reply Quote 0
                          • JonB
                            JonB last edited by

                            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 content attribute, similar to TableViewCells. You could also override the __getitem__ and perhaps subviews method, so that named or indexed calls to other subviews are redirected to your content... though i wouldnt recommend that approach.

                            Phuket2 1 Reply Last reply Reply Quote 0
                            • Phuket2
                              Phuket2 @JonB last edited by

                              @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.

                              1 Reply Last reply Reply Quote 0
                              • JonB
                                JonB last edited by

                                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.
                                https://gist.github.com/jsbain/1da126b6d60dfa15055d

                                Phuket2 1 Reply Last reply Reply Quote 0
                                • Phuket2
                                  Phuket2 @JonB last edited by

                                  @JonB, thanks.
                                  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.

                                  1 Reply Last reply Reply Quote 0
                                  • First post
                                    Last post
                                  Powered by NodeBB Forums | Contributors