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.
Beginner Help On Tableview In UI Module
-
Hi,
I am unable to figure out, how to make user choose two tablecell values from different tables, and get these values out the function to process them further.
Here i am able to get the values print to console from within the function. I just need them out as global variables i guess. Please help with simplest example. Thank you.
Ps. I think Pythonista is an excellent app, but really wish the documentation on UI Module was more elaborate with examples :)
Here is the script i am struggling with.. I actually need inputs from 4 such tables, i just put 2 tables here.# simple table import ui def cell_tapped1(sender): cellval1 = ds1.selected_row + 1 print ('Maha', cellval1) tv1 = ui.TableView() tv1.frame = (0,0,95,300) tv1.width, tv1.height = 95, 270 tv1.row_height = 30 ds1 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds1.action = cell_tapped1 tv1.data_source = tv1.delegate = ds1 def cell_tapped2(sender): cellval2 = ds2.selected_row + 1 print ('Antar',cellval2) tv2 = ui.TableView() tv2.frame = (95,0,95,300) tv2.width, tv2.height = 95, 270 tv2.row_height = 30 ds2 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds2.action = cell_tapped2 tv2.data_source = tv2.delegate = ds2 v = ui.View() v.name = 'Tables' v.background_color = "lightyellow" v.add_subview(tv1) v.add_subview(tv2) v.present('sheet')
-
@ramvee , I have put an example below. I didn't do the exact easiest thing below, but in my opinion you need something like this pretty soon.
So the idea is just to use a custom ui class. Will just help keep things together and work with the logic. Building Custom UI classes are well documented in the help file. They are very easy to understand and give you more control over your view.As far as controlling the users selection goes, I think the most would consider what most iOS apps do. Drill down. So you only ever have one list visible in the view at a time. Regardless you need some control over somethings. Eg, you could hide the titlebar and add a button to your view to dismiss it. Then you can do your own checks to see if you have a selection for each list. See ui.View.presents func parameters. When using custom ui views you get called back if a view is about to close, can also be helpful.
Anyway, I hope the below is useful. I didn't try to rework all your code. Just give an example modifying your code. You are more than likely want to create your tables in the class also. You can see, I also pointed the delegate of tv1 to the custom class. Don't have to, but it makes it a little easier in this case to store results etc.
# simple table import ui def cell_tapped1(sender): cellval1 = ds1.selected_row + 1 print ('Maha', cellval1) tv1 = ui.TableView() tv1.frame = (0,0,95,300) tv1.width, tv1.height = 95, 270 tv1.row_height = 30 ds1 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds1.action = cell_tapped1 tv1.data_source = tv1.delegate = ds1 def cell_tapped2(sender): cellval2 = ds2.selected_row + 1 print ('Antar',cellval2) tv2 = ui.TableView() tv2.frame = (95,0,95,300) tv2.width, tv2.height = 95, 270 tv2.row_height = 30 ds2 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds2.action = cell_tapped2 tv2.data_source = tv2.delegate = ds2 # Custom ui Class class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.table1_selection = None self.bg_color = 'lightyellow' self.make_view() def make_view(self): self.add_subview(tv1) self.add_subview(tv2) tv1.data_source.action = self.tv1_action def tv1_action(self, sender): self.table1_selection = sender.items[sender.selected_row] print(self.table1_selection) # the ui module calls this method automatically, if its definded # there are other callbacks you can see them in the help file def will_close(self): print('view closing...Selection = ', self.table1_selection) f=(0, 0, 300, 480) #v = ui.View(frame=f) v = MyClass(name='Tables', frame=f) #v.name = 'Tables' #v.background_color = "lightyellow" #v.add_subview(tv1) #v.add_subview(tv2) v.present('sheet')
-
@Phuket2
Hi,
Thank you for your prompt help. The script really helped me. As per your suggestion I will read up more on building custom classes. If you can suggest a simple way i can the get values out of the will_close function, I can do further processing, because my actual program starts from these inputs.
Ps. I have all your scripts posted in the forum, on my iPhone and printed out :). Thank you for all your help. I am finding programming using UI module very difficult.
Ram -
@ramvee , I at first also found the ui module difficult. But I soon realised it was for a number of reasons. Number 1 reason, I was very scared of it. Actually, nothing to be scared about. It really is well put together. I also didn't read enough. It does not take that long to read through the ui module help file. It's well worth doing.
I would not say it's perfect, but I would still say it's fantastic.Also searching the forum is great. The built in search is not that great. But if you do a google site search on the forum it comes up with a lot better results.
Getting the values out of the will_close is easy, as you can just access the values you have saved in your class. What's harder is to stop the view closing. Sorry, I just can't remember how to do it.
Anyway, in my version below you hide ui title bar and create your own. Don't let it bother you, it's not that many lines. But you can see you have full control. Again, I have just edited the code from before. It's a very simplistic approach.
One problem with trying to help you is that you can take many approaches to achieve the same result. Please don't think of this as the best way. It's just a way. When you experiment a little, you will see other ways, also other guys here might present other ideas.
But ok, below is a updated version , I did not go for pretty. Eg, the finished button could have be an image etc...
# simple table import ui, console def cell_tapped1(sender): cellval1 = ds1.selected_row + 1 print ('Maha', cellval1) tv1 = ui.TableView() tv1.frame = (0,0,95,300) tv1.width, tv1.height = 95, 270 tv1.row_height = 30 ds1 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds1.action = cell_tapped1 tv1.data_source = tv1.delegate = ds1 def cell_tapped2(sender): cellval2 = ds2.selected_row + 1 print ('Antar',cellval2) tv2 = ui.TableView() tv2.frame = (95,0,95,300) tv2.width, tv2.height = 95, 270 tv2.row_height = 30 ds2 = ui.ListDataSource(['Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury', 'Ketu','Venus' ]) ds2.action = cell_tapped2 tv2.data_source = tv2.delegate = ds2 # Custom ui Class class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.table1_selection = None self.bg_color = 'lightyellow' self.make_view() def make_view(self): self.add_subview(tv1) self.add_subview(tv2) tv1.data_source.action = self.tv1_action # make something that looks like a menu bar menu = ui.View(frame=(0, 0, self.bounds.width, 44)) menu.bg_color = 'cornflowerblue' lb = ui.Label() lb.text = self.name lb.size_to_fit() lb.center = menu.bounds.center() menu.add_subview(lb) self.add_subview(menu) # make a finished button btn = ui.Button(name='finished', frame=(0, 0, 70, 32)) btn.title = 'Finished' btn.border_width = .5 btn.corner_radius = 3 btn.tint_color = 'white' btn.center = menu.bounds.center() btn.x = 5 btn.action = self.finished menu.add_subview(btn) # adjust the y of the tables tv1.y = menu.height + 5 tv2.y = menu.height + 5 def tv1_action(self, sender): self.table1_selection = sender.items[sender.selected_row] print(self.table1_selection) def finished(self, sender): # here we can check some stuff and decide to close the view # or not. as an example, has been set. you can close the view # until you have clicked on table1 at least once if not self.table1_selection: result = console.alert('please select from table 1 first') self.close() # the ui module calls this method automatically, if its definded # there are other callbacks you can see them in the help file def will_close(self): print('view closing...Selection = ', self.table1_selection) f=(0, 0, 300, 480) #v = ui.View(frame=f) v = MyClass(name='Tables', frame=f) #v.name = 'Tables' #v.background_color = "lightyellow" #v.add_subview(tv1) #v.add_subview(tv2) # here we supress the title bar v.present('sheet', hide_title_bar = True)
Edit: ps, as we are using a custom view now, it's not that hard to extend of view in a meaningful way. You can still do it without a custom ui class, but your code gets fragmented pretty quickly. That's my opinion anyway
-
Another take...
# Two simple tables import ui neighbors = 'Sun Moon Mars Rahu Jupiter Saturn Mercury Ketu Venus'.split() class TwoTablesView(ui.View): def __init__(self, name='Two Tables', bg_color='lightyellow'): self.name = name self.bg_color = bg_color self.user_data = {} self.add_subview(self.make_table_view('Maha', (0, 0, 95, 300))) self.add_subview(self.make_table_view('Antar', (95, 0, 95, 300))) self.present() def make_table_view(self, name, frame): table_view = ui.TableView(name=name, frame=frame) table_view.row_height = 30 data_source = ui.ListDataSource(neighbors) data_source.name = name data_source.action = self.table_action table_view.data_source = table_view.delegate = data_source return table_view def table_action(self, sender): self.user_data[sender.name] = sender.items[sender.selected_row] def will_close(self): print(self.user_data) TwoTablesView()
-
Wow,
Thank You, @Phuket2 And @ccc for your prompt help. All your various scripts on this forum and github, help beginners like me a lot. Both scripts work well!
Hats Off To @ccc that was truly pythonic ! I regret not asking for help in this forum earlier.
And @Phuket2 , I will take your advice to really study the UI Module Help Documentation. I just wish there were more examples in it.
Grateful! Namaste _/_ -
@ramvee , not sure if it's evident to you or not. But all* ui elements are sub classes of ui.View (there are a few exceptions, ui.ButonItem being one of them). But for the most part all the ui Elements are ui.Views.
So the below sample works. It's not that you really want to do it that way. But it helps you understand how these items are passed around so easily.
So a button or a table, for example, are subclassed from ui.View. Meaning they look like a ui.View to functions/Methods that require a ui.View as a param. I am not sure I am explaining it well, but it is an important thing to get your head around. One way to think is that basically a ui Element is just a ui.View that has extended functionality for its purpose.Look there are a few exceptions, but not many. Unfortunately, the exceptions give me pause when writing this.
The below can be practical sometimes. But it's more just to show that a ui Element is subclassed from ui.View and can be used as such.
import ui f = (0, 0, 600, 800) tbl = ui.TableView(frame=f, name='Table' ) tbl.data_source = ui.ListDataSource(items=range(40)) tbl.present('sheet', title_bar_color = 'teal')
-
I didn't even know that. Shame on me.
Perhaps, I'll drink to forget my own limits.🤕 -
@ramvee , the next thing you will be thinking about is how to place your tables inside your view. The below is a simple example of placing 4 ui.TableViews into a View. It's a Custom ui View, but a ui.View anyway.
But the thing that's important to note that this view, is size and orientation friendly. If you change the style = 'panel' or 'fullscreen' you notice that the tables are scaled correctly. Also when you change the orientation of you device. Its because the flex settings of each view/ui.Table are set. In this case 'tlwhbr'. Meaning preserve top,left,width,height,bottom,right relative.
Again, flex is documented in the ui Module.
There is also another way. I will make another example. Both are valid, just depends on you are doing. The next example will use a callback to the ui Custom Class called layout.Anyway, play around with the num_tables var and the style param and when the style is 'sheet' mode change the w, h values in the main. Everything should adjust accordingly for screen size
# Pythonista Forum - @Phuket2 import ui class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.make_view() def make_view(self): num_tables = 4 for i in range(0, num_tables): tbl = ui.TableView(name=str(i)) tbl.width = self.width / num_tables tbl.height = self.height tbl.x = tbl.width * i tbl.data_source = ui.ListDataSource(items=range(40)) tbl.flex = 'tlwhbr' self.add_subview(tbl) if __name__ == '__main__': w, h = 600, 800 f = (0, 0, w, h) style = 'sheet' mc = MyClass(frame=f, bg_color='white', name='Tables') mc.present(style=style, animated=False)
-
@ramvee , ok below is using layout technique to position your views. Both ways, flex and layout have their merits. I think most would say if flex way suits your needs, then this would be preferred method. Other times you might find the layout method easier. Again, don't forget they are all just views. And views can have subviews. It seems a little confusing, but it's not really. So some views could be relying on flex for placement in ts view, but enclosing views could be using layout. Hmmmm.... sounds like double Dutch. It's sounds a lot harder than it actually is. If you play a little with the ui Designer, it may give you a better insight to subviews with in subviews. Keeping in mind a sub view is just a view that has a parent view. Again another thing worth understanding, subviews that is. There is not that much to understand really. But when you add a view to another view as a subview, the coordinates are based on its parents frame/bounds. Which is great. There is another thing. accessing a object via list notation. Look this is also documented. But it's important to understand that a ui.View is not a flat name space so to speak. You can use list notation to access named subview objects. But you can have many views being subviews of a view, in turn, their views can contain subviews and so on. Again, just hard to explain for me. But I would say again, don't let it scare you. It becomes pretty easy and you soon figure out, it's the way it should be.
I am trying my hardest not to confuse you. But sharing some of the things I had to get over when learning the ui Module. Get over these small speed bumps, then it will be easy going for you.
# Pythonista Forum - @Phuket2 import ui class MyClass(ui.View): def __init__(self, num_tables=4, *args, **kwargs): super().__init__(*args, **kwargs) self.make_view(num_tables) def make_view(self, num_tables): for i in range(0, num_tables): tbl = ui.TableView(name='tbl_' +str(i)) #tbl.width = self.width / num_tables #tbl.height = self.height #tbl.x = tbl.width * i tbl.data_source = ui.ListDataSource(items=range(40)) #tbl.flex = 'tlwhbr' self.add_subview(tbl) def layout(self): # Layout is called automatically by the ui Module, for Custom # ui.Views (aka, classes that subclass ui.View) is only called # when a views frame/bounds change. # get a list of tbls that are subviews. This case we only have # ui.TableViews as subviews, but we could also have others, such # as buttons, other ui.Views etc... tbls = [tbl for tbl in self.subviews if type(tbl) is ui.TableView] w = self.width / len(tbls) h = self.height for i, tbl in enumerate(tbls): tbl.width = w tbl.height = h tbl.x = i * w if __name__ == '__main__': w, h = 600, 800 f = (0, 0, w, h) style = 'sheet' mc = MyClass(num_tables=6,frame=f, bg_color='white', name='Tables') mc.present(style=style, animated=False) # access the table by name print(mc['tbl_1']) print(mc['tbl_2'].data_source)
Edit, you can see in the make_view we don't size or place objects. It's done in layout. We are just creating the objects
-
Thank You @Phuket2 , for all your help!
You made me understand flex properties very well :)
Your scripts with comments help me learn UI Elements. Realised I need to learn more about classes and magic methods to get further. On the job! :) -
@ramvee , I did make an error by referring to parent views. Technically it's correct, but the superview property is the parent view when it comes to ui.Views. Eg, you create a ui.View/element, the superview property will be None until you call the add_subview() method. But it would have been more correct to talk about superviews rather than a parent view.