Панель: обновление значения StaticText не отображается в пользовательском интерфейсе или не обновляется.Python

Программы на Python
Ответить
Anonymous
 Панель: обновление значения StaticText не отображается в пользовательском интерфейсе или не обновляется.

Сообщение Anonymous »

У меня возникла проблема с виджетом StaticText панели головиз. При обновлении его значения интерфейс/веб-страница не обновляется соответствующим образом. Только когда я нажимаю на перезагрузку страницы, она извлекает обновленное значение. Я сталкиваюсь с этой проблемой только в конкретном приложении. Предыстория: я искал дискретный ползунок диапазона, способный обрабатывать не только числа, но и строки. Ответ, данный здесь (https://discourse.holoviz.org/t/how-to- ... nce/2182/5), был самым близким, который я нашел. Изучая приведенное там решение, я создал другой DiscreteRangeSlider, взяв код из DiscreteSlider и RangeSlider. Кажется, работает нормально. В любом случае, есть одна проблема, которую я не понимаю. В следующих строках мне нужно было обойти: # following line is strangely not updating view, only on reload
# self._text.value = self.joined_label(*l_new)
# instead:
text = self._text.clone()
text.value = self.joined_label(*l_new)
self._composite[0] = self._text = text

Я обновлял метку только тогда, когда я нажимал на перезагрузку страницы, когда использовал вторую строку в приведенном выше фрагменте вместо обходного пути. Может ли кто-нибудь объяснить такое поведение и решить его?
Пожалуйста, посмотрите полный код ниже.
Спасибо, с уважением,
Джером
from bokeh.models import CustomJSTickFormatter
from panel.widgets.slider import _RangeSliderBase, IntRangeSlider
from panel.widgets import StaticText
from panel import Column
import numpy as np
import param
from typing import TYPE_CHECKING, Any, ClassVar
from collections.abc import Mapping
from bokeh.models.formatters import TickFormatter
from bokeh.models.widgets import RangeSlider as _BkRangeSlider
from bokeh.model import Model
from panel.viewable import Layoutable
from panel.widgets.base import CompositeWidget, Widget

class DiscreteRangeSlider(CompositeWidget, _RangeSliderBase):
"""
The RangeSlider widget allows selecting a floating-point range
using a slider with two handles.

Reference: https://panel.holoviz.org/reference/wid ... lider.html

:Example:

>>> RangeSlider(
... value=(1.0, 1.5), start=0.0, end=2.0, step=0.25, name="A tuple of floats"
... )
"""

value_throttled = param.Tuple(default=(None, None), length=2, allow_None=False, nested_refs=True, doc="""
The selected range as a tuple of floating point values. Updated when a handle is
released""")

start = param.Parameter(default=0, doc="""
The lower bound.""")

end = param.Parameter(default=1, doc="""
The upper bound.""")

options = param.ClassSelector(default=[], class_=(dict, list), doc="""
A list or dictionary of valid options.""")

formatter = param.String(default='%.3g', doc="""
A custom format string. Separate from format parameter since
formatting is applied in Python, not via the bokeh TickFormatter.""")

_rename: ClassVar[Mapping[str, str | None]] = {'formatter': None}

_source_transforms: ClassVar[Mapping[str, str | None]] = {
'value': None, 'value_throttled': None, 'options': None
}

_supports_embed: bool = True

_style_params: ClassVar[list[str]] = [
p for p in list(Layoutable.param) if p != 'name'
] + ['orientation']

_slider_style_params: ClassVar[list[str]] = [
'bar_color', 'direction', 'disabled', 'orientation'
]

_text_link = """
var labels = {labels}
target.text = labels[source.value]
"""

def __init__(self, **params):
self._syncing = False
super().__init__(**params)
if 'formatter' not in params and all(isinstance(v, (int, np.int_)) for v in self.values):
self.formatter = '%d'
if None not in self.values and self.options:
vb, ve = self.value
changed = False
if vb is None:
changed = True
vb = self.values[0]
if ve is None:
changed = True
ve = self.values[0]
if changed:
self.value = (vb, ve)
else:
for v in self.value:
if v not in self.values and not (v is None or self.options):
msg = f'Value {self.value_start} not a valid option, '
'ensure that the supplied value '
'is one of the declared options.'
raise ValueError(msg)

self._text = StaticText(
margin=(5, 0, 0, 5), styles={'white-space': 'nowrap'}
)
self._slider = None
self._composite = Column(self._text, self._slider)
self._update_options()
self.param.watch(self._update_options, ['options', 'formatter', 'name'])
self.param.watch(self._update_value, 'value')
self.param.watch(self._update_value, 'value_throttled')
self.param.watch(self._update_style, self._style_params)

def _update_options(self, *events):
values, labels = self.values, self.labels
if not self.options and self.value_start is None:
index_start = 0
label_start = (f'{self.name}: ' if self.name else '') + '-
' elif not self.options and self.value_end is None:
index_end = 0
label_end = (f'{self.name}: ' if self.name else '') + '-
' changed = False
if not (not self.options):
value_start, value_end = self.value
if value_start not in values:
changed = True
index_start = 0
value_start = values[0]
label_start = labels[index_start]
else:
index_start = values.index(value_start)
label_start = labels[index_start]
if value_end not in values:
changed = True
index_end = 0
value_end = values[0]
label_end = labels[index_end]
else:
index_end = values.index(value_end)
label_end = labels[index_end]
if changed:
self.value = (value_start, value_end)
index = (index_start, index_end)
label = self.joined_label(label_start, label_end)
disabled = True if len(values) in (0, 1) else self.disabled
end = 1 if disabled else len(self.options)-1

self._slider = IntRangeSlider(
start=0, end=end, value=index, tooltips=False,
show_value=False, margin=(0, 5, 5, 5),
_supports_embed=False, disabled=disabled,
**{p: getattr(self, p) for p in self._slider_style_params if p != 'disabled'}
)
self._update_style()
js_code = self._text_link.format(
labels='['+', '.join([repr(l) for l in labels])+']'
)
self._jslink = self._slider.jslink(self._text, code={'value': js_code})
self._slider.param.watch(self._sync_value, 'value')
self._slider.param.watch(self._sync_value, 'value_throttled')
self.param.watch(self._update_slider_params, self._slider_style_params)
self._text.value = label
self._composite[1] = self._slider

def _update_value(self, event):
"""
This will update the IntSlider (behind the scene)
based on changes to the DiscreteSlider (front).

_syncing options is to avoid infinite loop.

event.name is either value or value_throttled.
"""
values = self.values
labels = self.labels
vb, ve = getattr(self, event.name)
changed = False
if vb not in values:
changed = True
vb = values[0]
if ve not in values:
changed = True
ve = vb
if changed:
with param.edit_constant(self):
setattr(self, event.name, (vb, ve))
return
index = tuple([values.index(v) for v in getattr(self, event.name)])
if event.name == 'value':
l_new = [labels for i in index]
# following line is strangely not updating view, only on reload
# self._text.value = self.joined_label(*l_new)
# instead:
text = self._text.clone()
text.value = self.joined_label(*l_new)
self._composite[0] = self._text = text
if self._syncing:
return
try:
self._syncing = True
with param.edit_constant(self._slider):
setattr(self._slider, event.name, index)
finally:
self._syncing = False

def _update_style(self, *events):
style = {p: getattr(self, p) for p in self._style_params}
margin = style.pop('margin')
if isinstance(margin, tuple):
if len(margin) == 2:
t = b = margin[0]
r = l = margin[1]
else:
t, r, b, l = margin
else:
t = r = b = l = margin
text_margin = (t, 0, 0, l)
slider_margin = (0, r, b, l)
text_style = {k: v for k, v in style.items()
if k not in ('style', 'orientation')}
text_style['visible'] = self.show_value and text_style['visible']
self._text.param.update(margin=text_margin, **text_style)
self._slider.param.update(margin=slider_margin, **style)
if self.width:
style['width'] = self.width + l + r
col_style = {k: v for k, v in style.items()
if k != 'orientation'}
self._composite.param.update(**col_style)

def _update_slider_params(self, *events):
style = {e.name: e.new for e in events}
disabled = style.get('disabled', None)
if disabled is False:
if len(self.values) in (0, 1):
self.param.warning(
'A DiscreteSlider can only be disabled if it has more than 1 option.'
)
end = 1
del style['disabled']
else:
end = len(self.options) - 1
style['end'] = end
self._slider.param.update(**style)

def _sync_value(self, event):
"""
This will update the DiscreteSlider (front)
based on changes to the IntSlider (behind the scene).

_syncing options is to avoid infinite loop.

event.name is either value or value_throttled.
"""
if self._syncing:
return
try:
self._syncing = True
with param.edit_constant(self):
values = tuple([self.values[idx] for idx in event.new])
setattr(self, event.name, values)
except Exception as e:
print(e)
finally:
self._syncing = False

def _get_embed_state(self, root, values=None, max_opts=3):
model = self._composite[1]._models[root.ref['id']][0]
if values is None:
values = self.values
elif any(v not in self.values for v in values):
raise ValueError("Supplieed embed states were not found "
f"in the {type(self).__name__} widgets' values list.")
return self, model, values, lambda x: x.value, 'value', 'cb_obj.value'

@staticmethod
def joined_label(start, end):
return f"{start} ... {end}"

@property
def labels(self):
"""The list of labels to display"""
title = (self.name + ': ' if self.name else '')
if isinstance(self.options, dict):
return [title + (f'{o}
') for o in self.options] else:
return [title + ('%s
' % (o if isinstance(o, str) else (self.formatter % o)))
for o in self.options]

@property
def values(self):
"""The list of option values"""
return list(self.options.values()) if isinstance(self.options, dict) else self.options

if __name__ == "__main__":
discrete_range_slider = DiscreteRangeSlider(
options=[1, "a", "b", 40, 55]#, value=(1, 21)
)
discrete_range_slider.show()
print("Done")



Подробнее здесь: https://stackoverflow.com/questions/798 ... -interface
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»