Utils
Client and server-side utility functions.
import_module
Very similar to python’s importlib.import_module
implementation.
Use in the same way.
Takes two arguments, the name
to import, and an optional package
.
The ‘package’ argument is required when performing a relative import. It specifies the package to use as the anchor point from which to resolve the relative import to an absolute import.
Example implementation:
from anvil_extras.utils import import_module
from functools import cache
class MainForm(MainFormTemplate):
...
def link_click(self, sender, **event_args):
self.load_form(sender.tag)
@cache
def get_form(self, form_name):
form_module = import_module(f".{form_name}", __package__)
form_cls = getattr(form_module, form_name)
return form_cls()
def load_form(self, form_name):
form = self.get_form(form_name)
self.content_panel.clear()
self.content_panel.add_component(form)
Timing
timed decorator
Import the timed
decorator and apply it to a function:
import anvil.server
from anvil_extras.utils import timed
@anvil.server.callable
@timed
def target_function(args, **kwargs):
print("hello world")
The decorator takes a logging.Logger
instance as one of its optional keyword arguments.
On both the server and the client this can be a Logger from the anvil_extras logging module.
On the server, this can be from the Python logging
module.
The decorator also takes an optional level
keyword argument which must be one of the standard levels from the logging module.
When no argument is passed, the default level is logging.INFO
.
The default logger is an anvil_extras Logger instance, which will log to stdout. Messages will appear in your App’s logs and the IDE console. You can, however, create your own logger and pass that instead if you need more sophisticated behaviour:
import logging
from anvil_extras.utils import timed
my_logger = logging.getLogger(__name__)
@timed(logger=my_logger, level=logging.DEBUG)
def target_function(args, **kwargs):
...
from anvil_extras.utils import timed, logging
my_logger = logging.Logger(name="Timing", format={"{name}: {time:%H:%M:%S}-{msg}"}, level=logging.DEBUG)
@timed(logger=my_logger, level=logging.DEBUG)
def target_function(args, **kwargs):
...
Auto-Refresh
Whenever you set a form’s item
attribute, the form’s refresh_data_bindings
method is called automatically.
The utils
module includes a decorator you can add to a form’s class so that refresh_data_bindings
is called whenever item
changes at all.
To use it, import the decorator and apply it to the class for a form:
from anvil_extras.utils import auto_refreshing
from ._anvil_designer import MyFormTemplate
@auto_refreshing
class MyForm(MyFormTemplate):
def __init__(self, **properties):
self.init_components(**properties)
The form’s item
property will be proxied.
If your original item was a dictionary, whenever a value of the proxied item changes,
the form’s refresh_data_bindings
method will be called.
Note that the proxied item will make changes to the original item
.
It shouldn’t matter what the original item
is. It could be a dictionary, app_table Row or some other obsucre object.
Wait for writeback
Using wait_for_writeback
as a decorator prevents a function from executing before any queued writebacks have been completed.
This is particularly useful if you have a form with text fields. Race conditions can occur between a text field writing back to an item and a click event that uses the item.
To use wait_for_writeback
, import the decorator and apply it to a function, usually an event_handler:
from anvil_extras.utils import wait_for_writeback
class MyForm(MyFormTemplate):
...
@wait_for_writeback
def button_1_click(self, **event_args):
anvil.server.call("save_item", self.item)
The click event will now only be called after all active writebacks have finished executing.
Correct Canvas Resolution
Canvas elements can appear blurry on retina screens.
This helper function ensures a canvas element appears sharp.
It should be called inside the canvas reset
event.
from anvil_extras.utils import correct_canvas_resolution
class MyForm(MyFormTemplate):
...
def canvas_reset(self, **event_args):
c = self.canvas
correct_canvas_resolution(c)
...