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 the parent of a dynamically method as a function

    Pythonista
    attr dynamically parent
    6
    61
    43335
    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.
    • Phuket2
      Phuket2 @mikael last edited by

      @mikael , mine is in site-packages. I installed it with stash. It's ok, I don't know what's going on. I will look into it. As I say, nothing is going smoothly today 😜
      Have completely shut down my ipad. Didn't help

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

        Wrappers also chain nicely, so you might have e.g. styles in a different inheritance hierarchy and extra functionality in other.

        The only thing that is not chained is subject, so it probably makes sense inherit from a common base class and include a method that does the chaining, as follows:

        # coding: utf-8
        import ui
        from proxy import ObjectWrapper
        
        class ChainableWrapper(ObjectWrapper):
        	def __init__(self, obj):
        		ObjectWrapper.__init__(self, obj)
        	# Follow the chain of __subject__'s, if needed
        	def get_subject(self):
        		subject = self.__subject__
        		while isinstance(subject, ObjectWrapper):
        			subject = subject.__subject__
        		return subject
        
        class DefaultStyle(ChainableWrapper):
        	def __init__(self, obj):
        		super(DefaultStyle, self).__init__(obj)
        		self.background_color = '#fff7ee'
        		self.tint_color = 'black'
        	
        class HighlightStyle(DefaultStyle):
        	def __init__(self, obj):
        		super(HighlightStyle, self).__init__(obj)
        		self.tint_color = 'red'
        		
        class ToggleButton(ChainableWrapper):
        	def __init__(self, obj):
        		super(ToggleButton, self).__init__(obj)
        	def extra_functionality(self):
        		pass
        		
        button = ToggleButton(HighlightStyle(ui.Button()))
        button.title = 'Styled button'
        button.present()
        
        # Get the wrapped ui.* instance
        some_other_view.add_subview(button.get_subject())
        
        1 Reply Last reply Reply Quote 0
        • mikael
          mikael last edited by

          @omz, would there be any chance to have add_subview accept proxies in addition to direct view objects? And have ObjCInstance recognize proxies and use the underlying reference instead?

          Phuket2 2 Replies Last reply Reply Quote 0
          • Phuket2
            Phuket2 @mikael last edited by

            @mikael , just let you know, I got it working again. For some reason my version of ProxyTypes was corrupt 😂

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

              @mikael

              I wrote the below on another thread. I think it would help a lot without having to change much. Not sure what you think. Was not sure you seen it or not.

              Phuket2 posted a day ago reply quote 0
              I am not sure if being able to subclass ui compents is in the wind or will ever be. But I thought of something that may be quite useful, at least in my mind.
              The simplest form of my idea is that you could call a ui method that sets a callback function that is called upon creation of any ui component. We could be passed the object type, or worse case check with type.
              A little more refined would be able to also set different callbacks for the different types of ui components.

              That would offer a lot of flexibility and possibilities. Also, especially when testing would be so easy just to style the objects. I know, you can still just have a make button function anyway. But this just seems so much cleaner.

              Not sure the impact this would have on performance or threading issues etc. I assume threading type problems would not be an issue as it would only be called on creation.

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

                @Phuket2, thanks, I did see it, and read it with interest.

                In the end I think I did not yet fully grasp the intended use case and value when compared to the effort:

                • Styling objects has not been a major source of pain for me
                • It feels like it could actually be a bit of effort for omz to implement in the UI module
                • I try to avoid callbacks as long as I can
                • Could lead to some really hard-to-debug situations when you take someone else's code and you both have used these callbacks

                You could try it out by making a function that you use to wrap every object creation and call the callback(s).

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

                  I have revisited this topic and this whole thread, and have decided to turn my coat on the proxy thing. Now I think that with a simple utility function, derived from what @Phuket2 presented earlier in the thread, I can have the same clean, inheritance-friendly extension mechanism, which will also play nice with add_subview and ObjCInstance.

                  In concrete terms, here is the utility function:

                  import types
                  
                  def extend(this, with_that):
                  	items = [(i, getattr(with_that, i)) for i in dir(with_that) if not i.startswith('__')]
                  	for k, v in items:
                  		if callable(v):
                  			setattr(this, k, types.MethodType(v.__func__, this))
                  		else:
                  			setattr(this, k, v)
                  	return this
                  

                  With that, we can have just as clean custom component code as with the proxies, with the added benefit of the object still being the original ui component. (Different ways to set style properties used for demonstration.)

                  # coding: utf-8
                  import ui
                  
                  class DefaultStyle(object):
                  	
                  	background_color = '#fff7ee'
                  	tint_color = 'black'
                  		
                  	def __init__(self):
                  		self.action = self.click_handler
                  		self.click_color = 'blue'
                  		
                  	def click_handler(self, sender):
                  		self.tint_color = self.click_color
                  	
                  class HighlightStyle(DefaultStyle):	
                  	def __init__(self):
                  		super(HighlightStyle, self).__init__()
                  		self.tint_color = 'red'
                  		
                  view = ui.View()
                  		
                  button = extend(ui.Button(), HighlightStyle())
                  button.title = 'Styled button'
                  view.add_subview(button)
                  
                  view.present()
                  
                  button.frame = view.bounds.inset(300, 100)
                  

                  Note that new-style classes are needed, i.e. must inherit from object.

                  Phuket2 2 Replies Last reply Reply Quote 0
                  • Phuket2
                    Phuket2 @mikael last edited by

                    @mikael , this looks good. I think I am doing something stupid through. I can not get the button to appear in the ui.View
                    I changed things around a little, just to help me test.

                    I am setting the buttons x,y, attrs in different places. I was just trying to get it to work. When I do a print dir(button) it looks fine. I can see the added attrs I threw in the extend func are there.

                    I am sure I am missing something very stupid. Over thinking this now.

                    import types
                    import ui
                    
                    def extend(this, with_that):
                    	items = [(i, getattr(with_that, i)) for i in dir(with_that) if not i.startswith('__')]
                    	for k, v in items:
                    		if callable(v):
                    			setattr(this, k, types.MethodType(v.__func__, this))
                    		else:
                    			setattr(this, k, v)
                    			
                    	# add some attrs
                    	this.my_center = (0,0)		
                    	this.xxx = 0
                    	
                    	return this
                    
                    class DefaultStyle(object):
                    
                    	background_color = '#fff7ee'
                    	tint_color = 'black'
                    
                    	def __init__(self):
                    		self.action = self.click_handler
                    		self.click_color = 'blue'
                    
                    	def click_handler(self, sender):
                    		self.tint_color = self.click_color
                    
                    class HighlightStyle(DefaultStyle):
                    	def __init__(self):
                    		super(HighlightStyle, self).__init__()
                    		#DefaultStyle.__init__(self)
                    		self.tint_color = 'red'
                    		self.border_width = 1
                    		self.bg_color = 'yellow'
                    		self.x = 10
                    		self.y = 10
                    		
                    
                    if __name__ == '__main__':
                    	f = (0,0,500,500)
                    	v = ui.View(frame = f)
                    	v.bg_color = 'white'
                    
                    	button = extend(ui.Button(name = 'test'), HighlightStyle())
                    	print dir(button)
                    	button.title = 'Styled button'
                    	button.x = 10
                    	button.y = 10
                    	button.border_width = .5
                    	v.add_subview(button)
                    	print button.superview
                    
                    	v.present('sheet')
                    
                    1 Reply Last reply Reply Quote 0
                    • mikael
                      mikael last edited by

                      I think the button has to have a size before it is visible, not just location. E.g. with size_to_fit().

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

                        @mikael , thanks. That was it. I wrote a 3 a4 page rant, not to you , but about being frustrated. But I decided to delete it 😱😬
                        Also not to to @omz. Ok, I will shut up now.
                        But again thanks. I have to also think about every I was trying to squeeze out of this idea and see if it carries through.

                        One thought, I have never used ... As a param before. But if the -
                        def extend(this, with_that): Could be written something like
                        def extend(this, ...):
                        So you could pass x number of classes to extend to function could be a good idea. Eg, I added some attrs in the extend function, just as a test. But if I had a class MyCustomAttrs
                        I could possibly write-
                        def extend(this, with_style, with_attrs) etc...
                        As I say, I have never used ... As a param. I just assume that's the idea of it. Sure, you still have to do the right thing in the extend function. So you could pass it a list of objects to iterate over. This just seems nicer

                        1 Reply Last reply Reply Quote 1
                        • mikael
                          mikael last edited by

                          That sounds interesting and certainly feasible. Could you please open up a bit more what you would want to happen?

                          • Expand several target instances with one "expander"
                          • Expand one target instance with several "expanders"
                          • Apply additional attributes to the target instance
                          • Only apply the listed attributes from the "expander"
                          • Something else
                          Phuket2 1 Reply Last reply Reply Quote 0
                          • Phuket2
                            Phuket2 @mikael last edited by

                            @mikael , honestly, I am not sure yet. But I can see if you do
                            button = extend(ui.Button(), HighlightStyle())
                            Then have to do
                            button = extend(ui.Button(), MyExtraAttrs())

                            Just saying if you can pass in multiple classes , it would be nicer in my mind. Ok, have a precedence issue. But it makes sense that the last class passed would take precedence as it would if you did it line by line.

                            Again, I am not really sure the best way. I just think a solution like this has to be very compelling to make it worth wild

                            mikael 1 Reply Last reply Reply Quote 1
                            • mikael
                              mikael @Phuket2 last edited by

                              @Phuket2, that sounds like a wothwhile convenience feature. Here is a version that let's you apply several "expanders" with one call:

                              # coding: utf-8
                              import types
                              
                              # Extend the instance given as the first argument with the methods and properties of the instances given as the second and subsequent arguments
                              def extend(target, *args):
                              	for source in args:
                              		for key in dir(source):
                              			if key.startswith('__'): continue
                              			value = getattr(source, key)
                              			if callable(value):
                              				setattr(target, key, types.MethodType(value.__func__, target))
                              			else:
                              				setattr(target, key, value)
                              	return target
                              
                              1 Reply Last reply Reply Quote 0
                              • mikael
                                mikael last edited by

                                And sample usage/test case:

                                # coding: utf-8
                                import ui
                                from extend import extend
                                
                                class DefaultStyle(object):
                                	background_color = '#fff7ee'
                                	tint_color = 'black'	
                                	
                                class HighlightStyle(DefaultStyle):	
                                	def __init__(self):
                                		super(HighlightStyle, self).__init__()
                                		self.tint_color = 'red'
                                		
                                class ClickHandler(object):
                                	def __init__(self):
                                		self.action = self.click_handler
                                		self.click_color = 'blue'
                                	def click_handler(self, sender):
                                		self.tint_color = self.click_color	
                                		
                                view = ui.View()
                                		
                                button = extend(ui.Button(), HighlightStyle(), ClickHandler())
                                
                                button.title = 'Styled button'
                                view.add_subview(button)
                                
                                view.present()
                                
                                button.frame = view.bounds.inset(300, 100)
                                
                                Phuket2 2 Replies Last reply Reply Quote 0
                                • Phuket2
                                  Phuket2 @mikael last edited by

                                  @mikael nice. Yes. I just tested also.
                                  All my idea is that it needs to be very compelling and super useful to make sense that other will use it.
                                  Getting late here now, I will try and push it more tomorrow.

                                  import types
                                  import ui
                                  
                                  # Extend the instance given as the first argument with the methods and properties of the instances given as the second and subsequent arguments
                                  def extend(target, *args):
                                      for source in args:
                                          for key in dir(source):
                                              if key.startswith('__'): continue
                                              value = getattr(source, key)
                                              if callable(value):
                                                  setattr(target, key, types.MethodType(value.__func__, target))
                                              else:
                                                  setattr(target, key, value)
                                      return target
                                      
                                  class DefaultStyle(object):
                                  
                                  	background_color = '#fff7ee'
                                  	tint_color = 'black'
                                  
                                  	def __init__(self):
                                  		self.action = self.click_handler
                                  		self.click_color = 'blue'
                                  
                                  	def click_handler(self, sender):
                                  		self.tint_color = self.click_color
                                  
                                  class HighlightStyle(DefaultStyle):
                                  	def __init__(self):
                                  		super(HighlightStyle, self).__init__()
                                  		#DefaultStyle.__init__(self)
                                  		self.tint_color = 'red'
                                  		self.border_width = 1
                                  		self.bg_color = 'yellow'
                                  		self.x = 10
                                  		self.y = 10
                                  		
                                  class MyCustomAttrs(object):
                                  	def __init__(self):
                                  		self.xzy = 666
                                  		
                                  if __name__ == '__main__':
                                  	f = (0,0,500,500)
                                  	v = ui.View(frame = f)
                                  	v.bg_color = 'white'
                                  
                                  	button = extend(ui.Button(name = 'test'), HighlightStyle(), MyCustomAttrs())
                                  	print dir(button)
                                  	button.title = 'Styled button'
                                  	button.size_to_fit()
                                  	button.x = 10
                                  	button.y = 10
                                  	button.border_width = .5
                                  	v.add_subview(button)
                                  	print button.superview
                                  
                                  	v.present('sheet')
                                  
                                  1 Reply Last reply Reply Quote 0
                                  • Phuket2
                                    Phuket2 @mikael last edited by

                                    @mikael , but again for the idea to be relevant it has to compete with something like this. Looks like more code then we have been doing. But when you look at the one off code it's not.
                                    Just saying that's how I am trying to evaluate if it's worth while or not

                                    # coding: utf-8
                                    
                                    import ui
                                    
                                    def MyMother(sender):
                                    	print 'I love my Mums cooking'
                                    	
                                    _default_btn_style = \
                                    	{
                                    		'border_width' : .5,
                                    		'corner_radius' : 3,
                                    		'bg_color' : 'teal',
                                    		'tint_color' : 'white',
                                    		'action'  : MyMother,
                                    	}
                                    
                                    _btn_style = _default_btn_style
                                    
                                    _extra_attrs = \
                                    	{
                                    		'myx' : 0,
                                    		'myy' : 0,
                                    	}
                                    	
                                    
                                    def make_button(style = _btn_style, ext_attrs = _extra_attrs ,
                                    						*args, **kwargs):
                                    	btn = ui.Button()
                                    	
                                    	# process the normal atttrs
                                    	for k,v in kwargs.iteritems():
                                    		if hasattr(btn, k):
                                    			setattr(btn, k, v)
                                    	
                                    	# process a style dict
                                    	for k,v in style.iteritems():
                                    		if hasattr(btn, k):
                                    			setattr(btn, k, v)
                                    	
                                    	# process addtional attrs...
                                    	for k,v in ext_attrs.iteritems():
                                    		setattr(btn, k, v)
                                    
                                    	# if kwargs has a parent key, then we add the subview to the parent
                                    	if kwargs.has_key('parent'):
                                    		kwargs['parent'].add_subview(btn)
                                    		
                                    	btn.size_to_fit()
                                    	# size to fit is too tight
                                    	btn.frame = ui.Rect(*btn.bounds).inset(0, -5)
                                    		
                                    	return btn
                                    		
                                    if __name__ == '__main__':
                                    	f = (0,0,500,500)
                                    	v = ui.View(frame = f)
                                    	btn = make_button(title = 'What do I Like', parent = v)
                                    	v.present('sheet')
                                    
                                    mikael 1 Reply Last reply Reply Quote 0
                                    • TutorialDoctor
                                      TutorialDoctor last edited by

                                      I know this is an older post, but I am wondering why no one recommended using a button action?

                                      def test_func(sender):
                                          return sender
                                      
                                      v = ui.load_view()
                                      button = v['button1']
                                      button.action = test_func
                                      

                                      Then the only question would be how to pass parameters to a button's action function.
                                      "sender" is the button that called the function.

                                      mikael Phuket2 2 Replies Last reply Reply Quote 0
                                      • mikael
                                        mikael @TutorialDoctor last edited by

                                        @TutorialDoctor, we are not focused on the button, but on reasonably elegant ways to get around the fact that we can not subclass ui components. Button is just being used as a shared sample, so to say.

                                        1 Reply Last reply Reply Quote 1
                                        • mikael
                                          mikael @Phuket2 last edited by

                                          @Phuket2, to exaggerate a bit, it looks like where I have been working towards the most object-oriented, legible and maintainable way to create custom components, emphasizing maybe a low number of complex components, you might have been working on the most flexible and time-saving functional-programming button maker, with a possible further focus on generating relatively many less-complex components easily.

                                          Pretty opposite ends of the spectrum, one could say, but nevertheless this has been very instructive and interesting, and I have learned a lot about Python meta-programming in the process. Thanks!

                                          Out of curiosity, I will try and see what a roughly similar generator would look like in "my style".

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

                                            @mikael , also thanks. I have also enjoyed it and learnt a lot. For me it's about remembering what I have learnt 😳

                                            But I think I am starting to learn something that seems so obvious, but not really. We I guess depending on your experience. But that is to really get clear what you are trying to improve on. I can see I go off on all sort of tangents trying to solve problems that either don't exists, I just keep working on something without looking back and comparing the methods, basically having a bench mark.

                                            I still have some years left in me to learn 😁 So all good.

                                            mikael 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors