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.


    ui.TableViewCell - returning a custom class instead?

    Pythonista
    tableviewcell ui.tableview
    5
    22
    18829
    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

      @omz or others, is there a way to return a custom class rather than a TableViewCell to the tableview_cell_for_row method?
      I have a test class below I Am returning to the method. It does not complain or create an error, but the call backs are also not being called. I would love to be called back as the cell needs to be drawn. I could do all my own drawing with ui.path , DrawText etc.. Essentially giving me a virtual list.
      I can see the example I have done below can not be enough. But maybe copying the dict from a TableViewCell into the class or something like that might be enough to fake it into thinking its a real TableViewCell at the same doing the callbacks into the custom class.
      This would be so nice...

      Again, I can return below to the tableview_cell_for_row request for a TableViewCell without a error.

      class TestView(ui.View):
      	def __init__(self, *args, **kwargs):
      		pass
      	def draw(self):
      		print('in draw')
      		
      	def layout(self):
      		print('in layout')
      
      1 Reply Last reply Reply Quote 0
      • Phuket2
        Phuket2 last edited by

        Ok, I tried the below to try and fool ui.Table. I also tried adding a sender attr to the methods. But I am flying blind.
        Didn't help 😱😁

        class TestView(ui.View):
        	def __init__(self, *args, **kwargs):
        		#self.__dict__.update(ui.TableViewCell().__class__.__dict__)
        		pass
        		
        	@property
        	def content_view(self):
        		return ui.View()
        		
        	@property	
        	def image_view(self):
        		return ui.ImageView()
        		
        	@property
        	def text_label(self):
        		return ui.Label()
        	
        	@property
        	def accessory_type(self):
        		pass
        		
        	@accessory_type.setter
        	def accessory_type(self):
        		pass
        	
        	def selectable(self):
        		pass
        		
        	def selected_background_view(self):
        		pass
        	
        	def selected_background_view(self):
        		pass
        		
        	def draw(self):
        		print('in draw')
        		
        	def layout(self):
        		print('in layout')
        
        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          Since this is very much something that is used on the objc side of the house, it seems to me you'll need to return something which has an objc pointer to a SUI_TableViewCell. So, no, you cannot subclass like this. You can, of course put anything you want in the content_view, so you could make a factory method which instantiates a tableviewcell and fills the content, and adds whatever custom attributes you want

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

            @JonB , I understand. I know we have talked about this subject many times in so many different ways. But in the simplest terms, if the data_source also had a callback to render/draw your cell rather than just request a ui.TableCellView it would be so powerful. I am just talking about cells that are the same height as normal. And that would be great.
            But I don't think it would be a great leap from there for @omz to implement variable height cells. On the draw request you return the height of the of the cell.
            But, that's really secondary to what I am asking about. I would just like a way to be called back to draw the cell, but without objc or swizzles etc...
            Actually, it's a pretty obvious way of doing things in my mind. As far back as HyperCard you would write a XCMD/XFCN in c to do user draw lists. HyperCard/Supercard for a lot of people were considered a joke. It's a long conversation, but it was far from a joke. I know I talk about c in the above, but times have moved on now. One way or another a ui.TableViewCell should be able to be owner drawn in my mind.

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

              @phuket2, ui.TableView contains content_view, where you can fill whatever you want. Whatever custom view you want, just be sure to set the frame equal to the content view bounds.

              If you want to encapsulate that into a class, consider something like this
              https://gist.github.com/e5004c7ef2f44bba9b6fa7b470d26ba9

              basically, a custom class which basically returns a new ui.TableViewCell, and fills it with whatever it wants. Sort of overkill, I am not sure you really need instance methods on the tableview cell itself..... if anything i think you want just a plain custom view, and a factory function which returns a tableviewcell with the view stuffed in content_view Any instance methods you might want, such as for button callbacks, would live in the custom view, not a custom tableviewcell.

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

                Here is a cleaner example, with a draw method.
                https://gist.github.com/c5e7bad33643895c0f0db5e30c97df3a

                define your dustom view. then define an as_cell method which shoves your view into a tableviewcell and returns it. Your data source then simple instantiates your view, and calls as_cell, returning the result.

                Phuket2 2 Replies Last reply Reply Quote 3
                • Phuket2
                  Phuket2 @JonB last edited by

                  @JonB , I don't know if you just worked this out or not. But its brilliant. I don't understand the pattern or whatever you call it that you used, but this is fantastic. I understand about the content_view etc of TableCellView. But I was locked in to the idea that I couldn't draw the cells as need as you are doing. But as far as I can see all the work I did on virtual lists was not really necessary with this implementation. 20,000 cells are created instantly. Of course if you start adding ui elements to the view this could have a impact. But the few tests I have done, I am just just drawing into the cell. It's super quick and very responsive to scroll. In the past I have made some complicated TableViewCells using the normal ui Elements adding them to the content_view, but it does not take long before it starts getting bogged down and scrolling starts to get jerky. I know @omz does some tricky things under the hood (or maybe it's part of the iOS framework) for reusing cells etc.
                  anyway, thanks, this is great.,..and I love it's all Python
                  @cook I am sure this is right up your alley also....

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

                    @phuket2 I think I knew a while back that it's possible to do this. I don't know where from but I had seen something done like this. But yes... it's very easy (...wait...really? maybe not..some things I don't understand!!). I think I may utilize this for something. Thanks for the example @JonB

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

                      @cook , I know quite a bit about ui.TableViews and Cells etc... I know I have never seen this before. Not to say it wasn't written , I just never seen it. I spent so long trying to write a virtual cell class, but I have created 100,000 cell tables with @JonB method. They are just instant. It's so perfect. The trick is the drawing of the cell. I changed his init to pass the tableview, section, row and put them in instance vars. No real data yet, but that will be easy, can also pass the data_item in the init also. But for me, this is the biggest break though to date regarding the ui.TableView. It really opens it up what you can do. Ok, it's 95% there as still no support for variable height cells. But everything else is open. Also you are getting all the stability/optimizations of the underlying ui.Table. I have looked at this before, some magic happening behind the scenes to make this super fast and efficient

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

                        Guys here is a crude example of it working fast in my opinion. It really is nice. My example is crude and resizing things don't really work out. I just ran out of time. But it does illustrate the speed and flexibility of @JonB method.
                        gist

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

                          Whaaaaaaaaa Speedy Jonzales 👏

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

                            Really, this is so cool. I did nothing, just hanging on the work of others. I have done a million cells, does not phase it in the slightest. It's the 2 magics, @JonB 's a custom TableCellView and the magic sitting behind ui.TableView. Not sure if that magic is @omz or iOS foundation classes. But who cares, it's great.

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

                              Ok, then Speedy Jomzales!

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

                                @JonB, I thought your approach here might solve another problem I have with the ui.Rect class. I would like to be able to add attrs to ui.Rect, but as far as I know normally you can't just make an assignment as its not written in python. But I thought I would try your idea here, but it does not work. I am guessing the reason why what you do with TableViewCell works because ui.View can be subclassed. But I still gave it a go , just in case 😱

                                import ui
                                
                                class CustomUIRect(object):
                                	# my_color = None
                                	def as_rect(self, x=0, y=0, w=0, h=0):
                                		r = ui.Rect(x, y, w, h)
                                		return r
                                		
                                	def __init__(self, color):
                                		print('in init')
                                		self.my_color = color
                                	
                                if __name__ == '__main__':
                                	r = CustomUIRect('blue').as_rect()
                                	print(dir(r))
                                	print(r.my_color)
                                
                                1 Reply Last reply Reply Quote 0
                                • JonB
                                  JonB last edited by

                                  The difference is that, for the custom tableview cell, it returns a tableview cell with the content view filles with the current view. Your as_rect is returning a Rect...so you need to set any Rect attributes in the method.

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

                                    @JonB , thanks. I think it would be good if there was a ui.Rect on steroids for use with ui.Path etc. that could be subclassed. It's all about rects. I know we have ui.View etc. but whole views are not really required or wanted when doing ui.Path stuff. I think of it like using smart rects like a wire frame design. I think it's a good idea, but more than likely just a lack of skill on my part

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

                                      I guess I don't understand what you are trying to achieve. Just encapsulation of colors with the rect? Or is there something else? Your approach would work if you just convert the custom rect to a rect at the drawing time, rather than sending to your class.

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

                                        @JonB , basically I would just like to be able to subclass ui.Rect, which you can't as its written in Objective C I am guessing , like you can't directly ui.Switch for example.

                                        The reason, I want it I really want to make wire frame views. Meaning I just want to use rects to describe the views and yes a few more attrs and methods as required. I know you are thinking well that's just called a ui.View. But that's just so heavy compared to a extended ui.Rect.

                                        I went back to your FlowContainer repo today. However, I didn't strike me how it could help me constructing wire frame views. Not to say it won't later. As I say, I am a slow learner.

                                        I am not sure if you tried my crude example here, as basic as it is, if you try doing it with ui Elements you will see a huge difference, with speed.

                                        But @JonB , I don't understand your comments about my last code example. I didn't try to draw it. I just tried create the custom Rect adding a instance attr, then accessing the attr. Hmmm, even as I write it, I can see it's a little stupid what I am saying. But all I want to do is extend ui.Rect with attrs and methods. It's probably just so easy. But I don't see a clear way to do it.

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

                                          I think you might find a ui.View is not as heavy as you think... the app has probably a hundred Views nested dozens deep. Once you come up with something that is actually too "slow", then figure out how to optimize it..

                                          But, that aside, If you are in control of your drawing method, you can "extend" rects by encapsulation, rather than subclassing. In other words, you would have a class that includes a Rect instance attribute, a line and fill color attributes, and whatevr else you want to customize. Then you could simply have a draw() method which creates a Gstate, and draws to the current context (Gstate would ensure that setting fill color to draw a rect does not change thnglobal fill,color). You then call your custom draw from the draw() method of your top View. In other words, your WireFrameContainer would have a list of CustomRects and you cycle through calling draw for each CustomRect.

                                          @polymerchm made a pyui explorer a while back that showed the entire heirarchy of a pyui views as wireframes, to allow you to more easily encapsulate views inside conainer views, and move views from a deep layer to a top layer, etc.
                                          https://github.com/polymerchm/pyuiEdit
                                          Not sure if this is still compatible with the new pythonista verstions, but it might have some ideas.

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

                                            @phuket2 I mentioned that I had seen something like this before... And I remember now that I had attempted to do it a long while back. Maybe a year ago when I was first learning some UI things. But I didn't know what I was doing. Some stuff sort of worked, but I ran into problems because I was trying to stick a webview in the tableviewcell. It wasn't working nice and I just thought it wasn't ever going to work.

                                            I've learned a few things since. Now I've attempted to do again what I did before. The fix is really this little objc bit that gives the webview an opaque background - that way the selections in the tableview still work.

                                            If there is a difference here from what @jonb is doing, I don't know what it is. I'm limited with my knowledge of how classes work (in the deep bowels of classes) so I don't know what's going on.

                                            Anyway, try this out....

                                            import ui
                                            from objc_util import *		
                                            		
                                            class MyTableViewDataSource (object):
                                            	def __init__(self, data):
                                            		self.data = data
                                            	
                                            	def tableview_number_of_sections(self, tableview):
                                            		return 1
                                            
                                            	def tableview_number_of_rows(self, tableview, section):
                                            		return len(self.data)
                                            
                                            	def tableview_cell_for_row(self, tableview, section, row):
                                            		cell = ui.TableViewCell()
                                            		wv = ui.WebView(frame=cell.content_view.bounds, flex='WH')
                                            		wv.touch_enabled = False #necessary for tableview scrolling to work, unfortunate side effect is that you can't do any touch of content in the webview. Tried disabling scolling with objc but the result wasn't good either.
                                            		wv.scales_page_to_fit = False
                                            		wv.load_html(html)
                                            		#webview needs to have an opaque background for selection colors to work... do in objc
                                            		wv_obj = ObjCInstance(wv)
                                            		wv_obj.webView().setBackgroundColor_(ObjCClass('UIColor').clearColor())
                                            		wv_obj.webView().setOpaque_(False)
                                            		cell.content_view.add_subview(wv)
                                            		return cell
                                            
                                            	def tableview_can_delete(self, tableview, section, row):
                                            		return False
                                            
                                            	def tableview_can_move(self, tableview, section, row):
                                            		return False
                                            		
                                            	def tableview_did_select(self, tableview, section, row):
                                            		t.name = self.data[row]['name']
                                            		
                                            if __name__ == '__main__':
                                            	from faker import Faker
                                            	fake = Faker()
                                            	style = '<style>p {font: arial 10pt; color:#888888; margin:0px} a {color:#888888; text-decoration:none} h4 {margin:0px; background-color:#dd7575; color:white; border-radius:5px; padding: 0 5}</style>'
                                            	contact_list = []
                                            	for x in range(200):
                                            		name, email, phone, address = fake.name(), fake.email(), fake.phone_number(), fake.address()
                                            		html = style + '<h4 style="margin:0px">{}</h4><p>{}</p><p>{}</p><p>{}</p>'.format(name, email, phone, address)
                                            		contact_list.append({'html':html, 'name':name})
                                            	t=ui.TableView()
                                            	t.frame=(0,0,400,480)
                                            	t.data_source=MyTableViewDataSource(contact_list)
                                            	t.delegate = t.data_source
                                            	t.row_height = 100
                                            	t.present('sheet')
                                            
                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors