Form_show() and anvil.server.call()

When you call anvil.server.call(), it produces a short animation, which overlays the form. When the animation completes, it appears that a form_show() event is triggered.

Is that correct?

The form_show event isn’t directly linked to anvil.server.call(). But I can see why you thought that:

If you’re doing an open_form() (or showing a startup form), you’ll observe that the form_show event doesn’t trigger until after your __init__ function has completed. This is because having Form1 as your startup form is equivalent to having a startup module with the code:

open_form('Form1')

which in turn is the same as:

f = Form1()  # <-- __init__ runs here
open_form(f) # <-- form appears on the page here (and "form_show" triggers)

So you can see, the form_show event doesn’t trigger until after Form1's __init__ has finished executing – including any server calls in that function.

(This is also why calling open_form() from your form’s __init__() function often doesn’t work as you’d expect! You open the new form, sure, but it’s immediately replaced as soon as the __init__() function returns. This is why you’ll sometimes see people calling open_form() from the form_show event. Although if you’re doing that, you should probably consider using a startup module instead.)

Thanks, @meredydd. I’d be delighted to use a startup module. Unfortunately, it’s a big, pre-Package app, so that option doesn’t appear to be available. In this test-bed app, I’ve since switched to using a dedicated startup form, that switches to the “real” main form (or a “cannot log in” form) as appropriate, in its own form_show().

The problematic form_show() is actually part of a status-message component I’m building. I’ve put this component on the main form. If, having logged in using the main form, I log out, and log in again, then the component’s refresh() method gets called over and over. It’s called from its own 20-second timer, and from its own form_show(). Obviously, it’s not the timer, as the refreshes come much faster than that.

refresh() originally did an anvil.server.call(). When I switched to anvil.server.call_s() instead, the endless loop vanished.

I’m developing this component in a separate, test-bed app. I’ve done many edits since then, but I can probably find an example in the git history, if you’d like a clone to try out.

Hrm, that does sound strange – yes please for that clone link!

Also, you should join us in the package world. The water’s lovely :wink:
(And, more to the point, enabling packages in your app doesn’t break your code – all your imports will keep working, thanks to the Python 2 relative import rules! You can move your code into packages module by module, at your own pace.)

If you get the chance, please let me know when you’ve successfully cloned it. If I do anything to the app in the meantime, you won’t get the clone you need.

Now cloned, thank you! Can you tell me what I need to do to reproduce the failure?

I just checked it, and arrgh! It’s not happening anymore!

I’ll work backwards until I confirm it – which I should have done to start with, I apologize. Then I’ll email you a link.

To reproduce: Repeat this 2 or 3 times: Log in, log out. During the log-in prompt, the animation should start running continuously. It keeps running continuously after successful login.

After further investigation, this proves to be entirely my own fault, with logic spread over 3 separate functions. My apologies to everyone who spent time on it. I’m documenting my findings here so that others can see what I did wrong (and not repeat it!).

In a nutshell, I had inadvertently created a feedback loop, with a very subtle trigger.

  1. Creating a custom component on a form triggered the component’s form_show event
  2. Which triggered a function call
  3. Which (under the right conditions) caused the component to become visible (self.visible = True)
  4. Which triggered the form_show event again.
  5. Because the conditions were still true, this again (indirectly) caused the assignment: self.visible = True

I figured that since self.visible was already True, the assignment at 5 would not trigger any further actions. After all, nothing was actually changing.

I was wrong. The assignment, itself, triggers the form_show event, even when it leaves self.visible unchanged.

The solution:

  1. check to see whether self.visible actually needs changing, and
  2. execute the assignment only if necessary.
    e.g.,
        if self.visible != should_be_visible:
            self.visible = should_be_visible

So, this was a bug in my implementation, not a bug in Anvil.