Ability to save drawings

- Added `toolbox` to the common methods.
- `toolbox.save_drawings_under` can save drawings under a specific `topbar` widget. eg `chart.toolbox.save_drawings_under(chart.topbar[’symbol’]`)
- `toolbox.load_drawings` will load and display drawings stored under the tag/string given.
- `toolbox.export_drawings` will export all currently saved drawings to the given file path.
- `toolbox.import_drawings` will import the drawings stored at the given file path.

Fixes/Enhancements:
- `update` methods are no longer case sensitive.
- HorizontalLines no longer throw cyclic structure errors in the web console.
- `API` methods can now be normal methods or coroutines.
This commit is contained in:
louisnw
2023-07-20 21:52:17 +01:00
parent b2ceae59b7
commit 527130e618
10 changed files with 166 additions and 55 deletions

View File

@ -1,3 +1,4 @@
import json
import os
from datetime import timedelta, datetime
from base64 import b64decode
@ -59,23 +60,35 @@ class SeriesCommon:
}}
''')
@staticmethod
def _rename(data, mapper, is_dataframe):
if is_dataframe:
data.columns = [mapper[key] if key in mapper else key for key in data.columns]
else:
data.index = [mapper[key] if key in mapper else key for key in data.index]
def _df_datetime_format(self, df: pd.DataFrame, exclude_lowercase=None):
df = df.copy()
df.columns = df.columns.str.lower()
if exclude_lowercase:
df[exclude_lowercase] = df[exclude_lowercase.lower()]
if 'date' not in df.columns and 'time' not in df.columns:
df.columns = df.columns.str.lower()
if exclude_lowercase:
df[exclude_lowercase] = df[exclude_lowercase.lower()]
if 'date' in df.columns:
df = df.rename(columns={'date': 'time'})
self._rename(df, {'date': 'time'}, True)
elif 'time' not in df.columns:
df['time'] = df.index
self._set_interval(df)
df['time'] = self._datetime_format(df['time'])
return df
def _series_datetime_format(self, series):
def _series_datetime_format(self, series: pd.Series, exclude_lowercase=None):
series = series.copy()
if 'date' in series.keys():
series = series.rename({'date': 'time'})
if 'date' not in series.index and 'time' not in series.index:
series.index = series.index.str.lower()
if exclude_lowercase:
self._rename(series, {exclude_lowercase.lower(): exclude_lowercase}, False)
if 'date' in series.index:
self._rename(series, {'date': 'time'}, False)
series['time'] = self._datetime_format(series['time'])
return series
@ -353,6 +366,47 @@ class TopBar:
return widget
class ToolBox:
def __init__(self, chart):
self.run_script = chart.run_script
self.id = chart.id
self._return_q = chart._return_q
self._saved_drawings = {}
def save_drawings_under(self, widget: Widget):
"""
Drawings made on charts will be saved under the widget given. eg `chart.toolbox.save_drawings_under(chart.topbar['symbol'])`.
"""
self._save_under = widget
def load_drawings(self, tag: str):
"""
Loads and displays the drawings on the chart stored under the tag given.
"""
if not self._saved_drawings.get(tag):
return
self.run_script(f'if ("toolBox" in {self.id}) {self.id}.toolBox.loadDrawings({json.dumps(self._saved_drawings[tag])})')
def import_drawings(self, file_path):
"""
Imports a list of drawings stored at the given file path.
"""
with open(file_path, 'r') as f:
json_data = json.load(f)
self._saved_drawings = json_data
def export_drawings(self, file_path):
"""
Exports the current list of drawings to the given file path.
"""
with open(file_path, 'w+') as f:
json.dump(self._saved_drawings, f)
def _save_drawings(self, drawings):
self._saved_drawings[self._save_under.value] = json.loads(drawings)
class LWC(SeriesCommon):
def __init__(self, volume_enabled: bool = True, inner_width: float = 1.0, inner_height: float = 1.0, dynamic_loading: bool = False,
scale_candles_only: bool = False, topbar: bool = False, searchbox: bool = False, toolbox: bool = False,
@ -394,6 +448,7 @@ class LWC(SeriesCommon):
if toolbox:
self.run_script(JS['toolbox'])
self.run_script(f'{self.id}.toolBox = new ToolBox({self.id})')
self.toolbox: ToolBox = ToolBox(self)
if not topbar and not searchbox:
return
self.run_script(JS['callback'])