PySide6Plot 0.0.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.
- PySide6Plot/__init__.py +187 -0
- PySide6Plot/compoents/__init__.py +3 -0
- PySide6Plot/compoents/average_line.py +232 -0
- PySide6Plot/compoents/draw_line.py +624 -0
- PySide6Plot/compoents/frame_recorder.py +213 -0
- PySide6Plot/compoents/zoom_move.py +160 -0
- PySide6Plot/libs/__init__.py +3 -0
- PySide6Plot/libs/constant.py +19 -0
- PySide6Plot/libs/data_handler.py +230 -0
- PySide6Plot/libs/helpers.py +401 -0
- PySide6Plot/libs/plot_item.py +233 -0
- PySide6Plot/libs/style.py +119 -0
- PySide6Plot/resources/icons/ChevronLeft_black.svg +44 -0
- PySide6Plot/resources/icons/ChevronLeft_white.svg +44 -0
- PySide6Plot/resources/qss/dark/navigation_view_interface.qss +16 -0
- PySide6Plot/resources/qss/light/navigation_view_interface.qss +16 -0
- PySide6Plot/widgets/__init__.py +3 -0
- PySide6Plot/widgets/colorful_toggle_button.py +131 -0
- PySide6Plot/widgets/fluent_scroller.py +450 -0
- PySide6Plot/widgets/line_card.py +138 -0
- PySide6Plot/widgets/navigation_widget.py +49 -0
- PySide6Plot/widgets/q_plot_widget.py +750 -0
- PySide6Plot/widgets/removable_table.py +259 -0
- PySide6Plot/widgets/transparent_Line_edit.py +69 -0
- PySide6Plot/widgets/transparent_selector.py +208 -0
- PySide6Plot/widgets/value_select_box.py +369 -0
- PySide6Plot/widgets/zoom_bar.py +136 -0
- pyside6plot-0.0.1.dist-info/METADATA +37 -0
- pyside6plot-0.0.1.dist-info/RECORD +31 -0
- pyside6plot-0.0.1.dist-info/WHEEL +5 -0
- pyside6plot-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
from PySide6 import QtGui
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def limit_in_range(value, min_value, max_value):
|
|
7
|
+
"""
|
|
8
|
+
Limits the given value within the specified range.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
value (float): The value to be limited.
|
|
12
|
+
min_value (float): The minimum value of the range.
|
|
13
|
+
max_value (float): The maximum value of the range.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
float: The limited value within the range.
|
|
17
|
+
"""
|
|
18
|
+
if value < min_value:
|
|
19
|
+
return min_value
|
|
20
|
+
if value > max_value:
|
|
21
|
+
return max_value
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def color_to_rbg_tuple(color: QtGui.QColor) -> tuple:
|
|
26
|
+
"""
|
|
27
|
+
Converts a QColor object to an RGB tuple.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
color (QtGui.QColor): The QColor object to convert.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
tuple: The RGB tuple representing the color, in the format (red, green, blue).
|
|
34
|
+
"""
|
|
35
|
+
return (color.red(), color.green(), color.blue())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def tuple_to_color(color_tuple: tuple) -> QtGui.QColor:
|
|
39
|
+
"""
|
|
40
|
+
Converts a tuple of RGB values to a QColor object.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
color_tuple (tuple): A tuple containing RGB values.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
QtGui.QColor: A QColor object representing the RGB values.
|
|
47
|
+
"""
|
|
48
|
+
return QtGui.QColor(int(color_tuple[0]), int(color_tuple[1]), int(color_tuple[2]))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def project_path():
|
|
52
|
+
"""
|
|
53
|
+
Returns the absolute path of the project directory.
|
|
54
|
+
|
|
55
|
+
Return:
|
|
56
|
+
str: The absolute path of the project directory.
|
|
57
|
+
"""
|
|
58
|
+
return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.sep
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def project_path_qfile():
|
|
62
|
+
"""
|
|
63
|
+
Returns the project path with forward slashes instead of backslashes.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
str: The project path with forward slashes.
|
|
67
|
+
"""
|
|
68
|
+
return project_path().replace("\\", "/")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# from foxutils: https://github.com/qiauil/Foxutils/blob/dev/foxutils/helper/coding.py
|
|
72
|
+
class GeneralDataClass:
|
|
73
|
+
"""
|
|
74
|
+
A general data class that allows dynamic attribute setting and retrieval.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self, generation_dict=None, **kwargs) -> None:
|
|
78
|
+
"""
|
|
79
|
+
Initializes a new instance of the GeneralDataClass.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
generation_dict (dict): A dictionary containing attribute-value pairs to be set.
|
|
83
|
+
**kwargs: Additional attribute-value pairs to be set.
|
|
84
|
+
"""
|
|
85
|
+
if generation_dict is not None:
|
|
86
|
+
for key, value in generation_dict.items():
|
|
87
|
+
self.set(key, value)
|
|
88
|
+
for key, value in kwargs.items():
|
|
89
|
+
self.set(key, value)
|
|
90
|
+
|
|
91
|
+
def __len__(self):
|
|
92
|
+
"""
|
|
93
|
+
Returns the number of attributes in the GeneralDataClass.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
int: The number of attributes.
|
|
97
|
+
"""
|
|
98
|
+
return len(self.__dict__)
|
|
99
|
+
|
|
100
|
+
def __getitem__(self, key):
|
|
101
|
+
"""
|
|
102
|
+
Retrieves the value of the specified attribute.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
key (str): The name of the attribute.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Any: The value of the attribute.
|
|
109
|
+
"""
|
|
110
|
+
return self.__dict__[key]
|
|
111
|
+
|
|
112
|
+
def __iter__(self):
|
|
113
|
+
"""
|
|
114
|
+
Returns an iterator over the attribute-value pairs of the GeneralDataClass.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Iterator: An iterator over the attribute-value pairs.
|
|
118
|
+
"""
|
|
119
|
+
return iter(self.__dict__.items())
|
|
120
|
+
|
|
121
|
+
def keys(self):
|
|
122
|
+
"""
|
|
123
|
+
Returns a list of attribute names in the GeneralDataClass.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
list: A list of attribute names.
|
|
127
|
+
"""
|
|
128
|
+
return self.__dict__.keys()
|
|
129
|
+
|
|
130
|
+
def set(self, key, value):
|
|
131
|
+
"""
|
|
132
|
+
Sets the value of the specified attribute.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
key (str): The name of the attribute.
|
|
136
|
+
value (Any): The value to be set.
|
|
137
|
+
"""
|
|
138
|
+
setattr(self, key, value)
|
|
139
|
+
|
|
140
|
+
def set_items(self, **kwargs):
|
|
141
|
+
"""
|
|
142
|
+
Sets multiple attribute-value pairs.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
**kwargs: Attribute-value pairs to be set.
|
|
146
|
+
"""
|
|
147
|
+
for key, value in kwargs.items():
|
|
148
|
+
self.set(key, value)
|
|
149
|
+
|
|
150
|
+
def remove(self, *args):
|
|
151
|
+
"""
|
|
152
|
+
Removes the specified attributes.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
*args: Names of the attributes to be removed.
|
|
156
|
+
"""
|
|
157
|
+
for key in args:
|
|
158
|
+
delattr(self, key)
|
|
159
|
+
|
|
160
|
+
def add(self, **kwargs):
|
|
161
|
+
"""
|
|
162
|
+
Adds additional attribute-value pairs.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
**kwargs: Additional attribute-value pairs to be added.
|
|
166
|
+
"""
|
|
167
|
+
for k, v in kwargs.items():
|
|
168
|
+
setattr(self, k, v)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ConfigurationsHandler:
|
|
172
|
+
"""
|
|
173
|
+
A class that handles configurations for a specific application or module.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def __init__(self) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Initializes a new instance of the ConfigurationsHandler class.
|
|
179
|
+
"""
|
|
180
|
+
self.__configs_feature = {}
|
|
181
|
+
self.__configs = GeneralDataClass()
|
|
182
|
+
|
|
183
|
+
def add_config_item(
|
|
184
|
+
self,
|
|
185
|
+
name,
|
|
186
|
+
default_value=None,
|
|
187
|
+
default_value_func=None,
|
|
188
|
+
mandatory=False,
|
|
189
|
+
description="",
|
|
190
|
+
value_type=None,
|
|
191
|
+
option=None,
|
|
192
|
+
in_func=None,
|
|
193
|
+
out_func=None,
|
|
194
|
+
):
|
|
195
|
+
"""
|
|
196
|
+
Adds a new configuration item to the handler.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
name (str): The name of the configuration item.
|
|
200
|
+
default_value (Any, optional): The default value for the configuration item. Defaults to None.
|
|
201
|
+
default_value_func (Callable, optional): A function that returns the default value for the configuration item. Defaults to None.
|
|
202
|
+
mandatory (bool, optional): Indicates whether the configuration item is mandatory. Defaults to False.
|
|
203
|
+
description (str, optional): The description of the configuration item. Defaults to "".
|
|
204
|
+
value_type (type, optional): The expected type of the configuration item. Defaults to None.
|
|
205
|
+
option (List[Any], optional): The list of possible values for the configuration item. Defaults to None.
|
|
206
|
+
in_func (Callable, optional): A function to transform the input value of the configuration item. Defaults to None.
|
|
207
|
+
out_func (Callable, optional): A function to transform the output value of the configuration item. Defaults to None.
|
|
208
|
+
"""
|
|
209
|
+
if not mandatory and default_value is None and default_value_func is None:
|
|
210
|
+
raise Exception("Default value or default value func must be set for non-mandatory configuration.")
|
|
211
|
+
if mandatory and (default_value is not None or default_value_func is not None):
|
|
212
|
+
raise Exception("Default value or default value func must not be set for mandatory configuration.")
|
|
213
|
+
if not (default_value is None or isinstance(default_value, value_type)):
|
|
214
|
+
raise Exception(f"Default value must be {value_type}, but find {type(default_value)}.")
|
|
215
|
+
if option is not None:
|
|
216
|
+
if isinstance(option, list):
|
|
217
|
+
raise Exception(f"Option must be list, but find {type(option)}.")
|
|
218
|
+
if len(option) == 0:
|
|
219
|
+
raise Exception("Option must not be empty.")
|
|
220
|
+
for item in option:
|
|
221
|
+
if not isinstance(item, value_type):
|
|
222
|
+
raise Exception(f"Option must be list of {value_type}, but find {type(item)}.")
|
|
223
|
+
self.__configs_feature[name] = {
|
|
224
|
+
"default_value_func": default_value_func, # default_value_func must be a function with one parameter, which is the current configures
|
|
225
|
+
"mandatory": mandatory,
|
|
226
|
+
"description": description,
|
|
227
|
+
"value_type": value_type,
|
|
228
|
+
"option": option,
|
|
229
|
+
"in_func": in_func,
|
|
230
|
+
"out_func": out_func,
|
|
231
|
+
"default_value": default_value,
|
|
232
|
+
"in_func_ran": False,
|
|
233
|
+
"out_func_ran": False,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
def get_config_features(self, key):
|
|
237
|
+
"""
|
|
238
|
+
Retrieves the features of a specific configuration item.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
key (str): The name of the configuration item.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
dict: A dictionary containing the features of the configuration item.
|
|
245
|
+
"""
|
|
246
|
+
if key not in self.__configs_feature.keys():
|
|
247
|
+
raise Exception(f"{key} is not a supported configuration.")
|
|
248
|
+
return self.__configs_feature[key]
|
|
249
|
+
|
|
250
|
+
def set_config_features(self, key, feature):
|
|
251
|
+
"""
|
|
252
|
+
Sets the features of a specific configuration item.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
key (str): The name of the configuration item.
|
|
256
|
+
feature (dict): A dictionary containing the features of the configuration item.
|
|
257
|
+
"""
|
|
258
|
+
self.add_config_item(
|
|
259
|
+
key,
|
|
260
|
+
default_value=feature["default_value"],
|
|
261
|
+
default_value_func=feature["default_value_func"],
|
|
262
|
+
mandatory=feature["mandatory"],
|
|
263
|
+
description=feature["description"],
|
|
264
|
+
value_type=feature["value_type"],
|
|
265
|
+
option=feature["option"],
|
|
266
|
+
in_func=feature["in_func"],
|
|
267
|
+
out_func=feature["out_func"],
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def set_config_items(self, **kwargs):
|
|
271
|
+
"""
|
|
272
|
+
Sets the values of multiple configuration items.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
kwargs (Any): Keyword arguments representing the configuration items and their values.
|
|
276
|
+
"""
|
|
277
|
+
for key, value in kwargs.items():
|
|
278
|
+
if key not in self.__configs_feature.keys():
|
|
279
|
+
raise Exception(f"{key} is not a supported configuration.")
|
|
280
|
+
if self.__configs_feature[key]["value_type"] is not None and not isinstance(
|
|
281
|
+
value, self.__configs_feature[key]["value_type"]
|
|
282
|
+
):
|
|
283
|
+
raise Exception(
|
|
284
|
+
"{} must be {}, but find {}.".format(key, self.__configs_feature[key]["value_type"], type(value))
|
|
285
|
+
)
|
|
286
|
+
if self.__configs_feature[key]["option"] is not None and value not in self.__configs_feature[key]["option"]:
|
|
287
|
+
raise Exception(
|
|
288
|
+
"{} must be one of {}, but find {}.".format(key, self.__configs_feature[key]["option"], value)
|
|
289
|
+
)
|
|
290
|
+
self.__configs.set(key, value)
|
|
291
|
+
self.__configs_feature[key]["in_func_ran"] = False
|
|
292
|
+
self.__configs_feature[key]["out_func_ran"] = False
|
|
293
|
+
|
|
294
|
+
def configs(self):
|
|
295
|
+
"""
|
|
296
|
+
Retrieves the current configurations.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
GeneralDataClass: An instance of the GeneralDataClass containing the current configurations.
|
|
300
|
+
"""
|
|
301
|
+
for key in self.__configs_feature.keys():
|
|
302
|
+
not_set = False
|
|
303
|
+
if not hasattr(self.__configs, key):
|
|
304
|
+
not_set = True
|
|
305
|
+
elif self.__configs[key] is None:
|
|
306
|
+
not_set = True
|
|
307
|
+
if not_set:
|
|
308
|
+
if self.__configs_feature[key]["mandatory"]:
|
|
309
|
+
raise Exception(f"Configuration {key} is mandatory, but not set.")
|
|
310
|
+
elif self.__configs_feature[key]["default_value"] is not None:
|
|
311
|
+
self.__configs.set(key, self.__configs_feature[key]["default_value"])
|
|
312
|
+
self.__configs_feature[key]["in_func_ran"] = False
|
|
313
|
+
self.__configs_feature[key]["out_func_ran"] = False
|
|
314
|
+
elif self.__configs_feature[key]["default_value_func"] is not None:
|
|
315
|
+
self.__configs.set(key, None)
|
|
316
|
+
else:
|
|
317
|
+
raise Exception(f"Configuration {key} is not set.")
|
|
318
|
+
# default_value_func and infunc may depends on other configurations
|
|
319
|
+
for key in self.__configs.keys():
|
|
320
|
+
if self.__configs[key] is None and self.__configs_feature[key]["default_value_func"] is not None:
|
|
321
|
+
self.__configs.set(key, self.__configs_feature[key]["default_value_func"](self.__configs))
|
|
322
|
+
self.__configs_feature[key]["in_func_ran"] = False
|
|
323
|
+
self.__configs_feature[key]["out_func_ran"] = False
|
|
324
|
+
for key in self.__configs_feature.keys():
|
|
325
|
+
if self.__configs_feature[key]["in_func"] is not None and not self.__configs_feature[key]["in_func_ran"]:
|
|
326
|
+
self.__configs.set(key, self.__configs_feature[key]["in_func"](self.__configs[key], self.__configs))
|
|
327
|
+
self.__configs_feature[key]["in_func_ran"] = True
|
|
328
|
+
return self.__configs
|
|
329
|
+
|
|
330
|
+
def set_config_items_from_yaml(self, yaml_file):
|
|
331
|
+
"""
|
|
332
|
+
Sets the values of configuration items from a YAML file.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
yaml_file (str): The path to the YAML file.
|
|
336
|
+
"""
|
|
337
|
+
with open(yaml_file) as f:
|
|
338
|
+
yaml_configs = yaml.safe_load(f)
|
|
339
|
+
self.set_config_items(**yaml_configs)
|
|
340
|
+
|
|
341
|
+
def save_config_items_to_yaml(self, yaml_file, only_optional=False):
|
|
342
|
+
"""
|
|
343
|
+
Saves the values of configuration items to a YAML file.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
yaml_file (str): The path to the YAML file.
|
|
347
|
+
only_optional (bool, optional): Indicates whether to save only the optional configuration items. Defaults to False.
|
|
348
|
+
"""
|
|
349
|
+
config_dict = self.configs().__dict__
|
|
350
|
+
if only_optional:
|
|
351
|
+
output_dict = {}
|
|
352
|
+
for key in config_dict.keys():
|
|
353
|
+
if self.__configs_feature[key]["mandatory"]:
|
|
354
|
+
continue
|
|
355
|
+
output_dict[key] = config_dict[key]
|
|
356
|
+
else:
|
|
357
|
+
output_dict = config_dict
|
|
358
|
+
for key in output_dict.keys():
|
|
359
|
+
if self.__configs_feature[key]["out_func"] is not None and not self.__configs_feature[key]["out_func_ran"]:
|
|
360
|
+
output_dict[key] = self.__configs_feature[key]["out_func"](self.__configs[key], self.__configs)
|
|
361
|
+
self.__configs_feature[key]["out_func_ran"] = True
|
|
362
|
+
with open(yaml_file, "w") as f:
|
|
363
|
+
yaml.dump(output_dict, f)
|
|
364
|
+
|
|
365
|
+
def show_config_features(self):
|
|
366
|
+
"""
|
|
367
|
+
Displays the features of all configuration items.
|
|
368
|
+
"""
|
|
369
|
+
mandatory_configs = []
|
|
370
|
+
optional_configs = []
|
|
371
|
+
for key in self.__configs_feature.keys():
|
|
372
|
+
text = " " + str(key)
|
|
373
|
+
texts = []
|
|
374
|
+
if self.__configs_feature[key]["value_type"] is not None:
|
|
375
|
+
texts.append(str(self.__configs_feature[key]["value_type"].__name__))
|
|
376
|
+
if self.__configs_feature[key]["option"] is not None:
|
|
377
|
+
texts.append("possible option: " + str(self.__configs_feature[key]["option"]))
|
|
378
|
+
if self.__configs_feature[key]["default_value"] is not None:
|
|
379
|
+
texts.append("default value: " + str(self.__configs_feature[key]["default_value"]))
|
|
380
|
+
if len(texts) > 0:
|
|
381
|
+
text += " (" + ", ".join(texts) + ")"
|
|
382
|
+
text += ": "
|
|
383
|
+
text += str(self.__configs_feature[key]["description"])
|
|
384
|
+
if self.__configs_feature[key]["mandatory"]:
|
|
385
|
+
mandatory_configs.append(text)
|
|
386
|
+
else:
|
|
387
|
+
optional_configs.append(text)
|
|
388
|
+
print("Mandatory Configuration:")
|
|
389
|
+
for key in mandatory_configs:
|
|
390
|
+
print(key)
|
|
391
|
+
print("")
|
|
392
|
+
print("Optional Configuration:")
|
|
393
|
+
for key in optional_configs:
|
|
394
|
+
print(key)
|
|
395
|
+
|
|
396
|
+
def show_config_items(self):
|
|
397
|
+
"""
|
|
398
|
+
Displays the values of all configuration items.
|
|
399
|
+
"""
|
|
400
|
+
for key, value in self.configs().__dict__.items():
|
|
401
|
+
print(f"{key}: {value}")
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
from pyqtgraph import QtGui, QtCore
|
|
2
|
+
import pyqtgraph as pg
|
|
3
|
+
from abc import abstractclassmethod
|
|
4
|
+
import numpy as np
|
|
5
|
+
from .style import DEFAULT_STYLE
|
|
6
|
+
from .data_handler import ChildDataFrame, PricesDataFrame, VolumeDataFrame
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_plot_item(data_frame: ChildDataFrame, style=DEFAULT_STYLE):
|
|
10
|
+
"""
|
|
11
|
+
Returns a plot item based on the type of data frame.
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
data_frame (ChildDataFrame): The data frame to create the plot item for.
|
|
15
|
+
style (str, optional): The style of the plot item. Defaults to DEFAULT_STYLE.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
PlotItem: The plot item based on the type of data frame.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
TypeError: If the data frame is not of type PricesDataFrame.
|
|
22
|
+
"""
|
|
23
|
+
if isinstance(data_frame, PricesDataFrame):
|
|
24
|
+
return CandlestickPricesItem(data_frame, style=style)
|
|
25
|
+
elif isinstance(data_frame, VolumeDataFrame):
|
|
26
|
+
return CandlestickVolumeItem(data_frame, style=style)
|
|
27
|
+
else:
|
|
28
|
+
raise TypeError("data_frame must be PricesDataFrame")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AdaptiveGraphObject(pg.GraphicsObject):
|
|
32
|
+
"""
|
|
33
|
+
A base class for adaptive graph objects in the plotter.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.style = None
|
|
39
|
+
|
|
40
|
+
@abstractclassmethod
|
|
41
|
+
def get_local_plot_range(x_start, x_end):
|
|
42
|
+
"""
|
|
43
|
+
Abstract method to get the local plot range for the graph object.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
x_start (float): The starting x-coordinate of the plot range.
|
|
47
|
+
x_end (float): The ending x-coordinate of the plot range.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
tuple: A tuple containing the local plot range.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
@abstractclassmethod
|
|
55
|
+
def get_x_ticks():
|
|
56
|
+
"""
|
|
57
|
+
Abstract method to get the x-axis ticks for the graph object.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
list: A list of x-axis ticks.
|
|
61
|
+
"""
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
@abstractclassmethod
|
|
65
|
+
def get_feature_value(key):
|
|
66
|
+
"""
|
|
67
|
+
Abstract method to get the value of a specific feature for the graph object.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
key (str): The key of the feature.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Any: The value of the feature.
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CandlestickPricesItem(AdaptiveGraphObject):
|
|
79
|
+
"""
|
|
80
|
+
A class representing a candlestick plot item for displaying prices.
|
|
81
|
+
|
|
82
|
+
Attributes:
|
|
83
|
+
data (PricesDataFrame): The data containing the prices.
|
|
84
|
+
style (Style, optional): The style of the candlestick plot item. Defaults to DEFAULT_STYLE.
|
|
85
|
+
value_key (str, optional): The key representing the value to be used for plotting. Defaults to "close".
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self, data: PricesDataFrame, style=DEFAULT_STYLE):
|
|
89
|
+
"""
|
|
90
|
+
Initializes a CandlestickPricesItem object.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
data (PricesDataFrame): The data containing the prices.
|
|
94
|
+
style (Style, optional): The style of the candlestick plot item. Defaults to DEFAULT_STYLE.
|
|
95
|
+
"""
|
|
96
|
+
super().__init__()
|
|
97
|
+
self.data = data
|
|
98
|
+
self.style = style
|
|
99
|
+
self.picture = QtGui.QPicture()
|
|
100
|
+
p = QtGui.QPainter(self.picture)
|
|
101
|
+
w = style.bar_width
|
|
102
|
+
for t, open_price, close_price, high_price, low_price in self.data:
|
|
103
|
+
if close_price > open_price:
|
|
104
|
+
p.setBrush(pg.mkBrush(self.style.positive_color))
|
|
105
|
+
p.setPen(pg.mkPen(self.style.positive_color))
|
|
106
|
+
p.drawRect(QtCore.QRectF(t - w, open_price, w * 2, close_price - open_price))
|
|
107
|
+
else:
|
|
108
|
+
p.setBrush(pg.mkBrush(self.style.negative_color))
|
|
109
|
+
p.setPen(pg.mkPen(self.style.negative_color))
|
|
110
|
+
p.drawRect(QtCore.QRectF(t - w, close_price, w * 2, open_price - close_price))
|
|
111
|
+
p.drawRect(QtCore.QRectF(t - style.shadow_width / 2, low_price, style.shadow_width, high_price - low_price))
|
|
112
|
+
p.end()
|
|
113
|
+
|
|
114
|
+
def paint(self, p, *args):
|
|
115
|
+
"""
|
|
116
|
+
Paints the candlestick plot item.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
p (QPainter): The painter object used for painting.
|
|
120
|
+
*args: Additional arguments.
|
|
121
|
+
"""
|
|
122
|
+
p.drawPicture(0, 0, self.picture)
|
|
123
|
+
|
|
124
|
+
def boundingRect(self):
|
|
125
|
+
"""
|
|
126
|
+
Returns the bounding rectangle of the candlestick plot item.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
QRectF: The bounding rectangle.
|
|
130
|
+
"""
|
|
131
|
+
return QtCore.QRectF(self.picture.boundingRect())
|
|
132
|
+
|
|
133
|
+
def get_local_plot_range(self, x_start, x_end):
|
|
134
|
+
"""
|
|
135
|
+
Returns the local plot range based on the given x-axis start and end values.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
x_start (float): The start value of the x-axis.
|
|
139
|
+
x_end (float): The end value of the x-axis.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
PricesDataFrame: The local plot range.
|
|
143
|
+
"""
|
|
144
|
+
return self.data.get_local_range(x_start, x_end)
|
|
145
|
+
|
|
146
|
+
def get_x_ticks(self):
|
|
147
|
+
"""
|
|
148
|
+
Returns the x-axis ticks.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
list: The x-axis ticks.
|
|
152
|
+
"""
|
|
153
|
+
return self.data.get_x_ticks()
|
|
154
|
+
|
|
155
|
+
def get_feature_value(self, key="close"):
|
|
156
|
+
"""
|
|
157
|
+
Returns the feature values based on the given key.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
key (str, optional): The key representing the feature value. Defaults to "close".
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
ndarray: The feature values.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
ValueError: If the key is not one of 'open', 'close', 'high', 'low'.
|
|
167
|
+
"""
|
|
168
|
+
available_keys = ["open", "close", "high", "low"]
|
|
169
|
+
if key not in available_keys:
|
|
170
|
+
raise ValueError("value_key must be one of 'open','close','high','low'")
|
|
171
|
+
index = available_keys.index(key) + 1
|
|
172
|
+
return np.asarray([data[index] for data in self.data])
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class CandlestickVolumeItem(AdaptiveGraphObject):
|
|
176
|
+
"""
|
|
177
|
+
A class representing a candlestick volume item for plotting.
|
|
178
|
+
|
|
179
|
+
Attributes:
|
|
180
|
+
data (VolumeDataFrame): The volume data to be plotted. Must have fields: time, open, close, min, max.
|
|
181
|
+
style (Style, optional): The style configuration for the plot item. Defaults to DEFAULT_STYLE.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def __init__(self, data: VolumeDataFrame, style=DEFAULT_STYLE):
|
|
185
|
+
super().__init__()
|
|
186
|
+
self.data = data
|
|
187
|
+
self.style = style
|
|
188
|
+
self.picture = QtGui.QPicture()
|
|
189
|
+
p = QtGui.QPainter(self.picture)
|
|
190
|
+
p.setBrush(pg.mkBrush(self.style.volume_color))
|
|
191
|
+
p.setPen(pg.mkPen(self.style.volume_color))
|
|
192
|
+
w = style.bar_width
|
|
193
|
+
for t, volume in self.data:
|
|
194
|
+
p.drawRect(QtCore.QRectF(t - w, 0, w * 2, volume / 1e8))
|
|
195
|
+
p.end()
|
|
196
|
+
|
|
197
|
+
def paint(self, p, *args):
|
|
198
|
+
p.drawPicture(0, 0, self.picture)
|
|
199
|
+
|
|
200
|
+
def boundingRect(self):
|
|
201
|
+
return QtCore.QRectF(self.picture.boundingRect())
|
|
202
|
+
|
|
203
|
+
def get_local_plot_range(self, x_start, x_end):
|
|
204
|
+
"""
|
|
205
|
+
Get the local plot range for the volume item.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
x_start (float): The starting x-coordinate of the plot range.
|
|
209
|
+
x_end (float): The ending x-coordinate of the plot range.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
tuple: A tuple containing the minimum and maximum volume values within the plot range.
|
|
213
|
+
"""
|
|
214
|
+
min_v, max_v = self.data.get_local_range(x_start, x_end)
|
|
215
|
+
return 0, max_v / 1e8
|
|
216
|
+
|
|
217
|
+
def get_x_ticks(self):
|
|
218
|
+
"""
|
|
219
|
+
Get the x-axis ticks for the volume item.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
list: A list of x-axis tick values.
|
|
223
|
+
"""
|
|
224
|
+
return self.data.get_x_ticks()
|
|
225
|
+
|
|
226
|
+
def get_feature_value(self):
|
|
227
|
+
"""
|
|
228
|
+
Get the feature values for the volume item.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
numpy.ndarray: An array of feature values.
|
|
232
|
+
"""
|
|
233
|
+
return np.asarray([data[1] / 1e8 for data in self.data])
|