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.
Tableview not updating
-
Hi, I've got the code below to change a tableview's data to what's in the ui's textfield when the button is pushed. Can anyone explain to me please why it's not working?
I will type something into the text field and then press the button and the data in the tableview doesn't change as it is.
import ui import console def button_pushed(sender): textfield1 = v['textfield1'].text textfield2 = v['textfield2'].text textfield3 = v['textfield1'].text data = [textfield1, textfield2, textfield3] datasource = ui.ListDataSource(data) v['tableview1'].data_source=datasource v=ui.load_view() v.present('fullscreen')
Thanks in advance.
-
im not really sure you want to create a new datasource everytime you press a button, versus creating it once, and then setting the data. Also, if you used the ui designer, I think you already get a datasource, just set the datasource.items. ListDataSource then handles reloading the tableview, whenever items change.
Otherwise, tableviews need their
reload
method, orinsert_rows
called when modifying data. ListDataSource does that for you -
@JonB Thanks again Jon.
-
@wenchoheelio , hello me again :). Hey, i am guessing you are using UIFiles to create your interface. Thats perfectly fine and very good when your view gets complicated. But for simple views, doing a CustomView can be very easy and helpful. Esp when you are learning the ui module, you dont feel as disconnected from your view. Anyway, I did an example below. Its not perfect by any means. Many hard coded numbers in there. I am not sure where you are at. But just wanted you too see that ui CustomViews are not scary. Its just a class that subclasses ui.View.
Anyway, my example is below. Below the example, I have put the code I use each time I start a new file. Again, I am not saying its great. But for me its a standard way of starting to do something. I also do have other templates if I am going to be using a UIFile (.pyui) file also.The only reason I am sharing this is because I had problems in the begining. Knowing how to both use the UIFile(Designer) and Custom ui.View classes for interfaces will make life easier in my opinion. Working with both sort of helps you get a better understanding of what's going on. And more importantly, I think you can start to see how easy it actually it is.
import ui class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.txtfld1 = None self.txtfld2 = None self.txtfld3 = None self.tbl = None self.make_view() def make_view(self): self.txtfld1 = ui.TextField(frame=(0, 0, self.width, 32), text='(1) TextField_text') self.txtfld2 = ui.TextField(frame=(0, 32, self.width, 32), text='(2) TextField_text') self.txtfld3 = ui.TextField(frame=(0, 64, self.width, 32), text='(3) TextField_text') self.add_subview(self.txtfld1) self.add_subview(self.txtfld2) self.add_subview(self.txtfld3) self.tbl = ui.TableView(frame=(0, 128, self.width, 200)) self.tbl.data_source = ui.ListDataSource(items=[]) self.add_subview(self.tbl) btn = ui.Button(title='Update Table', border_width=.5, bg_color='white', action=self.do_update_list ) btn.width = 150 btn.height = 40 btn.center = self.center btn.y = self.height - btn.height - 20 self.add_subview(btn) def do_update_list(self, sender): self.tbl.data_source.items = [self.txtfld1.text, self.txtfld2.text, self.txtfld3.text ] if __name__ == '__main__': f = (0, 0, 300, 400) v = MyClass(frame=f, bg_color='teal', ) v.present(style='sheet', animated=False)
This is the template, I start out with most times I want to do something. Its simple, normally I am doing simple things, so its fine for me. It's Just good to have a consistent starting point and way of doing things. You make decide on a completely different template or None at all. Its just an idea.
import ui class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.make_view() def make_view(self): pass if __name__ == '__main__': f = (0, 0, 300, 400) v = MyClass(frame=f) v.present(style='sheet')
-
@Phuket2 Thanks a million for this! Since your last response, this is something I'm really looking into. I guess it's the next step for me. I actually have a tab on my computer open with the pythonista documentation for 'action and delegates' right now. You couldn't have timed a better response.
I guess another advantage to this, aside from it being customisable, teaching me about classes, actions, delegates, etc - it'll allow me to send code to other people and have it run without a pyui file.A couple more questions if you don't mind:
- I've seen a lot of people refer to 'superview' - what is a superview? Is that just a variable that people use often for the main view?
- Is a delegate the same as a sub class or a custom class?
-
@wenchoheelio , np.
As far as superview goes, I maybe will not be good at explaining this but i will try. First a few things to consider.- Just about all (with 1 or 2 rare exceptions) are subclassed from ui.View. Buttons, Tables, TextFields etc... So in essence they themselves are ui.Views. So all the attrs/methods that a ui.View has so does a button and the other ui controls. In the case of a btn it has some extra attrs that are distinct for a button, a TableView same idea has all the ui.View attts/methods in addition to those attrs/methods that a table needs to be a table.
You will notice all these controls have the superview property.
So a view can act as a container for another view. So when you make a view (v) to display on screen(window) and you add a button to it using add_subview(btn), v is the container for the btn. Hence, its said that the superview of the button is the view. The first view you display, the superview will be None as its the top level container.
So when you are in you callback method for a buttons action you are passed the object. The standard practice is to call that param name sender. Sender is the button. So, If you needed the name of the view you could do something like sender.superview.name. In other words sender.superview in this case is v, the container that the sender(btn) resides in.
I will do a little code a bit later today when I have extra time to help show this better.
So basically that all it is......with one exception. Think of layers. A View, can contain another view in which the new view can contain yet another view. The example that comes to mind that hopefully makes sense is the following.
You create a view, to that view you add a ui.ScrollView to the view. Inside the ScrollView you may add a series of buttons or whatever makes sense (The TableView works like this). But for sake of the example lets say we just added a button into the ScrollView (sv).
So the superview of the sv will be the view, but the superview of the button will be the sv.Its a little hard for me to explain it clearly. Its just to say the ui container model is hierarchical, not flat. In code, add_subview(v) is making this relationship. In the designer you can do the same thing if you say add a custom view on you sheet, then do a long press on the object, and from the menu that pops up select subviews.
Look I will try to get back later with some simple examples that hopefully explain this a little better. But please dont be turned off, its difficult for me to explain, but in practice its quite simple andI am still a novice.
- Just about all (with 1 or 2 rare exceptions) are subclassed from ui.View. Buttons, Tables, TextFields etc... So in essence they themselves are ui.Views. So all the attrs/methods that a ui.View has so does a button and the other ui controls. In the case of a btn it has some extra attrs that are distinct for a button, a TableView same idea has all the ui.View attts/methods in addition to those attrs/methods that a table needs to be a table.
-
@wenchoheelio , as far as the delegate goes, its just a class. Its a class that @omz come up with, to give more control to us programmers that need it. So somewhere in Pythonista's code, and you are working with a TextField, Pythonista would check to see if the delegate field is pointing to a class. Then as Pythonista receives different events from the TextField, if it has a delegate class, it would then check to see if it had a method in that class it should call in response to the event its dealing with. That might not be 100% correct, but the idea is close enough. The delegate class could have been an empty class. All still would have worked if no delegate class was set at all. This is because Pythonista is first checking to see if the method it wants to call is present in the class, not just blindly calling it expecting it to be there.
As you progress with your own skills, you may find this sort of programming pattern useful for your own programs.
You should take a look at the docs for creating custom ui.Views. I have put the standard code copied from the docs below.
You can see using a custom ui.class there are a lot of other events you can control. But again, you only need to include the methods you intend on using.
Why is it called a CustomClass, I guess because it subclasses ui.View.
class MyView (ui.View).
If you say do print(dir(MyView)) you will see all the ui.Views attrs and methods such as superview.
The the docs, @omz talks about delegates and actions. Also worth reading about. I am trying super hard not to confuse you. I am not expert. But I know roughly what's going on. We are not going into fine detail here. I mean edge cases, eg, ui.ButtonItem and ui.Navigation have a few little weird exceptions about them. But its not worth sweating about. Once you know the basics, these small exceptions will be easy enough to understand. So again, please if my explanation sounds complicated, just put it down to my poor literary skills. Because it easy. You should do some reading and practicing. Once you get some of the core concepts of how the ui jigsaw is put together, it will get very clear. And on top of that you will appreciate how well its put together.But honestly, I would reads about CustomViews. Read the docs, do some experiments. Also get comfortable with the ui.Views attrs and methods. Eg. All ui.Views have a subviews property.
You can iterate over that property to get a reference to every object in the view. Eg.
for obj in v.subviews: print(obj)
Important again to remember, the ui.View system is NOT a flat system. Views can contain other views which in turn can contain other views.
Here is a link discussion about walking though all the subviews for a given view. Useful, when you have a lot of views inside other views.import ui class MyView (ui.View): def __init__(self): # This will also be called without arguments when the view is loaded from a UI file. # You don't have to call super. Note that this is called *before* the attributes # defined in the UI file are set. Implement `did_load` to customize a view after # it's been fully loaded from a UI file. pass def did_load(self): # This will be called when a view has been fully loaded from a UI file. pass def will_close(self): # This will be called when a presented view is about to be dismissed. # You might want to save data here. pass def draw(self): # This will be called whenever the view's content needs to be drawn. # You can use any of the ui module's drawing functions here to render # content into the view's visible rectangle. # Do not call this method directly, instead, if you need your view # to redraw its content, call set_needs_display(). # Example: path = ui.Path.oval(0, 0, self.width, self.height) ui.set_color('red') path.fill() img = ui.Image.named('ionicons-beaker-256') img.draw(0, 0, self.width, self.height) def layout(self): # This will be called when a view is resized. You should typically set the # frames of the view's subviews here, if your layout requirements cannot # be fulfilled with the standard auto-resizing (flex) attribute. pass def touch_began(self, touch): # Called when a touch begins. pass def touch_moved(self, touch): # Called when a touch moves. pass def touch_ended(self, touch): # Called when a touch ends. pass def keyboard_frame_will_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. pass def keyboard_frame_did_change(self, frame): # Called when the on-screen keyboard appears/disappears # Note: The frame is in screen coordinates. pass v = MyView() v.present('sheet')
-
@wenchoheelio , have a look at the sample below. When you realise almost all the controls are subclassed from views, then you realise you can use present on a button for example. Its not so functional, but just more to make a point.
import ui def my_btn_action(sender): if sender.superview: sender.superview.close() else: sender.close() if __name__ == '__main__': btn = ui.Button(title='Click me to close', bg_color='deeppink', tint_color='white', action=my_btn_action ) btn.width = 200 btn.height = 200 btn.present(style='sheet', animated=False, hide_title_bar=True)