seolpyo-mplchart 0.1.3.1__py3-none-any.whl → 2.0.0.3__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 (50) hide show
  1. seolpyo_mplchart/__init__.py +164 -99
  2. seolpyo_mplchart/_base.py +117 -0
  3. seolpyo_mplchart/_chart/__init__.py +137 -0
  4. seolpyo_mplchart/_chart/_base.py +217 -0
  5. seolpyo_mplchart/_chart/_cursor/__init__.py +2 -0
  6. seolpyo_mplchart/_chart/_cursor/_artist.py +217 -0
  7. seolpyo_mplchart/_chart/_cursor/_cursor.py +165 -0
  8. seolpyo_mplchart/_chart/_cursor/_info.py +187 -0
  9. seolpyo_mplchart/_chart/_draw/__init__.py +2 -0
  10. seolpyo_mplchart/_chart/_draw/_artist.py +50 -0
  11. seolpyo_mplchart/_chart/_draw/_data.py +314 -0
  12. seolpyo_mplchart/_chart/_draw/_draw.py +103 -0
  13. seolpyo_mplchart/_chart/_draw/_lim.py +265 -0
  14. seolpyo_mplchart/_chart/_slider/__init__.py +1 -0
  15. seolpyo_mplchart/_chart/_slider/_base.py +268 -0
  16. seolpyo_mplchart/_chart/_slider/_data.py +105 -0
  17. seolpyo_mplchart/_chart/_slider/_mouse.py +176 -0
  18. seolpyo_mplchart/_chart/_slider/_nav.py +204 -0
  19. seolpyo_mplchart/_chart/test.py +121 -0
  20. seolpyo_mplchart/_config/__init__.py +3 -0
  21. seolpyo_mplchart/_config/ax.py +28 -0
  22. seolpyo_mplchart/_config/candle.py +30 -0
  23. seolpyo_mplchart/_config/config.py +21 -0
  24. seolpyo_mplchart/_config/cursor.py +49 -0
  25. seolpyo_mplchart/_config/figure.py +41 -0
  26. seolpyo_mplchart/_config/format.py +51 -0
  27. seolpyo_mplchart/_config/ma.py +15 -0
  28. seolpyo_mplchart/_config/slider/__init__.py +2 -0
  29. seolpyo_mplchart/_config/slider/config.py +24 -0
  30. seolpyo_mplchart/_config/slider/figure.py +20 -0
  31. seolpyo_mplchart/_config/slider/nav.py +9 -0
  32. seolpyo_mplchart/_config/unit.py +19 -0
  33. seolpyo_mplchart/_config/utils.py +67 -0
  34. seolpyo_mplchart/_config/volume.py +26 -0
  35. seolpyo_mplchart/_cursor.py +559 -0
  36. seolpyo_mplchart/_draw.py +634 -0
  37. seolpyo_mplchart/_slider.py +634 -0
  38. seolpyo_mplchart/base.py +70 -67
  39. seolpyo_mplchart/cursor.py +308 -271
  40. seolpyo_mplchart/draw.py +449 -237
  41. seolpyo_mplchart/slider.py +451 -396
  42. seolpyo_mplchart/test.py +173 -24
  43. seolpyo_mplchart/utils.py +15 -4
  44. seolpyo_mplchart/xl_to_dict.py +47 -0
  45. seolpyo_mplchart-2.0.0.3.dist-info/METADATA +710 -0
  46. seolpyo_mplchart-2.0.0.3.dist-info/RECORD +50 -0
  47. {seolpyo_mplchart-0.1.3.1.dist-info → seolpyo_mplchart-2.0.0.3.dist-info}/WHEEL +1 -1
  48. seolpyo_mplchart-0.1.3.1.dist-info/METADATA +0 -49
  49. seolpyo_mplchart-0.1.3.1.dist-info/RECORD +0 -13
  50. {seolpyo_mplchart-0.1.3.1.dist-info → seolpyo_mplchart-2.0.0.3.dist-info}/top_level.txt +0 -0
seolpyo_mplchart/test.py CHANGED
@@ -1,38 +1,187 @@
1
- import json
1
+ import sys
2
2
  from pathlib import Path
3
- from typing import Literal
3
+ name_pkg = 'seolpyo_mplchart'
4
+ path_pkg = Path(__file__)
5
+ while path_pkg.name != name_pkg:
6
+ path_pkg = path_pkg.parent
7
+ sys.path = [path_pkg.parent.__str__()] + sys.path
8
+
9
+ import json
10
+ import tkinter as tk
11
+ from tkinter import filedialog
4
12
 
5
- import matplotlib.pyplot as plt
6
13
  import pandas as pd
14
+ import matplotlib.pyplot as plt
15
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
7
16
 
17
+ import seolpyo_mplchart as mc
18
+ from seolpyo_mplchart.xl_to_dict import convert
8
19
 
9
- from seolpyo_mplchart import Chart
10
20
 
21
+ path_file = path_pkg / 'sample' / 'apple.txt'
22
+ with open(path_file, 'r', encoding='utf-8') as txt:
23
+ data = json.load(txt)
11
24
 
12
- _name = {'samsung', 'apple'}
13
- def sample(name: Literal['samsung', 'apple']='samsung'):
14
- if name not in _name:
15
- print('name should be either samsung or apple.')
16
- return
17
- file = Path(__file__).parent / f'data/{name}.txt'
18
- with open(file, 'r', encoding='utf-8') as txt:
19
- data = json.load(txt)
20
- data = data
21
- df = pd.DataFrame(data)
22
-
23
- c = Chart()
24
- if name == 'apple':
25
- c.unit_price = '$'
26
- c.unit_volume = 'vol'
27
- c.digit_price = 3
28
- c.label_ma = 'ma{}'
29
- c.candleformat = '{}\n\nclose: {}\nrate: {}\ncompare: {}\nopen: {}({})\nhigh: {}({})\nlow: {}({})\nvolume: {}({})'
30
- c.volumeformat = '{}\n\nvolume: {}\nvolume rate: {}'
25
+ df = pd.DataFrame(data[:])
26
+
27
+
28
+ def test():
29
+ import seolpyo_mplchart as mc
30
+ class C(mc.CursorChart):
31
+ # fraction = True
32
+ # watermark = 0
33
+ theme = 'light'
34
+ limit_wick = 2000
35
+ # limit_ma = None
36
+ # candle_on_ma = False
37
+ # slider_top = False
38
+ def on_click(self, e):
39
+ super().on_click(e)
40
+ # print(f'{e.button=}')
41
+ # print(f'{e.button.__str__()=}')
42
+ if e.button.__str__() == '3':
43
+ # print('refresh')
44
+ if self.theme == 'light':
45
+ self.theme = 'dark'
46
+ self.CONFIG = mc.set_theme(mc.SLIDERCONFIG_EN, theme=self.theme)
47
+ # self.CONFIG.FIGURE.RATIO.price = 18
48
+ # self.CONFIG.FIGURE.RATIO.volume = 4
49
+ else:
50
+ self.theme = 'light'
51
+ self.CONFIG = mc.set_theme(mc.SLIDERCONFIG, theme=self.theme)
52
+ # self.CONFIG.FIGURE.RATIO.slider = 9
53
+ # self.CONFIG.FIGURE.RATIO.price = 9
54
+ # self.CONFIG.FIGURE.RATIO.volume = 9
55
+ self.CONFIG.UNIT.digit = 2
56
+ self.refresh()
57
+ return
58
+ # def set_segments(self):
59
+ # super().set_segments()
60
+ # self.collection_candle.set_linewidth(1.3)
61
+ # return
62
+ # def get_candle_segment(self, *, is_up, x, left, right, top, bottom, high, low):
63
+ # if is_up:
64
+ # return [
65
+ # (x, bottom),
66
+ # (x, low),
67
+ # (x, bottom),
68
+ # (left, bottom),
69
+ # (x, bottom),
70
+ # (x, top),
71
+ # (right, top),
72
+ # (x, top),
73
+ # (x, high),
74
+ # ]
75
+ # else:
76
+ # return [
77
+ # (x, bottom),
78
+ # (x, low),
79
+ # (x, bottom),
80
+ # (right, bottom),
81
+ # (x, bottom),
82
+ # (x, top),
83
+ # (left, top),
84
+ # (x, top),
85
+ # (x, high),
86
+ # ]
87
+ c = C()
88
+ c.key_date = '기준일'
89
+ c.key_open = '시가'
90
+ c.key_high = '고가'
91
+ c.key_low = '저가'
92
+ c.key_close = '종가'
93
+ c.key_volume = '거래량'
94
+ # c.volume = None
31
95
  c.set_data(df)
96
+
32
97
  plt.show()
98
+ return
99
+
100
+
101
+ class TkChart:
102
+ chart = None
103
+
104
+ def __init__(self, window: tk.Tk):
105
+ window.wm_title('seolpyo tk chart')
106
+ self.window = window
107
+ # self.window.option_add('맑은고딕 14') # 모든 위젯 기본 폰트 크기 설정
108
+ window.protocol('WM_DELETE_WINDOW', lambda *_: (mc.close('all'), window.destroy()))
109
+
110
+ self.add_entry()
111
+ return
112
+
113
+ def open_file(self):
114
+ path_file = filedialog.askopenfilename(
115
+ title="Select a file",
116
+ filetypes=(("Xlsx Files", "*.xlsx"), ("All Files", "*.*"),)
117
+ )
118
+ # print(f'{path_file=}')
119
+ if path_file:
120
+ self.filname.config(state="normal") # 잠깐 풀고
121
+ self.filname.delete(0, tk.END)
122
+ self.filname.insert(0, path_file)
123
+ self.filname.config(state="readonly") # 다시 잠금
124
+
125
+ if not self.chart:
126
+ self.add_chart()
127
+ self.set_chart(path_file)
128
+ return path_file
129
+ return
33
130
 
131
+ def add_entry(self):
132
+ frame = tk.Frame(self.window)
133
+ frame.grid(column=0, row=0, sticky='w', padx=10, pady=10)
134
+
135
+ btn = tk.Button(frame, text='파일 열기', command=lambda *_: self.open_file())
136
+ btn.grid(column=0, row=0)
137
+
138
+ self.filname = tk.Entry(frame, state='readonly', width=100)
139
+ self.filname.grid(column=1, row=0, padx=10)
140
+ return
141
+
142
+ def add_chart(self):
143
+ self.chart = mc.SliderChart()
144
+ self.chart.key_date = '기준일'
145
+ self.chart.key_open = '시가'
146
+ self.chart.key_high = '고가'
147
+ self.chart.key_low = '저가'
148
+ self.chart.key_close = '종가'
149
+ self.chart.key_volume = '거래량'
150
+
151
+ frame = tk.Frame(self.window)
152
+ frame.grid(column=0, row=1, sticky='ewsn')
153
+ self.window.columnconfigure(0, weight=1)
154
+ self.window.rowconfigure(1, weight=1)
155
+
156
+ self.agg = FigureCanvasTkAgg(self.chart.figure, frame)
157
+ widget = self.agg.get_tk_widget()
158
+ widget.grid(column=0, row=0, sticky='ewsn')
159
+ frame.columnconfigure(0, weight=1)
160
+ frame.rowconfigure(0, weight=1)
161
+
162
+ return
163
+
164
+ def set_chart(self, path_file):
165
+ data = convert(path_file)
166
+ print(f'{len(data)=:,}')
167
+ df = pd.DataFrame(data)
168
+ self.chart.set_data(df)
169
+ return
170
+
171
+
172
+ def run():
173
+ root = tk.Tk()
174
+ _ = TkChart(root)
175
+ root.mainloop()
176
+ return
177
+
178
+
179
+ def test_tk():
180
+ run()
34
181
  return
35
182
 
36
183
 
37
184
  if __name__ == '__main__':
38
- sample('apple')
185
+ test()
186
+ # test_tk()
187
+
seolpyo_mplchart/utils.py CHANGED
@@ -1,13 +1,18 @@
1
1
  from re import search
2
2
 
3
+
3
4
  def convert_num(num):
4
5
  if isinstance(num, float) and num % 1: return num
5
6
  return int(num)
6
7
 
7
8
 
8
9
  def float_to_str(num: float, digit=0, plus=False):
9
- num.__round__(digit)
10
- text = f'{num:+,.{digit}f}' if plus else f'{num:,.{digit}f}'
10
+ if 0 < digit:
11
+ num.__round__(digit)
12
+ text = f'{num:+,.{digit}f}' if plus else f'{num:,.{digit}f}'
13
+ else:
14
+ num = round(num, digit).__int__()
15
+ text = f'{num:+,}' if plus else f'{num:,}'
11
16
  return text
12
17
 
13
18
 
@@ -16,7 +21,6 @@ dict_unit = {
16
21
  '조': 1_000_000_000_000,
17
22
  '억': 100_000_000,
18
23
  '만': 10_000,
19
- '천': 1_000,
20
24
  }
21
25
  dict_unit_en = {
22
26
  'Qd': 1_000_000_000_000_000,
@@ -26,15 +30,20 @@ dict_unit_en = {
26
30
  'K': 1_000,
27
31
  }
28
32
 
33
+
29
34
  def convert_unit(value, digit=0, word='원'):
35
+ # print(f'{value=:,}')
30
36
  v = abs(value)
31
37
  du = dict_unit if search('[가-힣]', word) else dict_unit_en
32
38
  for unit, n in du.items():
33
39
  if n <= v:
40
+ # print(f'{n=:,}')
41
+ # print(f'{unit=}')
34
42
  num = value / n
35
43
  return f'{float_to_str(num, digit)}{unit} {word}'
36
44
  if not value % 1: value = int(value)
37
45
  text = f'{float_to_str(value, digit)}{word}'
46
+ # print(f'{text=}')
38
47
  return text
39
48
 
40
49
 
@@ -42,4 +51,6 @@ if __name__ == '__main__':
42
51
  a = 456.123
43
52
  print(float_to_str(a))
44
53
  print(float_to_str(a, 2))
45
- print(float_to_str(a, 6))
54
+ print(float_to_str(a, 6))
55
+
56
+
@@ -0,0 +1,47 @@
1
+ from datetime import datetime, timedelta
2
+ from re import findall
3
+ from zipfile import ZipFile
4
+
5
+
6
+ base_date = datetime(1899, 12, 30).date()
7
+
8
+
9
+ def convert(path_file: str, sheet=1, row=4, date='A', Open='B', high='C', low='D', close='E', volume='F'):
10
+ "xlsx 파일에서 주가 정보를 추출합니다."
11
+ zipfile = ZipFile(path_file)
12
+ # print(f'{zipfile.filelist=}')
13
+ a = zipfile.read(f'xl/worksheets/sheet{sheet}.xml')
14
+ # print(f'{a=}')
15
+ # print(f'{type(a)=}')
16
+ b = a.decode('utf-8')
17
+ list_price: list[dict[str, str|int]] = []
18
+ for i in findall('<row.+?</row>', b)[row:]:
19
+ # print()
20
+ # print(f'{i=}')
21
+ dt = findall(f'<c r="{date}[0-9].+?<v>([0-9]+)</v>', i)
22
+ c = findall(f'<c r="{close}[0-9].+?<v>([0-9\.]+)</v>', i)
23
+ o = findall(f'<c r="{Open}[0-9].+?<v>([0-9\.]+)</v>', i)
24
+ h = findall(f'<c r="{high}[0-9].+?<v>([0-9\.]+)</v>', i)
25
+ l = findall(f'<c r="{low}[0-9].+?<v>([0-9\.]+)</v>', i)
26
+ v = findall(f'<c r="{volume}[0-9].+?<v>([0-9]+)</v>', i)
27
+ # print(f'{(dt, c, o, h, l, v)=}')
28
+ if not all([dt, o, h, l, c,]):
29
+ continue
30
+ try:
31
+ dt = base_date + timedelta(int(dt[0]))
32
+ c = float(c[0])
33
+ o = float(o[0])
34
+ h = float(h[0])
35
+ l = float(l[0])
36
+ except:
37
+ continue
38
+ try:
39
+ v = float(v[0])
40
+ except:
41
+ v = 0
42
+ # print(f'{(dt, c, o, h, l, v)=}')
43
+ # if 2020 < dt.year and dt.year < 2024: list_price.append({'기준일': f'{dt}', '종가': c, '시가': o, '고가': h, '저가': l, '거래량': v,})
44
+ list_price.append({'기준일': f'{dt}', '종가': c, '시가': o, '고가': h, '저가': l, '거래량': v,})
45
+ # for i in enumerate(list_price, 1): print(f' {i}')
46
+ return sorted(list_price, key=lambda x: x['기준일'])
47
+