drawings can be placed on any series, reimplement jupyter, implement editable text boxes, allow for whitespace data within charts if they are NaN values, fix legend bug

This commit is contained in:
louisnw
2024-06-01 13:21:45 +01:00
parent a8a11efcf6
commit 114b02bcbf
11 changed files with 118 additions and 78 deletions

View File

@ -316,6 +316,56 @@ class SeriesCommon(Pane):
"""
return HorizontalLine(self, price, color, width, style, text, axis_label_visible, func)
def trend_line(
self,
start_time: TIME,
start_value: NUM,
end_time: TIME,
end_value: NUM,
round: bool = False,
line_color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE = 'solid',
) -> TwoPointDrawing:
return TrendLine(*locals().values())
def box(
self,
start_time: TIME,
start_value: NUM,
end_time: TIME,
end_value: NUM,
round: bool = False,
color: str = '#1E80F0',
fill_color: str = 'rgba(255, 255, 255, 0.2)',
width: int = 2,
style: LINE_STYLE = 'solid',
) -> TwoPointDrawing:
return Box(*locals().values())
def ray_line(
self,
start_time: TIME,
value: NUM,
round: bool = False,
color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE = 'solid',
text: str = ''
) -> RayLine:
# TODO
return RayLine(*locals().values())
def vertical_line(
self,
time: TIME,
color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE ='solid',
text: str = ''
) -> VerticalLine:
return VerticalLine(*locals().values())
def clear_markers(self):
"""
Clears the markers displayed on the data.\n
@ -494,7 +544,6 @@ class Candlestick(SeriesCommon):
df = self._df_datetime_format(df)
self.candle_data = df.copy()
self._last_bar = df.iloc[-1]
self.run_script(f'{self.id}.series.setData({js_data(df)})')
if 'volume' not in df:
@ -690,56 +739,6 @@ class AbstractChart(Candlestick, Pane):
"""
return self._lines.copy()
def trend_line(
self,
start_time: TIME,
start_value: NUM,
end_time: TIME,
end_value: NUM,
round: bool = False,
line_color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE = 'solid',
) -> TwoPointDrawing:
return TrendLine(*locals().values())
def box(
self,
start_time: TIME,
start_value: NUM,
end_time: TIME,
end_value: NUM,
round: bool = False,
color: str = '#1E80F0',
fill_color: str = 'rgba(255, 255, 255, 0.2)',
width: int = 2,
style: LINE_STYLE = 'solid',
) -> TwoPointDrawing:
return Box(*locals().values())
def ray_line(
self,
start_time: TIME,
value: NUM,
round: bool = False,
color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE = 'solid',
text: str = ''
) -> RayLine:
# TODO
return RayLine(*locals().values())
def vertical_line(
self,
time: TIME,
color: str = '#1E80F0',
width: int = 2,
style: LINE_STYLE ='solid',
text: str = ''
) -> VerticalLine:
return VerticalLine(*locals().values())
def set_visible_range(self, start_time: TIME, end_time: TIME):
self.run_script(f'''
{self.id}.chart.timeScale().setVisibleRange({{
@ -775,8 +774,14 @@ class AbstractChart(Candlestick, Pane):
Global layout options for the chart.
"""
self.run_script(f"""
document.getElementById('container').style.backgroundColor = '{background_color}'
{self.id}.chart.applyOptions({{ layout: {js_json(locals())} }})""")
document.getElementById('container').style.backgroundColor = '{background_color}'
{self.id}.chart.applyOptions({{
layout: {{
background: {{color: "{background_color}"}},
{f'textColor: "{text_color}",' if text_color else ''}
{f'fontSize: {font_size},' if font_size else ''}
{f'fontFamily: "{font_family}",' if font_family else ''}
}}}})""")
def grid(self, vert_enabled: bool = True, horz_enabled: bool = True,
color: str = 'rgba(29, 30, 38, 5)', style: LINE_STYLE = 'solid'):

File diff suppressed because one or more lines are too long

View File

@ -145,6 +145,12 @@ body {
color: var(--color);
}
.topbar-textbox-input {
background-color: var(--bg-color);
color: var(--color);
border: 1px solid var(--color);
}
.topbar-menu {
position: absolute;
display: none;
@ -214,7 +220,7 @@ body {
pointer-events: none;
top: 10px;
left: 10px;
display: flex;
display: none;
flex-direction: column;
}
.legend-toggle-switch {

View File

@ -27,9 +27,12 @@ class Widget(Pane):
class TextWidget(Widget):
def __init__(self, topbar, initial_text, align):
super().__init__(topbar, value=initial_text)
self.run_script(f'{self.id} = {topbar.id}.makeTextBoxWidget("{initial_text}", "{align}")')
def __init__(self, topbar, initial_text, align, func):
super().__init__(topbar, value=initial_text, func=func)
callback_name = f'"{self.id}"' if func else ''
self.run_script(f'{self.id} = {topbar.id}.makeTextBoxWidget("{initial_text}", "{align}", {callback_name})')
def set(self, string):
self.value = string
@ -115,9 +118,9 @@ class TopBar(Pane):
self._widgets[name] = MenuWidget(self, options, default if default else options[0], separator, align, func)
def textbox(self, name: str, initial_text: str = '',
align: ALIGN = 'left'):
align: ALIGN = 'left', func: callable = None):
self._create()
self._widgets[name] = TextWidget(self, initial_text, align)
self._widgets[name] = TextWidget(self, initial_text, align, func)
def button(self, name, button_text: str, separator: bool = True,
align: ALIGN = 'left', toggle: bool = False, func: callable = None):

View File

@ -39,7 +39,7 @@ def parse_event_message(window, string):
def js_data(data: Union[pd.DataFrame, pd.Series]):
if isinstance(data, pd.DataFrame):
d = data.to_dict(orient='records')
filtered_records = [{k: v for k, v in record.items() if v is not None} for record in d]
filtered_records = [{k: v for k, v in record.items() if v is not None and not pd.isna(v)} for record in d]
else:
d = data.to_dict()
filtered_records = {k: v for k, v in d.items()}

View File

@ -167,19 +167,15 @@ class JupyterChart(StaticLWC):
def __init__(self, width: int = 800, height=350, inner_width=1, inner_height=1, scale_candles_only: bool = False, toolbox: bool = False):
super().__init__(width, height, inner_width, inner_height, scale_candles_only, toolbox, False)
# this isn't available at the moment
raise ModuleNotFoundError('JupyterChart is unavailable in lightweight charts 2.0; please downgrade to an earlier version.')
self.run_script(f'''
for (var i = 0; i < document.getElementsByClassName("tv-lightweight-charts").length; i++) {{
var element = document.getElementsByClassName("tv-lightweight-charts")[i];
element.style.overflow = "visible"
}}
document.getElementById('wrapper').style.overflow = 'hidden'
document.getElementById('wrapper').style.borderRadius = '10px'
document.getElementById('wrapper').style.width = '{self.width}px'
document.getElementById('wrapper').style.height = '100%'
document.getElementById('container').style.overflow = 'hidden'
document.getElementById('container').style.borderRadius = '10px'
document.getElementById('container').style.width = '{self.width}px'
document.getElementById('container').style.height = '100%'
''')
self.run_script(f'{self.id}.chart.resize({width}, {height})')