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.


    Need help with UI, tableviewcell_for_row

    Pythonista
    tableviewcell
    2
    7
    2817
    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.
    • crazyfox
      crazyfox last edited by

      Hi all,
      Any ideas on recreating the UI seen in Apple Mail rules (attached) or similar(smart mailboxes, etc)? i.e. Expandable table with multiple dropdowns in each row.

      I am populating tableviewcell_for_row with custom UIView class and other standard elements. The layout has been difficult to define and dropdown selections are restricted to row height. I think I can make it work but is not pretty.

      Is there a more elegant way to approach this?

      KP

      https://imgur.com/qzsDEOO

      cvp 1 Reply Last reply Reply Quote 0
      • cvp
        cvp @crazyfox last edited by cvp

        @crazyfox you could try this script' or this one but I don't remember from where I get it

        # coding: utf-8
        ''' DropDown 
        
        A custom view that acts as a flyout selector, based on a ListView and button.  
        It does not allow text input, as does jsbain's version.
        
        Author: Steven Pollack, Ph.D.
        Date:		July 26, 2015
        
        '''
        
        
        import ui,inspect,console
        import _ui # needed for Beta 1.6
        
        class _DropDownDelegate(object):
        	
        	def setitems(self,values):
        		if not values:
        			self.items = []
        		if not isinstance(values[0],dict):
        			self.items = [{'title':x, 'accessory_type':'none'} for x in values]
        		else:
        			self.items = []
        			for item in values:
        				if 'title' not in list(item.keys()):
        					raise ValueError("invalid dictionary.  missing critical key")
        				else:
        					item['accessory_type']='none' # just in case its not there.
        					self.items.append(item)
        		self._row = 0
        	
        	def __init__(self,dd):
        		self.dd = dd
        		self.setitems(dd._data)
        
        	def tableview_number_of_rows(self, tableview, section):
        		# Return the number of rows in the section
        		return len(self.items)
        
        	def tableview_cell_for_row(self, tableview, section, row):
        		# Create and return a cell for the given section/row
        		cell = ui.TableViewCell()
        		cell.text_label.text = self.items[row]['title']
        		return cell
        
        
        	def tableview_did_select(self, tableview, section, row):
        		# Called when a row was selected.
        		def animate():
        			self.dd.tv.frame = self.dd.tvFrame
        			self.dd.frame = tuple([self.dd.frame[x] for x in (0,1)]) + (self.dd.frame[2], self.dd.smallSize)
        		self._row = row
        		for i,_ in enumerate(self.items):
        			self.items[i]['accessory_type'] = 'none'
        		self.items[row]['accessory_type'] = 'checkmark'
        		tableview.content_offset = (0,row*tableview.row_height+self.dd.offset_eps)
        		tableview.reload_data()
        		self.dd.expanded = False
        		ui.animate(animate,duration=0.2)
        		if self.dd.action:
        			self.dd.action(self.dd,row)
        			
        
        			
        		
        		
        class DropDown(ui.View):
        	def __init__(self,	frame=(0,0,150,32),
        										buttonSize = (32,32),
        										data = "this is a test".split(),
        										font = None,
        										initialItem = 0,
        										offset_eps = 0,
        										action = None,
        										fullSize = 300,
        										name = 'dropdown'):
        		self.frame = frame	
        		self._position = [ self.frame[x] for x in (0,1)]
        		self.smallSize = frame[3]
        		self.bg_color = None	
        		self.border_width = 0
        		self.border_color = 'black'
        		self.buttonSize	= buttonSize	
        		self._data = data			
        		self.delegate = _DropDownDelegate(self)
        		if action:
        			if inspect.isfunction(action) and len(inspect.getargspec(action).args) == 2:
        				self.action = action
        			else:
        				raise TypeError('single argument function')	
        		self.tvFrame = (0,0, self.frame[2] - self.buttonSize[0], self.buttonSize[1])
        		self.tv = ui.TableView(frame=self.tvFrame)
        		self.tv.row_height = self.smallSize
        		self.tv.name = 'tableview'
        		self.tv.allows_selection = True
        		self.tv.delegate = self.tv.data_source = self.delegate
        		self.tv.border_color = 'black'
        		self.tv.border_width = 1
        		self.button = ui.Button(frame = (self.frame[2]-self.buttonSize[0], 0) + self.buttonSize)
        		self.button.bg_color = 'white'
        		self.button.name = 'button'
        		self.button.action = self.onArrow
        		self.button.border_width = 1
        		self.button.border_color = 'black'
        		self.button.image=ui.Image.named('ionicons-arrow-down-b-24')
        		self.expanded = False
        		self.add_subview(self.tv)
        		self.tv.frame = self.tvFrame
        		self.add_subview(self.button)
        		self.fullSize = fullSize
        		self.smallSize = self.frame[3]
        		self.offset_eps = offset_eps
        		self.name = name
        		self._hidden = False
        		
        	def send_to_back(self):
        		self.tv.send_to_back()
        		self.button.send_to_back()
        		
        	def bring_to_front(self):
        		self.tv.bring_to_front()
        		self.button.bring_to_front()
        		
        	def onArrow(self,button):
        		#console.hud_alert("{}".format(self.expanded))
        		self.bring_to_front()
        		if not self.expanded:
        			self.frame = tuple([self.frame[x] for x in (0,1)]) + (self.frame[2],self.fullSize)
        			self.tv.frame = tuple([self.tv.frame[x] for x in (0,1)]) + (self.tv.frame[2],self.fullSize)
        			self.expanded = True
        		else:
        			self.frame = tuple([self.frame[x] for x in (0,1)]) + (self.frame[2],self.smallSize)
        			self.tv.frame = tuple([self.tv.frame[x] for x in (0,1)]) + (self.tv.frame[2],self.smallSize)
        			self.tv.content_offset = (0,self.row*self.tv.row_height+self.offset_eps)
        			self.expanded = False
        												
        		
        	@property
        	def position(self):
        		return self._position
        
        	@position.setter
        	def position(self,value):
        		self._position = value
        		self.frame = self._position + tuple([self.frame[x] for x in (2,3)])
        		
        	@property
        	def hidden(self):
        		return self._hidden
        		
        	@hidden.setter
        	def hidden(self,value):
        		self._hidden = value
        		self.button.hidden = value
        		self.tv.hidden = value
        		
        	@property
        	def data(self):
        		return self._data
        	
        	@data.setter
        	def data(self,value):
        		self._data = value
        		self.delegate.setitems(value)
        		self.tv.reload_data()
        		
        	@property
        	def row(self):
        		return self.delegate._row
        		
        	@row.setter
        	def row(self,value):
        		if 0 <= value <= (len(self.delegate.items)-1):
        			self.delegate._row = value
        			self.tv.content_offset = (0,self.delegate._row*self.tv.row_height+self.offset_eps)
        			
        	@property
        	def current(self):
        		return self.delegate.items[self.delegate._row]
        		
        														
        if __name__ == '__main__':
        	def setRow():
        		dd.row = 2
        	
        	def test(sender,row):
        		print("from test row={}".format(row))
        		print(sender.current)
        		
        		
        	ddd = DropDown(data='Mary had a little lamb'.split(),action=test)
        	dd = DropDown(data=[{'title':'fred'}, # will autopopulate 'accessory_type" entry
        											{'title':'arthur', 'accessory_type':'none'},
        											{'title':'tina', 'accessory_type':'none', 'flag':'a'}], action=test)
        	root = ui.View(frame = (0,0,1000,900))
        	root.bg_color = '#deff92'
        	root.add_subview(ddd)
        	dd.position = (50,50)
        	root.present()
        
        1 Reply Last reply Reply Quote 0
        • crazyfox
          crazyfox last edited by

          Thanks @cvp
          This is what I’ve cobbled together (with inspiration from @cvp and @jonb.
          Very crude.

          1. I’m trying to understand how to position UI elements in table view row.
          2. looks like row_height in main table has to tall enough for dropdown views and touch capture.

          I’m an absolute beginner, so any feedback is appreciated.
          Thanks, KP

          [picture link] don’t know why I can’t do inline images. https://imgur.com/n4U3RCf

          
          import ui, console,dialogs
          
          pyui_str = r'''
          [
            {
              "nodes" : [
                {
                  "nodes" : [
          
                  ],
                  "frame" : "{{21, 24}, {480, 287}}",
                  "class" : "TableView",
                  "attributes" : {
                    "flex" : "WH",
                    "data_source_items" : "Row 1\nRow 2\nRow 3",
                    "name" : "tableview1",
                    "frame" : "{{120, 110}, {200, 200}}",
                    "data_source_number_of_lines" : 1,
                    "class" : "TableView",
                    "background_color" : "RGBA(1.0, 1.0, 1.0, 1.0)",
                    "data_source_delete_enabled" : true,
                    "data_source_font_size" : 18,
                    "row_height" : 66,
                    "uuid" : "BE2CD45E-DD3C-496F-98C3-93E1222A94AA"
                  },
                  "selected" : false
                }
              ],
              "frame" : "{{0, 0}, {523, 405}}",
              "class" : "View",
              "attributes" : {
                "enabled" : true,
                "background_color" : "RGBA(1.000000,1.000000,1.000000,1.000000)",
                "tint_color" : "RGBA(0.000000,0.478000,1.000000,1.000000)",
                "border_color" : "RGBA(0.000000,0.000000,0.000000,1.000000)",
                "flex" : ""
              },
              "selected" : false
            }
          ]
          '''
          
          
          
          class MyDropDown(ui.View):
              def __init__(self, tableview, section, row, items=[], frame=(0,0,200,200), *args, **kwargs):
                  super().__init__(*args, **kwargs)
                  self.frame = frame
                  self.border_width = 1
                  self.border_color = 'red'
                  self.background_color='#f6feff'
                  
                  tf = ui.TextField(name='tf')
                  tf.enabled = False
                  tf.frame = (self.x,self.y,self.width-32,32)
                  tf.border_width= 1
                  tf.corner_radius = 5
                  self.add_subview(tf)
                  self.tf = tf
                  
                  b = ui.Button()
                  b.frame = (tf.x+tf.width,self.y,32,32)
                  b.image = ui.Image.named('iob:arrow_down_b_32')
                  b.border_width = 1
                  b.corner_radius = 5
                  b.action = self.b_action
                  self.add_subview(b)
                  
                  tv = ui.TableView()
                  tv.frame = (self.x,tf.height,tf.width,self.height-32)
                  tv.border_width = 1
                  tv.corner_radius = 5
                  tv.data_source = ui.ListDataSource(items=items)
                  tv.height = min(24*3,24*len(items))
                  tv.row_height= 24
                  tv.delegate = self
                  tv.hidden = True
                  self.add_subview(tv)
                  self.tv = tv
                  
              def b_action(self,sender):
                  self.h = self.height
                  def showtable():
                      self.border_color = 'green'
                      self.tv.hidden = False
                      self.height = self.tv.height+32
                  ui.animate(showtable,.4)
          
              def tableview_did_select(self,tableview, section, row):
                  # Called when a row was selected
                  data = tableview.data_source.items[row]
                  self.tf.text = data
                  def hidetable():
                      self.border_color = 'red'
                      self.height = self.h
                  ui.animate(hidetable,.4)
                  tableview.hidden = True    
          
              def as_cell(self):
                  c=ui.TableViewCell()
                  self.frame=c.content_view.bounds
                  c.content_view.add_subview(self)
                  c.set_needs_display()
                  return c
                  
          class MainView(ui.View):
              def __init__(self, *args, **kwargs):
                  super().__init__(*args, **kwargs)
                  v = ui.load_str(pyui_str)
                  t = v['tableview1']
                  t.delegate = t.data_source = self
                  
                  t.row_height = 222
                  v.present()
                  
                  self.content_view = None
                  self.tbl = t
                  
              def tableview_number_of_rows(self, tableview, section):
                  # Return the number of rows in the section
                  #return len(self.items)
                  return 2
                  
              def tableview_cell_for_row(self, tableview, section, row):
                  # Create and return a cell for the given section/row
                  if row==0:
                      itemslist = ['as','sd','df']
                  elif row==1:
                      itemslist = ['wer','qwe','ghj']
                      
                  cell = ui.TableViewCell()
                  cell = MyDropDown(tableview, section, row, frame=(100,0,200,100), items=itemslist).as_cell()
          
                  seg = ui.SegmentedControl(name='segRL')
                  seg.segments = ['R','L']
                  seg.selected_index = -1
                  seg.frame = (0,0,96,32)
                  seg.action = self.seg_action
                  cell.content_view.add_subview(seg)
          
                  tf_stent = ui.TextField(name='tf_stent',placeholder='boo')
                  tf_stent.enabled = True
                  tf_stent.clear_button_mode = 'while_editing'
                  tf_stent.frame = (500,0,60,32)
                  tf_stent.border_width= 1
                  tf_stent.border_color='blue'
                  tf_stent.corner_radius = 5
                  cell.content_view.add_subview(tf_stent)
          
                  tfl = ui.TextField(name='tfl')
                  tfl.enabled = True
                  tfl.clear_button_mode = 'while_editing'
                  tfl.frame = (seg.width+4,0,120,32)
                  tfl.border_width= 1
                  tfl.corner_radius = 5
                  self.add_subview(tfl)
                  self.tfl = tfl
          
                  return cell
                  
              def change_row_ht(ht=44):
                  self.tbl.row_height = ht
                  #redraw display
                  self.tbl.set_needs_display()
                  
              def seg_action(self, sender):
                  sideRL = sender.segments[sender.selected_index]
                          
          MainView()
          
          
          cvp 3 Replies Last reply Reply Quote 0
          • cvp
            cvp @crazyfox last edited by

            @crazyfox first, replace load_str by load_view_str 😀

            crazyfox 1 Reply Last reply Reply Quote 0
            • cvp
              cvp @crazyfox last edited by

              @crazyfox sincerely, I don't understand your

                      cell = ui.TableViewCell()
                      cell = MyDropDown(tableview, section, row, frame=(100,0,200,100), items=itemslist).as_cell() 
              
              1 Reply Last reply Reply Quote 0
              • cvp
                cvp @crazyfox last edited by cvp

                @crazyfox try to start with this

                import ui, console,dialogs
                
                pyui_str = r'''
                [
                  {
                    "nodes" : [
                      {
                        "nodes" : [
                
                        ],
                        "frame" : "{{21, 24}, {480, 287}}",
                        "class" : "TableView",
                        "attributes" : {
                          "flex" : "WH",
                          "data_source_items" : "Row 1\nRow 2\nRow 3",
                          "name" : "tableview1",
                          "frame" : "{{120, 110}, {200, 200}}",
                          "data_source_number_of_lines" : 1,
                          "class" : "TableView",
                          "background_color" : "RGBA(1.0, 1.0, 1.0, 1.0)",
                          "data_source_delete_enabled" : true,
                          "data_source_font_size" : 18,
                          "row_height" : 66,
                          "uuid" : "BE2CD45E-DD3C-496F-98C3-93E1222A94AA"
                        },
                        "selected" : false
                      }
                    ],
                    "frame" : "{{0, 0}, {523, 405}}",
                    "class" : "View",
                    "attributes" : {
                      "enabled" : true,
                      "background_color" : "RGBA(1.000000,1.000000,1.000000,1.000000)",
                      "tint_color" : "RGBA(0.000000,0.478000,1.000000,1.000000)",
                      "border_color" : "RGBA(0.000000,0.000000,0.000000,1.000000)",
                      "flex" : ""
                    },
                    "selected" : false
                  }
                ]
                '''
                
                
                
                class MyDropDown(ui.View):
                    def __init__(self, items=[], frame=(0,0,200,200), *args, **kwargs):
                        super().__init__(*args, **kwargs)
                        self.frame = frame
                        self.border_width = 1
                        self.border_color = 'red'
                        self.background_color='#f6feff'
                        
                        tf = ui.TextField(name='tf')
                        tf.enabled = False
                        tf.frame = (0,0,self.width-32,32)
                        tf.border_width= 1
                        tf.corner_radius = 5
                        self.add_subview(tf)
                        self.tf = tf
                        
                        b = ui.Button()
                        b.frame = (tf.width,0,32,32)
                        b.image = ui.Image.named('iob:arrow_down_b_32')
                        b.border_width = 1
                        b.corner_radius = 5
                        b.action = self.b_action
                        self.add_subview(b)
                        
                        tv = ui.TableView()
                        tv.frame = (0,tf.height,tf.width,self.height-32)
                        tv.border_width = 1
                        tv.corner_radius = 5
                        tv.data_source = ui.ListDataSource(items=items)
                        tv.height = min(24*3,24*len(items))
                        tv.row_height= 24
                        tv.delegate = self
                        tv.hidden = True
                        self.add_subview(tv)
                        self.tv = tv
                        
                    def b_action(self,sender):
                        self.h = self.height
                        def showtable():
                            self.border_color = 'green'
                            self.tv.hidden = False
                            self.height = self.tv.height+32
                        ui.animate(showtable,.4)
                
                    def tableview_did_select(self,tableview, section, row):
                        # Called when a row was selected
                        data = tableview.data_source.items[row]
                        self.tf.text = data
                        def hidetable():
                            self.border_color = 'red'
                            self.height = self.h
                        ui.animate(hidetable,.4)
                        tableview.hidden = True    
                
                    def as_cell(self): # unused =======================
                        c=ui.TableViewCell()
                        self.frame=c.content_view.bounds
                        c.content_view.add_subview(self)
                        c.set_needs_display()
                        return c
                        
                class MainView(ui.View):
                    def __init__(self, *args, **kwargs):
                        super().__init__(*args, **kwargs)
                        v = ui.load_view_str(pyui_str)
                        t = v['tableview1']
                        t.delegate = t.data_source = self
                        
                        t.row_height = 222
                        v.present()
                        
                        self.content_view = None
                        self.tbl = t
                        
                    def tableview_number_of_rows(self, tableview, section):
                        # Return the number of rows in the section
                        #return len(self.items)
                        return 2
                        
                    def tableview_cell_for_row(self, tableview, section, row):
                        # Create and return a cell for the given section/row
                        if row==0:
                            itemslist = ['as','sd','df']
                        elif row==1:
                            itemslist = ['wer','qwe','ghj']
                            
                        cell = ui.TableViewCell()
                        dd1 = MyDropDown(frame=(100,0,200,32), items=itemslist)
                        cell.content_view.add_subview(dd1)
                        
                        seg = ui.SegmentedControl(name='segRL')
                        seg.segments = ['R','L']
                        seg.selected_index = -1
                        seg.frame = (0,0,96,32)
                        seg.action = self.seg_action
                        cell.content_view.add_subview(seg)
                
                        tf_stent = ui.TextField(name='tf_stent',placeholder='boo')
                        tf_stent.enabled = True
                        tf_stent.clear_button_mode = 'while_editing'
                        tf_stent.frame = (400,0,60,32)
                        tf_stent.border_width= 1
                        tf_stent.border_color='blue'
                        tf_stent.corner_radius = 5
                        cell.content_view.add_subview(tf_stent)
                        
                        dd2 = MyDropDown(frame=(464,0,200,32), items=itemslist)
                        cell.content_view.add_subview(dd2)
                
                        tfl = ui.TextField(name='tfl')
                        tfl.enabled = True
                        tfl.clear_button_mode = 'while_editing'
                        tfl.frame = (seg.width+4,0,120,32)
                        tfl.border_width= 1
                        tfl.corner_radius = 5
                        self.add_subview(tfl)
                        self.tfl = tfl
                
                        return cell
                        
                    def change_row_ht(ht=44):
                        self.tbl.row_height = ht
                        #redraw display
                        self.tbl.set_needs_display()
                        
                    def seg_action(self, sender):
                        sideRL = sender.segments[sender.selected_index]
                                
                MainView()
                
                

                1 Reply Last reply Reply Quote 1
                • crazyfox
                  crazyfox @cvp last edited by

                  @cvp Thank you for taking the time.
                  I think I was making it harder than it should be.

                  @crazyfox first, replace load_str by load_view_str 😀

                  ...I see it now. Editing/posting error trying include pyui string.

                  @crazyfox sincerely, I don't understand your

                          cell = ui.TableViewCell()
                          cell = MyDropDown(tableview, section, row, frame=(100,0,200,100), items=itemslist).as_cell() 
                  

                  ...This evolved out of series of TypeErrors and issues with arguments I could not fix elegantly.

                  Much more to learn. Thanks again for your help.
                  Until next mental block.
                  -KP

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