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 the parent of a dynamically method as a function
-
This version of the extension functionality now meets mostnof my design goals:
- Use standard class syntax for encapsulating data and functionality - in my case, much preferred to the dict syntax
- Can extend ui component functionality, "almost subclassing"
self
always refers to the instance being extended, also in__init__
- Constructor returns the instance being extended instead of the extender, which allows for chaining extenders
- Since the original UI class instance is returned, can be used in
add_subclass
andObjCInstance
These are still a fail:
-
Extra
__func__
is needed when calling overloaded methods from superclass, most often done in__init__
:SuperClass.__init__.__func__(self, params)
-
@mikael , I didn't get a chance to run your yet. I have been trying to get another thing working.but during the course of getting the other thing working I wanted a copy ui element function. I have only tried the below with a button. I am sure , there are some things in textfields etc that cause exceptions. That's why I made the exclude list. Maybe it's not great, but it's a idea anyway. Many times I just want a copy of a object with a few changes , normally just X and y and maybe title. But copy and deepcopy fail. I haven't tried them for a while actually. Of course that would be better as part of the solution.
def copy_obj(obj , **kwargs): new_obj = type(obj)() # copy the attrs from the passed object to the new object attr_exclude_list = ['left_button_items', 'right_button_items', 'navigation_view', 'on_screen', 'subviews', 'superview'] # is removed in the list comp because is callable. we add it back :) attr_include_list = ['action'] intrested_attrs = [k for k in dir(obj) if not k.startswith('_') and not callable(getattr(obj, k)) and k not in attr_exclude_list ] + attr_include_list for k in intrested_attrs: if hasattr(new_obj, k): print k, getattr(obj, k) setattr(new_obj, k, getattr(obj, k)) # overwite new attrs in the new object ,passed in **kwargs for k, v in kwargs.iteritems(): if hasattr(new_obj, k): setattr(new_obj, k, v) return new_obj
-
@mikael , the copy_obj should be more complete now. There is a test function also. But trying this on ui.NavigationView or ui.ButtonItem ends in tears 😱
But there is a list of elements it can get through. Also a quirky thing about unassigned attrs. Have to check for them. If not already assigned in the source object to copy, some fail. As source attr has a value of None, but the target is expecting a different type. I haven't really handled this well at the moment. I have done something.
Anyway work in progress, the print statements are still in
import warnings def copy_obj(obj , **kwargs): # an idea for ui object copying... # a list of object types we accept ui_can_copy = [ui.View, ui.Button, ui.Label, ui.TextField, ui.TextView, ui.TableView, ui.ImageView, ui.SegmentedControl, ui.ScrollView] if type(obj) not in ui_can_copy: warnings.warn('Can not copy object {}'.format(type(obj))) return new_obj = type(obj)() # copy the attrs from the passed object to the new object attr_exclude_list = ['left_button_items', 'right_button_items', 'navigation_view', 'on_screen', 'subviews', 'superview', 'content_offset', 'content_size'] # is removed in the list comp because is callable. we add it back :) attr_include_list = ['action'] intrested_attrs = [k for k in dir(obj) if not k.startswith('_') and not callable(getattr(obj, k)) and k not in attr_exclude_list ] + attr_include_list for k in intrested_attrs: if hasattr(new_obj, k): print k, type(getattr(obj, k)) attr = getattr(obj, k) ''' This is an important test. if we try to copy a string attr that has nit been previously set, it will fail if we do a blind copy/assignment ''' if attr: setattr(new_obj, k, attr) # overwite new attrs in the new object ,passed in **kwargs for k, v in kwargs.iteritems(): if hasattr(new_obj, k): setattr(new_obj, k, v) return new_obj def test_copy_obj(): # notice there us No navigation_view also no ui.ButtonItem ui_can_copy = [ui.View, ui.Button, ui.Label, ui.TextField, ui.TextView, ui.TableView, ui.ImageView, ui.SegmentedControl, ui.ScrollView] for obj in ui_can_copy: o = obj() print type(o) x = copy_obj(o)
-
@Phuket2, you can give getattr a value to be used if the attribute does not exist.
You again have an interesting problem at hand, although I am not sure what you need this for.
I looked at the ui module, and it does have code to instantiate ui objects from the JSON provided by the ui editor. Now if @omz just had some code in his back pocket to generate matching JSON from ui instances, your copy thing would be done, and we would have a method for serializing ui components. Still not sure what we need it for, though.
-
@mikael , yes I understand you can get define a default return for querying a attr. But it just needs more than what I have done at the moment. Need to know the type of what is expected, which is not that difficult. But requires even more knowledge of what attr you are dealing with. For example you can see that the attr requires a tuple. Well that's wide open.
But you mention why I/we need it. Imagine in the ui.designer you couldn't copy and paste elements. In my mind it's the same thing. In the ui.designer you can also copy and paste attributes. Why should we not have this in code? I just think we should have.
Look I know I have some strange ideas. But in all honesty, I can't see why in a perfect world I can't just copy a object in full and or its attributes as you would in a gui. Especially if the copy is done very low level. Then I change a few attrs of the very fast memory copied object.
Just my 2 cents worth.
-
@Phuket2, crazy ideas push the envelope and occasionally advance the state of the art, so I am all for them. And I can understand the analogy to UI designer.
Copying plain Python objects is just that easy, but unfortunately these ones have an ObjectC monster behind the friendly facade. And copying UIKit instances even in ObjectC is not totally trivial, see this link.
-
Fixed a simple bug, now self.methods can be called in
__init__
.import types class Extender(object): def __new__(extender_subclass, target_instance, *args, **kwargs): extender_instance = super(Extender, extender_subclass).__new__(extender_subclass) for key in dir(extender_instance): if key.startswith('__'): continue value = getattr(extender_instance, key) if callable(value): setattr(target_instance, key, types.MethodType(value.__func__, target_instance)) else: setattr(target_instance, key, value) if isinstance(extender_subclass.__init__, types.MethodType): extender_subclass.__init__.__func__(target_instance, *args, **kwargs) return target_instance
-
@mikael , i am struggling to get my head around your extender concept. I don't mean the way you have written it. Just a bit fancy for me 😱
If I want to add a new class to chain it so to speak, how would I do that? I tried a few things but failed.
-
@Phuket2, here's a quick, silly and familiar-looking example demonstrating both the subclass inheritance chain and chaining extenders around a button instance. If this is of no use, please share some problematic code.
import ui from extend import Extender class DefaultStyle(Extender): background_color = 'teal' tint_color = 'white' class BorderedStyle(DefaultStyle): border_width = .5 corner_radius = 3 class ButtonAction(Extender): def __init__(self, msg = None): self.msg = msg def action(self, sender): print self.msg button = BorderedStyle(ButtonAction(ui.Button(title = 'Click me'), 'Clicked')) button.present()
-
@mikael , ok. You mentioned chained before. These are nested Calls right? I am not being smart, I am just not sure of the terminology used in this context.
But even is not that practical, I would hate to nest say 5 calls
Edited -
@Phuket2, I see what you mean. This version does not chain as much as it nests, that's true. Let me see if I can do something about that.
-
Here's one way to chain, specifically, if you do not mind "polluting" your ui components with an extra method, named
e
here for brevity.# coding: utf-8 import ui from extend import Extender class ChainingExtender(Extender): def e(self, cls, *args, **kwargs): return cls(self, *args, **kwargs) class DefaultStyle(ChainingExtender): background_color = 'teal' tint_color = 'white' class BorderedStyle(DefaultStyle): def __init__(self, border_width): self.border_width = border_width self.border_color = 'white' class ButtonAction(ChainingExtender): def __init__(self, new_title = 'Not defined'): self.msg = new_title def action(self, sender): self.title = self.msg button = ButtonAction(ui.Button(title = 'Click me'), 'Clicked').e(BorderedStyle, 10) button.present()
-
@mikael , 0k I will have to look tomorrow. 1:47am here now 😱 With many black label and sodas 🎉
But I hope you don't mind, my idea is to keep pushing your idea until it breaks. When it can not be broken, not just by me , but then it should be great.