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.


    [Tip] ui.Rect, if you are not using it for ui you should

    Pythonista
    ui.rect scene.rect tip maxy
    3
    7
    5573
    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 last edited by

      I am sure I have done this type of tip before. But as time passes on and new users join they get old. This is only for people who don't know about ui.Rect. There is a small problem with ui.Rect. It's not documented as a ui.Rect. It's actually documented as - class scene.Rect(x, y, w, h). Also the autocomplete in the editor does not recognise ui.Rect. Anyway ui.Rect and scene.Rect are the same. Regardless, ui.Rect is recognised by the interpreter.

      So why is it important to know about it? Firstly , attrs in ui.View like frame and bounds are ui.Rects. They can be assigned a tuple or a ui.Rect, omz takes care of that. But it's the methods you can use on ui.Rects that makes it worth knowing about. It's not rocket science stuff, but can save you a lot of time and reduce bugs. There is a link to the documentation below. But it can really help reduce your code and simplify things.

      I have posted below an example of adding variable height views to a scrollview. Maybe the code is not as tight as I could be, but you can see it's pretty simple. Granted, you can do the same thing doing the calls yourself, but there is a lot of help built in already.

      Again, this is only useful if you are unaware of ui.Rect/scene.Rect

      Hope it's useful for someone

      scene.Rect Documentation

      The sample code below should be size and orientation friendly

      # Pythonista Forum - @Phuket2
      import ui, editor
      
      class MyClass(ui.View):
      	def __init__(self, *args, **kwargs):
      		super().__init__(*args, **kwargs)
      		self.sv = None
      		self.v_gap = 10  	# vertical space between views
      		self.make_view()
      		
      	def make_view(self):
      		sv = ui.ScrollView(frame = self.bounds.inset(20, 20))
      		sv.flex = 'wh'
      		sv.bg_color = 'white'
      		sv.corner_radius = 3
      		self.sv = sv
      		self.add_subview(sv)
      	
      	def add_view(self, h):
      		parent = self.sv  # our parent is the ScrollView
      		r = ui.Rect(0, 0, parent.width, h).inset(5, 5)
      		v = ui.View(frame = r)
      		v.y = self.calc_y()
      		v.border_width = 1
      		v.corner_radius = 6
      		v.flex = 'w'
      		parent.add_subview(v)
      		parent.content_size = (0, v.frame.max_y + self.v_gap )
      	
      	def calc_y(self):
      		# returns the new y for the next element that will be added to 
      		# scrollview. 
      		
      		# seems stupid to do this.  i am tending more to do this style
      		# now. have a var that points to our parent contextually.  
      		# i make less mistakes this way, and think faster
      		parent = self.sv  # our parent is the ScrollView
      		 
      		if not len(parent.subviews):
      			return self.v_gap
      		
      		return parent.subviews[len(parent.subviews)-1].frame.max_y + self.v_gap
      		
      
      if __name__ == '__main__':
      	_use_theme = True
      	w, h = 600, 800
      	w, h = 320, 568 # iphone 5 up
      	f = (0, 0, w, h)
      	style = 'sheet'
      	animated = False
      	
      	mc = MyClass(frame=f, bg_color='white', name = 'ScrollView Test')
      	
      	if not _use_theme:
      		mc.present(style=style, animated=animated)
      	else:
      		editor.present_themed(mc, theme_name='Oceanic', style=style, animated=animated)
      	# its just an example... we do the call to create the scrollview
      	# views here.
      	h_list = [100, 200, 50, 100, 60, 80, 90, 300, 44, 60, 90, 300, 90]
      	for h in h_list:
      		mc.add_view(h)
      
      1 Reply Last reply Reply Quote 1
      • Phuket2
        Phuket2 last edited by

        I have been working on a list of items where only one is selectable as in radio buttons in another script. But I come up with something that I think is ok. Was easily to add to this code. And that's somehow the point in my mind.

        # Pythonista Forum - @Phuket2
        import ui, editor
        
        class MyClass(ui.View):
        	def __init__(self, *args, **kwargs):
        		super().__init__(*args, **kwargs)
        		self.sv = None
        		self.v_gap = 10  	# vertical space between views
        		self.make_view()
        
        	def make_view(self):
        		sv = ui.ScrollView(frame = self.bounds.inset(20, 20))
        		sv.flex = 'wh'
        		sv.bg_color = 'white'
        		sv.corner_radius = 3
        		self.sv = sv
        		self.add_subview(sv)
        	
        	def add_view(self, h):
        		parent = self.sv  # our parent is the ScrollView
        		r = ui.Rect(0, 0, parent.width, h).inset(5, 5)
        		v = ui.View(frame = r)
        		v.y = self.calc_y()
        		v.border_width = 1
        		v.corner_radius = 6
        		v.flex = 'w'
        		parent.add_subview(v)
        		
        		# added this
        		r = ui.Rect(0, 0, v.height, v.height).inset(10, 10)
        		btn = ui.Button(name = 'btn', frame = r)
        		btn.choice = False	# dynamically added attr
        		btn.image = ui.Image.named('iob:ios7_checkmark_outline_256')
        		btn.tint_color = 'lightgray'
        		btn.action = self.action
        		v.add_subview(btn)
        		
        		parent.content_size = (0, v.frame.max_y + self.v_gap )
        	
        	def calc_y(self):
        		# returns the new y for the next element that will be added to 
        		# scrollview. 
        		
        		# seems stupid to do this.  i am tending more to do this style
        		# now. have a var that points to our parent contextually.  
        		# i make less mistakes this way, and think faster
        		parent = self.sv  # our parent is the ScrollView
        		 
        		if not len(parent.subviews):
        			return self.v_gap
        		
        		return parent.subviews[len(parent.subviews)-1].frame.max_y + self.v_gap
        	
        	def action(self, sender):
        		# added this
        		# the action for the button
        		parent = self.sv  # our parent is the ScrollView
        		for v in parent.subviews:
        			btn = v['btn']
        			btn.tint_color = 'lightgray'
        			btn.choice = False
        		
        		sender.tint_color = 'deeppink'
        		sender.choice = True
        		
        
        if __name__ == '__main__':
        	_use_theme = True
        	w, h = 600, 800
        	w, h = 320, 568 # iphone 5 up
        	f = (0, 0, w, h)
        	style = ''
        	animated = False
        	
        	mc = MyClass(frame=f, bg_color='white', name = 'ScrollView Test')
        	
        	if not _use_theme:
        		mc.present(style=style, animated=animated)
        	else:
        		editor.present_themed(mc, theme_name='Oceanic', style=style, animated=animated)
        	# its just an example... we do the call to create the scrollview
        	# views here.
        	#h_list = [80, 80, 80, 80, 60, 80, 90, 300, 44, 60, 90, 300, 90]
        	h_list = [80] * 20
        	for h in h_list:
        		mc.add_view(h)
        
        1 Reply Last reply Reply Quote 0
        • jmv38
          jmv38 last edited by

          thank you very much for sharing this @Phuket2 , i am trying to put up some interface and this hepls a lot. Btw, super().whatever(blabla) generates an error for e, i had to replace it by ui.View.whatever(self, blabla) and then it works.

          Thanks

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

            @jmv38 , glad it helps. The super() call is python 3, so I guess you are on on the Pythonista 2.x app. Super is nice as you don't have to specify the parent implicitly by name. But anyway, it's python 3 only.

            I have been working on the below a little today. (Many friends turned up tonight, so things went a little south).

            Again about making a view. I fixed a issue I had before when dealing with margins. It's still not 100% in my mind. I need to put some more thought into the top and bottom margins. I know how to adjust them, it's just how it to it the right way, if at all. Anyway, i feel this is more flexible as it just returns a list of ui.Rects.

            
            # Pythonista Forum - @Phuket2
            import ui, editor
            
            def make_view_rects(view, v_list, vert=True,
            						abs_size=True, margin=(0, 0)):
            								
            	# return a list of ui.Rects
            	r = ui.Rect(*view.bounds)
            	
            	def translate_number(wh, n):
            		if n == 0 or n > 1: return n
            		return wh if n == -1 else wh * n
            	
            	# adjust v_list to account for margins, if we want positive numbers
            	# to remain a fixed size, i.e if you pass 44, and a margin, it will
            	# remain 44
            	if abs_size:
            		extra = margin[0] * 2 if vert else margin[1] * 2
            		v_list = [x if x == 0 else (x + extra) for x in v_list]
            						
            	# translate the numbers in v_list to absolute numbers, except zero
            	v_list = [translate_number((r.height if vert else r.width), x)
            								if x else x for x in v_list]
            	
            	zero_count = v_list.count(0)
            	
            	# aviod divide by zero error
            	var = ((r.height if vert else r.width) - sum(v_list)) / zero_count\
            	if zero_count else ((r.height if vert else r.width) - sum(v_list))
            		
            	# replaces 0 in v_list with var.
            	v_list = [var if x == 0 else x for x in v_list]
            	
            	lst = []  # a list of the rects created, we return this
            	xy = 0    # keep track of width or height
            	for num in v_list:
            		lst.append(ui.Rect(0, xy, r.width, num).inset(*margin) if vert
            				else ui.Rect(xy, 0, num, r.height).inset(*margin))
            		xy += num
            		
            	return lst
            
            
            class MyClass(ui.View):
            	def __init__(self, *args, **kwargs):
            		super().__init__(*args, **kwargs)
            		self.question_panel = None
            		self.responses_panel = None
            		self.toolbar_panel = None
            		
            		self.make_view()
            		
            	def make_view(self):
            		inset_w = 10
            		inset_h = 10
            		v_rects = make_view_rects(self, [60, 0, 0, 0, 60],
            									abs_size=True,
            									vert=True,
            									margin=(inset_h, inset_w))
            		for r in v_rects:
            			v = ui.View(frame=r, bg_color='cornflowerblue')
            			v.border_width = .5
            			v.corner_radius = 6
            			v.flex = 'tlwhbr'
            			self.add_subview(v)
            
            if __name__ == '__main__':
            	_use_theme = True
            	w, h = 600, 800
            	w, h = 375, 667
            	
            	f = (0, 0, w, h)
            	style = 'sheet'
            	
            	mc = MyClass(frame=f, bg_color='white')
            	
            	if not _use_theme:
            		mc.present(style=style, animated=False)
            	else:
            		editor.present_themed(mc, theme_name='Oceanic', style=style, animated=False)
            
            1 Reply Last reply Reply Quote 0
            • coomlata1
              coomlata1 last edited by

              to @jmv38...How, specifically in code, did you replace super() in @Phuket2 code, to get this to work in Pythonista 2.x.

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

                @coomlata1 , if you replace the super line with

                ui.View.__init__(self, *args, **kwargs)
                

                I think there are a few variations, but this is what I used when I was on python 2.7x

                The code posted are pretty simple examples. But when you are inheriting from different classes and experimenting a lot, super is really great because you don't have to refer to the base class like you do in py2 way. So chopping and refactoring your code is a lot easier. It eaves me a lot of fiddling around. If it didn't I would would use the older style to keep it py2 compatible

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

                  @ccc or anyone else, any remarks or suggesting to tighten up the make_view_rects function. I really like it. And I think that it only returns Rects rancher than creating views it's self is more appealing.

                  There are 2 issues I can see. One is the view param. This is a leftover, a ui.Rect should be passed in stead of the view. The other is that the margins give the right distance between the items. But if vert=True, the top and bottom margin is / 2. same goes when you go horizontally. I am not sure the result is entirely incorrect. I think more often than not you would want that result.
                  Anyway, happy to listen to any feed back if anyone has some.

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