Exchanging data between multiple open forms

How do I exchange data (or access properties & events) between (open) forms?

Why? I have a constant always up form that interacts with variable forms below it controlled by a menu.

eg a drop-down choice on the constant form might cause a flurry of activity in the open variable (menu-selected) form.

How?

OK, two ways.

Generically, you can refer to a form’s parent like this :

self.parent.xxxxx

which will then allow you to access any component on the parent, including other forms.

Secondly, and probably better if you are always using the container form as the reference or top level form, is to use :

get_open_form()

Here’s @meredydd explaining its use :

but in brief, that function refers to the form that was last opened with open_form(), either explicitly called or implied by the default form for the project.

1 Like

Despite all the posts here I’m still having trouble accessing components & data on other forms.

I have a NavForm which has two panels (one above the other).
On the panels are:

  1. A constant form
  2. A variable form selected by the sidebar.

Right now, I can’t access the NavForm from the child forms.

  1. If I use .parent I get the panel.
  2. If I use .parent.parent it generates a bug.
  3. If I use get_open_form() I get None.
  4. If I try and set a global variable in the parent form class and set it to self it generates a bug.

Help! @david.wylie

EDIT
I can see why (3) & (4) doesn’t work, it’s because I was calling in INIT & the form isn’t referenceable until SHOW. And it never gets to SHOW because I have bugs in the child form created in INIT (due to trying to reference the parent form!). And I can’t use (4) because setting a global variable containing the reference to the top form in the top form class still isn’t accessible to child forms because of (2) etc.

@paul.k.pallaghy

It looks like you’re getting the hang of it; you’re exactly right about why get_open_form() and .parent don’t work during __init__().

Perhaps the simplest thing you could do would be to pass a reference to the parent form into the constructor for the variable form when you create it (ie as an argument to __init__()).

Otherwise, your global variable is probably the way to go. What do you mean it “generates a bug” when you try to set a global variable to self from the parent class’s __init__ function? That should do exactly the right thing…

(One suggestion: If you’re going to do the global-variable thing, make a new Module to hold that variable. That way it can be imported from several different forms at once without creating a circular import, which Python doesn’t like.)

1 Like

Thanks @meredydd.

So what is the way to pass a ref to the parent form thru init?
init(self, self)??
OR
self.page_panel.add_component(PageForm(), self)??

As far as setting a global variable, I did it after the class line:
navform_GV = self
and that generates a bug:
NameError: name ‘self’ is not defined at NavForm, line 17

Also, how can I access ‘global’ variables from a child form if I don’t have a reference to the parent form ALREADY?
I mean, to access GVs of a form itself we need to do: self.xxxxx.
So HOW would I access GVs on a parent form from a child form?

I’m still digesting the module proposal.

EDIT: OK, in the parent form show event handler I can now set the global variable to self.
BUT how can I access it from a chid form? Remember, .parent just gives me the panel. I really don’t understand why .parent.parent doesn’t work . .

You know what, an example is probably best here!

Here is a multi-page app, where you navigate between pages by clicking on links in the sidebar. I’ve defined a Globals module to hold a reference to the top-level form, and then the inner pages (in this case, Form2 can use that module to get hold of the top-level form).

https://anvil.works/ide#clone:7OL6GABL7TRBUM3Q=NWD2EAWDT3I3UHII5C6FLFFG

2 Likes

Hi,

I was facing the same problem, I have a main form which opens sub forms (on the same view) on click, and found a solution below.

I am able to pass data between the ‘Manpower’ sub form to main form by calling the .item() function of the sub form.

And finally I can combine the data dictionary data and pass it to my data table using a server module.

from .ManpowerTab import ManpowerTab

# reuse tab forms
manpower_panel = ManpowerTab()

class MainTabs(MainTabsTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
...
    # Nav-link functions
  def manpower_link_click(self, **event_args):
    """This method is called when the link is clicked"""
    get_open_form().content_panel_2.clear()
    get_open_form().content_panel_2.add_component(manpower_panel)
    # Set initial values for manpower tab
    manpower_panel.item = {<some data>}

  def submit_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    dict_formdata = {**self.item, **manpower_panel.item}
    alert(f"Submitted! Data: {dict_formdata}")
...

Hope it helps.

1 Like

A pattern from the AI community is sometimes used to good effect for such situations: the Whiteboard pattern, for collaboration between experts. All the “experts” (forms) share a common “whiteboard” (Python variable, usually a list or a dict). Each can see what the others have “written”, and add/remove/update their own “writings”, for the others to see and use.

This pattern fits well with Anvil’s Data Bindings technique, as the visual components can be tied directly to entries on the “whiteboard”.

3 Likes

THANK YOU!! After trying i’ll let u know

Hello, Thank you for letting me know this. can u explain further about it??

The pattern is described here as the Blackboard Pattern. But it was named at a time when chalkboards were common, and whiteboards had not yet been invented. (Whiteboards are a lot cleaner, with no chalk dust to breathe and clean up!)

On the scale we’re talking about here, the “whiteboard” would be a global variable, in some module outside of your forms, but accessible to all of them. Typically, the variable contains a Python dict (or other structured object), where each form places information for other forms to read, and to “reason about”.

The advantage: It lets your forms collect information and coordinate, without each one having to know anything about the others’ internal details, such as where to find that information within the other form(s), what the other form(s) call it, and so on.

Thus, you are free to completely restructure any form, rename any variable inside a form, without impacting the other forms. Only the name of the “whiteboard”, and its contents, needs to be (relatively) stable.

Where needed, this technique can be very helpful. But one can also overdo it.

This works best when the variable collects information, from several sources, about a single context or situation, with well-defined scope and limits. That is, it has a limited number of “contained” variables, and they’re all different aspects of a single topic. They’re cohesive.

“Grab-bag” or “catch-all” variables, which try to know everything about everything, can quickly turn a well-structured program into a real mess.

2 Likes