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?
-
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.
-
-
@JonB oh...interesting. So would that just be:
pyObject.restype = c_void_p pyObject.argtypes = []
-
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.
-
@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. -
@JonB Jinx.
-
@shinyformica, @JonB, just took another look at Gestures, which uses the mind-boggling? combo of
retain_global
andweakref
s, but seems to work ok for most people. Is there a clear advantage to the pyobject approach that would recommend refactoring Gestures? -
@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.
-
@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.
-
@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)