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.


    Getting Python ui widget from objc instance?

    Pythonista
    widget objc
    3
    14
    5260
    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

      @mikael did you notice any GC issues when storing a strong ref to the python object in the objc instance?

      I don't know how things are hooked up underneath...whether the fact that the python widget object has an objc_instance member, which holds the objc object, which would then in turn have a member pointing back at the python object creates an un-breakable reference loop that would prevent GC, and thus potentially cause a memory leak.

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

        pyObject() works, but you have to manually set the restype/argtypes. If the view is visible, I think you are guaranteed that it has not been gc'd, though any callbacks need to be careful to import what it needs explicitly within the callback, since module level imports may have been cleared. Also, callbacks should not use any globals.

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

          https://forum.omz-software.com/topic/5422/access-to-pyobject-of-objcinstance-is-crashy/18

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

            @JonB oh...interesting. So would that just be:

            pyObject.restype = c_void_p
            pyObject.argtypes = []
            
            1 Reply Last reply Reply Quote 0
            • JonB
              JonB last edited by

              That thread was yours ;)

              Make sure you are doing it on_main_thread, and for whatever reason a c_void_p seems more robust than py_object restype.

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

                @JonB ...you know what's most embarrassing here? That thread you pointed to is MY OWN DANGED THREAD!

                Apparently I already encountered and was shown the solution to this issue with pyObject()...just forgot.
                Thanks again.

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

                  @JonB Jinx.

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

                    @shinyformica, @JonB, just took another look at Gestures, which uses the mind-boggling? combo of retain_global and weakrefs, but seems to work ok for most people. Is there a clear advantage to the pyobject approach that would recommend refactoring Gestures?

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

                      @mikael, the weakref to the gestures instance I think was a suggestion I made a while back to make sure the delegate methods access the correct python object, but not prevent it from being GC'd. I don't think you can use pyObject in that case, because the delegate is an objc class you created, not a ui.View.

                      In the slightly altered version of your Gestures module I use, I turned off the retain global bit, and also replaced the somewhat heavyweight use of a ui.Button instance as a mechanism for capturing the gesture with a small objc class with just the handler function.

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

                        @shinyformica, ok, thanks.

                        There is the option of turning retain_global off, if you want to take over the responsibility of making sure that Gestures sticks around. Or did you have some clever way of not needing to worry about that?

                        Also, a PR or some lines of code for the handler function would be welcome.

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

                          @mikael in my use case, I'm actually making a Gestures instance per-custom-widget. Since each of my widgets can have 2 or 3 gestures, it is simpler to just let each one have its own Gestures instance and install the various recognizers on each one. Gestures isn't particularly heavyweight, especially without the internal ui.Button instance, and then I don't need any global tracking of who has gestures installed, since when the widget goes away, it takes the gestures instance with it.

                          Creating a little handler method in the gesture delegate class, which can be attached to a gesture recognizer is as simple as:

                          ...existing gesture delegate definition...
                          
                          def handleGesture_(_self, _cmd, recognizer):
                                  import objc_util
                                  delegate = objc_util.ObjCInstance(_self)
                                  if not delegate: return
                                  recognizer = objc_util.ObjCInstance(recognizer)
                                  if not recognizer: return
                                  gestures = delegate._gestures()
                                  if not gestures: return
                                  gestures._handleGestureRecognizer(recognizer)
                          
                              methods = [...
                                          handleGesture_]
                          

                          Then, where you make the recognizers, instead of the whole button action thing, with all the associated bookkeeping:

                          recognizer = \
                                      objc_util.ObjCClass(recognizer_type).alloc()
                                      recognizer.initWithTarget_action_(self._delegate,
                                              objc_util.sel("handleGesture:")).autorelease()
                          view.objc_instance.addGestureRecognizer_(recognizer)
                          
                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post
                          Powered by NodeBB Forums | Contributors