You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In trying to migrate to using the MVP (ltk.Model class) I have come across some issues.
I will lay them out here in the hopes that it might help in future development.
Using ltk 0.2.20 and jquery@3.7.1 with pyscript 2024.11.1 and pyodide
On a side note <shift-ctrl> doesn't seem to work anymore on rollover in pyodide but is working in mpy. Not sure if its something in the config below (pyodide)
To get around the potential memory leak in widget() I have successfully been using a global var. The MVP approach looks like it may supersede the need for widget()
I am also using a global var to make easy ref to the following reactive component. This is working well.
I am also using a global to deal with rippling updates, if I update two or more reactive variables. This is working well.
I.e.
g_RC_values = None # Hold reactive Model
g_inhibit_update = False # prevent rippling updates
class Reactive_component(ltk.Model):
"""
These sets of triplet variables update each other. Doing unit conversions
"""
# Length
Len_measure: str = "250yd"
Len_units: str = "yd"
Len_altlabel: str = "229m"
#
Sk_wraps: str = "20wpi"
Sk_units: str = "wpi"
Sk_altlabel: str = "7.9wpcm"
def changed(self, name, value):
"""
Called whenever a UI element is changed by user.
Also invoked for internal changes
- g_inhibit_update is used to prevent needless rippling
"""
global g_inhibit_update
print("Change requested:",name,value)
if not g_inhibit_update:
print("Changing:",name,value)
g_inhibit_update = True # inhibit rippling calls while we update
# Skein Length
if name in ["Len_measure", "Len_units"]:
vars = ["Len_measure", "Len_units", "Len_altlabel", ["250","yd","m"]]
if name == "Len_measure":
self.update_primary(value, *vars)
elif name == "Len_units":
self.recalc_units(value, *vars)
# Skein units
if name in ["Sk_wraps", "Sk_units"]:
vars = ["Sk_wraps", "Sk_units", "Sk_altlabel", ["20","wpi","wpcm"]]
if name == "Sk_wraps":
self.update_primary(value, *vars)
elif name == "Sk_units":
self.recalc_units(value, *vars)
# reset ripple updates
g_inhibit_update = False
else:
print(" -Skipping change")
if __name__ == "__main__":
g_RC_values = Reactive_component()
w = Widget(g_RC_values)
widget = w.create()
widget.appendTo(ltk.window.document.body)
The reason that I am using the names of the variables rather than the variables themselves is because of a side effect of ltk.Model using class variables.
Specifically:
I can set a class var just fine with self.Len_measure = "foo"
but if I pass that through a function header and try to assign it, then an instance variable is created and the class var is not updated.
E.g.
# in changed()
self.update_myvar(self.Len_measure)
# and
def update_myvar(self, myvar):
myvar = "Foo"
# The iv is created and updated but the classvar is unchanged
So to get around this I send the name of the classvar through (as in changed() above and do a more complicated operation like so:
def update_primary(self, value, primary_var, unit_var, alt_var,
defaults=["250","yd","m"]):
"""
Update the primary, units, alts on basis of input.
value can have units or not. Use as supplied and update alt.
Use defaults if missing or error
- Have to use self.__setattr__("varname", value) because its class var !
"""
num, unit = parse_units(value)
if num and num > -1:
# got a valid measure but maybe no units
if not unit:
# use on-screen units
units = self.__dict__[unit_var]
self.__setattr__(primary_var, f"{format_nice(num)}{units}")
if units in metric:
num = num * convert_factors[units]
self.__setattr__(alt_var, convert_imp(num, unit_swaps[units], self.Len_units=="in"))
else: # unit from measure
if unit not in convert_factors.keys():
unit = defaults[1] #"yd"
self.__setattr__(unit_var, unit)
usnum,_ = parse_to_US(value)
self.__setattr__(primary_var, convert_imp(usnum, unit))
self.__setattr__(alt_var, convert_imp(usnum, unit_swaps[unit]))
else: # not valid so set default
self.__setattr__(primary_var, defaults[0]+defaults[1])
self.__setattr__(unit_var, defaults[1])
self.__setattr__(alt_var, convert_imp(int(defaults[0]), defaults[2]))
In short:
I am using self.__setattr__(primary_var, newvalue) instead of primary_var = newvalue because its a classvar.
So I am ok with my solution and its as neat as I can work out how to make it (by using the expanding *vars parameter).
But I wonder if there is another way to architect it so it works differently.
Working example here:
MVP Feedback:
In trying to migrate to using the MVP (
ltk.Model
class) I have come across some issues.I will lay them out here in the hopes that it might help in future development.
On a side note
<shift-ctrl>
doesn't seem to work anymore on rollover in pyodide but is working in mpy. Not sure if its something in the config below (pyodide)To get around the potential memory leak in
widget()
I have successfully been using a global var. The MVP approach looks like it may supersede the need forwidget()
I am also using a global var to make easy ref to the following reactive component. This is working well.
I am also using a global to deal with rippling updates, if I update two or more reactive variables. This is working well.
I.e.
The reason that I am using the names of the variables rather than the variables themselves is because of a side effect of
ltk.Model
using class variables.Specifically:
self.Len_measure = "foo"
E.g.
So to get around this I send the name of the classvar through (as in
changed()
above and do a more complicated operation like so:In short:
I am using
self.__setattr__(primary_var, newvalue)
instead ofprimary_var = newvalue
because its a classvar.So I am ok with my solution and its as neat as I can work out how to make it (by using the expanding
*vars
parameter).But I wonder if there is another way to architect it so it works differently.
Working example here:
Cheers...
The text was updated successfully, but these errors were encountered: