Refactoring/Enhancements/Fixes
Breaking Changes:
- Removed the `api` parameter; callbacks no longer need to be in a specific class.
- Topbar callbacks now take a chart as an argument (see updated callback examples)
- Removed the `topbar` parameter from chart declaration. The Topbar will be automatically created upon declaration of a topbar widget.
- Removed the `searchbox` parameter from charts. It will be created upon subscribing to it in `chart.events`.
- Removed `dynamic_loading`.
- Removed ‘volume_enabled’ parameter. Volume will be enabled if the volumn column is present in the dataframe.
- Widgets’ `func` parameter is now declared last.
- Switchers take a tuple of options rather than a variable number of arguments.
- `add_hotkey` renamed to `hotkey`
- Horizontal lines now take a `func` argument rather than `interactive`. This event will emit the Line object that was moved.
- Removed the `name` parameter from `line.set`. Line object names are now declared upon creation.
Enhancements:
- Added the `button` widget to the Topbar.
- Added the color picker to the drawing context menu.
- Charts now have a `candle_data` method, which returns the current data displayed on the chart as a DataFrame.
- Fixed callbacks are now located in the `chart.events` object:
- search (e.g `chart.events.search += on_search`)
- new_bar
- range_change
- Added volume to the legend
- Drawings can now be accessed through `chart.toolbox.drawings`
- added the `style` and `name` parameters to `create_line`
Bug Fixes:
- Fixed a bug causing new charts not to load after `exit` was called.
- Refactored rayline placement to ensure they do not move the visible range.
- Fixed a bug causing the visible range to shift when trendlines are moved past the final candlestick.
- Fixed a bug preventing trendlines and raylines on irregular timeframes.
- Fixed a bug causing the legend to prevent mouse input into the chart.
This commit is contained in:
@ -14,10 +14,8 @@ class CallbackAPI:
|
||||
self.emit_q, self.return_q = emit_queue, return_queue
|
||||
|
||||
def callback(self, message: str):
|
||||
messages = message.split('_~_')
|
||||
name, chart_id = messages[:2]
|
||||
args = messages[2:]
|
||||
self.return_q.put(*args) if name == 'return' else self.emit_q.put((name, chart_id, *args))
|
||||
name, args = message.split('_~_')
|
||||
self.return_q.put(*args) if name == 'return' else self.emit_q.put((name, args.split(';;;')))
|
||||
|
||||
|
||||
class PyWV:
|
||||
@ -60,22 +58,21 @@ class PyWV:
|
||||
|
||||
|
||||
class Chart(LWC):
|
||||
def __init__(self, volume_enabled: bool = True, width: int = 800, height: int = 600, x: int = None, y: int = None,
|
||||
on_top: bool = False, maximize: bool = False, debug: bool = False,
|
||||
api: object = None, topbar: bool = False, searchbox: bool = False, toolbox: bool = False,
|
||||
inner_width: float = 1.0, inner_height: float = 1.0, dynamic_loading: bool = False, scale_candles_only: bool = False):
|
||||
super().__init__(volume_enabled, inner_width, inner_height, dynamic_loading, scale_candles_only, topbar, searchbox, toolbox, 'pywebview.api.callback')
|
||||
def __init__(self, width: int = 800, height: int = 600, x: int = None, y: int = None,
|
||||
on_top: bool = False, maximize: bool = False, debug: bool = False, toolbox: bool = False,
|
||||
inner_width: float = 1.0, inner_height: float = 1.0, scale_candles_only: bool = False):
|
||||
super().__init__(inner_width, inner_height, scale_candles_only, toolbox, 'pywebview.api.callback')
|
||||
global chart, num_charts
|
||||
|
||||
if chart:
|
||||
self._q, self._exit, self._start, self._process = chart._q, chart._exit, chart._start, chart._process
|
||||
self._emit_q, self._return_q = mp.Queue(), mp.Queue()
|
||||
chart._charts[self.id] = self
|
||||
self._api = chart._api
|
||||
for key, val in self._handlers.items():
|
||||
chart._handlers[key] = val
|
||||
self._handlers = chart._handlers
|
||||
self._loaded = chart._loaded_list[num_charts]
|
||||
self._q.put(('create_window', (self._html, on_top, width, height, x, y)))
|
||||
else:
|
||||
self._api = api
|
||||
self._q, self._emit_q, self._return_q = (mp.Queue() for _ in range(3))
|
||||
self._loaded_list = [mp.Event() for _ in range(10)]
|
||||
self._loaded = self._loaded_list[0]
|
||||
@ -117,20 +114,9 @@ class Chart(LWC):
|
||||
self._exit.clear()
|
||||
return
|
||||
elif not self._emit_q.empty():
|
||||
name, chart_id, arg = self._emit_q.get()
|
||||
if self._api:
|
||||
self._api.chart = self._charts[chart_id]
|
||||
if self._api and name == 'save_drawings':
|
||||
func = self._api.chart.toolbox._save_drawings
|
||||
elif name in ('on_search', 'on_horizontal_line_move'):
|
||||
func = getattr(self._api, name)
|
||||
else:
|
||||
func = self._methods[name]
|
||||
if self._api and hasattr(self._api.chart, 'topbar') and (widget := self._api.chart.topbar._widget_with_method(name)):
|
||||
widget.value = arg
|
||||
await func() if asyncio.iscoroutinefunction(func) else func()
|
||||
else:
|
||||
await func(*arg.split(';;;')) if asyncio.iscoroutinefunction(func) else func(*arg.split(';;;'))
|
||||
name, args = self._emit_q.get()
|
||||
func = self._handlers[name]
|
||||
await func(*args) if asyncio.iscoroutinefunction(func) else func(*args)
|
||||
continue
|
||||
value = self.polygon._q.get()
|
||||
func, args = value[0], value[1:]
|
||||
@ -148,12 +134,10 @@ class Chart(LWC):
|
||||
"""
|
||||
Exits and destroys the chart window.\n
|
||||
"""
|
||||
if not self.loaded:
|
||||
global num_charts, chart
|
||||
chart = None
|
||||
num_charts = 0
|
||||
else:
|
||||
self._q.put((self.i, 'exit'))
|
||||
self._exit.wait()
|
||||
global num_charts, chart
|
||||
chart = None
|
||||
num_charts = 0
|
||||
self._q.put((self.i, 'exit'))
|
||||
self._exit.wait()
|
||||
self._process.terminate()
|
||||
del self
|
||||
|
||||
Reference in New Issue
Block a user