diff --git a/lightweight_charts/abstract.py b/lightweight_charts/abstract.py index 89a617d..4e0bf89 100644 --- a/lightweight_charts/abstract.py +++ b/lightweight_charts/abstract.py @@ -14,7 +14,7 @@ from .topbar import TopBar from .util import ( BulkRunScript, Pane, Events, IDGen, as_enum, jbool, js_json, TIME, NUM, FLOAT, LINE_STYLE, MARKER_POSITION, MARKER_SHAPE, CROSSHAIR_MODE, MARKER_TYPE, - PRICE_SCALE_MODE, marker_position, marker_shape, js_data, is_vbt_indicator + PRICE_SCALE_MODE, marker_position, marker_shape, js_data, is_vbt_indicator, apply_opacity ) current_dir = os.path.dirname(os.path.abspath(__file__)) @@ -913,13 +913,15 @@ class AbstractChart(Candlestick, Pane): def create_histogram( self, name: str = '', color: str = None, price_line: bool = False, price_label: bool = True, - scale_margin_top: float = 0.0, scale_margin_bottom: float = 0.0 + scale_margin_top: float = 0.0, scale_margin_bottom: float = 0.0, opacity: float = None, ) -> Histogram: """ Creates and returns a Histogram object. """ if color is None: color = get_next_color() + if opacity is not None: + color = apply_opacity(color, opacity) return Histogram( self, name, color, price_line, price_label, scale_margin_top, scale_margin_bottom) diff --git a/lightweight_charts/helpers.py b/lightweight_charts/helpers.py index e3e14af..daa82b8 100644 --- a/lightweight_charts/helpers.py +++ b/lightweight_charts/helpers.py @@ -148,13 +148,15 @@ def chart(panes: list[Panel], sync=False, title='', size="m"): active_chart.markers_set(markers) for tup in pane.histogram: - series, name, color, _, _ = (tup + (None, None, None, None, None))[:5] + series, name, color, opacity, _ = (tup + (None, None, None, None, None))[:5] if series is None: continue #conditionally include color kwargs = {'name': name} if color is not None: - kwargs['color'] = color + kwargs['color'] = color + if opacity is not None: + kwargs['opacity'] = opacity tmp = active_chart.create_histogram(**kwargs) #green transparent "rgba(53, 94, 59, 0.6)" tmp.set(series) diff --git a/lightweight_charts/util.py b/lightweight_charts/util.py index edd01b3..1b9f2a0 100644 --- a/lightweight_charts/util.py +++ b/lightweight_charts/util.py @@ -5,7 +5,43 @@ from random import choices from typing import Literal, Union from numpy import isin import pandas as pd +import re +from matplotlib.colors import to_rgba +def apply_opacity(color, opacity): + """ + Converts any color format (named, hex, RGB, or RGBA) to RGBA format and applies a specified opacity. + + Parameters: + - color (str): The color in named, hex, RGB, or RGBA format. + - opacity (float): The opacity value to apply, ranging from 0.0 to 1.0. + + Returns: + - str: The color in 'rgba(r, g, b, opacity)' format with the specified opacity applied. + + Raises: + - ValueError: If the opacity is not within the range of 0 to 1. + """ + # Validate the opacity + if not (0 <= opacity <= 1): + raise ValueError("Opacity must be between 0 and 1") + + # Check if color is already in rgba format + rgba_regex = r'rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([0-9\.]+))?\)' + match = re.match(rgba_regex, color) + + if match: + # Color is in RGB or RGBA format + r, g, b = int(match.group(1)), int(match.group(2)), int(match.group(3)) + current_opacity = float(match.group(4)) if match.group(4) else 1 + # Apply the new opacity by multiplying with the current opacity if it exists + final_opacity = current_opacity * opacity + else: + # Color is a named color or hex; convert it using matplotlib + rgba = to_rgba(color) + r, g, b, _ = [int(255 * x) for x in rgba] + final_opacity = opacity # Directly use the given opacity + return f"rgba({r}, {g}, {b}, {final_opacity})" def is_vbt_indicator(variable): # Get the module path of the variable's type diff --git a/setup.py b/setup.py index 0863e60..e5db9f1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r', encoding='utf-8') as f: setup( name='lightweight_charts', - version='2.0.17', + version='2.0.18', packages=find_packages(), python_requires='>=3.8', install_requires=[