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.
Wait for view to close?
-
Hi there,
Pretty simple question: how can I close the currently presented view when I tap a button in that view and open a new one when it's closed?
I've tried numerous ways, but it keeps giving me "Value error: View is already being presented or animation in progress".
class Workflow(): def __init__(self): self.webpicker = ui.load_view("WebPicker") self.webpicker.delegate = self self.datetimepicker = ui.load_view("DatetimePicker") self.webpicker.present('sheet') def did_pick_url(self, url): print(url) self.webpicker.close() self.pick_date() def pick_date(self): self.datetimepicker.present('sheet') workflow = Workflow()
In the presented view (custom WebPicker class):
class WebPicker(ui.View): def did_load(self): self['btn_ok'].action = self.btn_ok_tapped def btn_ok_tapped(self, sender): self.delegate.did_pick_url("http://someurl.com")
-
@Absolutist I'd use
self.webpicker.wait_modal()
after it's present. Then you can move your pick date onto the next line and it will run after the webpicker view closes. -
The view closing code is fine, however when closing the sheet the "sliding away" animation takes a second or so to run, during that time the view is still being considered presented. The
View.wait_modal()
method can be used to handle this situation. If you modify your code as follows:class Workflow(): def __init__(self): self.webpicker = ui.load_view("WebPicker") self.webpicker.delegate = self self.datetimepicker = ui.load_view("DatetimePicker") self.webpicker.present('sheet') self.webpicker.wait_modal() # add this to wait until webpicker is off-screen self.datetimepicker.present('sheet') def did_pick_url(self, url): print(url) self.webpicker.close() # move self.pick_date() to __init__ workflow = Workflow()
When init is called,
webpicker
is presented and itswait_modal()
is called, which will wait forwebpicker
to go off-screen (i. e. be closed).did_pick_url()
gets called once OK is pressed,webpicker
is closed,wait_modal()
ends, anddatetimepicker
is presented. You can do this as often as you like to create a series of dialogs. -
Thanks for the replies, that seems to do the trick. However: what if I don't know what the next view will be on init? The next view is dependant on what button the user presses.
-
@Absolutist Then I would make an attribute at the beginning of init... something like self.NextView = None. Then in your button taps you can set it to 'datetimepicker' or whatever else... and use it after the wait modal to determine which view to present next.
If you want to structure it more, you can make a class for each kind of view, and then move to logic of which view to present next into a separate controller class along with the wait modals. That's what I do in my menu system...
class MenuController (object): def __init__(self): _m = Menu('') _m.wait_modal() if _m._row != '': _ml = MenuLevel(_m._row.lower()) _ml.wait_modal() if _ml._row == '*back': MenuController() else: etc...
-
Thanks for the reply Tony. I'll try that.
As for the Controller object: Do you have an example of your menu system somewhere? I'm interested to see how you've set this up.
An other solution could be to use a navigation controller and just push a the relevant view onto the stack. I haven't looked into Navigation controllers much, but I recon it can be done like that.
-
@Absolutist I don' t know if you've seen it but there' s a helpful post from @omz giving a nav view example that's not in the documentation... http://omz-forums.appspot.com/pythonista/post/5307966269947904
There's an early version of the menu system in the 'Any Workaround?' thread I think... I was having problems a bit like yours, but to do with getting the ui thread to finish before going on with the next step. This controller approach, and having the ui classes just set some value until wait modal is over is the best I've got so far... but launching another script after can still get you into the threading issue (whether by exec or silent notification).
I reused code from the Menu class in the MenuLevel class by copy and paste... but I'm thinking to make a BaseMenu class that both Menu and MenuLevel are subclasses of.
-
@Absolutist I've done that now... and it's given much better code reuse...
class Menu (BaseMenu): def Tailor(self): self.name = 'Menu' self.left_button_items = [] self.right_button_items = [] def MakeList(self): self.lItems = ({'title': 'Documents', 'image': 'ionicons-ios7-cloud-outline-32','accessory_type': 'disclosure_indicator'}), ({'title': 'Snippets', 'image': 'ionicons-ios7-pricetag-outline-32','accessory_type': 'disclosure_indicator'}), ({'title': 'Tools', 'image': 'ionicons-ios7-search-32', 'accessory_type': 'disclosure_indicator'}), ({'title': 'Projects', 'image': 'ionicons-ios7-star-outline-32', 'accessory_type': 'disclosure_indicator'}) class MenuLevel (BaseMenu): def MakeList(self): self.lItems = list() for entry in os.listdir(os.path.expanduser('~/Documents/' + self.name.lower() + '/')): if os.path.splitext(entry)[1] == '.py': self.lItems.append({'title': os.path.splitext(entry)[0]})