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.


    I just don't get it

    Pythonista
    3
    4
    2235
    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.
    • polymerchm
      polymerchm last edited by

      Perhaps this is a 1.6 issue, but I think I'm just missing something. I want to create a spinner widget that allows me to enter a number and increment/decrement it via two buttons (up and down). I have commented out much to isolate what I am doing wrong. On pythonista 1.6, this give me a ivory filled square with a bottom and right black shadow. No upper or left "border". And no uparrow. There is no pyui file, as I want to build this on the fly.

      # spinner class
      
      import ui
      
      class Spinner(ui.View):
      	''' creates a view with a data entry field and up/down arrows which allow for increment/decrement
      	valid types are int, list, float.  A list will be fixed possibilities and limits are ignored
      	'''
      	
      	def __init__(self, parent,
      	             			 initialValue= 10,
      	             			 increment=1,
      	             			 frame=(20,20, 200,200),
      	             			 limits=(0,100),
      	             			 dataType='int',
      	             			 action=None
      	             ):
      		self.parent = parent
      		self.initialValue = initialValue
      		self._value = initialValue
      		self.increment = increment
      		self.action = action
      		self.dataType = dataType
      		self.limits = limits
      		self.frame = frame
      		self.list = []
      		
      		
      		
      	def buildView(self):
      		# build the view here
      		
      		self.v = ui.View(frame=self.frame)
      		self.v.background_color = "white"
      		self.border_color = 'black'
      		self.border_width = 3
      		self.parent.add_subview(self.v)
      		
      		
      #		self.vInput = ui.Label( )
      #		self.vInput.text = "{}".format(self.initialValue)
      #		self.vInput.bounds = (0,0,40,40)
      #		self.vInput.text_color = 'black'
      #		self.vInput.border_color = 'black'
      #		self.vInput.alignment = ui.ALIGN_CENTER
      #		self.vInput.border_width = 1
      #		self.v.add_subview(self.vInput)
      #		self.vInput.bring_to_front()
      #		self.vInput.hidden = False
      		
      		self.upArrow= ui.Button()
      		self.upArrow.bounds = (0,60,50,50)
      		self.upArrow.bg_color = 'ivory'
      		self.upArrow.border_color = 'black'
      		self.upArrow.border_width = 1
      		self.upArrow.name = 'upBtn'
      		self.upArrow.action = self.onArrow
      		self.upArrow.background_image = ui.Image.named('ionicons-arrow-up-b-24')
      		self.upArrow.enabled = True
      		self.v.add_subview(self.upArrow)
      		self.parent.add_subview(self.v)
      #		
      #		self.dnArrow = ui.Button()
      #		self.v.add_subview(self.dnArrow)
      #		self.dnArrow.name = 'dnBtn'
      #		self.dnArrow.action = self.onArrow
      		
      				
      	@property
      	def value(self):
      		return self._value
      		
      	@value.setter
      	def value(self,input):
      		self._value = input
      	
      	def onArrow(self,sender):
      		pass
      		
      	def reset(self):
      		self.value = initialValue		
      
      if __name__ == '__main__':
      	view = ui.View(background_color = 'white')
      	
      	spinner = Spinner(view,frame=(200,200,100,100),initialValue = 500,
      	                  limits=(0,1000),increment=10)
      	spinner.buildView()
      	view.present('full_screen')
      	
      
      1 Reply Last reply Reply Quote 0
      • ccc
        ccc last edited by

        The basic problem is that a Spinner is a subclass of ui.View but you were also trying to add a ui.View (self.v) to it which confused the logic.

        # spinner class
        
        import ui
        
        def make_button(name, bg_image_name, loc_xy=(10, 10)):
            button = ui.Button(name=name)
            button.frame = (loc_xy[0], loc_xy[1], 50, 50)
            button.bg_color = 'ivory'
            button.border_color = 'black'
            button.border_width = 1
            button.background_image = ui.Image.named(bg_image_name)
            button.enabled = True
            return button
        
        def make_label(text, loc_xy = (10, 10)):
            label = ui.Label( )
            label.text = str(text)
            label.bounds = (loc_xy[0], loc_xy[1], 40, 40)
            label.text_color = 'black'
            label.border_color = 'black'
            label.alignment = ui.ALIGN_CENTER
            label.border_width = 1
            label.bring_to_front()
            return label
        
        class Spinner(ui.View):
            ''' creates a view with a data entry field and up/down arrows which allow for increment/decrement
            valid types are int, list, float.  A list will be fixed possibilities and limits are ignored
            '''
        
            def __init__(self, initialValue= 10, increment=1, limits=(0,100), dataType='int', action=None):
                self._value = self.initialValue = initialValue
                self.increment = increment
                self.limits = limits
                self.dataType = dataType
                self.action = action
                self.list = []
                self.add_ui()
        
            def add_ui(self):
                # add user interface elements
                self.background_color = "white"
                self.border_color = 'blue'
                self.border_width = 3
        
                self.label = make_label(self._value, (10, 10))
                self.add_subview(self.label)
        
                self.upArrow = make_button('upBtn', 'ionicons-arrow-up-b-24', (80, 25))
                self.upArrow.action = self.onArrow
                self.add_subview(self.upArrow)
                
                self.downArrow = make_button('downBtn', 'ionicons-arrow-down-b-24', (140, 25))
                self.downArrow.action = self.onArrow
                self.add_subview(self.downArrow)
        
            @property
            def value(self):
                return self._value
        
            @value.setter
            def value(self,input):
                self._value = input
        
            def onArrow(self,sender):
                # you might want to consider tap-and-hold functionality
                increment = self.increment * (-1 if 'down' in sender.name.lower() else 1)
                if self.limits[0] <= self._value + increment <= self.limits[1]:
                    self._value += increment
                    self.label.text = str(self._value)
        
            def reset(self):
                self._value = self.initialValue       
        
        if __name__ == '__main__':
            view = ui.View(background_color = 'white')
            spinner = Spinner(initialValue = 500, limits = (0,1000), increment = 10)
            view.present('full_screen')
            spinner.frame = view.bounds
            view.add_subview(spinner)
        
        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          I'm pretty sure a view can only be added as a subview to one view, one time.
          The code above, you added v to the parent twice! In that process, the button is getting its frame confused.

          My suggestion: since Spinner is a ui.View, the behavior should act like a View. That means, to get it to snow, you would use view.add_subview(spinner) in main. The spinner should not add anything into the parent view.
          v should be a subview of self, not of the parent view.

          See Checkout ui.CheckBox in the uicomponents for an example... In your case you'll add two buttons and a textfield to self. Or, for another example, see dropdown, which does have a textfield and a single button, so the setup will look similar to what you want.

          In the dropdown case, I specifically DID need to add something to the root view, though you can find that dynamically --- but the only time you need to do something like that is if you need to display something outside your custom view's frame... In this case I wanted to prevent interaction with any other ui components, so I create a view that covers up the root view until a choice is made.

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

            Here is my quick solution: includes a textfield for direct data entry . I didn't include limits, but I see ccc's approach does.

            import ui
            class Spinner(ui.View):
                def __init__(self,frame=(0,0,300,32),name='spinner', value=0):
                    '''Simple spinner, supports direct entry.
                    '''
                    self.frame=frame
                    self.textfield=ui.TextField(frame=frame,name='textfield')
                    self.textfield.autocapitalization_type=ui.AUTOCAPITALIZE_NONE 
                    self.textfield.autocorrection_type=False 
            
                    self.up=ui.Button(name='button',bg_color=None)
                    self.down=ui.Button(name='button',bg_color=None)
                    self.add_subview(self.textfield)
                    self.add_subview(self.up)
                    self.add_subview(self.down)
                    h=frame[3]
                    self.up.frame=  (self.width-64, h-32, 32,32)
                    self.down.frame=(self.width-32, h-32, 32,32)
                    
                    self.up.image=ui.Image.named('ionicons-arrow-up-b-32')
                    self.down.image=ui.Image.named('ionicons-arrow-down-b-32')
                    self.up.action=self.inc
                    self.down.action=self.dec
            
                    self.up.flex='l'
                    self.down.flex='l'
                    self.textfield.flex='w'  
                    self.textfield.text=str(value)
                def inc(self,sender):
                    self.textfield.text = str(self.value+1)
                def dec(self,sender):
                    self.textfield.text = str(self.value-1)
                         
                @property
                def value(self):
                    return int(self.textfield.text)
                    
                @value.setter
                def text(self,value):
                    self.textfield.text=value
                
                #lets you set the spinner.action    
                @property
                def action(self):
                    return self.textfield.action
                    
                @action.setter
                def action(self,value):
                    if callable(value):
                        self.textfield.action=value
                    else: 
                        self.textfield.action = lambda : None
                    
            
                    
            if __name__=='__main__':
                v=ui.View()
                v.present()
                
                s=Spinner()
                v.add_subview(s)
            
            1 Reply Last reply Reply Quote 0
            • First post
              Last post
            Powered by NodeBB Forums | Contributors