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.


    Custom View Class - Problems when loading with bindings into a class

    Pythonista
    2
    9
    3781
    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 Phuket2

      @Jonb, @ccc, @omz or anyone else that may know. Using the code below the Custom View Controls don't work as they do when not using bindings and WrapInstance.
      In this post, all works normally.

      I wondered if there is something I am missing to make this work. I think I sort of get why it's not. I am thinking to do with the global a and locals, in this case the scope gets limited to the class the pyui is being loaded into.
      I get the following warning:
      Warning: Could not resolve custom view class: name 'OwnerDrawn' is not defined

      Anyway, it would be great to get a fix for this. Up until now, loading a pyui file into a class seemed to be flawless. However this seems to be a flaw.
      Hopefully, I have overlook something stupid here

      import ui
      
      def WrapInstance(obj):
      	class Wrapper(obj.__class__):
      		def __new__(cls):
      			return obj
      	return Wrapper
      	
      class OwnerDrawn(ui.View):
      	def __init__(self, *args, **kwargs):
      		super().__init__(*args, **kwargs)	
      		print('in here')
      			
      	def draw(self):
      		s = ui.Path.oval(*self.bounds)
      		ui.set_color('red')
      		s.fill()
      			
      class PYUIViewer(ui.View):
      	# this acts as a normal Custom ui.View class
      	# the root view of the class is the pyui file read in	 
      	def __init__(self, pyui_fn, *args, **kwargs):
      		ui.load_view(pyui_fn,
      		bindings={'MyClass': WrapInstance(self), 'self': self})
      		# call after so our kwargs modify attrs
      		super().__init__(*args, **kwargs)
      		
      if __name__ == '__main__':
      	w, h = 600, 800
      	f = (0, 0, w, h)
      	fn = 'Search_bar.pyui'
      	style = 'sheet'
      	
      	v = PYUIViewer(fn, frame=f)
      	v.present('sheet')
      	
      

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

        @Phuket2 If you are using bindings, load_view won't search the current frame for bindings. so, you need to explicitly include any bindings you will need, such as OwnerDrawn.

        I don't actually see why you need to use fancy bindings in this case, but I guess you are trying to apshow a generic case.

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

          @JonB , I did try adding extra bindings to the bindings dict. Well without knowing what I am doing you can imagine how horribly wrong it went. But it's not just for the generic case actually. Example, there is no step control exposed in the designer or no segment control that uses images rather than text. With is method, so easy to write the control externally and have it used in the view. I know you could just add it to the view after loading the pyui file. But it's not the same. You lose the advantages of using the designer to layout your view.
          Then when you combine it with the Custom Attributes, it gets more powerful again. Anyway, I am working on something now to show that off.
          But if you wouldn't mind, could you please show me the syntax to add extra bindings. Really, I did try to figure it out myself, I just fumbled around. But at least by trying, I did get my head around the normal case.
          Thanks in advance 😬😬

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

            lets review how load_view works.

            If the pyui has a CustomView defined, load_view checks the bindings dict for a key with that name to get the type object representing the class. So, if your pyui has a CustomClass of OwnerDrawn your bindings needs to have a key called OwnerDrawn, whose value is a class type, and subclass of View. If you don't provide the optional bindings to load_view, it grabs the globals and locals dicts from the caller and uses those.

            To instantiate the custom class, it simply calls the custom class's constructor to create an instance. So, normally if you used load_view standalone, it would see your custom view is OwnerDrawn, then creates a new OwnerDrawn(), and starts setting attributes to that instance, which it returns. If you want to call load_view from inside of __init__, this wouldn't work, since load view returns a new instance. So instead we wrap our instance in a wrapper class that looks like a View subclass, but whose new simply returns a particular instance. This is only needed for the special case of loading the custom view from inside init of the custom view class.

            Then, for custom attributes, load_view is looking for a dict, or a string that evals to a dict I guess. The values can refer to objects in locals, globals, ( or the custom bindings), or the special this which gets inserted that refers to the instance. I think that is newish, so the self binding might no longer be needed, you should be able to refer to this in the custom attribute section.

            So.... for your generic approach you should simply load_view without mucking about with the custom bindings. i.e PYUIviewer can instantiate another view using either the custom class itself, or load_view. Inside OwnerDrawn, you would call load_view with the wrapped instance binding.

            I wouldn't suggest having multiple classes that use the same pyui with a generic custom class that you fake in init... that will be confusing, and you lose the ability to insert such a custom class from the ui editor and have it mean anything.

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

              @JonB , thanks for taking your time to respond. Look at am a bit slow. I am beinging to understand it a little bit more. And I will spend more time trying to get my head around it.

              I have a feeling you have misunderstood my question here. I am not sure. Maybe I just don't get it.

              What I am talking about is to.

              1. Load the pyui into the class via the wrapping method. Works great as long as you set Custom View Class at the root level. Which makes sense.

              But any custom view control you add to a pyui also has its own Custom View Class. Seperate from the Custom attributes.

              1. In the same pyui file I have loaded into a class, I would also like to have the ability to set the Custom View Class of any Custom View Controls I place inside the pyui file.

              The problem I get when I do that when loading the pyui into the class, is any Custom View Class assignments generate a warning -

              Warning: Could not resolve custom view class: name 'OwnerDrawn' is not defined

              The only thing I can think of is that by loading the pyui into the class that the global scope is limited to the wrapped class and that's why the Custom View Controls can't find the class that is referred to.

              So it's not about trying to combine the 2 load methods into a generic call. It's about getting the wrapping method to be able to do the same as the other way.

              I am not sure if you where aware or not that every custom view control could has its own unique Custom View Class. I didn't. I thought it was only a root level setting.

              Again, I don't want to waste your time. If the answer is in your response, just say so and keep working on it.
              Thanks

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

                Setting custom bindings at the root level uses those custom bindings everywhere in load_view; if you use bindings you lose access to globals. So, what you would need to do at your top level view would be:

                bindings={  }
                stackframe = inspect.currentframe().f_back
                for key,value in stackframe.f_globals:
                     bindings[key]=value
                for key,value in stackframe.f_locals:
                     bindings[key]=value
                bindings{'PYUIViewer':WrapInstance(self)} # any overrides you need go here
                

                By the way, I have shown this with PYUIViewer, rather than the generic MyClass. This assumes the custom class of your root is PYUIViewer, not MyClass. even though the bindings override it, your Custom View Class at the root level should match the Python name if your class, otherwise you won't be able to load it as a subview... plus if you name everything MyClass you are going to be confused later. I know my examples sometimes used MyClass as a throwaway example name, but the intent was to replace it with a class name that describes the class.

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

                  @JonB, I had to add .items() to the locals and globals. I assume that was correct. But none of the Custom Views are found now.

                  The only thing I changed was the ui.load_view. Which I assume I have also done correctly.

                         stackframe = inspect.currentframe().f_back
                  		
                  		for key,value in stackframe.f_globals.items():
                  			bindings[key]=value
                  		
                  		for key,value in stackframe.f_locals.items():
                  			bindings[key]=value
                  			
                  		bindings={'PYUIViewer':WrapInstance(self)}
                  		# any overrides you need go here
                  		
                  		ui.load_view(pyui_fn, bindings)
                  

                  Sorry, the indents are a little off, but in code they are correct

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

                    maybe post your pyui to to slack. I assume you updated the pyui to remove MyClass and changed it to match what you used in bindings? I tried this on a simple example and it works fine.

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

                      @JonB , thanks. I have posted it to slack in codinghelp

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