seolpyo-mplchart 1.1.1__py3-none-any.whl → 1.2.1__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.

@@ -0,0 +1,618 @@
1
+ import matplotlib.pyplot as plt
2
+ from matplotlib.axes import Axes
3
+ from matplotlib.collections import LineCollection
4
+ from matplotlib.text import Text
5
+ from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
6
+ import pandas as pd
7
+
8
+ from ._base import convert_unit
9
+ from ._cursor import BaseMixin as BM, Mixin as M
10
+
11
+
12
+ class Mixin(M):
13
+ def on_click(self, e):
14
+ "This function works if mouse button click event active."
15
+ return
16
+ def on_release(self, e):
17
+ "This function works if mouse button release event active."
18
+ return
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': 0.5, 'color': '#d0d0d0', 'linestyle': '-', 'dashes': (1, 0)}
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)
88
+ return
89
+
90
+
91
+ class CollectionMixin(PlotMixin):
92
+ min_distance = 30
93
+ color_navigator_line = '#1e78ff'
94
+ color_navigator_cover = 'k'
95
+
96
+ def _add_collection(self):
97
+ super()._add_collection()
98
+
99
+ self.collection_slider = LineCollection([], animated=True)
100
+ self.ax_slider.add_artist(self.collection_slider)
101
+
102
+ # 슬라이더 네비게이터
103
+ self.navigator = LineCollection([], animated=True, edgecolors=[self.color_navigator_cover, self.color_navigator_line], alpha=(0.3, 1.0))
104
+ self.ax_slider.add_artist(self.navigator)
105
+
106
+ lineKwargs = {'edgecolor': 'k', 'linewidth': 1, 'linestyle': '-'}
107
+ lineKwargs.update(self.lineKwargs)
108
+ lineKwargs.update({'segments': [], 'animated': True})
109
+
110
+ self.slider_vline = LineCollection(**lineKwargs)
111
+ self.ax_slider.add_artist(self.slider_vline)
112
+
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'))
118
+
119
+ self.text_slider = Text(**textKwargs, horizontalalignment='center', verticalalignment='top')
120
+ self.ax_slider.add_artist(self.text_slider)
121
+ return
122
+
123
+ def _get_segments(self):
124
+ super()._get_segments()
125
+
126
+ keys = []
127
+ for i in reversed(self.list_ma):
128
+ keys.append('_x')
129
+ keys.append(f'ma{i}')
130
+
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
+ return
135
+
136
+ def _get_color_segment(self):
137
+ super()._get_color_segment()
138
+
139
+ self.collection_slider.set_edgecolor(self.edgecolor_ma + [self.color_priceline])
140
+ return
141
+
142
+ def change_background_color(self, color):
143
+ super().change_background_color(color)
144
+
145
+ self.ax_slider.set_facecolor(color)
146
+ self.text_slider.set_backgroundcolor(color)
147
+ return
148
+
149
+ def change_tick_color(self, color):
150
+ super().change_tick_color(color)
151
+
152
+ for i in ['top', 'bottom', 'left', 'right']: self.ax_slider.spines[i].set_color(self.color_tick)
153
+ self.ax_slider.tick_params(colors=color)
154
+ return
155
+
156
+ def change_text_color(self, color):
157
+ super().change_text_color(color)
158
+
159
+ self.text_slider.set_color(color)
160
+ return
161
+
162
+ def change_line_color(self, color):
163
+ super().change_line_color(color)
164
+
165
+ self.text_slider.get_bbox_patch().set_edgecolor(color)
166
+ self.slider_vline.set_edgecolor(color)
167
+ return
168
+
169
+
170
+ class NavigatorMixin(CollectionMixin):
171
+ def _set_slider_lim(self):
172
+ xmax = self.list_index[-1]
173
+ # 슬라이더 xlim
174
+ xdistance = xmax / 30
175
+ self.slider_xmin, self.slider_xmax = (-xdistance, xmax + xdistance)
176
+ self.ax_slider.set_xlim(self.slider_xmin, self.slider_xmax)
177
+
178
+ # 슬라이더 ylim
179
+ ymin, ymax = (self.df[self.low].min(), self.df[self.high].max())
180
+ ysub = ymax - ymin
181
+ self.sldier_ymiddle = ymin + (ysub / 2)
182
+ ydistance = ysub / 5
183
+ self.slider_ymin, self.slider_ymax = (ymin-ydistance, ymax+ydistance)
184
+ self.ax_slider.set_ylim(self.slider_ymin, self.slider_ymax)
185
+
186
+ # 슬라이더 텍스트 y
187
+ self.text_slider.set_y(ymax)
188
+
189
+ self.navigator.set_linewidth([ysub, 5])
190
+
191
+ # 네비게이터 라인 선택 범위
192
+ xsub = self.slider_xmax - self.slider_xmin
193
+ self._navLineWidth = xsub * 8 / 1_000
194
+ if self._navLineWidth < 1: self._navLineWidth = 1
195
+ self._navLineWidth_half = self._navLineWidth / 2
196
+ return
197
+
198
+ def _set_navigator(self, navmin, navmax):
199
+ navseg = [
200
+ (
201
+ (self.slider_xmin, self.sldier_ymiddle),
202
+ (navmin, self.sldier_ymiddle)
203
+ ),
204
+ (
205
+ (navmin, self.slider_ymin),
206
+ (navmin, self.slider_ymax)
207
+ ),
208
+ (
209
+ (navmax, self.sldier_ymiddle),
210
+ (self.slider_xmax, self.sldier_ymiddle)
211
+ ),
212
+ (
213
+ (navmax, self.slider_ymin),
214
+ (navmax, self.slider_ymax)
215
+ ),
216
+ ]
217
+
218
+ self.navigator.set_segments(navseg)
219
+ return
220
+
221
+
222
+ class DataMixin(NavigatorMixin):
223
+ navcoordinate = (0, 0)
224
+
225
+ 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):
226
+ self._generate_data(df, sort_df, calc_ma, set_candlecolor, set_volumecolor, calc_info, *args, **kwargs)
227
+ self._get_segments()
228
+
229
+ vmin, vmax = self.navcoordinate
230
+ if change_lim or (vmax-vmin) < self.min_distance:
231
+ vmin, vmax = self.get_default_lim()
232
+ self.navcoordinate = (vmin, vmax)
233
+
234
+ self._set_lim(vmin, vmax)
235
+ self._set_slider_lim()
236
+ self._set_navigator(vmin, vmax)
237
+
238
+ self._length_text = self.df[(self.volume if self.volume else self.high)].apply(lambda x: len(str(x))).max()
239
+ return
240
+
241
+ def get_default_lim(self):
242
+ xmax = self.list_index[-1]
243
+ xmin = xmax - 120
244
+ if xmin < 0: xmin = 0
245
+ return (xmin, xmax)
246
+
247
+
248
+ class BackgroundMixin(DataMixin):
249
+ def _copy_bbox(self):
250
+ renderer = self.figure.canvas.renderer
251
+
252
+ self.ax_slider.xaxis.draw(renderer)
253
+ self.ax_slider.yaxis.draw(renderer)
254
+ self.collection_slider.draw(renderer)
255
+ self.background_emtpy = renderer.copy_from_bbox(self.figure.bbox)
256
+
257
+ self._draw_artist()
258
+ self.background = renderer.copy_from_bbox(self.figure.bbox)
259
+
260
+ self.navigator.draw(self.figure.canvas.renderer)
261
+ self.background_with_nav = renderer.copy_from_bbox(self.figure.bbox)
262
+ return
263
+
264
+ def _restore_region(self, is_empty=False, with_nav=True):
265
+ if not self.background: self._create_background()
266
+
267
+ if is_empty: self.figure.canvas.renderer.restore_region(self.background_emtpy)
268
+ elif with_nav: self.figure.canvas.renderer.restore_region(self.background_with_nav)
269
+ else: self.figure.canvas.renderer.restore_region(self.background)
270
+ return
271
+
272
+
273
+ class MouseMoveMixin(BackgroundMixin):
274
+ in_slider = False
275
+ is_click_slider = False
276
+
277
+ def _on_move(self, e):
278
+ self._on_move_action(e)
279
+
280
+ self._restore_region((self.is_click_slider and self.in_slider))
281
+
282
+ if self.in_slider:
283
+ self._on_move_slider(e)
284
+ elif self.in_price_chart:
285
+ self._on_move_price_chart(e)
286
+ elif self.in_volume_chart:
287
+ self._on_move_volume_chart(e)
288
+
289
+ self._blit()
290
+ return
291
+
292
+ def _on_move_action(self, e: MouseEvent):
293
+ self._check_ax(e)
294
+
295
+ self.intx = None
296
+ if self.in_slider or self.in_price_chart or self.in_volume_chart: self._get_x(e)
297
+
298
+ self._change_cursor(e)
299
+ return
300
+
301
+ def _change_cursor(self, e: MouseEvent):
302
+ # 마우스 커서 변경
303
+ if self.is_click_slider: return
304
+ elif not self.in_slider:
305
+ self.figure.canvas.set_cursor(cursors.POINTER)
306
+ return
307
+
308
+ navleft, navright = self.navcoordinate
309
+ if navleft == navright: return
310
+
311
+ x = e.xdata
312
+ leftmin, leftmax = (navleft-self._navLineWidth, navleft+self._navLineWidth_half)
313
+ rightmin, rightmax = (navright-self._navLineWidth_half, navright+self._navLineWidth)
314
+ if x < leftmin: self.figure.canvas.set_cursor(cursors.POINTER)
315
+ elif x < leftmax: self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
316
+ elif x < rightmin: self.figure.canvas.set_cursor(cursors.MOVE)
317
+ elif x < rightmax: self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
318
+ else: self.figure.canvas.set_cursor(cursors.POINTER)
319
+ return
320
+
321
+ def _check_ax(self, e: MouseEvent):
322
+ ax = e.inaxes
323
+ if not ax or e.xdata is None or e.ydata is None:
324
+ self.in_slider, self.in_price_chart, self.in_volume_chart = (False, False, False)
325
+ else:
326
+ self.in_slider = ax is self.ax_slider
327
+ self.in_price_chart = False if self.in_slider else ax is self.ax_price
328
+ self.in_volume_chart = False if (self.in_slider or self.in_price_chart) else ax is self.ax_volume
329
+ return
330
+
331
+ def _on_move_slider(self, e: MouseEvent):
332
+ x = e.xdata
333
+
334
+ if self.intx is not None:
335
+ renderer = self.figure.canvas.renderer
336
+ self.slider_vline.set_segments([((x, self.slider_ymin), (x, self.slider_ymax))])
337
+ self.slider_vline.draw(renderer)
338
+
339
+ if self.in_slider:
340
+ self.text_slider.set_text(f'{self.df[self.date][self.intx]}')
341
+ self.text_slider.set_x(x)
342
+ self.text_slider.draw(renderer)
343
+ return
344
+
345
+ def _on_move_price_chart(self, e: MouseEvent):
346
+ self.slider_vline.set_segments([((e.xdata, self.slider_ymin), (e.xdata, self.slider_ymax))])
347
+ self.slider_vline.draw(self.figure.canvas.renderer)
348
+ return super()._on_move_price_chart(e)
349
+
350
+ def _on_move_volume_chart(self, e: MouseEvent):
351
+ self.slider_vline.set_segments([((e.xdata, self.slider_ymin), (e.xdata, self.slider_ymax))])
352
+ self.slider_vline.draw(self.figure.canvas.renderer)
353
+ return super()._on_move_volume_chart(e)
354
+
355
+
356
+ class ClickMixin(MouseMoveMixin):
357
+ x_click = None
358
+ is_move = False
359
+ click_navleft, click_navright = (False, False)
360
+
361
+ def _connect_event(self):
362
+ super()._connect_event()
363
+
364
+ self.figure.canvas.mpl_connect('button_press_event', lambda x: self._on_click(x))
365
+ return
366
+
367
+ def _on_click(self, e: MouseEvent):
368
+ if self.in_slider: self._on_click_slider(e)
369
+ return
370
+
371
+ def _on_click_slider(self, e: MouseEvent):
372
+ if self.is_click_slider or e.button != MouseButton.LEFT: return
373
+
374
+ self.background_with_nav_pre = self.background_with_nav
375
+
376
+ self.is_click_slider = True
377
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
378
+
379
+ x = e.xdata.__int__()
380
+ navmin, navmax = self.navcoordinate
381
+
382
+ leftmin, leftmax = (navmin-self._navLineWidth, navmin+self._navLineWidth_half)
383
+ rightmin, rightmax = (navmax-self._navLineWidth_half, navmax+self._navLineWidth)
384
+
385
+ grater_than_left, less_then_right = (leftmax < x, x < rightmin)
386
+ if grater_than_left and less_then_right:
387
+ self.is_move = True
388
+ self.x_click = x
389
+ else:
390
+ if not grater_than_left and leftmin <= x:
391
+ self.click_navleft = True
392
+ self.x_click = navmax
393
+ elif not less_then_right and x <= rightmax:
394
+ self.click_navright = True
395
+ self.x_click = navmin
396
+ else:
397
+ self.x_click = x
398
+ return
399
+
400
+
401
+ class SliderSelectMixin(ClickMixin):
402
+ limit_ma = 8_000
403
+
404
+ def _on_move_slider(self, e):
405
+ if self.is_click_slider: self._set_navcoordinate(e)
406
+ return super()._on_move_slider(e)
407
+
408
+ def _set_navcoordinate(self, e: MouseEvent):
409
+ x = e.xdata.__int__()
410
+ navmin, navmax = self.navcoordinate
411
+
412
+ if self.is_move:
413
+ xsub = self.x_click - x
414
+ navmin, navmax = (navmin-xsub, navmax-xsub)
415
+
416
+ # 값 보정
417
+ if navmax < 0: navmin, navmax = (navmin-navmax, 0)
418
+ if self.list_index[-1] < navmin: navmin, navmax = (self.list_index[-1], self.list_index[-1] + (navmax-navmin))
419
+
420
+ self.navcoordinate = (navmin, navmax)
421
+ self.x_click = x
422
+
423
+ self._set_lim(navmin, navmax, simpler=True, set_ma=(navmax-navmin < self.limit_ma))
424
+
425
+ self._set_navigator(navmin, navmax)
426
+ self.navigator.draw(self.figure.canvas.renderer)
427
+
428
+ self._draw_artist()
429
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
430
+ self._restore_region(False, True)
431
+ else:
432
+ navmin, navmax = (x, self.x_click) if x < self.x_click else (self.x_click, x)
433
+
434
+ # 슬라이더가 차트를 벗어나지 않도록 선택 영역 제한
435
+ if navmax < 0 or self.list_index[-1] < navmin:
436
+ seg = self.navigator.get_segments()
437
+ navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
438
+
439
+ nsub = navmax - navmin
440
+ if nsub < self.min_distance:
441
+ self._restore_region(False, False)
442
+ self._set_navigator(navmin, navmax)
443
+ self.navigator.draw(self.figure.canvas.renderer)
444
+ else:
445
+ self._set_lim(navmin, navmax, simpler=True, set_ma=(nsub < self.limit_ma))
446
+ self._set_navigator(navmin, navmax)
447
+
448
+ self.navigator.draw(self.figure.canvas.renderer)
449
+
450
+ self._draw_artist()
451
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
452
+ self._restore_region(False, True)
453
+ return
454
+
455
+
456
+ class ReleaseMixin(SliderSelectMixin):
457
+ def _connect_event(self):
458
+ super()._connect_event()
459
+
460
+ self.figure.canvas.mpl_connect('button_release_event', lambda x: self._on_release(x))
461
+ return
462
+
463
+ def _on_release(self, e: MouseEvent):
464
+ if self.in_slider and self.is_click_slider: self._on_release_slider(e)
465
+ return
466
+
467
+ def _on_release_slider(self, e: MouseEvent):
468
+ if not self.is_move:
469
+ seg = self.navigator.get_segments()
470
+ navmin, navmax = (int(seg[1][0][0]), int(seg[3][0][0]))
471
+ nsub = navmax - navmin
472
+ if self.min_distance <= nsub: self.navcoordinate = (navmin, navmax)
473
+ else:
474
+ self.background_with_nav = self.background_with_nav_pre
475
+ navmin, navmax = self.navcoordinate
476
+ self._set_lim(navmin, navmax, simpler=True, set_ma=(nsub < self.limit_ma))
477
+ self._restore_region(False, True)
478
+ self._blit()
479
+ self._set_navigator(*self.navcoordinate)
480
+
481
+ self.is_click_slider = False
482
+ self.is_move = False
483
+ self.click_navleft, self.click_navright = (False, False)
484
+ return
485
+
486
+
487
+ class ChartClickMixin(ReleaseMixin):
488
+ is_click_chart = False
489
+
490
+ def _on_click(self, e: MouseEvent):
491
+ if self.in_price_chart or self.in_volume_chart: self._on_click_chart(e)
492
+ elif self.in_slider: self._on_click_slider(e)
493
+ return
494
+
495
+ def _on_click_chart(self, e: MouseEvent):
496
+ if self.is_click_chart: return
497
+
498
+ self.is_click_chart = True
499
+ self._x_click = e.x.__round__(2)
500
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
501
+ return
502
+
503
+ def _on_release(self, e):
504
+ if self.is_click_chart and (self.in_price_chart or self.in_volume_chart): self._on_release_chart(e)
505
+ elif self.is_click_slider and self.in_slider: self._on_release_slider(e)
506
+ return
507
+
508
+ def _on_release_chart(self, e):
509
+ self.is_click_chart = False
510
+ self.figure.canvas.set_cursor(cursors.POINTER)
511
+ return
512
+
513
+ def _change_cursor(self, e):
514
+ if self.is_click_chart: return
515
+ return super()._change_cursor(e)
516
+
517
+ def _on_move(self, e):
518
+ self._on_move_action(e)
519
+
520
+ need_slider_action = self.is_click_slider and self.in_slider
521
+ need_chart_action = False if need_slider_action else self.is_click_chart and (self.in_price_chart or self.in_volume_chart)
522
+ self._restore_region((need_slider_action or need_chart_action))
523
+
524
+ if self.in_slider:
525
+ self._on_move_slider(e)
526
+ elif self.in_price_chart:
527
+ self._on_move_price_chart(e)
528
+ elif self.in_volume_chart:
529
+ self._on_move_volume_chart(e)
530
+
531
+ self._blit()
532
+ return
533
+
534
+ def _on_move_price_chart(self, e):
535
+ if self.is_click_chart: self._move_chart(e)
536
+ return super()._on_move_price_chart(e)
537
+
538
+ def _on_move_volume_chart(self, e):
539
+ if self.is_click_chart: self._move_chart(e)
540
+ return super()._on_move_volume_chart(e)
541
+
542
+ def _move_chart(self, e: MouseEvent):
543
+ x = e.x.__round__(2)
544
+ left, right = self.navcoordinate
545
+ nsub = right - left
546
+ xsub = x - self._x_click
547
+ xdiv = (xsub / (1200 / nsub)).__int__()
548
+ if not xdiv:
549
+ self.navigator.draw(self.figure.canvas.renderer)
550
+ self._draw_artist()
551
+ else:
552
+ left, right = (left-xdiv, right-xdiv)
553
+ if right < 0 or self.df.index[-1] < left: self._restore_region(False, True)
554
+ else:
555
+ self.navcoordinate = (left, right)
556
+ self._set_lim(left, right, simpler=True, set_ma=((right-left) < self.limit_ma))
557
+ self._set_navigator(left, right)
558
+ self.navigator.draw(self.figure.canvas.renderer)
559
+
560
+ self._draw_artist()
561
+ self.background_with_nav = self.figure.canvas.renderer.copy_from_bbox(self.figure.bbox)
562
+ self._restore_region(False, True)
563
+ self._x_click = x
564
+ return
565
+
566
+
567
+ class BaseMixin(ChartClickMixin):
568
+ pass
569
+
570
+
571
+ class Chart(BaseMixin, Mixin):
572
+ def _add_collection(self):
573
+ super()._add_collection()
574
+ return self.add_artist()
575
+
576
+ def _draw_artist(self):
577
+ super()._draw_artist()
578
+ return self.draw_artist()
579
+
580
+ def _get_segments(self):
581
+ self.generate_data()
582
+ return super()._get_segments()
583
+
584
+ def _on_draw(self, e):
585
+ super()._on_draw(e)
586
+ return self.on_draw(e)
587
+
588
+ def _on_pick(self, e):
589
+ self.on_pick(e)
590
+ return super()._on_pick(e)
591
+
592
+ def _set_candle_segments(self, index_start, index_end):
593
+ super()._set_candle_segments(index_start, index_end)
594
+ self.set_segment(index_start, index_end)
595
+ return
596
+
597
+ def _set_wick_segments(self, index_start, index_end, simpler=False):
598
+ super()._set_wick_segments(index_start, index_end, simpler)
599
+ self.set_segment(index_start, index_end, simpler)
600
+ return
601
+
602
+ def _set_line_segments(self, index_start, index_end, simpler=False, set_ma=True):
603
+ super()._set_line_segments(index_start, index_end, simpler, set_ma)
604
+ self.set_segment(index_start, index_end, simpler, set_ma)
605
+ return
606
+
607
+ def _on_move(self, e):
608
+ super()._on_move(e)
609
+ return self.on_move(e)
610
+
611
+ def _on_click(self, e):
612
+ super()._on_click(e)
613
+ return self.on_click(e)
614
+
615
+ def on_release(self, e):
616
+ super().on_release(e)
617
+ return self.on_release(e)
618
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: seolpyo-mplchart
3
- Version: 1.1.1
3
+ Version: 1.2.1
4
4
  Summary: Fast candlestick chart using Python. Includes navigator, slider, navigation, and text information display functions
5
5
  Author-email: white-seolpyo <white-seolpyo@naver.com>
6
6
  License: MIT License
@@ -1,4 +1,8 @@
1
- seolpyo_mplchart/__init__.py,sha256=JP4O-rrw4sLIiagUi1BH3tvXwWdPSPpWTOLCmS6o7A0,18176
1
+ seolpyo_mplchart/__init__.py,sha256=ruj3FHpJD6PxBxWrzr-eIuhoGLiwXNJOdpBWHdxlOfE,18189
2
+ seolpyo_mplchart/_base.py,sha256=0qdImsIMPzTTkkHzPv479BVe_ojrn45FidGE46eT5x4,3797
3
+ seolpyo_mplchart/_cursor.py,sha256=SagSVM46ATmSswB_klmlIMtw9tX3nYuiI1FKVYtQbYk,20416
4
+ seolpyo_mplchart/_draw.py,sha256=J2gthHuDifXwVtoSD-prAfPYxoKj-YC4cVp1DW7Xf3g,22107
5
+ seolpyo_mplchart/_slider.py,sha256=0HsNeEf7lClFYB1WHkS3gbkWz_XKQlJ9q3YTYjcwS-4,22824
2
6
  seolpyo_mplchart/base.py,sha256=0qdImsIMPzTTkkHzPv479BVe_ojrn45FidGE46eT5x4,3797
3
7
  seolpyo_mplchart/cursor.py,sha256=qq1WJJa7vCE5C2XeGBECt2XqxR_WxfybZZ5u6zVx5Ps,20415
4
8
  seolpyo_mplchart/draw.py,sha256=MiqDhbMJOSlWD6qdAghsyrZwCixcwm4nfmiinvwtt-o,21520
@@ -7,7 +11,7 @@ seolpyo_mplchart/test.py,sha256=TFnIXphJsl-B7iIhBh7-PZKUz2Gjh7mwNwrk8aUS4SA,180
7
11
  seolpyo_mplchart/utils.py,sha256=a3XycRBTndrsjBw_1VKTxbSvOGpVYXHRK87v7azgRe8,1433
8
12
  seolpyo_mplchart/data/apple.txt,sha256=0izAfweu1lLsC0IwVthdVlo9reG8KGbKGTSX5knI5Zc,1380864
9
13
  seolpyo_mplchart/data/samsung.txt,sha256=UejaSkbzr4E5K3lkelCT0yJiWUPfmViBEaTyoXyphIs,2476424
10
- seolpyo_mplchart-1.1.1.dist-info/METADATA,sha256=wLwMeRNY8zIC2vAUtW2ZYe3cbCGnV72tUgw8TYx_cCs,2352
11
- seolpyo_mplchart-1.1.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
12
- seolpyo_mplchart-1.1.1.dist-info/top_level.txt,sha256=KgqFn7rKWize7OjMaTCHxKm9ie6vqnyb5c8fN7y_tSo,17
13
- seolpyo_mplchart-1.1.1.dist-info/RECORD,,
14
+ seolpyo_mplchart-1.2.1.dist-info/METADATA,sha256=pCHqJax5S7KPfxt4EemBYDCmnABIT2pF6dm6e8ijr7c,2352
15
+ seolpyo_mplchart-1.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
16
+ seolpyo_mplchart-1.2.1.dist-info/top_level.txt,sha256=KgqFn7rKWize7OjMaTCHxKm9ie6vqnyb5c8fN7y_tSo,17
17
+ seolpyo_mplchart-1.2.1.dist-info/RECORD,,