Enhancements:

- Hotkeys can now use any character, and modifier keys are not required.
- Refactored the colors of the topbar, searchbox, toolbox, and widgets for consistency.
- Toolbox/interval refactoring and simplification.
- Histograms now show up in the legend, and will use shorthand notation by default (e.g 34k rather than 34000).
This commit is contained in:
louisnw
2023-09-24 15:09:45 +01:00
parent 43eab9854d
commit d43e7c24e7
9 changed files with 303 additions and 256 deletions

View File

@ -105,6 +105,23 @@ class Window:
''', run_last=True)
return subchart
def style(self, background_color: str = '#0c0d0f', hover_background_color: str = '#3c434c',
click_background_color: str = '#50565E',
active_background_color: str = 'rgba(0, 122, 255, 0.7)',
muted_background_color: str = 'rgba(0, 122, 255, 0.3)',
border_color: str = '#3C434C', color: str = '#d8d9db', active_color: str = '#ececed'):
self.run_script(f'''
window.pane = {{
backgroundColor: '{background_color}',
hoverBackgroundColor: '{hover_background_color}',
clickBackgroundColor: '{click_background_color}',
activeBackgroundColor: '{active_background_color}',
mutedBackgroundColor: '{muted_background_color}',
borderColor: '{border_color}',
color: '{color}',
activeColor: '{active_color}',
}}''')
class SeriesCommon(Pane):
def __init__(self, chart: 'AbstractChart', name: str = None):
@ -113,7 +130,7 @@ class SeriesCommon(Pane):
if hasattr(chart, '_interval'):
self._interval = chart._interval
else:
self._interval = pd.Timedelta(seconds=1)
self._interval = 1
self._last_bar = None
self.name = name
self.num_decimals = 2
@ -124,11 +141,36 @@ class SeriesCommon(Pane):
common_interval = df['time'].diff().value_counts()
if common_interval.empty:
return
self._interval = common_interval.index[0]
self._interval = common_interval.index[0].total_seconds()
units = [
pd.Timedelta(microseconds=df['time'].dt.microsecond.value_counts().index[0]),
pd.Timedelta(seconds=df['time'].dt.second.value_counts().index[0]),
pd.Timedelta(minutes=df['time'].dt.minute.value_counts().index[0]),
pd.Timedelta(hours=df['time'].dt.hour.value_counts().index[0]),
pd.Timedelta(days=df['time'].dt.day.value_counts().index[0]),
]
self.offset = 0
for value in units:
value = value.total_seconds()
if value == 0:
continue
elif value > self._interval:
break
self.offset = value
break
self.run_script(
f'if ({self.id}.toolBox) {self.id}.interval = {self._interval.total_seconds() * 1000}'
f'if ({self.id}.toolBox) {self.id}.interval = {self._interval}'
)
def _push_to_legend(self):
self.run_script(f'''
{self._chart.id}.lines.push({self.id})
if ('legend' in {self._chart.id}) {{
{self._chart.id}.legend.lines.push({self._chart.id}.legend.makeLineRow({self.id}))
}}''')
@staticmethod
def _format_labels(data, labels, index, exclude_lowercase):
def rename(la, mapper):
@ -162,8 +204,7 @@ class SeriesCommon(Pane):
def _single_datetime_format(self, arg):
if isinstance(arg, (str, int, float)) or not pd.api.types.is_datetime64_any_dtype(arg):
arg = pd.to_datetime(arg)
interval_seconds = self._interval.total_seconds()
arg = interval_seconds * (arg.timestamp() // interval_seconds)
arg = self._interval * (arg.timestamp() // self._interval)+self.offset
return arg
def set(self, df: pd.DataFrame = None, format_cols: bool = True):
@ -186,7 +227,6 @@ class SeriesCommon(Pane):
self._last_bar = series
self.run_script(f'{self.id}.series.update({js_data(series)})')
def marker(self, time: datetime = None, position: MARKER_POSITION = 'below',
shape: MARKER_SHAPE = 'arrow_up', color: str = '#2196F3', text: str = ''
) -> str:
@ -362,8 +402,7 @@ class VerticalSpan(Pane):
else:
self.run_script(f'''
{self.id}.setData(calculateTrendLine(
{start_time.timestamp()}, 1, {end_time.timestamp()}, 1,
{chart._interval.total_seconds() * 1000}, {chart.id}))
{start_time.timestamp()}, 1, {end_time.timestamp()}, 1, {chart.id}))
''')
def delete(self):
@ -401,13 +440,6 @@ class Line(SeriesCommon):
}}
null''')
def _push_to_legend(self):
self.run_script(f'''
{self._chart.id}.lines.push({self.id})
if ('legend' in {self._chart.id}) {{
{self._chart.id}.legend.lines.push({self._chart.id}.legend.makeLineRow({self.id}))
}}''')
def _set_trend(self, start_time, start_value, end_time, end_value, ray=False, round=False):
if round:
start_time = self._single_datetime_format(start_time)
@ -418,9 +450,7 @@ class Line(SeriesCommon):
self.run_script(f'''
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: false}})
{self.id}.series.setData(
calculateTrendLine({start_time}, {start_value},
{end_time}, {end_value},
{self._chart._interval.total_seconds() * 1000},
calculateTrendLine({start_time}, {start_value}, {end_time}, {end_value},
{self._chart.id}, {jbool(ray)}))
{self._chart.id}.chart.timeScale().applyOptions({{shiftVisibleRangeOnNewBar: true}})
''')
@ -451,7 +481,8 @@ class Histogram(SeriesCommon):
color: '{color}',
lastValueVisible: {jbool(price_label)},
priceLineVisible: {jbool(price_line)},
priceScaleId: '{self.id}'
priceScaleId: '{self.id}',
priceFormat: {{type: "volume"}},
}}),
markers: [],
horizontal_lines: [],
@ -681,7 +712,11 @@ class AbstractChart(Candlestick, Pane):
"""
Creates and returns a Histogram object.
"""
return Histogram(self, name, color, price_line, price_label, scale_margin_top, scale_margin_bottom)
histogram = Histogram(
self, name, color, price_line, price_label,
scale_margin_top, scale_margin_bottom)
histogram._push_to_legend()
return histogram
def lines(self) -> List[Line]:
"""