seolpyo-mplchart 0.1.3.4__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of seolpyo-mplchart might be problematic. Click here for more details.
- seolpyo_mplchart/__init__.py +221 -70
- seolpyo_mplchart/base.py +70 -67
- seolpyo_mplchart/cursor.py +246 -272
- seolpyo_mplchart/draw.py +383 -238
- seolpyo_mplchart/slider.py +414 -405
- seolpyo_mplchart/test.py +5 -32
- seolpyo_mplchart/utils.py +15 -4
- {seolpyo_mplchart-0.1.3.4.dist-info → seolpyo_mplchart-1.0.0.dist-info}/METADATA +4 -8
- seolpyo_mplchart-1.0.0.dist-info/RECORD +13 -0
- seolpyo_mplchart-0.1.3.4.dist-info/RECORD +0 -13
- {seolpyo_mplchart-0.1.3.4.dist-info → seolpyo_mplchart-1.0.0.dist-info}/WHEEL +0 -0
- {seolpyo_mplchart-0.1.3.4.dist-info → seolpyo_mplchart-1.0.0.dist-info}/top_level.txt +0 -0
seolpyo_mplchart/slider.py
CHANGED
|
@@ -1,504 +1,542 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
from matplotlib.axes import Axes
|
|
1
3
|
from matplotlib.collections import LineCollection
|
|
4
|
+
from matplotlib.text import Text
|
|
2
5
|
from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
|
|
3
6
|
import pandas as pd
|
|
4
7
|
|
|
8
|
+
from .base import convert_unit
|
|
9
|
+
from .cursor import BaseMixin as BM, Mixin as M
|
|
5
10
|
|
|
6
|
-
from .cursor import CursorMixin, Chart as CM
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
class Mixin:
|
|
12
|
+
class Mixin(M):
|
|
10
13
|
def on_click(self, e):
|
|
11
14
|
"This function works if mouse button click event active."
|
|
12
15
|
return
|
|
13
16
|
def on_release(self, e):
|
|
14
17
|
"This function works if mouse button release event active."
|
|
15
18
|
return
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PlotMixin(BM):
|
|
22
|
+
slider_top = True
|
|
23
|
+
ratio_ax_slider = 3
|
|
24
|
+
ratio_ax_none = 2
|
|
25
|
+
|
|
26
|
+
def _get_plot(self):
|
|
27
|
+
if self.slider_top:
|
|
28
|
+
self.figure, axes = plt.subplots(
|
|
29
|
+
4, # row 수
|
|
30
|
+
figsize=self.figsize, # 기본 크기
|
|
31
|
+
height_ratios=(self.ratio_ax_slider, self.ratio_ax_legend, self.ratio_ax_price, self.ratio_ax_volume) # row 크기 비율
|
|
32
|
+
)
|
|
33
|
+
axes: list[Axes]
|
|
34
|
+
self.ax_slider, self.ax_legend, self.ax_price, self.ax_volume = axes
|
|
35
|
+
else:
|
|
36
|
+
self.figure, axes = plt.subplots(
|
|
37
|
+
5, # row 수
|
|
38
|
+
figsize=self.figsize, # 기본 크기
|
|
39
|
+
height_ratios=(self.ratio_ax_legend, self.ratio_ax_price, self.ratio_ax_volume, self.ratio_ax_none, self.ratio_ax_slider) # row 크기 비율
|
|
40
|
+
)
|
|
41
|
+
axes: list[Axes]
|
|
42
|
+
self.ax_legend, self.ax_price, self.ax_volume, ax_none, self.ax_slider = axes
|
|
43
|
+
|
|
44
|
+
ax_none.set_axis_off()
|
|
45
|
+
ax_none.xaxis.set_animated(True)
|
|
46
|
+
ax_none.yaxis.set_animated(True)
|
|
47
|
+
|
|
48
|
+
self.ax_slider.set_label('slider ax')
|
|
49
|
+
self.ax_legend.set_label('legend ax')
|
|
50
|
+
self.ax_price.set_label('price ax')
|
|
51
|
+
self.ax_volume.set_label('volume ax')
|
|
52
|
+
|
|
53
|
+
self.figure.canvas.manager.set_window_title(f'{self.title}')
|
|
54
|
+
self.figure.set_facecolor(self.color_background)
|
|
55
|
+
|
|
56
|
+
# 플롯간 간격 제거(Configure subplots)
|
|
57
|
+
self.figure.subplots_adjust(**self.adjust)
|
|
58
|
+
|
|
59
|
+
self.ax_legend.set_axis_off()
|
|
60
|
+
|
|
61
|
+
# y ticklabel foramt 설정
|
|
62
|
+
self.ax_slider.yaxis.set_major_formatter(lambda x, _: convert_unit(x, word=self.unit_price, digit=2))
|
|
63
|
+
self.ax_price.yaxis.set_major_formatter(lambda x, _: convert_unit(x, word=self.unit_price, digit=2))
|
|
64
|
+
self.ax_volume.yaxis.set_major_formatter(lambda x, _: convert_unit(x, word=self.unit_volume, digit=2))
|
|
65
|
+
|
|
66
|
+
gridKwargs = {'visible': True, 'linewidth': 1, 'color': '#d0d0d0', 'linestyle': '--'}
|
|
67
|
+
gridKwargs.update(self.gridKwargs)
|
|
68
|
+
# 공통 설정
|
|
69
|
+
for ax in (self.ax_slider, self.ax_price, self.ax_volume):
|
|
70
|
+
ax.xaxis.set_animated(True)
|
|
71
|
+
ax.yaxis.set_animated(True)
|
|
72
|
+
|
|
73
|
+
# x tick 외부 눈금 표시하지 않기
|
|
74
|
+
ax.xaxis.set_ticks_position('none')
|
|
75
|
+
# x tick label 제거
|
|
76
|
+
ax.set_xticklabels([])
|
|
77
|
+
# y tick 우측으로 이동
|
|
78
|
+
ax.tick_params(left=False, right=True, labelleft=False, labelright=True, colors=self.color_tick_label)
|
|
79
|
+
# Axes 외곽선 색 변경
|
|
80
|
+
for i in ['top', 'bottom', 'left', 'right']: ax.spines[i].set_color(self.color_tick)
|
|
81
|
+
|
|
82
|
+
# 차트 영역 배경 색상
|
|
83
|
+
ax.set_facecolor(self.color_background)
|
|
84
|
+
|
|
85
|
+
# grid(구분선, 격자) 그리기
|
|
86
|
+
# 어째서인지 grid의 zorder 값을 선언해도 1.6을 값으로 한다.
|
|
87
|
+
ax.grid(**gridKwargs)
|
|
18
88
|
return
|
|
19
89
|
|
|
20
90
|
|
|
21
|
-
class
|
|
91
|
+
class CollectionMixin(PlotMixin):
|
|
22
92
|
min_distance = 30
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
_x_click, _x_release = (0, 0)
|
|
27
|
-
is_click, is_move = (False, False)
|
|
28
|
-
_navcoordinate = (0, 0)
|
|
93
|
+
color_navigator_line = '#1e78ff'
|
|
94
|
+
color_navigator_cover = 'k'
|
|
29
95
|
|
|
30
96
|
def _add_collection(self):
|
|
31
97
|
super()._add_collection()
|
|
32
98
|
|
|
99
|
+
self.collection_slider = LineCollection([], animated=True)
|
|
100
|
+
self.ax_slider.add_artist(self.collection_slider)
|
|
101
|
+
|
|
33
102
|
# 슬라이더 네비게이터
|
|
34
|
-
self.navigator = LineCollection([], animated=True, edgecolors=[self.
|
|
103
|
+
self.navigator = LineCollection([], animated=True, edgecolors=[self.color_navigator_cover, self.color_navigator_line], alpha=(0.2, 1.0))
|
|
35
104
|
self.ax_slider.add_artist(self.navigator)
|
|
36
|
-
return
|
|
37
105
|
|
|
38
|
-
|
|
39
|
-
|
|
106
|
+
lineKwargs = {'edgecolor': 'k', 'linewidth': 1, 'linestyle': '-'}
|
|
107
|
+
lineKwargs.update(self.lineKwargs)
|
|
108
|
+
lineKwargs.update({'segments': [], 'animated': True})
|
|
40
109
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
self._navLineWidth = xsub * 0.008
|
|
44
|
-
if self._navLineWidth < 1: self._navLineWidth = 1
|
|
45
|
-
self._navLineWidth_half = self._navLineWidth / 2
|
|
46
|
-
return
|
|
110
|
+
self.slider_vline = LineCollection(**lineKwargs)
|
|
111
|
+
self.ax_slider.add_artist(self.slider_vline)
|
|
47
112
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return
|
|
113
|
+
textboxKwargs = {'boxstyle': 'round', 'facecolor': 'w'}
|
|
114
|
+
textboxKwargs.update(self.textboxKwargs)
|
|
115
|
+
textKwargs = self.textKwargs
|
|
116
|
+
textKwargs.update({'animated': True, 'bbox': textboxKwargs, 'horizontalalignment': '', 'verticalalignment': ''})
|
|
117
|
+
(textKwargs.pop('horizontalalignment'), textKwargs.pop('verticalalignment'))
|
|
54
118
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
self.canvas.set_cursor(cursors.POINTER)
|
|
119
|
+
self.text_slider = Text(**textKwargs, horizontalalignment='center', verticalalignment='top')
|
|
120
|
+
self.ax_slider.add_artist(self.text_slider)
|
|
58
121
|
return
|
|
59
122
|
|
|
60
|
-
def
|
|
61
|
-
|
|
123
|
+
def _get_segments(self):
|
|
124
|
+
super()._get_segments()
|
|
62
125
|
|
|
63
|
-
|
|
126
|
+
keys = []
|
|
127
|
+
for i in reversed(self.list_ma):
|
|
128
|
+
keys.append('_x')
|
|
129
|
+
keys.append(f'ma{i}')
|
|
64
130
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
gtl, ltr = (lmax < x, x < rmin)
|
|
71
|
-
if gtl and ltr:
|
|
72
|
-
self._x_click = x
|
|
73
|
-
self.is_move = True
|
|
74
|
-
self.canvas.set_cursor(cursors.MOVE)
|
|
75
|
-
else:
|
|
76
|
-
self.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
77
|
-
if not gtl and lmin <= x:
|
|
78
|
-
self._x_click = right
|
|
79
|
-
elif not ltr and x <= rmax:
|
|
80
|
-
self._x_click = left
|
|
81
|
-
else:
|
|
82
|
-
self._x_click = x
|
|
83
|
-
|
|
84
|
-
# 그리기 후 최초 클릭이면 좌표 수정
|
|
85
|
-
if left == right:
|
|
86
|
-
self._navcoordinate = (x, x)
|
|
131
|
+
segment_slider = self.df[keys + ['_x', self.close] ].values
|
|
132
|
+
segment_slider = segment_slider.reshape(segment_slider.shape[0], len(self.list_ma)+1, 2).swapaxes(0, 1)
|
|
133
|
+
self.collection_slider.set_segments(segment_slider)
|
|
134
|
+
self.collection_slider.set_edgecolor(self.edgecolor_ma + [self.color_priceline])
|
|
87
135
|
return
|
|
88
136
|
|
|
89
|
-
def _on_release(self, e: MouseEvent):
|
|
90
|
-
if e.inaxes is not self.ax_slider: return
|
|
91
|
-
self.is_click, self.is_move = (False, False)
|
|
92
137
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
138
|
+
class NavigatorMixin(CollectionMixin):
|
|
139
|
+
def _set_slider_lim(self):
|
|
140
|
+
xmax = self.list_index[-1]
|
|
141
|
+
# 슬라이더 xlim
|
|
142
|
+
xdistance = xmax / 30
|
|
143
|
+
self.slider_xmin, self.slider_xmax = (-xdistance, xmax + xdistance)
|
|
144
|
+
self.ax_slider.set_xlim(self.slider_xmin, self.slider_xmax)
|
|
96
145
|
|
|
146
|
+
# 슬라이더 ylim
|
|
147
|
+
ymin, ymax = (self.df[self.low].min(), self.df[self.high].max())
|
|
148
|
+
ysub = ymax - ymin
|
|
149
|
+
self.sldier_ymiddle = ymin + (ysub / 2)
|
|
150
|
+
ydistance = ysub / 5
|
|
151
|
+
self.slider_ymin, self.slider_ymax = (ymin-ydistance, ymax+ydistance)
|
|
152
|
+
self.ax_slider.set_ylim(self.slider_ymin, self.slider_ymax)
|
|
97
153
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
self.background = None
|
|
101
|
-
self._restore_region()
|
|
102
|
-
return
|
|
154
|
+
# 슬라이더 텍스트 y
|
|
155
|
+
self.text_slider.set_y(ymax)
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
if not self.background: self._create_background()
|
|
157
|
+
self.navigator.set_linewidth([ysub, 5])
|
|
106
158
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
159
|
+
# 네비게이터 라인 선택 범위
|
|
160
|
+
xsub = self.slider_xmax - self.slider_xmin
|
|
161
|
+
self._navLineWidth = xsub * 8 / 1_000
|
|
162
|
+
if self._navLineWidth < 1: self._navLineWidth = 1
|
|
163
|
+
self._navLineWidth_half = self._navLineWidth / 2
|
|
111
164
|
return
|
|
112
165
|
|
|
113
|
-
def
|
|
114
|
-
|
|
166
|
+
def _set_navigator(self, navmin, navmax):
|
|
167
|
+
navseg = [
|
|
168
|
+
(
|
|
169
|
+
(self.slider_xmin, self.sldier_ymiddle),
|
|
170
|
+
(navmin, self.sldier_ymiddle)
|
|
171
|
+
),
|
|
172
|
+
(
|
|
173
|
+
(navmin, self.slider_ymin),
|
|
174
|
+
(navmin, self.slider_ymax)
|
|
175
|
+
),
|
|
176
|
+
(
|
|
177
|
+
(navmax, self.sldier_ymiddle),
|
|
178
|
+
(self.slider_xmax, self.sldier_ymiddle)
|
|
179
|
+
),
|
|
180
|
+
(
|
|
181
|
+
(navmax, self.slider_ymin),
|
|
182
|
+
(navmax, self.slider_ymax)
|
|
183
|
+
),
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
self.navigator.set_segments(navseg)
|
|
187
|
+
return
|
|
115
188
|
|
|
116
|
-
self.background_empty = renderer.copy_from_bbox(self.fig.bbox)
|
|
117
189
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
self.slidercollection.draw(renderer)
|
|
121
|
-
self.background_empty_with_nav = self.canvas.renderer.copy_from_bbox(self.fig.bbox)
|
|
190
|
+
class DataMixin(NavigatorMixin):
|
|
191
|
+
navcoordinate = (0, 0)
|
|
122
192
|
|
|
123
|
-
|
|
124
|
-
self.
|
|
193
|
+
def set_data(self, df, sort_df=True, calc_ma=True, set_candlecolor=True, set_volumecolor=True, calc_info=True, change_lim=True, *args, **kwargs):
|
|
194
|
+
self._generate_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *args, **kwargs)
|
|
195
|
+
self._get_segments()
|
|
125
196
|
|
|
126
|
-
self.
|
|
127
|
-
|
|
128
|
-
|
|
197
|
+
vmin, vmax = self.navcoordinate
|
|
198
|
+
if change_lim or (vmax-vmin) < self.min_distance:
|
|
199
|
+
vmin, vmax = self.get_default_lim()
|
|
200
|
+
self.navcoordinate = (vmin, vmax)
|
|
129
201
|
|
|
130
|
-
|
|
131
|
-
|
|
202
|
+
self._set_lim(vmin, vmax)
|
|
203
|
+
self._set_slider_lim()
|
|
204
|
+
self._set_navigator(vmin, vmax)
|
|
132
205
|
|
|
133
|
-
self.
|
|
134
|
-
|
|
206
|
+
self._length_text = self.df[(self.volume if self.volume else self.high)].apply(lambda x: len(str(x))).max()
|
|
207
|
+
return
|
|
135
208
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else:
|
|
140
|
-
self.candlecollection.draw(renderer)
|
|
141
|
-
self.macollection.draw(renderer)
|
|
209
|
+
def get_default_lim(self):
|
|
210
|
+
xmax = self.list_index[-1]
|
|
211
|
+
return (xmax-120, xmax)
|
|
142
212
|
|
|
143
|
-
self.ax_volume.xaxis.draw(renderer)
|
|
144
|
-
self.ax_volume.yaxis.draw(renderer)
|
|
145
213
|
|
|
146
|
-
|
|
147
|
-
|
|
214
|
+
class BackgroundMixin(DataMixin):
|
|
215
|
+
def _copy_bbox(self):
|
|
216
|
+
renderer = self.figure.canvas.renderer
|
|
148
217
|
|
|
218
|
+
self.ax_slider.xaxis.draw(renderer)
|
|
219
|
+
self.ax_slider.yaxis.draw(renderer)
|
|
220
|
+
self.collection_slider.draw(renderer)
|
|
221
|
+
self.background_emtpy = renderer.copy_from_bbox(self.figure.bbox)
|
|
149
222
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
super()._set_data(df, sort_df, calc_ma, change_lim, calc_info)
|
|
223
|
+
self._draw_artist()
|
|
224
|
+
self.background = renderer.copy_from_bbox(self.figure.bbox)
|
|
153
225
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
self._ymiddle = self._slider_ymax - ysub / 2
|
|
157
|
-
self.navigator.set_linewidth((ysub, 5))
|
|
226
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
227
|
+
self.background_with_nav = renderer.copy_from_bbox(self.figure.bbox)
|
|
158
228
|
return
|
|
159
229
|
|
|
160
|
-
def
|
|
161
|
-
|
|
162
|
-
self._set_navigator(*self._navcoordinate)
|
|
230
|
+
def _restore_region(self, is_empty=False, with_nav=True):
|
|
231
|
+
if not self.background: self._create_background()
|
|
163
232
|
|
|
164
|
-
self.
|
|
165
|
-
self.
|
|
166
|
-
self.
|
|
167
|
-
self._restore_region()
|
|
168
|
-
self._blit()
|
|
233
|
+
if is_empty: self.figure.canvas.renderer.restore_region(self.background_emtpy)
|
|
234
|
+
elif with_nav: self.figure.canvas.renderer.restore_region(self.background_with_nav)
|
|
235
|
+
else: self.figure.canvas.renderer.restore_region(self.background)
|
|
169
236
|
return
|
|
170
237
|
|
|
171
|
-
def _on_move(self, e: MouseEvent):
|
|
172
|
-
self._restore_region((not self.is_click))
|
|
173
238
|
|
|
239
|
+
class MouseMoveMixin(BackgroundMixin):
|
|
240
|
+
in_slider = False
|
|
241
|
+
is_click_slider = False
|
|
242
|
+
|
|
243
|
+
def _on_move(self, e):
|
|
174
244
|
self._on_move_action(e)
|
|
175
245
|
|
|
246
|
+
self._restore_region((self.is_click_slider and self.in_slider))
|
|
247
|
+
|
|
176
248
|
if self.in_slider:
|
|
177
|
-
self.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
self._slider_move_action(e)
|
|
183
|
-
elif self.is_click:
|
|
184
|
-
self.navigator.draw(self.canvas.renderer)
|
|
185
|
-
else:
|
|
186
|
-
if self.in_slider or self.in_price or self.in_volume:
|
|
187
|
-
self._slider_move_action(e)
|
|
188
|
-
if self.in_price or self.in_volume:
|
|
189
|
-
self._chart_move_action(e)
|
|
249
|
+
self._on_move_slider(e)
|
|
250
|
+
elif self.in_price_chart:
|
|
251
|
+
self._on_move_price_chart(e)
|
|
252
|
+
elif self.in_volume_chart:
|
|
253
|
+
self._on_move_volume_chart(e)
|
|
190
254
|
|
|
191
255
|
self._blit()
|
|
192
256
|
return
|
|
193
257
|
|
|
194
|
-
def
|
|
195
|
-
|
|
196
|
-
x = self.intx
|
|
197
|
-
left, right = self._navcoordinate
|
|
198
|
-
|
|
199
|
-
if not self.is_click:
|
|
200
|
-
lmin, lmax = (left-self._navLineWidth, left+self._navLineWidth_half)
|
|
201
|
-
rmin, rmax = (right-self._navLineWidth_half, right+self._navLineWidth)
|
|
202
|
-
ltel, gter = (x <= lmax, rmin <= x)
|
|
203
|
-
if ltel and lmin <= x:
|
|
204
|
-
self.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
205
|
-
elif gter and x <= rmax:
|
|
206
|
-
self.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
207
|
-
elif not ltel and not gter: self.canvas.set_cursor(cursors.MOVE)
|
|
208
|
-
else: self.canvas.set_cursor(cursors.POINTER)
|
|
209
|
-
else:
|
|
210
|
-
# 네비게이터 좌표 수정
|
|
211
|
-
intx = x.__int__()
|
|
212
|
-
if self.is_move:
|
|
213
|
-
xsub = self._x_click - intx
|
|
214
|
-
left, right = (left-xsub, right-xsub)
|
|
215
|
-
self._x_click = intx
|
|
216
|
-
else:
|
|
217
|
-
if intx == left: left = intx
|
|
218
|
-
elif intx == right: right = intx
|
|
219
|
-
else:
|
|
220
|
-
if self._x_click < intx: left, right = (self._x_click, intx)
|
|
221
|
-
else: left, right = (intx, self._x_click)
|
|
222
|
-
|
|
223
|
-
nsub = right - left
|
|
224
|
-
if right < 0 or self.df.index[-1] < left or nsub < self.min_distance: left, right = self._navcoordinate
|
|
225
|
-
self._navcoordinate = (left, right)
|
|
226
|
-
return
|
|
258
|
+
def _on_move_action(self, e: MouseEvent):
|
|
259
|
+
self._check_ax(e)
|
|
227
260
|
|
|
228
|
-
|
|
229
|
-
|
|
261
|
+
self.intx = None
|
|
262
|
+
if self.in_slider or self.in_price_chart or self.in_volume_chart: self._get_x(e)
|
|
230
263
|
|
|
231
|
-
|
|
232
|
-
right = ((xmax, self._ymiddle), (self.xmax, self._ymiddle))
|
|
233
|
-
leftline = ((xmin, self._slider_ymin), (xmin, self._slider_ymax))
|
|
234
|
-
rightline = ((xmax, self._slider_ymin), (xmax, self._slider_ymax))
|
|
235
|
-
self.navigator.set_segments((left, leftline, right, rightline))
|
|
264
|
+
self._change_cursor(e)
|
|
236
265
|
return
|
|
237
266
|
|
|
267
|
+
def _change_cursor(self, e: MouseEvent):
|
|
268
|
+
# 마우스 커서 변경
|
|
269
|
+
if self.is_click_slider: return
|
|
270
|
+
elif not self.in_slider:
|
|
271
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
navleft, navright = self.navcoordinate
|
|
275
|
+
if navleft == navright: return
|
|
276
|
+
|
|
277
|
+
x = e.xdata
|
|
278
|
+
leftmin, leftmax = (navleft-self._navLineWidth, navleft+self._navLineWidth_half)
|
|
279
|
+
rightmin, rightmax = (navright-self._navLineWidth_half, navright+self._navLineWidth)
|
|
280
|
+
if x < leftmin: self.figure.canvas.set_cursor(cursors.POINTER)
|
|
281
|
+
elif x < leftmax: self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
282
|
+
elif x < rightmin: self.figure.canvas.set_cursor(cursors.MOVE)
|
|
283
|
+
elif x < rightmax: self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
284
|
+
else: self.figure.canvas.set_cursor(cursors.POINTER)
|
|
285
|
+
return
|
|
238
286
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if e.
|
|
242
|
-
|
|
287
|
+
def _check_ax(self, e: MouseEvent):
|
|
288
|
+
ax = e.inaxes
|
|
289
|
+
if not ax or e.xdata is None or e.ydata is None:
|
|
290
|
+
self.in_slider, self.in_price_chart, self.in_volume_chart = (False, False, False)
|
|
291
|
+
else:
|
|
292
|
+
self.in_slider = ax is self.ax_slider
|
|
293
|
+
self.in_price_chart = False if self.in_slider else ax is self.ax_price
|
|
294
|
+
self.in_volume_chart = False if (self.in_slider or self.in_price_chart) else ax is self.ax_volume
|
|
295
|
+
return
|
|
243
296
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
self._set_navigator(*self._navcoordinate)
|
|
247
|
-
self._lim()
|
|
297
|
+
def _on_move_slider(self, e: MouseEvent):
|
|
298
|
+
x = e.xdata
|
|
248
299
|
|
|
249
|
-
self.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
300
|
+
if self.intx is not None:
|
|
301
|
+
renderer = self.figure.canvas.renderer
|
|
302
|
+
self.slider_vline.set_segments([((x, self.slider_ymin), (x, self.slider_ymax))])
|
|
303
|
+
self.slider_vline.draw(renderer)
|
|
304
|
+
|
|
305
|
+
if self.in_slider:
|
|
306
|
+
self.text_slider.set_text(f'{self.df[self.date][self.intx]}')
|
|
307
|
+
self.text_slider.set_x(x)
|
|
308
|
+
self.text_slider.draw(renderer)
|
|
253
309
|
return
|
|
254
310
|
|
|
255
|
-
def _on_move(self, e):
|
|
256
|
-
self._restore_region(with_nav=(not self.is_click), empty_with_nav=self.is_click)
|
|
257
311
|
|
|
258
|
-
|
|
312
|
+
class ClickMixin(MouseMoveMixin):
|
|
313
|
+
x_click = None
|
|
314
|
+
is_move = False
|
|
315
|
+
click_navleft, click_navright = (False, False)
|
|
259
316
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if self.is_click:
|
|
263
|
-
nsub = self._navcoordinate[1] - self._navcoordinate[0]
|
|
264
|
-
if self.min_distance <= nsub: self._lim()
|
|
265
|
-
if self.is_move: self._set_navigator(*self._navcoordinate)
|
|
266
|
-
elif self.intx is not None: self._set_navigator(self._x_click, self.intx)
|
|
267
|
-
self.navigator.draw(self.canvas.renderer)
|
|
268
|
-
self._draw_blit_artist()
|
|
269
|
-
self._slider_move_action(e)
|
|
270
|
-
elif self.is_click:
|
|
271
|
-
self.navigator.draw(self.canvas.renderer)
|
|
272
|
-
self._draw_blit_artist()
|
|
273
|
-
else:
|
|
274
|
-
if self.in_slider or self.in_price or self.in_volume:
|
|
275
|
-
self._slider_move_action(e)
|
|
276
|
-
if self.in_price or self.in_volume:
|
|
277
|
-
self._chart_move_action(e)
|
|
317
|
+
def _connect_event(self):
|
|
318
|
+
super()._connect_event()
|
|
278
319
|
|
|
279
|
-
self.
|
|
320
|
+
self.figure.canvas.mpl_connect('button_press_event', lambda x: self._on_click(x))
|
|
280
321
|
return
|
|
281
322
|
|
|
282
|
-
def
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def _lim(self):
|
|
286
|
-
xmin, xmax = self._navcoordinate
|
|
287
|
-
|
|
288
|
-
xmax += 1
|
|
289
|
-
self.ax_price.set_xlim(xmin, xmax)
|
|
290
|
-
self.ax_volume.set_xlim(xmin, xmax)
|
|
291
|
-
|
|
292
|
-
indmin, indmax = (xmin, xmax)
|
|
293
|
-
if xmin < 0: indmin = 0
|
|
294
|
-
if indmax < 1: indmax = 1
|
|
295
|
-
if indmin == indmax: indmax += 1
|
|
296
|
-
ymin, ymax = (self.df[self.low][indmin:indmax].min(), self.df[self.high][indmin:indmax].max())
|
|
297
|
-
ysub = (ymax - ymin) / 15
|
|
298
|
-
pmin, pmax = (ymin-ysub, ymax+ysub)
|
|
299
|
-
self.ax_price.set_ylim(pmin, pmax)
|
|
300
|
-
|
|
301
|
-
ymax = self.df[self.volume][indmin:indmax].max()
|
|
302
|
-
# self._vol_ymax = ymax*1.2
|
|
303
|
-
volmax = ymax * 1.2
|
|
304
|
-
self.ax_volume.set_ylim(0, volmax)
|
|
305
|
-
|
|
306
|
-
self.set_text_coordante(xmin, xmax, pmin, pmax, volmax)
|
|
323
|
+
def _on_click(self, e: MouseEvent):
|
|
324
|
+
if self.in_slider: self._on_click_slider(e)
|
|
307
325
|
return
|
|
308
326
|
|
|
327
|
+
def _on_click_slider(self, e: MouseEvent):
|
|
328
|
+
if self.is_click_slider or e.button != MouseButton.LEFT: return
|
|
329
|
+
|
|
330
|
+
self.background_with_nav_pre = self.background_with_nav
|
|
309
331
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
limit_volume = 1_500
|
|
313
|
-
default_left, default_right = (180, 10)
|
|
314
|
-
_draw_blit = False
|
|
332
|
+
self.is_click_slider = True
|
|
333
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
315
334
|
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
x = e.xdata.__int__()
|
|
336
|
+
navmin, navmax = self.navcoordinate
|
|
318
337
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
338
|
+
leftmin, leftmax = (navmin-self._navLineWidth, navmin+self._navLineWidth_half)
|
|
339
|
+
rightmin, rightmax = (navmax-self._navLineWidth_half, navmax+self._navLineWidth)
|
|
340
|
+
|
|
341
|
+
grater_than_left, less_then_right = (leftmax < x, x < rightmin)
|
|
342
|
+
if grater_than_left and less_then_right:
|
|
343
|
+
self.is_move = True
|
|
344
|
+
self.x_click = x
|
|
345
|
+
else:
|
|
346
|
+
if not grater_than_left and leftmin <= x:
|
|
347
|
+
self.click_navleft = True
|
|
348
|
+
self.x_click = navmax
|
|
349
|
+
elif not less_then_right and x <= rightmax:
|
|
350
|
+
self.click_navright = True
|
|
351
|
+
self.x_click = navmin
|
|
352
|
+
else:
|
|
353
|
+
self.x_click = x
|
|
328
354
|
return
|
|
329
355
|
|
|
330
|
-
def _set_data(self, df: pd.DataFrame, sort_df=True, calc_ma=True, change_lim=True, calc_info=True):
|
|
331
|
-
super()._set_data(df, sort_df, calc_ma, False, calc_info)
|
|
332
356
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
self.blitcandle.set_segments(seg)
|
|
336
|
-
self.blitcandle.set_edgecolor(self.df['edgecolor'])
|
|
357
|
+
class SliderSelectMixin(ClickMixin):
|
|
358
|
+
limit_ma = 8_000
|
|
337
359
|
|
|
338
|
-
|
|
339
|
-
self.
|
|
360
|
+
def _on_move_slider(self, e):
|
|
361
|
+
if self.is_click_slider: self._set_navcoordinate(e)
|
|
362
|
+
return super()._on_move_slider(e)
|
|
340
363
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
else:
|
|
345
|
-
v = self.df[['x', 'zero', 'x', self.volume]].sort_values([self.volume], axis=0, ascending=False)
|
|
346
|
-
volseg = v[:self.limit_volume].values
|
|
364
|
+
def _set_navcoordinate(self, e: MouseEvent):
|
|
365
|
+
x = e.xdata.__int__()
|
|
366
|
+
navmin, navmax = self.navcoordinate
|
|
347
367
|
|
|
348
|
-
self.
|
|
368
|
+
if self.is_move:
|
|
369
|
+
xsub = self.x_click - x
|
|
370
|
+
navmin, navmax = (navmin-xsub, navmax-xsub)
|
|
349
371
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if
|
|
353
|
-
else: self._navcoordinate = (index-self.default_left, index+self.default_right)
|
|
372
|
+
# 값 보정
|
|
373
|
+
if navmax < 0: navmin, navmax = (navmin-navmax, 0)
|
|
374
|
+
if self.list_index[-1] < navmin: navmin, navmax = (self.list_index[-1], self.list_index[-1] + (navmax-navmin))
|
|
354
375
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return
|
|
376
|
+
self.navcoordinate = (navmin, navmax)
|
|
377
|
+
self.x_click = x
|
|
358
378
|
|
|
359
|
-
|
|
360
|
-
renderer = self.canvas.renderer
|
|
379
|
+
self._set_lim(navmin, navmax, simpler=True, set_ma=(navmax-navmin < self.limit_ma))
|
|
361
380
|
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
self._set_navigator(navmin, navmax)
|
|
382
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
364
383
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
elif self.candle_on_ma:
|
|
369
|
-
self.macollection.draw(renderer)
|
|
370
|
-
if self._draw_blit: self.blitcandle.draw(renderer)
|
|
371
|
-
else: self.candlecollection.draw(renderer)
|
|
384
|
+
self._draw_artist()
|
|
385
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
386
|
+
self._restore_region(False, True)
|
|
372
387
|
else:
|
|
373
|
-
if self.
|
|
374
|
-
|
|
375
|
-
|
|
388
|
+
navmin, navmax = (x, self.x_click) if x < self.x_click else (self.x_click, x)
|
|
389
|
+
|
|
390
|
+
# 슬라이더가 차트를 벗어나지 않도록 선택 영역 제한
|
|
391
|
+
if navmax < 0 or self.list_index[-1] < navmin:
|
|
392
|
+
seg = self.navigator.get_segments()
|
|
393
|
+
navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
|
|
394
|
+
|
|
395
|
+
nsub = navmax - navmin
|
|
396
|
+
if nsub < self.min_distance:
|
|
397
|
+
self._restore_region(False, False)
|
|
398
|
+
self._set_navigator(navmin, navmax)
|
|
399
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
400
|
+
else:
|
|
401
|
+
self._set_lim(navmin, navmax, simpler=True, set_ma=(nsub < self.limit_ma))
|
|
402
|
+
self._set_navigator(navmin, navmax)
|
|
403
|
+
|
|
404
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
405
|
+
|
|
406
|
+
self._draw_artist()
|
|
407
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
408
|
+
self._restore_region(False, True)
|
|
409
|
+
return
|
|
376
410
|
|
|
377
|
-
self.ax_volume.xaxis.draw(renderer)
|
|
378
|
-
self.ax_volume.yaxis.draw(renderer)
|
|
379
411
|
|
|
380
|
-
|
|
412
|
+
class ReleaseMixin(SliderSelectMixin):
|
|
413
|
+
def _connect_event(self):
|
|
414
|
+
super()._connect_event()
|
|
415
|
+
|
|
416
|
+
self.figure.canvas.mpl_connect('button_release_event', lambda x: self._on_release(x))
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
def _on_release(self, e: MouseEvent):
|
|
420
|
+
if self.in_slider and self.is_click_slider: self._on_release_slider(e)
|
|
421
|
+
return
|
|
422
|
+
|
|
423
|
+
def _on_release_slider(self, e: MouseEvent):
|
|
424
|
+
if not self.is_move:
|
|
425
|
+
seg = self.navigator.get_segments()
|
|
426
|
+
navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
|
|
427
|
+
nsub = navmax - navmin
|
|
428
|
+
if self.min_distance <= nsub: self.navcoordinate = (navmin, navmax)
|
|
429
|
+
else:
|
|
430
|
+
self.background_with_nav = self.background_with_nav_pre
|
|
431
|
+
navmin, navmax = self.navcoordinate
|
|
432
|
+
self._set_lim(navmin, navmax, simpler=True, set_ma=(nsub < self.limit_ma))
|
|
433
|
+
self._restore_region(False, True)
|
|
434
|
+
self._blit()
|
|
435
|
+
self._set_navigator(*self.navcoordinate)
|
|
436
|
+
|
|
437
|
+
self.is_click_slider = False
|
|
438
|
+
self.is_move = False
|
|
439
|
+
self.click_navleft, self.click_navright = (False, False)
|
|
381
440
|
return
|
|
382
441
|
|
|
383
442
|
|
|
384
|
-
class
|
|
443
|
+
class ChartClickMixin(ReleaseMixin):
|
|
385
444
|
is_click_chart = False
|
|
386
445
|
|
|
387
446
|
def _on_click(self, e: MouseEvent):
|
|
388
|
-
if
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
else: return
|
|
392
|
-
else: return
|
|
447
|
+
if self.in_price_chart or self.in_volume_chart: self._on_click_chart(e)
|
|
448
|
+
elif self.in_slider: self._on_click_slider(e)
|
|
449
|
+
return
|
|
393
450
|
|
|
394
|
-
|
|
451
|
+
def _on_click_chart(self, e: MouseEvent):
|
|
452
|
+
if self.is_click_chart: return
|
|
395
453
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
gtl, ltr = (lmax < x, x < rmin)
|
|
402
|
-
if gtl and ltr:
|
|
403
|
-
self._x_click = x
|
|
404
|
-
self.is_move = True
|
|
405
|
-
self.canvas.set_cursor(cursors.MOVE)
|
|
406
|
-
else:
|
|
407
|
-
self.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
408
|
-
if not gtl and lmin <= x:
|
|
409
|
-
self._x_click = right
|
|
410
|
-
elif not ltr and x <= rmax:
|
|
411
|
-
self._x_click = left
|
|
412
|
-
else:
|
|
413
|
-
self._x_click = x
|
|
454
|
+
self.is_click_chart = True
|
|
455
|
+
self._x_click = e.x.__round__(2)
|
|
456
|
+
self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
457
|
+
return
|
|
414
458
|
|
|
415
|
-
|
|
416
|
-
if
|
|
417
|
-
|
|
459
|
+
def _on_release(self, e):
|
|
460
|
+
if self.is_click_chart and (self.in_price_chart or self.in_volume_chart): self._on_release_chart(e)
|
|
461
|
+
elif self.is_click_slider and self.in_slider: self._on_release_slider(e)
|
|
418
462
|
return
|
|
419
463
|
|
|
420
|
-
def
|
|
421
|
-
if not self.is_click: return
|
|
422
|
-
elif e.inaxes is self.ax_slider: return super()._on_release(e)
|
|
423
|
-
elif not self.in_price and not self.in_volume and not self.is_click_chart: return
|
|
424
|
-
# 차트 click release action
|
|
425
|
-
self.canvas.set_cursor(cursors.POINTER)
|
|
426
|
-
self.is_click, self.is_move = (False, False)
|
|
464
|
+
def _on_release_chart(self, e):
|
|
427
465
|
self.is_click_chart = False
|
|
466
|
+
self.figure.canvas.set_cursor(cursors.POINTER)
|
|
467
|
+
return
|
|
468
|
+
|
|
469
|
+
def _change_cursor(self, e):
|
|
470
|
+
if self.is_click_chart: return
|
|
471
|
+
return super()._change_cursor(e)
|
|
472
|
+
|
|
473
|
+
def _on_move(self, e):
|
|
474
|
+
self._on_move_action(e)
|
|
475
|
+
|
|
476
|
+
need_slider_action = self.is_click_slider and self.in_slider
|
|
477
|
+
need_chart_action = False if need_slider_action else self.is_click_chart and (self.in_price_chart or self.in_volume_chart)
|
|
478
|
+
self._restore_region((need_slider_action or need_chart_action))
|
|
479
|
+
|
|
480
|
+
if self.in_slider:
|
|
481
|
+
self._on_move_slider(e)
|
|
482
|
+
elif self.in_price_chart:
|
|
483
|
+
self._on_move_price_chart(e)
|
|
484
|
+
elif self.in_volume_chart:
|
|
485
|
+
self._on_move_volume_chart(e)
|
|
428
486
|
|
|
429
|
-
self._restore_region(empty=True)
|
|
430
|
-
self._copy_bbox()
|
|
431
|
-
self._restore_region()
|
|
432
487
|
self._blit()
|
|
433
488
|
return
|
|
434
489
|
|
|
435
|
-
def
|
|
436
|
-
self.
|
|
437
|
-
|
|
438
|
-
self._x_click = e.x.__int__()
|
|
439
|
-
self.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
|
|
440
|
-
return
|
|
490
|
+
def _on_move_price_chart(self, e):
|
|
491
|
+
if self.is_click_chart: self._move_chart(e)
|
|
492
|
+
return super()._on_move_price_chart(e)
|
|
441
493
|
|
|
442
|
-
def
|
|
443
|
-
if self.is_click_chart: self.
|
|
444
|
-
|
|
445
|
-
return
|
|
494
|
+
def _on_move_volume_chart(self, e):
|
|
495
|
+
if self.is_click_chart: self._move_chart(e)
|
|
496
|
+
return super()._on_move_volume_chart(e)
|
|
446
497
|
|
|
447
|
-
def
|
|
448
|
-
x = e.x.
|
|
449
|
-
left, right = self.
|
|
498
|
+
def _move_chart(self, e: MouseEvent):
|
|
499
|
+
x = e.x.__round__(2)
|
|
500
|
+
left, right = self.navcoordinate
|
|
450
501
|
nsub = right - left
|
|
451
502
|
xsub = x - self._x_click
|
|
452
503
|
xdiv = (xsub / (1200 / nsub)).__int__()
|
|
453
|
-
if xdiv:
|
|
504
|
+
if not xdiv:
|
|
505
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
506
|
+
self._draw_artist()
|
|
507
|
+
else:
|
|
454
508
|
left, right = (left-xdiv, right-xdiv)
|
|
455
|
-
if
|
|
456
|
-
|
|
509
|
+
if right < 0 or self.df.index[-1] < left: self._restore_region(False, True)
|
|
510
|
+
else:
|
|
511
|
+
self.navcoordinate = (left, right)
|
|
512
|
+
self._set_lim(left, right, simpler=True, set_ma=((right-left) < self.limit_ma))
|
|
513
|
+
self._set_navigator(left, right)
|
|
514
|
+
self.navigator.draw(self.figure.canvas.renderer)
|
|
515
|
+
|
|
516
|
+
self._draw_artist()
|
|
517
|
+
self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
|
|
518
|
+
self._restore_region(False, True)
|
|
457
519
|
self._x_click = x
|
|
458
520
|
return
|
|
459
521
|
|
|
460
|
-
def _on_move(self, e):
|
|
461
|
-
self._restore_region(with_nav=(not self.is_click), empty_with_nav=self.is_click)
|
|
462
522
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if self.in_slider and not self.is_click_chart:
|
|
466
|
-
self._change_coordinate()
|
|
467
|
-
if self.is_click:
|
|
468
|
-
nsub = self._navcoordinate[1] - self._navcoordinate[0]
|
|
469
|
-
if self.is_move: self._set_navigator(*self._navcoordinate)
|
|
470
|
-
else:
|
|
471
|
-
self._draw_blit = 900 < nsub
|
|
472
|
-
if self.intx is not None: self._set_navigator(self._x_click, self.intx)
|
|
473
|
-
|
|
474
|
-
if self.min_distance <= nsub: self._lim()
|
|
475
|
-
|
|
476
|
-
self.navigator.draw(self.canvas.renderer)
|
|
477
|
-
self._draw_blit_artist()
|
|
478
|
-
self._slider_move_action(e)
|
|
479
|
-
elif self.is_click:
|
|
480
|
-
if self.is_click_chart and (self.in_price or self.in_volume):
|
|
481
|
-
if (self.vmin, self.vmax) != self._navcoordinate:
|
|
482
|
-
self._change_coordinate_chart(e)
|
|
483
|
-
self._lim()
|
|
484
|
-
self._set_navigator(*self._navcoordinate)
|
|
485
|
-
self.navigator.draw(self.canvas.renderer)
|
|
486
|
-
self._draw_blit_artist()
|
|
487
|
-
else:
|
|
488
|
-
if self.in_slider or self.in_price or self.in_volume:
|
|
489
|
-
self._slider_move_action(e)
|
|
490
|
-
if self.in_price or self.in_volume:
|
|
491
|
-
self._chart_move_action(e)
|
|
523
|
+
class BaseMixin(ChartClickMixin):
|
|
524
|
+
pass
|
|
492
525
|
|
|
493
|
-
self._blit()
|
|
494
|
-
return
|
|
495
526
|
|
|
527
|
+
class Chart(BaseMixin, Mixin):
|
|
528
|
+
def _add_collection(self):
|
|
529
|
+
super()._add_collection()
|
|
530
|
+
return self.add_collection()
|
|
496
531
|
|
|
497
|
-
|
|
498
|
-
|
|
532
|
+
def _draw_artist(self):
|
|
533
|
+
super()._draw_artist()
|
|
534
|
+
return self.draw_artist()
|
|
499
535
|
|
|
536
|
+
def _get_segments(self):
|
|
537
|
+
self.generate_data()
|
|
538
|
+
return super()._get_segments()
|
|
500
539
|
|
|
501
|
-
class Chart(SliderMixin, CM, Mixin):
|
|
502
540
|
def _on_draw(self, e):
|
|
503
541
|
super()._on_draw(e)
|
|
504
542
|
return self.on_draw(e)
|
|
@@ -511,40 +549,11 @@ class Chart(SliderMixin, CM, Mixin):
|
|
|
511
549
|
super()._on_move(e)
|
|
512
550
|
return self.on_move(e)
|
|
513
551
|
|
|
514
|
-
def _draw_artist(self):
|
|
515
|
-
super()._draw_artist()
|
|
516
|
-
return self.draw_artist()
|
|
517
|
-
def _draw_blit_artist(self):
|
|
518
|
-
super()._draw_blit_artist()
|
|
519
|
-
return self.draw_artist()
|
|
520
|
-
|
|
521
552
|
def _on_click(self, e):
|
|
522
553
|
super()._on_click(e)
|
|
523
554
|
return self.on_click(e)
|
|
524
|
-
def _on_release(self, e):
|
|
525
|
-
super()._on_release(e)
|
|
526
|
-
return self.on_release(e)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if __name__ == '__main__':
|
|
530
|
-
import json
|
|
531
|
-
from time import time
|
|
532
555
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
file = Path(__file__).parent / 'data/samsung.txt'
|
|
537
|
-
# file = Path(__file__).parent / 'data/apple.txt'
|
|
538
|
-
with open(file, 'r', encoding='utf-8') as txt:
|
|
539
|
-
data = json.load(txt)
|
|
540
|
-
data = data
|
|
541
|
-
df = pd.DataFrame(data)
|
|
542
|
-
|
|
543
|
-
t = time()
|
|
544
|
-
# c = SimpleMixin()
|
|
545
|
-
c = SliderMixin()
|
|
546
|
-
c.set_data(df)
|
|
547
|
-
t2 = time() - t
|
|
548
|
-
print(f'{t2=}')
|
|
549
|
-
plt.show()
|
|
556
|
+
def on_release(self, e):
|
|
557
|
+
super().on_release(e)
|
|
558
|
+
return self.on_release(e)
|
|
550
559
|
|