Skip to content

Commit 114b02b

Browse files
committed
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
1 parent a8a11ef commit 114b02b

11 files changed

Lines changed: 118 additions & 78 deletions

File tree

docs/source/reference/abstract_chart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Time can be given in the index rather than a column, and volume can be omitted i
2020
If `keep_drawings` is `True`, any drawings made using the `toolbox` will be redrawn with the new data. This is designed to be used when switching to a different timeframe of the same symbol.
2121
2222
`None` can also be given, which will erase all candle and volume data displayed on the chart.
23+
24+
You can also add columns to color the candles (https://tradingview.github.io/lightweight-charts/tutorials/customization/data-points)
2325
```
2426
2527

lightweight_charts/abstract.py

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,56 @@ def horizontal_line(self, price: NUM, color: str = 'rgb(122, 146, 202)', width:
316316
"""
317317
return HorizontalLine(self, price, color, width, style, text, axis_label_visible, func)
318318

319+
def trend_line(
320+
self,
321+
start_time: TIME,
322+
start_value: NUM,
323+
end_time: TIME,
324+
end_value: NUM,
325+
round: bool = False,
326+
line_color: str = '#1E80F0',
327+
width: int = 2,
328+
style: LINE_STYLE = 'solid',
329+
) -> TwoPointDrawing:
330+
return TrendLine(*locals().values())
331+
332+
def box(
333+
self,
334+
start_time: TIME,
335+
start_value: NUM,
336+
end_time: TIME,
337+
end_value: NUM,
338+
round: bool = False,
339+
color: str = '#1E80F0',
340+
fill_color: str = 'rgba(255, 255, 255, 0.2)',
341+
width: int = 2,
342+
style: LINE_STYLE = 'solid',
343+
) -> TwoPointDrawing:
344+
return Box(*locals().values())
345+
346+
def ray_line(
347+
self,
348+
start_time: TIME,
349+
value: NUM,
350+
round: bool = False,
351+
color: str = '#1E80F0',
352+
width: int = 2,
353+
style: LINE_STYLE = 'solid',
354+
text: str = ''
355+
) -> RayLine:
356+
# TODO
357+
return RayLine(*locals().values())
358+
359+
def vertical_line(
360+
self,
361+
time: TIME,
362+
color: str = '#1E80F0',
363+
width: int = 2,
364+
style: LINE_STYLE ='solid',
365+
text: str = ''
366+
) -> VerticalLine:
367+
return VerticalLine(*locals().values())
368+
319369
def clear_markers(self):
320370
"""
321371
Clears the markers displayed on the data.\n
@@ -494,7 +544,6 @@ def set(self, df: Optional[pd.DataFrame] = None, keep_drawings=False):
494544
df = self._df_datetime_format(df)
495545
self.candle_data = df.copy()
496546
self._last_bar = df.iloc[-1]
497-
498547
self.run_script(f'{self.id}.series.setData({js_data(df)})')
499548

500549
if 'volume' not in df:
@@ -690,56 +739,6 @@ def lines(self) -> List[Line]:
690739
"""
691740
return self._lines.copy()
692741

693-
def trend_line(
694-
self,
695-
start_time: TIME,
696-
start_value: NUM,
697-
end_time: TIME,
698-
end_value: NUM,
699-
round: bool = False,
700-
line_color: str = '#1E80F0',
701-
width: int = 2,
702-
style: LINE_STYLE = 'solid',
703-
) -> TwoPointDrawing:
704-
return TrendLine(*locals().values())
705-
706-
def box(
707-
self,
708-
start_time: TIME,
709-
start_value: NUM,
710-
end_time: TIME,
711-
end_value: NUM,
712-
round: bool = False,
713-
color: str = '#1E80F0',
714-
fill_color: str = 'rgba(255, 255, 255, 0.2)',
715-
width: int = 2,
716-
style: LINE_STYLE = 'solid',
717-
) -> TwoPointDrawing:
718-
return Box(*locals().values())
719-
720-
def ray_line(
721-
self,
722-
start_time: TIME,
723-
value: NUM,
724-
round: bool = False,
725-
color: str = '#1E80F0',
726-
width: int = 2,
727-
style: LINE_STYLE = 'solid',
728-
text: str = ''
729-
) -> RayLine:
730-
# TODO
731-
return RayLine(*locals().values())
732-
733-
def vertical_line(
734-
self,
735-
time: TIME,
736-
color: str = '#1E80F0',
737-
width: int = 2,
738-
style: LINE_STYLE ='solid',
739-
text: str = ''
740-
) -> VerticalLine:
741-
return VerticalLine(*locals().values())
742-
743742
def set_visible_range(self, start_time: TIME, end_time: TIME):
744743
self.run_script(f'''
745744
{self.id}.chart.timeScale().setVisibleRange({{
@@ -775,8 +774,14 @@ def layout(self, background_color: str = '#000000', text_color: Optional[str] =
775774
Global layout options for the chart.
776775
"""
777776
self.run_script(f"""
778-
document.getElementById('container').style.backgroundColor = '{background_color}'
779-
{self.id}.chart.applyOptions({{ layout: {js_json(locals())} }})""")
777+
document.getElementById('container').style.backgroundColor = '{background_color}'
778+
{self.id}.chart.applyOptions({{
779+
layout: {{
780+
background: {{color: "{background_color}"}},
781+
{f'textColor: "{text_color}",' if text_color else ''}
782+
{f'fontSize: {font_size},' if font_size else ''}
783+
{f'fontFamily: "{font_family}",' if font_family else ''}
784+
}}}})""")
780785

781786
def grid(self, vert_enabled: bool = True, horz_enabled: bool = True,
782787
color: str = 'rgba(29, 30, 38, 5)', style: LINE_STYLE = 'solid'):

lightweight_charts/js/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lightweight_charts/js/styles.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ body {
145145
color: var(--color);
146146
}
147147

148+
.topbar-textbox-input {
149+
background-color: var(--bg-color);
150+
color: var(--color);
151+
border: 1px solid var(--color);
152+
}
153+
148154
.topbar-menu {
149155
position: absolute;
150156
display: none;
@@ -214,7 +220,7 @@ body {
214220
pointer-events: none;
215221
top: 10px;
216222
left: 10px;
217-
display: flex;
223+
display: none;
218224
flex-direction: column;
219225
}
220226
.legend-toggle-switch {

lightweight_charts/topbar.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ async def async_wrapper(v):
2727

2828

2929
class TextWidget(Widget):
30-
def __init__(self, topbar, initial_text, align):
31-
super().__init__(topbar, value=initial_text)
32-
self.run_script(f'{self.id} = {topbar.id}.makeTextBoxWidget("{initial_text}", "{align}")')
30+
def __init__(self, topbar, initial_text, align, func):
31+
super().__init__(topbar, value=initial_text, func=func)
32+
33+
callback_name = f'"{self.id}"' if func else ''
34+
35+
self.run_script(f'{self.id} = {topbar.id}.makeTextBoxWidget("{initial_text}", "{align}", {callback_name})')
3336

3437
def set(self, string):
3538
self.value = string
@@ -115,9 +118,9 @@ def menu(self, name, options: tuple, default: str = None, separator: bool = True
115118
self._widgets[name] = MenuWidget(self, options, default if default else options[0], separator, align, func)
116119

117120
def textbox(self, name: str, initial_text: str = '',
118-
align: ALIGN = 'left'):
121+
align: ALIGN = 'left', func: callable = None):
119122
self._create()
120-
self._widgets[name] = TextWidget(self, initial_text, align)
123+
self._widgets[name] = TextWidget(self, initial_text, align, func)
121124

122125
def button(self, name, button_text: str, separator: bool = True,
123126
align: ALIGN = 'left', toggle: bool = False, func: callable = None):

lightweight_charts/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def parse_event_message(window, string):
3939
def js_data(data: Union[pd.DataFrame, pd.Series]):
4040
if isinstance(data, pd.DataFrame):
4141
d = data.to_dict(orient='records')
42-
filtered_records = [{k: v for k, v in record.items() if v is not None} for record in d]
42+
filtered_records = [{k: v for k, v in record.items() if v is not None and not pd.isna(v)} for record in d]
4343
else:
4444
d = data.to_dict()
4545
filtered_records = {k: v for k, v in d.items()}

lightweight_charts/widgets.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,15 @@ class JupyterChart(StaticLWC):
167167
def __init__(self, width: int = 800, height=350, inner_width=1, inner_height=1, scale_candles_only: bool = False, toolbox: bool = False):
168168
super().__init__(width, height, inner_width, inner_height, scale_candles_only, toolbox, False)
169169

170-
# this isn't available at the moment
171-
172-
raise ModuleNotFoundError('JupyterChart is unavailable in lightweight charts 2.0; please downgrade to an earlier version.')
173-
174170
self.run_script(f'''
175171
for (var i = 0; i < document.getElementsByClassName("tv-lightweight-charts").length; i++) {{
176172
var element = document.getElementsByClassName("tv-lightweight-charts")[i];
177173
element.style.overflow = "visible"
178174
}}
179-
document.getElementById('wrapper').style.overflow = 'hidden'
180-
document.getElementById('wrapper').style.borderRadius = '10px'
181-
document.getElementById('wrapper').style.width = '{self.width}px'
182-
document.getElementById('wrapper').style.height = '100%'
175+
document.getElementById('container').style.overflow = 'hidden'
176+
document.getElementById('container').style.borderRadius = '10px'
177+
document.getElementById('container').style.width = '{self.width}px'
178+
document.getElementById('container').style.height = '100%'
183179
''')
184180
self.run_script(f'{self.id}.chart.resize({width}, {height})')
185181

src/general/handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ export class Handler {
106106

107107
// TODO definitely a better way to do this
108108
if (this.scale.height === 0 || this.scale.width === 0) {
109-
this.legend.div.style.display = 'none'
109+
// if (this.legend.div.style.display == 'flex') this.legend.div.style.display = 'none'
110110
if (this.toolBox) {
111111
this.toolBox.div.style.display = 'none'
112112
}
113113
}
114114
else {
115-
this.legend.div.style.display = 'flex'
115+
// this.legend.div.style.display = 'flex'
116116
if (this.toolBox) {
117117
this.toolBox.div.style.display = 'flex'
118118
}

src/general/legend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class Legend {
3737
this.div = document.createElement('div');
3838
this.div.classList.add('legend');
3939
this.div.style.maxWidth = `${(handler.scale.width * 100) - 8}vw`
40+
this.div.style.display = 'none';
4041

4142
this.text = document.createElement('span')
4243
this.text.style.lineHeight = '1.8'

src/general/styles.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ body {
145145
color: var(--color);
146146
}
147147

148+
.topbar-textbox-input {
149+
background-color: var(--bg-color);
150+
color: var(--color);
151+
border: 1px solid var(--color);
152+
}
153+
148154
.topbar-menu {
149155
position: absolute;
150156
display: none;
@@ -214,7 +220,7 @@ body {
214220
pointer-events: none;
215221
top: 10px;
216222
left: 10px;
217-
display: flex;
223+
display: none;
218224
flex-direction: column;
219225
}
220226
.legend-toggle-switch {

0 commit comments

Comments
 (0)