- Significant refactoring resulting in a 34% reduction in size of the codebase (excluding the Lightweight Charts package) and greater efficiency.
- Upgraded to Lightweight Charts v4.0.1. - Added a ‘hover’ item to the returning dictionary from subscribe_click. - Markers and SubCharts no longer use a UUID for identification, but an 8 character string.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,82 +0,0 @@
|
||||
from typing import Literal, Union
|
||||
from uuid import UUID
|
||||
import webview
|
||||
from multiprocessing import Queue
|
||||
|
||||
from lightweight_charts.js import LWC
|
||||
|
||||
_q = Queue()
|
||||
_result_q = Queue()
|
||||
|
||||
|
||||
class Webview(LWC):
|
||||
def __init__(self, chart):
|
||||
super().__init__(chart.volume_enabled, chart.inner_width, chart.inner_height)
|
||||
self.chart = chart
|
||||
self.started = False
|
||||
self._js_api_code = 'pywebview.api.onClick'
|
||||
|
||||
self.webview = webview.create_window('', html=self._html, on_top=chart.on_top, js_api=self._js_api,
|
||||
width=chart.width, height=chart.height, x=chart.x, y=chart.y)
|
||||
self.webview.events.loaded += self._on_js_load
|
||||
|
||||
def run_script(self, script): self.webview.evaluate_js(script)
|
||||
|
||||
def _on_js_load(self):
|
||||
self.loaded = True
|
||||
while len(self.js_queue) > 0:
|
||||
func, args, kwargs = self.js_queue[0]
|
||||
|
||||
if 'SUB' in func:
|
||||
c_id = args[0]
|
||||
args = args[1:]
|
||||
getattr(self._subcharts[c_id], func.replace('SUB', ''))(*args)
|
||||
else:
|
||||
getattr(self, func)(*args)
|
||||
del self.js_queue[0]
|
||||
|
||||
_loop(self.chart, controller=self)
|
||||
|
||||
def show(self):
|
||||
if self.loaded:
|
||||
self.webview.show()
|
||||
else:
|
||||
webview.start(debug=self.chart.debug)
|
||||
|
||||
def hide(self): self.webview.hide()
|
||||
|
||||
def exit(self):
|
||||
self.webview.destroy()
|
||||
del self
|
||||
|
||||
def create_line(self, color: str = 'rgba(214, 237, 255, 0.6)', width: int = 2):
|
||||
return super().create_line(color, width).id
|
||||
|
||||
def create_subchart(self, volume_enabled: bool = True, position: Literal['left', 'right', 'top', 'bottom'] = 'left',
|
||||
width: float = 0.5, height: float = 0.5, sync: Union[bool, UUID] = False):
|
||||
return super()._pywebview_subchart(volume_enabled, position, width, height, sync)
|
||||
|
||||
|
||||
def _loop(chart, controller=None):
|
||||
wv = Webview(chart) if not controller else controller
|
||||
chart._result_q.put(wv.id)
|
||||
while 1:
|
||||
obj = wv
|
||||
func, args = chart._q.get()
|
||||
|
||||
if 'SUB' in func:
|
||||
obj = obj._subcharts[args[0]]
|
||||
args = args[1:]
|
||||
func = func.replace('SUB', '')
|
||||
|
||||
try:
|
||||
result = getattr(obj, func)(*args)
|
||||
except KeyError as e:
|
||||
return
|
||||
if func == 'show':
|
||||
chart._exit.set()
|
||||
elif func == 'exit':
|
||||
chart._exit.set()
|
||||
|
||||
chart._result_q.put(result) if result is not None else None
|
||||
|
||||
@ -21,71 +21,56 @@ class ColorError(ValueError):
|
||||
return f'{self.msg}'
|
||||
|
||||
|
||||
class IDGen(list):
|
||||
def generate(self):
|
||||
var = ''.join(choices(ascii_lowercase, k=8))
|
||||
if var not in self:
|
||||
self.append(var)
|
||||
return var
|
||||
self.generate()
|
||||
|
||||
|
||||
def _valid_color(string):
|
||||
if string[:3] == 'rgb' or string[:4] == 'rgba' or string[0] == '#':
|
||||
return True
|
||||
raise ColorError('Colors must be in the format of either rgb, rgba or hex.')
|
||||
|
||||
|
||||
LINE_TYPE = Literal['solid', 'dotted', 'dashed', 'large_dashed', 'sparse_dotted']
|
||||
def _js_bool(b: bool): return 'true' if b is True else 'false' if b is False else None
|
||||
|
||||
POSITION = Literal['above', 'below', 'inside']
|
||||
|
||||
SHAPE = Literal['arrow_up', 'arrow_down', 'circle', 'square']
|
||||
LINE_STYLE = Literal['solid', 'dotted', 'dashed', 'large_dashed', 'sparse_dotted']
|
||||
|
||||
MARKER_POSITION = Literal['above', 'below', 'inside']
|
||||
|
||||
MARKER_SHAPE = Literal['arrow_up', 'arrow_down', 'circle', 'square']
|
||||
|
||||
CROSSHAIR_MODE = Literal['normal', 'magnet']
|
||||
|
||||
PRICE_SCALE_MODE = Literal['normal', 'logarithmic', 'percentage', 'index100']
|
||||
|
||||
|
||||
def _line_type(lt: LINE_TYPE):
|
||||
return {
|
||||
'solid': 'Solid',
|
||||
'dotted': 'Dotted',
|
||||
'dashed': 'Dashed',
|
||||
'large_dashed': 'LargeDashed',
|
||||
'sparse_dotted': 'SparseDotted',
|
||||
None: None,
|
||||
}[lt]
|
||||
def _line_style(line: LINE_STYLE):
|
||||
js = 'LightweightCharts.LineStyle.'
|
||||
return js+line[:line.index('_')].title() + line[line.index('_') + 1:].title() if '_' in line else js+line.title()
|
||||
|
||||
|
||||
def _position(p: POSITION):
|
||||
def _crosshair_mode(mode: CROSSHAIR_MODE):
|
||||
return f'LightweightCharts.CrosshairMode.{mode.title()}' if mode else None
|
||||
|
||||
|
||||
def _price_scale_mode(mode: PRICE_SCALE_MODE):
|
||||
return f"LightweightCharts.PriceScaleMode.{'IndexedTo100' if mode == 'index100' else mode.title() if mode else None}"
|
||||
|
||||
|
||||
def _marker_shape(shape: MARKER_SHAPE):
|
||||
return shape[:shape.index('_')]+shape[shape.index('_')+1:].title() if '_' in shape else shape.title()
|
||||
|
||||
|
||||
def _marker_position(p: MARKER_POSITION):
|
||||
return {
|
||||
'above': 'aboveBar',
|
||||
'below': 'belowBar',
|
||||
'inside': 'inBar',
|
||||
None: None,
|
||||
}[p]
|
||||
|
||||
|
||||
def _shape(shape: SHAPE):
|
||||
return {
|
||||
'arrow_up': 'arrowUp',
|
||||
'arrow_down': 'arrowDown',
|
||||
'circle': 'Circle',
|
||||
'square': 'Square',
|
||||
None: None,
|
||||
}[shape]
|
||||
|
||||
|
||||
def _crosshair_mode(mode: CROSSHAIR_MODE): return mode.title() if mode else None
|
||||
|
||||
|
||||
def _js_bool(b: bool): return 'true' if b is True else 'false' if b is False else None
|
||||
|
||||
|
||||
def _price_scale_mode(mode: PRICE_SCALE_MODE):
|
||||
return 'IndexedTo100' if mode == 'index100' else mode.title() if mode else None
|
||||
|
||||
|
||||
class IDGen:
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
|
||||
def generate(self):
|
||||
var = ''.join(choices(ascii_lowercase, k=8))
|
||||
if var in self.list:
|
||||
self.generate()
|
||||
else:
|
||||
self.list.append(var)
|
||||
return var
|
||||
}[p]
|
||||
@ -21,24 +21,16 @@ class WxChart(LWC):
|
||||
|
||||
super().__init__(volume_enabled, inner_width=inner_width, inner_height=inner_height)
|
||||
|
||||
self.webview.AddScriptMessageHandler('wx_msg')
|
||||
self._script_func = self.webview.RunScript
|
||||
self._js_api_code = 'window.wx_msg.postMessage'
|
||||
self.webview.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, lambda e: self._js_api.onClick(eval(e.GetString())))
|
||||
|
||||
self.webview.AddScriptMessageHandler('wx_msg')
|
||||
self.webview.Bind(wx.html2.EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, lambda e: self._js_api.onClick(eval(e.GetString())))
|
||||
self.webview.Bind(wx.html2.EVT_WEBVIEW_LOADED, self._on_js_load)
|
||||
self.webview.SetPage(self._html, '')
|
||||
self._create_chart()
|
||||
|
||||
def run_script(self, script): self.webview.RunScript(script)
|
||||
|
||||
def _on_js_load(self, e):
|
||||
self.loaded = True
|
||||
for func, args, kwargs in self.js_queue:
|
||||
if 'SUB' in func:
|
||||
c_id = args[0]
|
||||
args = args[1:]
|
||||
getattr(self._subcharts[c_id], func.replace('SUB', ''))(*args)
|
||||
else:
|
||||
getattr(self, func)(*args)
|
||||
def _on_js_load(self, e): super()._on_js_load()
|
||||
|
||||
def get_webview(self): return self.webview
|
||||
|
||||
@ -49,18 +41,13 @@ class QtChart(LWC):
|
||||
self.webview = QWebEngineView(widget)
|
||||
except NameError:
|
||||
raise ModuleNotFoundError('QWebEngineView was not found, and must be installed to use QtChart.')
|
||||
|
||||
super().__init__(volume_enabled, inner_width=inner_width, inner_height=inner_height)
|
||||
|
||||
self._script_func = self.webview.page().runJavaScript
|
||||
|
||||
self.webview.loadFinished.connect(self._on_js_load)
|
||||
self.webview.page().setHtml(self._html)
|
||||
|
||||
def run_script(self, script): self.webview.page().runJavaScript(script)
|
||||
|
||||
def _on_js_load(self):
|
||||
self.loaded = True
|
||||
for func, args, kwargs in self.js_queue:
|
||||
getattr(super(), func)(*args, **kwargs)
|
||||
self._create_chart()
|
||||
|
||||
def get_webview(self): return self.webview
|
||||
|
||||
|
||||
Reference in New Issue
Block a user