Greetings,
I have the following code which works under Panel 0.14.2 and executes under 1.2.3 but does not produce a correct result. I was hoping someone on the forum maybe able to explain why.
The code under 0.14.2 creates two rows (A and B) with each row having a FloatInput and controls (up and down) which alter the order of the display of the two rows. Up arrow moves the row up, down arrow moves the row down. This works fine under 0.14.2. When Up arrow of layer A is pressed, layer A and its FloatInput move to the top (correct layer A value maintained 7.9). Under 1.2.3 when either using the up or down arrows results in the layer FloatInput values in both layers being modified (swaped).
For example, I have included screen shots which show the different outcomes when using the different version of Panel. On the left Panel 0.14.2 (correct) on the right Panel 1.2.3 (incorrect) values for FloatInput after layer two (A) up button pressed.
I have attached the minimal example code below.
Can anyone please explain how the code needs to be modified to get the version working correctly under Panel 1.2.3
Regards
mike
import panel as pn
import param
import logging
log = logging.getLogger('test_logger')
logging.basicConfig(format='%(lineno)d - %(name)s - %(asctime)s - %(levelname)s '
'- %(message)s', level=logging.DEBUG)
log.setLevel(logging.DEBUG)
pn.extension()
class LayerModel(param.Parameterized):
scale_min = param.Number(0, instantiate=True)
tname = param.String(default='none', instantiate=True)
def handle_set_scale_min(self, *events):
for event in events:
if event.name == 'value':
self.scale_min = float(event.obj.value)
log.info('handle_set_scale_min: %s' % self.scale_min)
log.debug(f"(event: {event.name} changed from {event.old} to {event.new})")
self.param.trigger('scale_min')
class LayerListItem(param.Parameterized):
delete = param.Event()
move_layer_up = param.Event()
move_layer_down = param.Event()
class LayerList(param.Parameterized):
def add_layer(self, layer_model):
log.info('LayerList.add_layer')
new_item = LayerListItem(
parent=self,
layer_model=layer_model,
)
new_item.param.watch(self._delete_layer, ['delete'])
new_item.param.watch(self._move_layer_up, ['move_layer_up'])
new_item.param.watch(self._move_layer_down, ['move_layer_down'])
self.layer_list.append(new_item)
log.debug(f'New item {new_item}')
self.param.trigger('layer_list')
def _delete_layer(self, *events):
for event in events:
if event.name == 'delete':
list_index = self.layer_list.index(event.obj)
log.debug('DELETE layer at index: %s' % list_index)
layer_item = self.layer_list[list_index]
self.layer_list.remove(event.obj)
self.param.trigger('layer_list')
def _move_layer_up(self, *events):
x = 0
for event in events:
if event.name == 'move_layer_up':
# no move
if len(self.layer_list) == 1:
return
layer_index = self.layer_list.index(event.obj)
if layer_index < len(self.layer_list) - 1:
self.layer_list.pop(layer_index)
self.layer_list.insert(layer_index+1, event.obj)
self.param.trigger('layer_list')
def _move_layer_down(self, *events):
for event in events:
if event.name == 'move_layer_down':
# do move
if len(self.layer_list) == 1:
return
layer_index = self.layer_list.index(event.obj)
if layer_index > 0:
self.layer_list.pop(layer_index)
self.layer_list.insert(layer_index-1, event.obj)
self.param.trigger('layer_list')
layer_list = param.List([], item_type=LayerListItem, instantiate=True)
delete_layer = param.Action(_delete_layer)
move_layer_up = param.Action(_move_layer_up)
move_layer_down = param.Action(_move_layer_down)
class LayerLegendView():
def __init__(self, layer_item):
self.layer_model = layer_item.layer_model
self.title = pn.pane.HTML(
object='Doc ' + self.layer_model.tname,
styles={'font-weight': 'bold'}
)
self.scale_min_input = pn.widgets.FloatInput(
value=self.layer_model.scale_min,
name= self.layer_model.tname + "_min",
max_width=120,
sizing_mode='stretch_width'
)
log.debug(f'Min input {self.scale_min_input}')
test = self.scale_min_input.param.watch(self.layer_model.handle_set_scale_min, 'value')
log.debug(f'Watcher {test}')
def z_up_button(item):
return pn.Param(
item,
widgets={
'move_layer_up': {
'widget_type': pn.widgets.Button,
'name': '▲',
}})
def z_down_button(item):
return pn.Param(item,
widgets={
'move_layer_down': {
'widget_type': pn.widgets.Button,
'name': '▼',
}}
)
def remove_button(item):
return pn.Param(
item,
widgets={
'delete': {
'widget_type': pn.widgets.Button,
'name': '✖',
}})
self.control_pane = pn.Column(
objects=[
z_up_button(layer_item.param.move_layer_up),
remove_button(layer_item.param.delete),
z_down_button(layer_item.param.move_layer_down),
],
)
legend_margin = (0, 5, 0, 5)
self.legend_pane = pn.Column(
objects=[
pn.Row(
self.title,
sizing_mode='stretch_width',
margin=(10, 5, 0, 5)
),
pn.Row(
pn.Column(self.scale_min_input, width=80),
margin=legend_margin
),
],
sizing_mode='stretch_width',
styles=dict(border='1px dashed black'),
)
def ui(self):
x = 0
return pn.Column(
'',
pn.Row(
self.legend_pane,
self.control_pane
),
)
def ui(layer_list_model):
@pn.depends(layer_list_model.param.layer_list)
def render_list(item_list):
def get_legend(layer_item):
legend = LayerLegendView(layer_item)
return legend.ui()
col = pn.Column(sizing_mode='stretch_width')
for ix, layer_item in enumerate(reversed(item_list)):
log.debug(f'Current items in tem list {ix} layer {layer_item}')
for layer_item in reversed(item_list):
col.append(pn.Row(
get_legend(layer_item),
sizing_mode='stretch_width'
))
log.debug(f'Column {col}')
return col
rv = pn.Column(render_list, loading_indicator=True)
log.debug(f'Panel {rv}')
return rv
a = LayerModel(tname='A', scale_min=7.9, instantiate=True)
b = LayerModel(tname='B', scale_min=8.0, instantiate=True)
z = LayerList()
z.add_layer(a)
z.add_layer(b)
first_app = pn.Column(ui(z))
first_app.servable()