@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.
Forum Archive
__init_subclass__ Just asking (py 3.6)
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 and ui.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 default ui.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 (see ui.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
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 been pos
@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.
@JonB, I found this Talk from PyCon 2017. Know thy self, methods and method-bindings. I haven't had time to watch it all yet, but pretty sure this will help me understand all this a bit better.
I also mention for others who might read this thread.