Polling from a ui.View (built in timers in ui.Views)
@omz, did you ever go further with the idea of a ui.View having its own timer that you could hook into to do updates etc. ? Personally, I think I would be a great advance for ui.View or Custom ui.Views. I realise there are different ways to do it already. But it's so easy to get it wrong, well at least for me. I am sure you could implement it many ways. For me, I think the best would be a callback same as draw in subclassed ui.Views. Like the draw method, if a timer method is not defined then no additional overhead or speed issues would impact the class. Also some way to set the interval of the callback as well as a way to able to suspend/activate the timer callbacks. I think it could be more flexible if it did not automatically suspend if it's not the foremost view. Eg, you may have a view/s as an overlay/s with the alpha set so it's transparent, but you want to still want it to execute the code in the timer callback method.
Anyway, I think this would be super helpful. Not sure what others think.
@omz, I am not technoway. Sorry for jumping in; I have been waiting for the beta invite in order to test the polling functionality.
@mikael Ah, I'm sorry, I'll send a new invite to the email address you're using for this forum.
@omz, got it, thanks!
@omz, some feedback on the
updatefeature, all of it positive:
- Very stable and consistent. Stops when the view is closed, no threading hassles.
- Very intuitive to use. Changing the
update_intervalto 0.0 stops updates, and positive numbers start them again.
With my limited Threading skills, I was unable to create stable and predictable UI animations with either ui.delay or custom Threads. With
update, no issues. This is my vote for moving the feature out of beta.
@mikael Thanks for your feedback! I appreciate it.
@Phuket2, thank you for taking this up.
@mikael , thanks all to @omz. When i had to do something threaded or in a loop, I never felt confident about it. I have a small thing now where its just a label update to show when something expires. It's so nice and simple with update mechanism.
My experience has been same as yours. Very stable.
@mikael , I mentioned something in the github issues area about ui.TableViewCell. @omz said just add your view to the cell. I had forgotten @JonB had helped me with this a long time ago. Anyway, i was playing around. The below maybe is not pretty. But I find it interesting and it shows off a few things. Also how well update works. Well i think it does anyway.
EDIT: to see the cool stuff I think you have to tap a cell and also scroll. You can see how things are getting suspended when you scroll. I think its nice
import ui from random import choice _color_list = ['purple', 'orange', 'deeppink', 'lightblue', 'cornflowerblue' 'red', 'yellow', 'green', 'pink', 'navy', 'teal', 'olive', 'lime', 'maroon', 'aqua', 'silver', 'fuchsia', ] class MyCustomCell(ui.View): def __init__(self, parent, *args, **kwargs): super().__init__(*args, **kwargs) self.cell = parent self.tableview = None self.blink_count = 0 self.lb = None self.frame = self.cell.frame self.flex = 'wh' self.width -= 10 self.x = 5 self.height -= 10 self.y = 5 self.alpha = .5 self.corner_radius = 6 # this allows the touch events to pass through my subview self.touch_enabled = False self.update_interval = .2 lb = ui.Label(frame=(0, 0, 24, 24), bg_color='black', text_color='white', alignment=ui.ALIGN_CENTER) lb.center = self.center lb.corner_radius = 12 self.lb = lb self.add_subview(lb) def rect_onscreen(self): ''' Have to write this method. Would be nice if this was built in. like ui.TableView.is_visible for example. I know its just some rect math, but it means you need to save extra references etc.. to calculate it yourself. ''' return True def update(self): if not self.tableview: return # I did not implement this yet. A little drunk and having a party today. # but gives the idea... if not self.rect_onscreen(): return if self.blink_count == 98: self.update_interval = 0 self.blink_count += 1 self.lb.text = str(self.blink_count) self.bg_color = choice(_color_list) def create_cell(): ''' Create and return a ui.TableViewCell. We add a custom ui.View to the TableViewCell.content_view. This means our view is sitting on top of the normal TableViewCell contents. All is still there. Also create an attr in the cell at runtime that points to our custom class. I guess this can be done many ways. I choose this way for the example. To me its at least clear for access. ''' cell = ui.TableViewCell() myc = MyCustomCell(cell) cell.content_view.add_subview(myc) cell.my_cell = myc return cell class MyDataSource(object): def __init__(self, data): self.data = data self.sel_item = 0 self.cells = [create_cell() for _ in range(len(self.data))] def tableview_number_of_rows(self, tableview, section): # Return the number of rows in the section return len(self.data) def tableview_cell_for_row(self, tableview, section, row): # Create and return a cell for the given section/row cell = self.cells[row] cell.text_label.text = self.data[row] # just showing we can access our class from the my_cell attr # we added. In this case I want to save the tableview attr cell.my_cell.tableview = tableview return cell def tableview_did_select(self, tableview, section, row): # Called when a row was selected. self.select_row(row) def select_row(self, sel_row): for cell in self.cells: cell.accessory_type = "" self.cells[sel_row].accessory_type = 'checkmark' self.sel_item = sel_row def get_table(items): tbl = ui.TableView(frame=(0, 0, 300, 400)) tbl.data_source = MyDataSource(items) tbl.delegate = tbl.data_source return tbl if __name__ == '__main__': v = get_table(['Ian', 'Fred', 'John', 'Paul', 'Gaew', 'Pete', 'Ole', 'Christian', 'Mary', 'Susan', 'Juile' 'Simone', 'Terry', 'Michael', 'James']) v.present(style='sheet', animated=False)
@Phuket2, could you make it a gist?
@Phuket2, it sure works, although your animations are so subtle, I hardly noticed them at first. :-)
@mikael , lol. But it's stress testing :)
Have to say, you can click around, drag around etc.. everything keeps working which is great
@omz - Hi. I also found using update_interval and update very straightforward and it worked without a hitch.
Sorry I did not give any feedback before now. I didn't know where to give feedback. Now, I see that this is the place to do that.
I had no issues with programming the beta version at all.
There is a bug in an example program, which also exist in the release (non-beta) version. The Calculator.py example in the "Widget" folder gives "v is not defined" - "v" should be "widget_view". When that change is made, the program works. (The other Calculator.py program in the "User Interface" folder has no issues).
I noticed the download in the App store has been changed to be named, "Pythonista 3", I presume to match the version of Python used. The main application screen still has "Pythonista 2 Documents".
Thanks for creating such a great App.
@technoway Thanks for your feedback.
Pythonista 3 is the current version. The folder "Pythonista 2 Documents" exists to access files from the old version. It only shows up if you had version 2 installed at some point, and you can turn it off in the settings.
If not running the Pythonista 3 beta, someone can also have an update method be periodically called as shown below.
This is not my idea, I just simplified an idea in the TimedRefreshView.py example program that I found at:
This program below, named say_random_digit.py, says a random digit from 0 to 9 every 5 seconds.
I had to change the data member "self.update_interval" to "self.updatex_interval" and the method "update" to "updatex" so as not to conflict with the names in the Pythonista 3 beta, which I am running.
I like having this functionality built into the class much more than having to implement it, so I look forward to the beta becoming the released product.
import ui import threading import speech from random import randint class TimedUpdateView(ui.View): def __init__(self): self.updatex_interval = 5 self.update_after_delay() def updatex(self): """ Say a random digit from 0 to 9 every 5 seconds. """ speech.say('%s' % (randint(0, 9))) def update_after_delay(self): """ This just method calls the updatex method periodically """ self.updatex() update_thread = threading.Timer(self.updatex_interval, self.update_after_delay).run() if __name__ == "__main__": v = TimedUpdateView() v.present('sheet')
I just updated my iPhone 6s to IOS 12.0.1, and the 'update' method is not longer called in my ui.View derived class.
I wonder if anyone else has encountered this?
@technoway, 12.0.1 on iPhone X, still works as previously.
You sure you have update_interval set?
Thank you mikael and JonB for the help.
You sure you have update_interval set?
Yes. I have:
self.update_interval = 1.0
in my derived class's
It likely I did something to break code that was working, but I don't see anything wrong. I'll debug my application - if this works for others, then I'm sure I introduced a bug somewhere. It's odd though, because I only made two very small changes and then updated the OS.
Next time, before I upgrade the OS, I'll test before to make sure it's the OS that breaks code, and not my code changes.