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 loading a .pyui into itself (with instance method actions)?

    Pythonista
    bindings loadview custom-view pyui action
    2
    9
    6095
    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.
    • shinyformica
      shinyformica last edited by

      I see some pretty old threads about this topic, but I was wondering what the current definitive "best practices" way is to create a custom View subclass which can load itself from a .pyui file, and hook up the actions of its subviews to instance methods?

      So something along the lines of:

      class MyView(ui.View):
          def __init__(self):
              ui.load_view(...)
      
          def buttonAction(self, sender):
              print "button pressed:",sender
      
      instance = MyView()
      instance.present()
      

      Where buttonAction is set in the UI editor as the Action attribute of a Button that is a subview of the base view in the pyui file.
      Now, obviously the above doesn't work, since if I try to load a .pyui which has a base view set to the custom view subclass "MyView" calling "ui.load_view()" is recursive, trying to insantiate a MyView which in turn loads the pyui which needs to instantiate another MyView, etc. And if I try to have the base view in the pyui file just be a plain ui.View class, calling ui.load_view() in the init() of my custom class doesn't appear to load anything, at just creates an empty view.

      There's an old thread from two years ago discussing a way of doing this by wrapping the instantiation:

      https://forum.omz-software.com/topic/2154/using-a-custom-pu-pyui-view-in-another-one-using-ui-editor/12

      specifically this bit:

      class MyView(ui.View):
          def __init__(self, *args, **kwargs):
              
              class selfwrapper(ui.View):
                  def __new__(cls):
                      return self
                  
              if kwargs.get('pyui_file'):
                  ui.load_view(... ,bindings={'selfwrapper':selfwrapper, 'self':self})
      

      Is something like that still the right way to do this?

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

        Searching around in the forum I stumbled on this thread which has a reply about halfway through specifically about loading a custom View subclass from a pyui file:

        https://forum.omz-software.com/topic/4697/pyui-button-image-options

        This is only about 8 months old, so I will assume that there is no current "better way" to do it?
        And thanks to @Phuket2 for posting such a clear piece of example code.

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

          That's the right approach -- though (what might not be obvious) is that you ought to use a descriptive name (not MyClass).

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

            Thanks @JonB! Those are just examples, of course...the actual objects are named things that are meaningful in context.

            So I tried this out, and the custom View class was able to load the .pyui into itself and present it. But I can't figure out how to get actions to hook up to instance methods. Actually, I can't get any actions to hook up to anything: bound methods, class functions, or functions in the global scope.

            I tried setting the action to "self.action" and "MyView.action", neither worked. And even if there's a function in the global scope named "action" that doesn't get called either if I set the action function to just that.

            Anyone have an example of something like this where a subview action function is able to call an instance method?

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

              Looking around in the inspection panel with the script running and the UI loaded and displayed, I can see that the only thing which actually fills the button action attribute with something is if I use: "<class name>.<action method name>"

              I assume because that's the only method which actually resolves to something in scope. Filling it out with "self.<action method name>" or "<global function name>" leaves it set to None at runtime.

              Unfortunately the unbound method hooked up via "<class name>.<action method name>" can't be called without the class instance as the first argument, and it isn't being given that, so it produces a traceback when the button is pressed. I can manually put the correct method into the action attribute post-load:

              def did_load(self):
                  self['button1'].action = self.button1Pressed
              

              so I guess this issue is easily circumvented if I just automate that process.

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

                sorry, i thought Phuket's version included the self binding:

                        bindings['self'] = self
                

                after the other binding is set, in the PYUILoader init.

                That makes self available to use in actions, when calling via the MyClass() instantiation technique. However, when loading directly from pyui, such as when a pyui contains another custom method, it becomes much trickier. Somewhere I posted a technique to use inspect to go up the stackframe to find the parent to bind to (the action is simply eval'd, allowing you to do all sorts of such mischief)... it is also possible to use MyClass.action, except you have to detect it is called as a function with no sender argument, and find the real object by travsersing up the superview tree. I can post updated code later...

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

                  Yep, that did it. Thanks!

                  So...how on earth did you all ever find that these "bindings" existed in the call to load_view?

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

                    The ui module is pure python (though it relies on the _ui module for
                    so you can view the source.

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

                      Clearly I need to explore the API modules more :)

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