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.


    Close and re-present a ui.View?

    Pythonista
    ui.view.present view ui.view present
    4
    7
    5386
    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.
    • shinyformica
      shinyformica last edited by

      I have a small "options" UI which I want to display as a popover window when the user taps a certain place in the main interface.
      It's working the first time I tap, the popover shows up in the place specified by popover_location in the call to view.present(). But after dismissing it the first time, trying to show it again produces this traceback:

      Traceback (most recent call last):
        File "/private/var/mobile/Library/Mobile Documents.../Gestures.py", line 443, in _general_action
          action(data)
        File "/private/var/mobile/Library/Mobile Documents.../controls.py", line 1578, in _handleSingleTap
          picker.present(style="popover", animated=False, popover_location=(x,y))
      ValueError: View is already being presented or animation is in progress
      

      I removed everything from the ui.View subclass which defines the popover so it is an empty shell:

      class Popover(ui.View):
          def __init__(self, func, *args, **kws):
              views.PyUiView.__init__(self, *args, **kws)
              print "popover initialized"
      
          def will_close(self):
              print "popover closing"
      

      and I still get the traceback the second time I try to call present() from the code which handles displaying the view, or any time thereafter.
      Is calling present() on the same view more than once not allowed (I don't see anything indicating that in the docs)? I'm not explicitly calling view.close(), but I thought that once the popover was dismissed, that was automatic, which is why I do see will_close() being called. Even if I attach a callback which is called from will_close() and tries calling view.close() on the popover from the outside, it still doesn't prevent the traceback.

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

        Here's a bit of simple code which emulates what I'm doing in my more complicated code:

        import ui
        
        class Popover(ui.View):
            def __init__(self, *args, **kws):
                ui.View.__init__(self, *args, **kws)
                self.label = ui.Label()
                self.label.text = "Popover"
                self.add_subview(self.label)
        
            def layout(self):
                self.label.x = 0
                self.label.y = 0
                self.label.width = self.width
                self.label.height = self.height
        
            def will_close(self):
                print "popover closing"
        
        class MyView(ui.View):
            def __init__(self, *args, **kws):
                ui.View.__init__(self, *args, **kws)
                self.popover = None
                self.button = ui.Button()
                self.button.title = "Show Popover"
                self.button.action = self.showPopover
                self.add_subview(self.button)
        
            def layout(self):
                self.button.x = 0
                self.button.y = 0
                self.button.width = self.width
                self.button.height = self.height
        
            def showPopover(self, sender):
                print "show popover"
                if self.popover is None:
                    self.popover = Popover()
                self.popover.present(style="popover", popover_location=self.button.center)
                #self.popover.close()
        
        def run():
            v = MyView()
            v.present('full_screen')
        
        if __name__ == '__main__':
            run()
        

        First time I hit the "Show Popover" button, it shows. Second time, I get the traceback.

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

          I think there may be a bug in popover, I remember something like that..

          An alternative is to use a "shield view", which is a semi transparent view added as a subviews of the root view that covers the entire screen, then your settings is a subview of the shield. See for example
          https://github.com/polymerchm/ccMVC/blob/master/Shield.py
          This concept can be extended to detect touches on the shield, to dismiss it when user taps outside the settings view.

          Another option is to use my overlay class, which is somewhat more flexible than popover.https://github.com/jsbain/viewbrowser/blob/master/overlay.py

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

            https://github.com/omz/Pythonista-Issues/issues/114

            It is possible that calling close on_main_thread might do the job, after some delay.

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

              @JonB well that bug report definitely looks like it's exactly what I'm seeing. Unfortunately the comment thread isn't making much sense to me:

              In case someone else does stumble over that odd behaviour and this comment: The trick is to open the new instance first and then close the old one when dealing with an instance of a popover view. It is a bit counterintuitive, but it works for me.
              
              
              I just tested this in 311014 and it works if you wait reasonable amount of time. Closing it in favour of #450 and #392. When these two issues will be done, you will know when exactly you can present it again.
              

              How would one "open the new instance first, and then close the old one"? I don't really have control over when the popover is dismissed, and I don't want a new one to show until some time later, when the user taps the right place again. And the second comment...what is a "reasonable amount of time"? I've waited 30 seconds to a minute after closing the popover, and it still tracebacks.

              Calling close on_main_thread seems like a good bet...do you mean that I wait some small amount of time after the view is closed, and then call close again on_main_thread?

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

                I wonder whether you could just hide the popover and then show it again while repositioning the anchor: https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/1622324-sourcerect?language=objc

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

                  @shinyformica try this, it is the way omz wrote dialog: using wait_modal and resetting the view just after.

                  import ui
                  from objc_util import * 
                  
                  class Popover(ui.View):
                      def __init__(self, *args, **kws):
                          ui.View.__init__(self, *args, **kws)
                          self.width = 200
                          self.height = 50
                          self.label = ui.Label()
                          self.label.text = "test"
                          self.add_subview(self.label)
                          self.label.x = 0
                          self.label.y = 0
                          self.label.width = self.width
                          self.label.height = self.height
                          
                      def will_close(self):
                          print("popover closing")
                  
                  class MyView(ui.View):
                      def __init__(self, *args, **kws):
                          ui.View.__init__(self, *args, **kws)
                          self.popover = None
                          self.button = ui.ButtonItem()
                          self.button.title = "Show Popover"
                          self.button.action = self.showPopover
                          self.right_button_items = (self.button,)
                  
                      def showPopover(self, sender):
                          print("show popover")
                          if self.popover is None:
                              self.popover = Popover()
                          self.popover.present(style="popover", popover_location=(self.width-100,50), hide_title_bar=True)
                          self.popover.wait_modal()
                          self.popover = None
                  
                  def run():
                      v = MyView()
                      v.present('full_screen')
                  
                  if __name__ == '__main__':
                      run()```
                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post
                  Powered by NodeBB Forums | Contributors