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.
__init_subclass__ Just asking (py 3.6)
-
@omz, I understand ui Elements are done in Objective-C and can't be sub classed. But does the introduction of the init_subclass hook in py3.6 give any new opportunities? I am guessing the answer is no, but wanted to ask anyway.
-
When you define an
__init_subclass__
method on a class, that method is called when you subclass that class. This only really helps you when you are writing the base class, which isn't the case here.The reason why the
ui.View
subclasses can't be subclassed is different. It's not that classes implemented in C cannot be subclassed - after all,object
andui.View
are also implemented in C, and you can subclass those. The issue is that (as I understand it) C classes do not support subclassing by default, and if you want to support it, you need to add extra custom handling for each class. There are quite a lot of defaultui.View
subclasses, so omz might have decided that it's not worth the effort to write and test subclassing support for each one. -
@dgelessus , thanks for the good explanation. I am wondering if I got the right magic method here. I was watching a video about 3.6 changes and they mentioned a magic method that you get called on before the init is called. Basically so you can avoid having to do the tricks to craft a class before it's built.
I have to go back and watch the video. It sounded important and interesting. I was initially thinking that it might have been a help to the stuff @jonb did with bindings to get a loaded pyui file into custom class.
Anyway, I will rewatch the video and see -
@dgelessus is right. The reason
ui.View
subclasses (and some other things) cannot be subclassed further is that supporting subclassing requires additional precautions. It's not that it's impossible (seeui.View
), but it's more work than simply not allowing subclassing (e.g.ui.Rect
). -
@omz , ok. Fair enough. But has come a long way. Can add attrs at runtime, can pass all **kwargs for creation. The bindings also helped with pyui classes. So just saying many enhancements have been made to supplement not being able to subclass thinks like ui.Button.
The below example has always frustrated me. That is if you add a method to a ui Element at runtime, you cant get a reference to the instance of the object calling the method. Maybe there is a way I don't know about. But as far as I know you cant.
I am looking from the point of view, if these items cant be subclassed, what do you really need from subclassing the item. The one I mention here would be a good one to have as I see it.
import ui def pos(sender=None, x=0, y=0): print('hello, but i dont which object called me') print(f'btw x={x} and y={y}') ''' i would like it possible that sender was valid here. i dont want to have to pass in the reference to the object as a param ''' if __name__ == '__main__': btn = ui.Button(frame = (20, 20, 80, 32), bg_color='white', border_width =.5) btn.title = 'Hit Me' btn.present(style='sheet') btn.pos = pos btn.pos(None, 10, 20)
-
__new__
gets called before init, maybe that is what you were thinking?BTW, what you are trying can be accomplished using
MethodType
--binding an instance method to an instance.Adapted from stackoverflow:
import types def patch_me(target): def pos(self,x,y): print ("x=",x,"y=",y) print ("called from", self) target.pos = types.MethodType(pos,target) #add more if needed import ui a = ui.Button() print (a) patch_me(a) #patch instance a.pos(10,20)
Using this approach, hopefully it should be obvious that you can sort of "subclass" ui.Button, though only sort of (the button still thinks it is a ui.Button, and you cannot override existing methods).
You could probably do this in a more generic way, say you define a custom class, which takes a Button in
__new__
, which then traverses over instance methods in your NewClass and binds them to your button instance, then calls your NewClass.__init__
on the button, and finally returns the now patched button. -
@JonB, thanks again. I remember code around like this before. Not sure it was exactly the same of not.
But I could not get this example working. I sort of can see what is happening. I get the trace back as listed below.I played around with this statement target.method = types.MethodType(method,target) , but could not figure out how to get it correct.
Did you run it also?
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/MyProjects/scratch/patching_ui_methods.py", line 13, in <module>
patch_me(a) #patch instance
File "/private/var/mobile/Containers/Shared/AppGroup/3533032E-E336-4C25-BBC4-112A6BF2AF75/Pythonista3/Documents/MyProjects/scratch/patching_ui_methods.py", line 7, in patch_me
target.method = types.MethodType(method,target)
NameError: name 'method' is not defined -
sorry, i edited it in real time to make it specifc to your pos method, but then forgot the generic one. I fixed the code above.
method
should have beenpos
-
@JonB , thanks a lot. Sorry, I couldn't work it out. I was close, but still a million miles away. I never remember it being this easy to do this. I remembered I wanted to call positioning code on the ui.object rather than passing the object into functions. Also other things. But this is a nice addition to have. I didn't re watch that video yet, but i will. When i was watching it, the first thing that went though my mind is that it may simplify the pyui wrapper class you made. Although there is nothing wrong with it, still works fine.
Ok, but thanks again.
-