seolpyo-mplchart 1.4.1__py3-none-any.whl → 2.1.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.
Files changed (83) hide show
  1. seolpyo_mplchart/__init__.py +53 -333
  2. seolpyo_mplchart/_chart/__init__.py +145 -0
  3. seolpyo_mplchart/_chart/_base.py +217 -0
  4. seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
  5. seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
  6. seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
  7. seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
  8. seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
  9. seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
  10. seolpyo_mplchart/_chart/_draw/_data.py +314 -0
  11. seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
  12. seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
  13. seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
  14. seolpyo_mplchart/_chart/_slider/_base.py +268 -0
  15. seolpyo_mplchart/_chart/_slider/_data.py +105 -0
  16. seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
  17. seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
  18. seolpyo_mplchart/_chart/base/__init__.py +111 -0
  19. seolpyo_mplchart/_chart/base/a_canvas.py +250 -0
  20. seolpyo_mplchart/_chart/base/b_artist.py +143 -0
  21. seolpyo_mplchart/_chart/base/c_draw.py +100 -0
  22. seolpyo_mplchart/_chart/base/d_segment.py +262 -0
  23. seolpyo_mplchart/_chart/base/e_axis.py +267 -0
  24. seolpyo_mplchart/_chart/base/f_background.py +62 -0
  25. seolpyo_mplchart/_chart/base/g_event.py +66 -0
  26. seolpyo_mplchart/_chart/base/h_data.py +138 -0
  27. seolpyo_mplchart/_chart/base/test.py +58 -0
  28. seolpyo_mplchart/_chart/cursor/__init__.py +125 -0
  29. seolpyo_mplchart/_chart/cursor/b_artist.py +130 -0
  30. seolpyo_mplchart/_chart/cursor/c_draw.py +96 -0
  31. seolpyo_mplchart/_chart/cursor/d_segment.py +359 -0
  32. seolpyo_mplchart/_chart/cursor/e_axis.py +65 -0
  33. seolpyo_mplchart/_chart/cursor/g_event.py +233 -0
  34. seolpyo_mplchart/_chart/cursor/h_data.py +61 -0
  35. seolpyo_mplchart/_chart/cursor/test.py +69 -0
  36. seolpyo_mplchart/_chart/slider/__init__.py +169 -0
  37. seolpyo_mplchart/_chart/slider/a_canvas.py +260 -0
  38. seolpyo_mplchart/_chart/slider/b_artist.py +91 -0
  39. seolpyo_mplchart/_chart/slider/c_draw.py +54 -0
  40. seolpyo_mplchart/_chart/slider/d_segment.py +166 -0
  41. seolpyo_mplchart/_chart/slider/e_axis.py +70 -0
  42. seolpyo_mplchart/_chart/slider/f_background.py +37 -0
  43. seolpyo_mplchart/_chart/slider/g_event.py +353 -0
  44. seolpyo_mplchart/_chart/slider/h_data.py +102 -0
  45. seolpyo_mplchart/_chart/slider/test.py +71 -0
  46. seolpyo_mplchart/_chart/test.py +121 -0
  47. seolpyo_mplchart/_config/__init__.py +3 -0
  48. seolpyo_mplchart/_config/ax.py +28 -0
  49. seolpyo_mplchart/_config/candle.py +31 -0
  50. seolpyo_mplchart/_config/config.py +21 -0
  51. seolpyo_mplchart/_config/cursor.py +49 -0
  52. seolpyo_mplchart/_config/figure.py +40 -0
  53. seolpyo_mplchart/_config/format.py +51 -0
  54. seolpyo_mplchart/_config/ma.py +17 -0
  55. seolpyo_mplchart/_config/slider/__init__.py +2 -0
  56. seolpyo_mplchart/_config/slider/config.py +24 -0
  57. seolpyo_mplchart/_config/slider/figure.py +19 -0
  58. seolpyo_mplchart/_config/slider/nav.py +10 -0
  59. seolpyo_mplchart/_config/unit.py +19 -0
  60. seolpyo_mplchart/_config/utils.py +67 -0
  61. seolpyo_mplchart/_config/volume.py +27 -0
  62. seolpyo_mplchart/_cursor.py +27 -25
  63. seolpyo_mplchart/_draw.py +7 -18
  64. seolpyo_mplchart/_slider.py +26 -20
  65. seolpyo_mplchart/_utils/__init__.py +10 -0
  66. seolpyo_mplchart/_utils/nums.py +67 -0
  67. seolpyo_mplchart/_utils/theme/__init__.py +15 -0
  68. seolpyo_mplchart/_utils/theme/dark.py +57 -0
  69. seolpyo_mplchart/_utils/theme/light.py +56 -0
  70. seolpyo_mplchart/_utils/utils.py +28 -0
  71. seolpyo_mplchart/_utils/xl/__init__.py +15 -0
  72. seolpyo_mplchart/_utils/xl/csv.py +46 -0
  73. seolpyo_mplchart/_utils/xl/xlsx.py +49 -0
  74. seolpyo_mplchart/sample/apple.txt +6058 -0
  75. seolpyo_mplchart/sample/samsung.txt +5938 -0
  76. seolpyo_mplchart/test.py +172 -56
  77. seolpyo_mplchart/xl_to_dict.py +47 -0
  78. seolpyo_mplchart-2.1.0.dist-info/METADATA +718 -0
  79. seolpyo_mplchart-2.1.0.dist-info/RECORD +89 -0
  80. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/WHEEL +1 -1
  81. seolpyo_mplchart-1.4.1.dist-info/METADATA +0 -57
  82. seolpyo_mplchart-1.4.1.dist-info/RECORD +0 -17
  83. {seolpyo_mplchart-1.4.1.dist-info → seolpyo_mplchart-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,166 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.backend_bases import MouseEvent
3
+ from matplotlib.collections import LineCollection
4
+ from matplotlib.text import Text
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ from ..._config import SliderConfigData
9
+
10
+
11
+ class Base:
12
+ CONFIG: SliderConfigData
13
+ df: pd.DataFrame
14
+
15
+ ax_slider: Axes
16
+ collection_slider: LineCollection
17
+ collection_nav: LineCollection
18
+ collection_slider_vline: LineCollection
19
+ artist_text_slider: Text
20
+
21
+ set_segments: callable
22
+ set_color_segments: callable
23
+
24
+ slider_ymin: float
25
+ slider_ymax: float
26
+
27
+ slider_xmin: float
28
+ slider_xmax: float
29
+ _nav_width: float
30
+
31
+
32
+ class SliderMixin(Base):
33
+ def _set_slider_collection(self):
34
+ keys = []
35
+ for i in reversed(self.CONFIG.MA.ma_list):
36
+ keys.append('x')
37
+ keys.append(f'ma{i}')
38
+
39
+ series = self.df[keys + ['x', 'close']]
40
+ series['x'] = series['x'] - 0.5
41
+ segment_slider = series.values
42
+ sizes = [segment_slider.shape[0], len(self.CONFIG.MA.ma_list)+1, 2]
43
+ segment_slider = segment_slider.reshape(*sizes).swapaxes(0, 1)
44
+ self.collection_slider.set_segments(segment_slider)
45
+
46
+ linewidth = []
47
+ ma_colors = []
48
+ for n, _ in enumerate(self.CONFIG.MA.ma_list):
49
+ linewidth.append(0.9)
50
+ try:
51
+ ma_colors.append(self.CONFIG.MA.color_list[n])
52
+ except:
53
+ ma_colors.append(self.CONFIG.MA.color_default)
54
+
55
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
56
+
57
+ self.collection_slider.set_linewidth(linewidth + [2.4])
58
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
59
+ return
60
+
61
+ def _set_slider_color_collection(self):
62
+ ma_colors = []
63
+ for n, _ in enumerate(self.CONFIG.MA.ma_list):
64
+ try:
65
+ ma_colors.append(self.CONFIG.MA.color_list[n])
66
+ except:
67
+ ma_colors.append(self.CONFIG.MA.color_default)
68
+
69
+ self.collection_slider.set_edgecolor(ma_colors + [self.CONFIG.CANDLE.line_color])
70
+ return
71
+
72
+
73
+ class VlineMixin(Base):
74
+ in_slider: bool
75
+
76
+ def _set_slider_vline(self, e: MouseEvent):
77
+ xdata = e.xdata
78
+ if xdata is None:
79
+ return
80
+ xdata = round(xdata, 2)
81
+ # print(f'{xdata=}')
82
+ if not self.in_slider:
83
+ xdata -= 0.5
84
+
85
+ seg = [((xdata, self.slider_ymin), (xdata, self.slider_ymax))]
86
+ # print(f'{seg=}')
87
+ self.collection_slider_vline.set_segments(seg)
88
+ return
89
+
90
+ def _set_slider_text(self, e: MouseEvent):
91
+ xdata = e.xdata
92
+ if xdata is None:
93
+ return
94
+ idx = round(xdata)
95
+ if idx < 0:
96
+ return
97
+
98
+ try:
99
+ text = self.df.iloc[idx]['date']
100
+ except:
101
+ return
102
+
103
+ self.artist_text_slider.set_text(text)
104
+ self.artist_text_slider.set_x(round(xdata, 2))
105
+ return 1
106
+
107
+
108
+ class NavMixin(Base):
109
+ segment_nav: np.ndarray
110
+
111
+ def _set_nav_segment(self, xmin, xmax):
112
+ xmin0 = xmin - self._nav_width
113
+ xmin1 = xmin
114
+ xmax0 = xmax
115
+ xmax1 = xmax + self._nav_width
116
+ seg = [
117
+ # 좌측 오버레이
118
+ (
119
+ (self.slider_xmin, self.slider_ymax),
120
+ (xmin0, self.slider_ymax),
121
+ (xmin0, self.slider_ymin),
122
+ (self.slider_xmin, self.slider_ymin),
123
+ ),
124
+ # 우측 오버레이
125
+ (
126
+ (xmax1, self.slider_ymax),
127
+ (self.slider_xmax, self.slider_ymax),
128
+ (self.slider_xmax, self.slider_ymin),
129
+ (xmax1, self.slider_ymin),
130
+ ),
131
+ # 좌측 네비게이터
132
+ (
133
+ (xmin1, self.slider_ymax),
134
+ (xmin1, self.slider_ymin),
135
+ (xmin0, self.slider_ymin),
136
+ (xmin0, self.slider_ymax),
137
+ ),
138
+ # 우측 네비게이터
139
+ (
140
+ (xmax0, self.slider_ymin),
141
+ (xmax0, self.slider_ymax),
142
+ (xmax1, self.slider_ymax),
143
+ (xmax1, self.slider_ymin),
144
+ ),
145
+ ]
146
+ self.segment_nav = np.array(seg)
147
+
148
+ self.collection_nav.set_segments(self.segment_nav)
149
+ return
150
+
151
+
152
+ class SegmentMixin(SliderMixin, VlineMixin, NavMixin):
153
+ segment_nav: np.ndarray
154
+
155
+ def set_segments(self):
156
+ super().set_segments()
157
+
158
+ self._set_slider_collection()
159
+ self._set_slider_color_collection()
160
+ return
161
+
162
+ def set_color_segments(self):
163
+ super().set_color_segments()
164
+
165
+ self._set_slider_color_collection()
166
+ return
@@ -0,0 +1,70 @@
1
+ from matplotlib.collections import LineCollection
2
+ import numpy as np
3
+
4
+
5
+ class Base:
6
+ limit_volume = 200
7
+ limit_ma = 8_000
8
+
9
+ x_click: int
10
+ vxmin: int
11
+ vxmax: int
12
+
13
+ axis: callable
14
+ _set_nav_segment: callable
15
+ set_collections: callable
16
+
17
+ key_volume: str
18
+ segment_volume_wick: np.ndarray
19
+ _set_volume_collection_wick_segments: callable
20
+ _set_ma_collection_segments: callable
21
+ facecolor_volume: np.ndarray
22
+ edgecolor_volume: np.ndarray
23
+ collection_volume: LineCollection
24
+ collection_ma: LineCollection
25
+
26
+
27
+ class Mixin(Base):
28
+ def axis(self, xmin, *, xmax):
29
+ super().axis(xmin, xmax=xmax)
30
+ self._set_nav_segment(self.vxmin, xmax=self.vxmax-1)
31
+ return
32
+
33
+ def _set_volume_collection_wick_segments(self, ind_start, ind_end):
34
+ if not self.key_volume or not self.x_click:
35
+ super()._set_volume_collection_wick_segments(ind_start, ind_end=ind_end)
36
+ else:
37
+ indsub = ind_end - ind_start
38
+ if indsub <= self.limit_volume:
39
+ super()._set_volume_collection_wick_segments(ind_start, ind_end=ind_end)
40
+ else:
41
+ # 일부 거래량만 그리기
42
+ seg_volume = self.segment_volume_wick[ind_start:ind_end]
43
+ values = seg_volume[:, 1, 1]
44
+ top_index = np.argsort(-values)[:self.limit_volume]
45
+ seg = seg_volume[top_index]
46
+ facecolors = self.facecolor_volume[ind_start:ind_end][top_index]
47
+ edgecolors = self.edgecolor_volume[ind_start:ind_end][top_index]
48
+ self.collection_volume.set_segments(seg)
49
+ self.collection_volume.set_linewidth(1.3)
50
+ self.collection_volume.set_facecolor(facecolors)
51
+ self.collection_volume.set_edgecolor(edgecolors)
52
+ return
53
+
54
+ def _set_ma_collection_segments(self, ind_start, ind_end):
55
+ if not self.x_click:
56
+ super()._set_ma_collection_segments(ind_start, ind_end=ind_end)
57
+ else:
58
+ indsub = ind_end - ind_start
59
+ if indsub <= self.limit_ma:
60
+ super()._set_ma_collection_segments(ind_start, ind_end=ind_end)
61
+ else:
62
+ # 이평선 그리지 않기
63
+ self.collection_ma.set_segments([])
64
+ return
65
+
66
+
67
+ class AxisMixin(Mixin):
68
+ limit_volume = 200
69
+ limit_ma = 8_000
70
+
@@ -0,0 +1,37 @@
1
+ from matplotlib.axes import Axes
2
+
3
+
4
+ class Base:
5
+ _background = None
6
+ _background_background = None
7
+
8
+ _creating_background = False
9
+
10
+ draw_background: callable
11
+ _draw_ax: callable
12
+ _draw_ax_slider: callable
13
+ draw_artists: callable
14
+ _draw_nav: callable
15
+
16
+
17
+ class SliderMixin(Base):
18
+ ax_slider: Axes
19
+
20
+ def draw_background(self):
21
+ self._draw_ax(self.ax_slider)
22
+ self._draw_ax_slider()
23
+
24
+ super().draw_background()
25
+ return
26
+
27
+ def draw_artists(self):
28
+ super().draw_artists()
29
+
30
+ self._draw_nav()
31
+ return
32
+
33
+
34
+ class BackgroundMixin(SliderMixin):
35
+ _background = None
36
+ _creating_background = False
37
+
@@ -0,0 +1,353 @@
1
+ from matplotlib.axes import Axes
2
+ from matplotlib.backend_bases import MouseEvent, MouseButton, cursors
3
+ from matplotlib.collections import LineCollection
4
+ from matplotlib.text import Text
5
+ import numpy as np
6
+
7
+ from ..cursor.g_event import EventMixin as Base
8
+
9
+
10
+ class BaseMixin(Base):
11
+ ax_slider: Axes
12
+
13
+ slider_xmin: int
14
+ slider_xmax: int
15
+ slider_ymin: float
16
+ slider_ymax: float
17
+
18
+ in_slider = False
19
+ is_click_slider = False
20
+ is_click_chart = False
21
+ is_move_chart = False
22
+
23
+ click_nav_left = False
24
+ click_nav_right = False
25
+
26
+ min_distance = 5
27
+
28
+ segment_nav: np.ndarray
29
+
30
+ _set_slider_vline: callable
31
+ _draw_slider_vline: callable
32
+ _set_slider_text: callable
33
+ _draw_slider_text: callable
34
+ draw_chart: callable
35
+ axis: callable
36
+ _set_cursor: callable
37
+ _move_chart: callable
38
+ _draw_nav: callable
39
+ _restore_region_background: callable
40
+
41
+ collection_slider_vline: LineCollection
42
+ artist_text_slider: Text
43
+ collection_nav: LineCollection
44
+
45
+ x_click: int
46
+ _nav_width: float
47
+ navcoordinate: tuple[int, int]
48
+ vxmin: int
49
+ vxmax: int
50
+
51
+ def get_nav_xlim(self):
52
+ seg = self.segment_nav
53
+ # print(f'{seg=}')
54
+ xmin = seg[-2][0][0]
55
+ xmax = seg[-1][0][0]
56
+
57
+ return (int(xmin), int(xmax))
58
+
59
+
60
+ class CursorMixin(BaseMixin):
61
+ def _set_cursor(self, e: MouseEvent):
62
+ # 마우스 커서 변경
63
+ if self.is_click_slider:
64
+ return
65
+ elif not self.in_slider:
66
+ self.figure.canvas.set_cursor(cursors.POINTER)
67
+ return
68
+
69
+ xmin, xmax = self.get_nav_xlim()
70
+ if xmin == xmax:
71
+ return
72
+
73
+ x = e.xdata
74
+
75
+ left0 = xmin - self._nav_width
76
+ left1 = xmin
77
+
78
+ if left0 <= x and x <= left1:
79
+ # 커서가 좌경계선 위에 위치
80
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
81
+ return
82
+
83
+ right0 = xmax
84
+ right1 = xmax + self._nav_width
85
+ if right0 <= x and x <= right1:
86
+ # 커서가 우경계선 위에 위치
87
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
88
+ return
89
+
90
+ if left1 < x and x < right0:
91
+ # 커서가 조회영역 위에 위치
92
+ self.figure.canvas.set_cursor(cursors.MOVE)
93
+ return
94
+
95
+ self.figure.canvas.set_cursor(cursors.POINTER)
96
+ return
97
+
98
+
99
+ class AxMixin(BaseMixin):
100
+ def _check_ax(self, e: MouseEvent):
101
+ ax = e.inaxes
102
+ # print(f'{ax=}')
103
+ self.in_chart = False
104
+ self.in_slider, self.in_chart_price, self.in_chart_volume = (False, False, False)
105
+
106
+ if e.xdata is None or e.ydata is None:
107
+ return
108
+
109
+ if ax is self.ax_slider:
110
+ if (
111
+ (self.slider_xmin <= e.xdata and e.xdata <= self.slider_xmax)
112
+ and (self.slider_ymin <= e.ydata and e.ydata <= self.slider_ymax)
113
+ ):
114
+ self.in_slider = True
115
+ else:
116
+ super()._check_ax(e)
117
+ return
118
+
119
+
120
+ class ClickMixin(BaseMixin):
121
+ is_click_slider = False
122
+ is_click_chart = False
123
+
124
+ click_nav_left = False
125
+ click_nav_right = False
126
+
127
+ def on_click(self, e: MouseEvent):
128
+ if e.xdata is not None and e.ydata is not None:
129
+ if self.in_chart and not self.is_click_chart:
130
+ if e.button == MouseButton.LEFT:
131
+ self._on_click_chart(e)
132
+ elif self.in_slider and not self.is_click_slider:
133
+ if e.button == MouseButton.LEFT:
134
+ self._on_click_slider(e)
135
+ return
136
+
137
+ def _on_click_chart(self, e: MouseEvent):
138
+ # 조회영역 이동 시작
139
+ self.is_click_chart = True
140
+ self.is_move_chart = True
141
+
142
+ x = int(e.xdata)
143
+ self.x_click = x
144
+
145
+ xmin, xmax = self.get_nav_xlim()
146
+ self.navcoordinate = (xmin, xmax)
147
+
148
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
149
+ return
150
+
151
+ def _on_click_slider(self, e: MouseEvent):
152
+ self.is_click_slider = True
153
+ self.figure.canvas.set_cursor(cursors.RESIZE_HORIZONTAL)
154
+
155
+ xmin, xmax = self.get_nav_xlim()
156
+ x = round(e.xdata)
157
+
158
+ xmin0 = xmin - self._nav_width
159
+ xmin1 = xmin
160
+ if xmin0 <= x and x <= xmin1:
161
+ # 좌경계선 이동 시작
162
+ self.navcoordinate = (xmin, xmax)
163
+ self.click_nav_left = True
164
+ self.x_click = xmin
165
+ return
166
+
167
+ xmax0 = xmax
168
+ if xmin1 < x and x < xmax0:
169
+ # 조회영역 이동 시작
170
+ self.navcoordinate = (xmin, xmax)
171
+ self.is_move_chart = True
172
+ self.x_click = x
173
+ return
174
+
175
+ if xmax0 <= x and x <= (xmax + self._nav_width):
176
+ # 우경계선 이동 시작
177
+ self.navcoordinate = (xmin, xmax)
178
+ self.click_nav_right = True
179
+ self.x_click = xmax
180
+ return
181
+
182
+ self.navcoordinate = (xmin, xmax)
183
+ self.x_click = x
184
+ return
185
+
186
+
187
+ class ReleaseMixin(BaseMixin):
188
+ def on_release(self, e: MouseEvent):
189
+ if e.button == MouseButton.LEFT:
190
+ if (
191
+ self.is_click_chart
192
+ and self.in_chart
193
+ ):
194
+ self._on_release_chart(e)
195
+ elif self.is_click_slider and self.in_slider:
196
+ self._on_release_slider(e)
197
+ return
198
+
199
+ def _on_release_chart(self, e):
200
+ self.x_click = None
201
+ self.is_click_chart = False
202
+ self.is_move_chart = False
203
+ self.figure.canvas.set_cursor(cursors.POINTER)
204
+
205
+ xmin, xmax = self.navcoordinate
206
+ self.axis(xmin, xmax=xmax)
207
+ self.figure.canvas.draw()
208
+ return
209
+
210
+ def _on_release_slider(self, e: MouseEvent):
211
+ if self.x_click:
212
+ xmin, xmax = self.get_nav_xlim()
213
+ xsub = xmax - xmin
214
+ min_distance = 5 if not self.min_distance or self.min_distance < 5 else self.min_distance
215
+ # print(f'{xsub=}')
216
+ # print(f'{min_distance=}')
217
+ if min_distance <= xsub:
218
+ self.navcoordinate = (xmin, xmax)
219
+ else:
220
+ xmin, xmax = self.navcoordinate
221
+
222
+ self.x_click = None
223
+ self.is_click_slider = False
224
+ self.is_move_chart = False
225
+ self.click_nav_left, self.click_nav_right = (False, False)
226
+ self.axis(xmin, xmax=xmax)
227
+
228
+ self.figure.canvas.draw()
229
+ return
230
+
231
+
232
+ class ChartMixin(BaseMixin):
233
+ def _move_chart(self, e: MouseEvent):
234
+ if self.is_click_chart and self.in_chart:
235
+ xdata = int(e.xdata)
236
+ # print(f'{(self.x_click, xdata)=}')
237
+ xsub = self.x_click - xdata
238
+ if xsub:
239
+ pre_xmin, pre_xmax = self.navcoordinate
240
+ xmin, xmax = (pre_xmin+xsub, pre_xmax+xsub)
241
+ # print(f'{(xmin, xmax)=}')
242
+ if 0 <= xmax and xmin <= self.index_list[-1] and xmin != xmax:
243
+ self.axis(xmin, xmax=xmax)
244
+ self.navcoordinate = (xmin, xmax)
245
+ elif self.is_click_slider and self.in_slider:
246
+ xdata = round(e.xdata)
247
+ pre_xmin, pre_xmax = self.navcoordinate
248
+ if self.click_nav_left:
249
+ if xdata < pre_xmax:
250
+ xmin, xmax = (xdata, pre_xmax)
251
+ else:
252
+ xmin, xmax = (pre_xmax, xdata)
253
+ elif self.click_nav_right:
254
+ if xdata < pre_xmin:
255
+ xmin, xmax = (xdata, pre_xmin)
256
+ else:
257
+ xmin, xmax = (pre_xmin, xdata)
258
+ else:
259
+ if self.is_move_chart:
260
+ xsub = self.x_click - xdata
261
+ xmax = -1
262
+ if xsub:
263
+ pre_xmin, pre_xmax = self.navcoordinate
264
+ xmin, xmax = (pre_xmin-xsub, pre_xmax-xsub)
265
+ else:
266
+ if xdata == self.x_click:
267
+ xmax = -1
268
+ elif xdata < self.x_click:
269
+ xmin, xmax = (xdata, self.x_click)
270
+ else:
271
+ xmin, xmax = (self.x_click, xdata)
272
+
273
+ if 0 <= xmax and xmin <= self.index_list[-1] and xmin != xmax:
274
+ self.axis(xmin, xmax=xmax)
275
+ return
276
+
277
+
278
+ class MoveMixin(BaseMixin):
279
+ def _set_and_draw_vline(self, e: MouseEvent):
280
+ self._set_slider_vline(e)
281
+ self._draw_slider_vline()
282
+ if self.in_slider and self._set_slider_text(e):
283
+ self._draw_slider_text()
284
+ return
285
+
286
+ def _set_and_draw_crossline(self, e: MouseEvent):
287
+ self._set_and_draw_vline(e)
288
+
289
+ super()._set_and_draw_crossline(e)
290
+ return
291
+
292
+ def _on_move_action(self, e: MouseEvent):
293
+ if e.xdata is not None:
294
+ if self.x_click is not None:
295
+ self._restore_region_background()
296
+
297
+ self._move_chart(e)
298
+ self.draw_chart()
299
+ self._draw_nav()
300
+ if self.in_slider:
301
+ self._set_and_draw_vline(e)
302
+
303
+ self.figure.canvas.blit()
304
+ self.figure.canvas.flush_events()
305
+ else:
306
+ self._set_cursor(e)
307
+
308
+ if self.in_slider:
309
+ self._restore_region()
310
+
311
+ self._set_and_draw_vline(e)
312
+
313
+ self.figure.canvas.blit()
314
+ self.figure.canvas.flush_events()
315
+ else:
316
+ super()._on_move_action(e)
317
+ else:
318
+ super()._on_move_action(e)
319
+ return
320
+
321
+ def need_restore(self):
322
+ if not super().need_restore():
323
+ if self.collection_slider_vline.get_segments():
324
+ self.collection_slider_vline.set_segments([])
325
+ return True
326
+ return
327
+
328
+
329
+ class EventMixin(AxMixin, CursorMixin, ClickMixin, ReleaseMixin, ChartMixin, MoveMixin,):
330
+ x_click = None
331
+ in_chart = False
332
+ in_slider = False
333
+ in_chart_price = False
334
+ in_chart_volume = False
335
+
336
+ is_move_chart = False
337
+ is_click_slider = False
338
+ is_click_chart = False
339
+ x_click: float
340
+
341
+ click_nav_left = False
342
+ click_nav_right = False
343
+
344
+ min_distance = 5
345
+
346
+ navcoordinate: tuple[int, int]
347
+
348
+ def connect_events(self):
349
+ super().connect_events()
350
+
351
+ self.figure.canvas.mpl_connect('button_press_event', lambda x: self.on_click(x))
352
+ self.figure.canvas.mpl_connect('button_release_event', lambda x: self.on_release(x))
353
+ return