plotext-plus 1.0.9__py3-none-any.whl → 1.0.10__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.
- plotext_plus/__init__.py +20 -15
- plotext_plus/__main__.py +1 -0
- plotext_plus/_api.py +632 -371
- plotext_plus/_build.py +765 -149
- plotext_plus/_core.py +609 -164
- plotext_plus/_date.py +50 -32
- plotext_plus/_default.py +35 -28
- plotext_plus/_dict.py +807 -103
- plotext_plus/_doc.py +867 -296
- plotext_plus/_doc_utils.py +279 -245
- plotext_plus/_figure.py +1295 -303
- plotext_plus/_global.py +238 -140
- plotext_plus/_matrix.py +217 -63
- plotext_plus/_monitor.py +1036 -489
- plotext_plus/_output.py +29 -23
- plotext_plus/_shtab.py +2 -0
- plotext_plus/_themes.py +363 -247
- plotext_plus/_utility.py +581 -313
- plotext_plus/api.py +418 -332
- plotext_plus/charts.py +47 -24
- plotext_plus/core.py +570 -177
- plotext_plus/mcp_cli.py +15 -13
- plotext_plus/mcp_server.py +813 -332
- plotext_plus/plotext_cli.py +414 -275
- plotext_plus/plotting.py +86 -70
- plotext_plus/themes.py +10 -13
- plotext_plus/utilities.py +33 -33
- plotext_plus/utils.py +240 -140
- {plotext_plus-1.0.9.dist-info → plotext_plus-1.0.10.dist-info}/METADATA +7 -2
- plotext_plus-1.0.10.dist-info/RECORD +33 -0
- plotext_plus-1.0.9.dist-info/RECORD +0 -33
- {plotext_plus-1.0.9.dist-info → plotext_plus-1.0.10.dist-info}/WHEEL +0 -0
- {plotext_plus-1.0.9.dist-info → plotext_plus-1.0.10.dist-info}/entry_points.txt +0 -0
- {plotext_plus-1.0.9.dist-info → plotext_plus-1.0.10.dist-info}/licenses/LICENSE +0 -0
plotext_plus/utils.py
CHANGED
|
@@ -1,47 +1,71 @@
|
|
|
1
1
|
# This file contains some plotext functions which are only available to the top main level and not to sub figures (which are written in _figure.py and _monitor.py). These are functions which requires some coding and would be too long to be added directly in _core.py
|
|
2
2
|
|
|
3
|
-
from plotext_plus._utility import marker_codes, hd_symbols, sin
|
|
4
|
-
from plotext_plus._figure import _figure_class
|
|
5
|
-
from plotext_plus._utility import themes as _themes
|
|
6
|
-
import plotext_plus._utility as ut
|
|
7
|
-
from time import time, sleep
|
|
8
|
-
from math import sqrt, ceil
|
|
9
3
|
import datetime as dt
|
|
4
|
+
from math import ceil
|
|
5
|
+
from math import sqrt
|
|
6
|
+
from time import sleep
|
|
7
|
+
from time import time
|
|
8
|
+
|
|
9
|
+
import plotext_plus._utility as ut
|
|
10
|
+
from plotext_plus._figure import _FigureClass
|
|
11
|
+
from plotext_plus._utility import hd_symbols
|
|
12
|
+
from plotext_plus._utility import marker_codes
|
|
13
|
+
from plotext_plus._utility import themes as _themes
|
|
10
14
|
|
|
11
|
-
figure =
|
|
15
|
+
figure = _FigureClass() # the main figure at top level
|
|
12
16
|
|
|
13
17
|
##############################################
|
|
14
18
|
####### Simple Bar Functions ########
|
|
15
19
|
##############################################
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
|
|
22
|
+
def simple_bar(*args, width=None, marker=None, color=None, title=None):
|
|
18
23
|
x, y = ut.set_data(*args)
|
|
19
24
|
marker = ut.correct_marker(marker)
|
|
20
25
|
|
|
21
26
|
color_ok = ut.is_color(color) or (isinstance(color, list) and len(color) == len(x))
|
|
22
27
|
color = [color] if color_ok else None
|
|
23
28
|
|
|
24
|
-
simple_stacked_bar(x, [y], width
|
|
29
|
+
simple_stacked_bar(x, [y], width=width, marker=marker, colors=color, title=title)
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
|
|
32
|
+
def simple_stacked_bar(
|
|
33
|
+
*args, width=None, marker=None, colors=None, title=None, labels=None
|
|
34
|
+
):
|
|
35
|
+
x, y, y_scaled, width = ut.bar_data(*args, width=width)
|
|
28
36
|
marker = ut.correct_marker(marker)
|
|
29
|
-
|
|
30
|
-
bars = len(Y); stacked_bars = len(Y[0])
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
bars = len(y_scaled)
|
|
39
|
+
stacked_bars = len(y_scaled[0])
|
|
40
|
+
|
|
41
|
+
colors_ok1 = (
|
|
42
|
+
isinstance(colors, list)
|
|
43
|
+
and isinstance(colors[0], list)
|
|
44
|
+
and ut.matrix_size(colors) == [bars, stacked_bars]
|
|
45
|
+
)
|
|
33
46
|
colors_ok2 = isinstance(colors, list) and len(colors) == stacked_bars
|
|
34
|
-
colors =
|
|
47
|
+
colors = (
|
|
48
|
+
ut.transpose(colors)
|
|
49
|
+
if colors_ok1
|
|
50
|
+
else (
|
|
51
|
+
[colors] * bars if colors_ok2 else [ut.color_sequence[:stacked_bars]] * bars
|
|
52
|
+
)
|
|
53
|
+
)
|
|
35
54
|
|
|
36
55
|
title = ut.get_title(title, width)
|
|
37
|
-
bars = [ut.single_bar(x[i],
|
|
56
|
+
bars = [ut.single_bar(x[i], y_scaled[i], y[i], marker, colors[i]) for i in range(bars)]
|
|
38
57
|
labels = ut.get_simple_labels(marker, labels, colors[0], width)
|
|
39
|
-
figure.monitor.matrix.canvas = title +
|
|
58
|
+
figure.monitor.matrix.canvas = title + "\n".join(bars) + labels
|
|
40
59
|
figure.monitor.fast_plot = True
|
|
41
60
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
|
|
62
|
+
def simple_multiple_bar(
|
|
63
|
+
*args, width=None, marker=None, colors=None, title=None, labels=None
|
|
64
|
+
):
|
|
65
|
+
x, y, y_scaled, width = ut.bar_data(*args, width=width, mode="multiple")
|
|
66
|
+
bars = len(y_scaled)
|
|
67
|
+
multiple_bars = len(y_scaled[0])
|
|
68
|
+
lx = len(x[0])
|
|
45
69
|
marker = ut.correct_marker(marker)
|
|
46
70
|
|
|
47
71
|
colors_ok = isinstance(colors, list) and len(colors) == multiple_bars
|
|
@@ -49,78 +73,96 @@ def simple_multiple_bar(*args, width = None, marker = None, colors = None, title
|
|
|
49
73
|
|
|
50
74
|
out = ut.get_title(title, width)
|
|
51
75
|
for i in range(bars):
|
|
52
|
-
xn = [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
xn = [
|
|
77
|
+
x[i] if j == (multiple_bars - 1) // 2 else ut.space * lx
|
|
78
|
+
for j in range(multiple_bars)
|
|
79
|
+
]
|
|
80
|
+
new = [
|
|
81
|
+
ut.single_bar(xn[j], [y_scaled[i][j]], y[j][i], marker, [colors[j]])
|
|
82
|
+
for j in range(multiple_bars)
|
|
83
|
+
]
|
|
84
|
+
out += "\n".join(new)
|
|
85
|
+
out += "\n\n" if i != bars - 1 else ""
|
|
56
86
|
labels = ut.get_simple_labels(marker, labels, colors, width)
|
|
57
87
|
figure.monitor.matrix.canvas = out + labels
|
|
58
88
|
figure.monitor.fast_plot = True
|
|
59
89
|
|
|
90
|
+
|
|
60
91
|
##############################################
|
|
61
92
|
############# Play GIF ################
|
|
62
93
|
##############################################
|
|
63
94
|
|
|
95
|
+
|
|
64
96
|
def play_gif(path):
|
|
65
|
-
from PIL import Image
|
|
97
|
+
from PIL import Image
|
|
98
|
+
from PIL import ImageSequence
|
|
99
|
+
|
|
66
100
|
path = ut.correct_path(path)
|
|
67
101
|
if not ut.is_file(path):
|
|
68
102
|
return
|
|
69
103
|
im = Image.open(path)
|
|
70
|
-
index = 1
|
|
71
104
|
for image in ImageSequence.Iterator(im):
|
|
72
105
|
load_time = time()
|
|
73
106
|
figure.clt()
|
|
74
|
-
image = image.convert(
|
|
75
|
-
figure.monitor._draw_image(image, fast
|
|
107
|
+
image = image.convert("RGB")
|
|
108
|
+
figure.monitor._draw_image(image, fast=True)
|
|
76
109
|
figure.show()
|
|
77
110
|
load_time = time() - load_time
|
|
78
|
-
frame_time = image.info[
|
|
111
|
+
frame_time = image.info["duration"] / 10**3
|
|
79
112
|
if load_time < frame_time:
|
|
80
113
|
sleep(frame_time - load_time)
|
|
81
|
-
|
|
114
|
+
|
|
115
|
+
|
|
82
116
|
##############################################
|
|
83
117
|
########## Video Functions ############
|
|
84
118
|
##############################################
|
|
85
119
|
|
|
86
|
-
|
|
120
|
+
|
|
121
|
+
def play_video(path, from_youtube=False):
|
|
87
122
|
path = ut.correct_path(path)
|
|
88
123
|
if not ut.is_file(path):
|
|
89
124
|
return
|
|
90
125
|
_play_video(path, from_youtube)
|
|
91
126
|
|
|
127
|
+
|
|
92
128
|
def play_youtube(url):
|
|
93
129
|
import pafy
|
|
130
|
+
|
|
94
131
|
video = pafy.new(url)
|
|
95
132
|
best = video.getbest()
|
|
96
|
-
_play_video(best.url, from_youtube
|
|
133
|
+
_play_video(best.url, from_youtube=True)
|
|
134
|
+
|
|
97
135
|
|
|
98
136
|
def get_youtube(url, path, log):
|
|
99
137
|
import pafy
|
|
138
|
+
|
|
100
139
|
video = pafy.new(url)
|
|
101
|
-
best = video.getbest(preftype
|
|
140
|
+
best = video.getbest(preftype="mp4")
|
|
102
141
|
path = "youtube-video.mp4" if path is None else path
|
|
103
142
|
path = ut.correct_path(path)
|
|
104
|
-
best.download(filepath
|
|
105
|
-
print(ut.format_strings(
|
|
143
|
+
best.download(filepath=path, quiet=not log)
|
|
144
|
+
print(ut.format_strings("YouTube video downloaded as", path)) if log else None
|
|
106
145
|
|
|
107
|
-
|
|
146
|
+
|
|
147
|
+
def _play_video(path, from_youtube=False):
|
|
108
148
|
import cv2
|
|
109
149
|
from ffpyplayer.player import MediaPlayer
|
|
110
150
|
from PIL import Image
|
|
151
|
+
|
|
111
152
|
cap = cv2.VideoCapture(path)
|
|
112
|
-
player = MediaPlayer(path)
|
|
113
|
-
fr = 0
|
|
153
|
+
player = MediaPlayer(path) # , paused = True, loglevel = 'quiet');
|
|
154
|
+
fr = 0
|
|
114
155
|
while fr == 0:
|
|
115
156
|
fr = cap.get(cv2.CAP_PROP_FPS)
|
|
116
|
-
frame_time = 1 / fr
|
|
117
|
-
#to_list = lambda frame: [[tuple(int(el) for el in tup) for tup in row] for row in frame]
|
|
118
|
-
pt
|
|
157
|
+
frame_time = 1 / fr
|
|
158
|
+
# to_list = lambda frame: [[tuple(int(el) for el in tup) for tup in row] for row in frame]
|
|
159
|
+
def pt(time):
|
|
160
|
+
return f"{round(10 ** 3 * time, 1):05.1f} "
|
|
119
161
|
real_time = video_time = 0
|
|
120
162
|
while True:
|
|
121
163
|
load_time = time()
|
|
122
|
-
check_video, frame = cap.read()
|
|
123
|
-
audio, check_audio = player.get_frame(show
|
|
164
|
+
check_video, frame = cap.read()
|
|
165
|
+
audio, check_audio = player.get_frame(show=False)
|
|
124
166
|
load_time = time() - load_time
|
|
125
167
|
if not check_video:
|
|
126
168
|
break
|
|
@@ -129,61 +171,73 @@ def _play_video(path, from_youtube = False):
|
|
|
129
171
|
real_time += load_time
|
|
130
172
|
video_time += frame_time
|
|
131
173
|
show_time = 0
|
|
132
|
-
shown = False
|
|
133
174
|
if video_time >= real_time:
|
|
134
|
-
shown = True
|
|
135
175
|
show_time = time()
|
|
136
176
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if from_youtube else frame
|
|
137
|
-
#frame = to_list(frame)
|
|
177
|
+
# frame = to_list(frame)
|
|
138
178
|
image = Image.fromarray(frame)
|
|
139
|
-
|
|
179
|
+
|
|
140
180
|
# Synchronized frame rendering - ensure each step completes before next
|
|
141
181
|
figure.clt()
|
|
142
182
|
# Small delay to ensure clear operation completes
|
|
143
183
|
sleep(0.001) # 1ms delay
|
|
144
|
-
figure.monitor._draw_image(image, fast
|
|
184
|
+
figure.monitor._draw_image(image, fast=True)
|
|
145
185
|
# Small delay to ensure image drawing completes before display
|
|
146
186
|
sleep(0.001) # 1ms delay
|
|
147
187
|
figure.show()
|
|
148
188
|
# Brief delay after display to prevent frame overlap
|
|
149
189
|
sleep(0.002) # 2ms delay for terminal rendering
|
|
150
|
-
|
|
190
|
+
|
|
151
191
|
show_time = time() - show_time
|
|
152
192
|
sleep_time = 0
|
|
153
193
|
if real_time < video_time:
|
|
154
194
|
sleep_time = time()
|
|
155
195
|
sleep(video_time - real_time)
|
|
156
196
|
sleep_time = time() - sleep_time
|
|
157
|
-
|
|
197
|
+
load_time + show_time + sleep_time
|
|
158
198
|
real_time += show_time + sleep_time
|
|
159
|
-
#print('load: ' + pt(load_time), 'show: ' + pt(show_time), 'sleep: ' + pt(sleep_time), 'total: ' + pt(total_time), 'frame: ' + pt(frame_time), 'real: ' + pt(real_time), 'video: ' + pt(video_time), 'r/v:', round(real_time / video_time, 3)) if shown else None
|
|
199
|
+
# print('load: ' + pt(load_time), 'show: ' + pt(show_time), 'sleep: ' + pt(sleep_time), 'total: ' + pt(total_time), 'frame: ' + pt(frame_time), 'real: ' + pt(real_time), 'video: ' + pt(video_time), 'r/v:', round(real_time / video_time, 3)) if shown else None
|
|
160
200
|
player.close_player()
|
|
161
201
|
cap.release()
|
|
162
202
|
cv2.destroyAllWindows()
|
|
163
203
|
figure.clf()
|
|
164
204
|
|
|
205
|
+
|
|
165
206
|
##############################################
|
|
166
207
|
############ Utilities ###############
|
|
167
208
|
##############################################
|
|
168
209
|
|
|
169
|
-
test_data_url =
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
210
|
+
test_data_url = (
|
|
211
|
+
"https://raw.githubusercontent.com/piccolomo/plotext/master/data/data.txt"
|
|
212
|
+
)
|
|
213
|
+
test_bar_data_url = (
|
|
214
|
+
"https://raw.githubusercontent.com/piccolomo/plotext/master/data/bar_data.txt"
|
|
215
|
+
)
|
|
216
|
+
test_image_url = (
|
|
217
|
+
"https://raw.githubusercontent.com/piccolomo/plotext/master/data/cat.jpg"
|
|
218
|
+
)
|
|
219
|
+
test_gif_url = (
|
|
220
|
+
"https://raw.githubusercontent.com/piccolomo/plotext/master/data/homer.gif"
|
|
221
|
+
)
|
|
222
|
+
test_video_url = (
|
|
223
|
+
"https://raw.githubusercontent.com/piccolomo/plotext/master/data/moonwalk.mp4"
|
|
224
|
+
)
|
|
225
|
+
test_youtube_url = "https://www.youtube.com/watch?v=ZNAvVVc4b3E&t=75s"
|
|
175
226
|
|
|
176
227
|
##############################################
|
|
177
228
|
######### Matplotlib Backend ##########
|
|
178
229
|
##############################################
|
|
179
230
|
|
|
180
|
-
|
|
231
|
+
|
|
232
|
+
def from_matplotlib(fig, marker=None):
|
|
181
233
|
fig.canvas.draw()
|
|
182
234
|
slots = (rows, cols) = fig.axes[0].get_subplotspec().get_gridspec().get_geometry()
|
|
183
|
-
figure.clf()
|
|
235
|
+
figure.clf() # clt()
|
|
184
236
|
figure.subplots(*slots)
|
|
185
|
-
round10
|
|
186
|
-
|
|
237
|
+
def round10(data):
|
|
238
|
+
return [round(el, 10) for el in data]
|
|
239
|
+
def to_rgb(rgb_norm):
|
|
240
|
+
return tuple([round(255 * el) for el in rgb_norm[:3]])
|
|
187
241
|
figure.axes_color(to_rgb(fig.patch.get_facecolor()))
|
|
188
242
|
for sub in fig.axes[:]:
|
|
189
243
|
p = sub.get_subplotspec().get_geometry()[2]
|
|
@@ -200,20 +254,20 @@ def from_matplotlib(fig, marker = None):
|
|
|
200
254
|
monitor.canvas_color(to_rgb(sub.get_facecolor()))
|
|
201
255
|
for point in sub.collections:
|
|
202
256
|
label = point.get_label()
|
|
203
|
-
label = label if label[0] !=
|
|
204
|
-
#point.set_offset_position('data')
|
|
257
|
+
label = label if label[0] != "_" else ""
|
|
258
|
+
# point.set_offset_position('data')
|
|
205
259
|
x, y = ut.transpose(point.get_offsets())
|
|
206
260
|
color = [ut.to_rgb(point.to_rgba(el)) for el in point.get_facecolors()[0]]
|
|
207
261
|
# can't find the right point colors
|
|
208
|
-
monitor.scatter(x, y, label
|
|
262
|
+
monitor.scatter(x, y, label=label, marker=marker)
|
|
209
263
|
for line in sub.get_lines():
|
|
210
264
|
label = line.get_label()
|
|
211
|
-
label = label if label[0] !=
|
|
265
|
+
label = label if label[0] != "_" else ""
|
|
212
266
|
x, y = line.get_data()
|
|
213
|
-
monitor.plot(x, y, marker
|
|
267
|
+
monitor.plot(x, y, marker=marker, color=line.get_c(), label=label)
|
|
214
268
|
for b in sub.patches:
|
|
215
269
|
label = b.get_label()
|
|
216
|
-
label = label if label[0] !=
|
|
270
|
+
label = label if label[0] != "_" else ""
|
|
217
271
|
color = b.get_facecolor()
|
|
218
272
|
color = ut.to_rgb(color)
|
|
219
273
|
box = b.get_bbox()
|
|
@@ -223,148 +277,194 @@ def from_matplotlib(fig, marker = None):
|
|
|
223
277
|
fill = b.get_fill()
|
|
224
278
|
fillx = fill if y0 == 0 else False
|
|
225
279
|
filly = fill if x0 == 0 else False
|
|
226
|
-
monitor.plot(
|
|
280
|
+
monitor.plot(
|
|
281
|
+
x, y, fillx=fillx, filly=filly, marker=marker, color=color, label=label
|
|
282
|
+
)
|
|
227
283
|
monitor.xlim(*sub.get_xlim())
|
|
228
284
|
monitor.ylim(*sub.get_ylim())
|
|
229
285
|
|
|
286
|
+
|
|
230
287
|
##############################################
|
|
231
288
|
####### Presentation Functions ########
|
|
232
289
|
##############################################
|
|
233
|
-
|
|
290
|
+
|
|
291
|
+
|
|
234
292
|
def markers():
|
|
235
293
|
markers = list(hd_symbols.keys())[::-1] + list(marker_codes.keys())
|
|
236
|
-
|
|
237
|
-
rows = int(sqrt(
|
|
238
|
-
cols = ceil(
|
|
294
|
+
marker_count = len(markers)
|
|
295
|
+
rows = int(sqrt(marker_count))
|
|
296
|
+
cols = ceil(marker_count / rows)
|
|
239
297
|
y = ut.sin(1)
|
|
240
|
-
figure.clf()
|
|
298
|
+
figure.clf()
|
|
299
|
+
figure.theme("pro")
|
|
300
|
+
figure.xfrequency(0)
|
|
301
|
+
figure.yfrequency(0)
|
|
302
|
+
figure.frame(1)
|
|
241
303
|
figure.subplots(rows, cols)
|
|
242
304
|
figure.frame(0)
|
|
243
305
|
for row in range(1, rows + 1):
|
|
244
306
|
for col in range(1, cols + 1):
|
|
245
307
|
i = (row - 1) * cols + col - 1
|
|
246
|
-
if i <
|
|
308
|
+
if i < marker_count:
|
|
247
309
|
subplot = figure.subplot(row, col)
|
|
248
310
|
figure.frame(1)
|
|
249
|
-
default =
|
|
311
|
+
default = " [default]" if markers[i] == "hd" else ""
|
|
250
312
|
subplot.title(markers[i] + default)
|
|
251
|
-
subplot.scatter(y, marker
|
|
252
|
-
subplot.ticks_style(
|
|
253
|
-
#figure.ticks_color(figure._utility.title_color)
|
|
313
|
+
subplot.scatter(y, marker=markers[i])
|
|
314
|
+
subplot.ticks_style("bold")
|
|
315
|
+
# figure.ticks_color(figure._utility.title_color)
|
|
254
316
|
figure.show()
|
|
255
317
|
figure.clf()
|
|
256
318
|
|
|
319
|
+
|
|
257
320
|
def colors():
|
|
258
|
-
print(ut.colorize("String Color Codes", style
|
|
321
|
+
print(ut.colorize("String Color Codes", style="bold"))
|
|
259
322
|
bg = "default"
|
|
260
|
-
c = ut.no_duplicates(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
323
|
+
c = ut.no_duplicates(
|
|
324
|
+
[
|
|
325
|
+
el.replace("+", "")
|
|
326
|
+
for el in ut.colors
|
|
327
|
+
if el not in ["default", "black", "white"]
|
|
328
|
+
]
|
|
329
|
+
)
|
|
330
|
+
cp = [ut.colorize(ut.pad_string(el + "+", 10), el + "+", background=bg) for el in c]
|
|
331
|
+
c = [ut.colorize(ut.pad_string(el, 10), el, background=bg) for el in c]
|
|
332
|
+
c = [" " + c[i] + cp[i] for i in range(len(c))]
|
|
333
|
+
c = "\n".join(c)
|
|
334
|
+
print(" " + ut.colorize(ut.pad_string("default", 20), background=bg))
|
|
335
|
+
print(
|
|
336
|
+
" "
|
|
337
|
+
+ ut.colorize(ut.pad_string("black", 10), "black", background="gray")
|
|
338
|
+
+ ut.colorize(ut.pad_string("white", 10), "white", background=bg)
|
|
339
|
+
)
|
|
267
340
|
print(c)
|
|
268
341
|
print()
|
|
269
|
-
#print(colorize("\n\nInteger Color Codes:", style = ''))
|
|
270
|
-
c = ut.colorize("Integer Color Codes", style
|
|
342
|
+
# print(colorize("\n\nInteger Color Codes:", style = ''))
|
|
343
|
+
c = ut.colorize("Integer Color Codes", style="bold", show=False) + "\n"
|
|
271
344
|
for row in range(16):
|
|
272
|
-
cr =
|
|
345
|
+
cr = " "
|
|
273
346
|
for col in range(16):
|
|
274
347
|
i = row * 16 + col
|
|
275
348
|
cr += ut.colorize(ut.pad_string(i, 5), i)
|
|
276
|
-
c += cr +
|
|
349
|
+
c += cr + "\n"
|
|
277
350
|
print(c)
|
|
278
|
-
c =
|
|
351
|
+
c = "\n"
|
|
279
352
|
rgb = (100, 200, 85)
|
|
280
|
-
rgb_string =
|
|
281
|
-
print(
|
|
353
|
+
rgb_string = "(" + ", ".join([str(el) for el in rgb]) + ")"
|
|
354
|
+
print(
|
|
355
|
+
ut.colorize("RGB Tuples like:", style="bold"),
|
|
356
|
+
ut.colorize(rgb_string, rgb, "bold"),
|
|
357
|
+
)
|
|
358
|
+
|
|
282
359
|
|
|
283
360
|
def styles():
|
|
284
|
-
from plotext_plus._utility import
|
|
285
|
-
|
|
286
|
-
|
|
361
|
+
from plotext_plus._utility import colorize
|
|
362
|
+
from plotext_plus._utility import styles
|
|
363
|
+
from plotext_plus._utility import title_color
|
|
364
|
+
|
|
365
|
+
c = [colorize(el, style=el) for el in styles]
|
|
366
|
+
c = "\n".join(c)
|
|
287
367
|
print(c)
|
|
288
|
-
mul =
|
|
289
|
-
print(
|
|
368
|
+
mul = "bold italic dim"
|
|
369
|
+
print(
|
|
370
|
+
"\n"
|
|
371
|
+
+ colorize("multiple styles are accepted, ", title_color)
|
|
372
|
+
+ "eg: "
|
|
373
|
+
+ colorize(mul, style=mul)
|
|
374
|
+
)
|
|
375
|
+
|
|
290
376
|
|
|
291
377
|
def themes():
|
|
292
378
|
themes = list(_themes.keys())[::]
|
|
293
|
-
|
|
294
|
-
rows = int(sqrt(
|
|
295
|
-
cols = ceil(
|
|
296
|
-
y1 = ut.sin(periods
|
|
297
|
-
y2 = ut.sin(periods
|
|
379
|
+
theme_count = len(themes)
|
|
380
|
+
rows = int(sqrt(theme_count))
|
|
381
|
+
cols = ceil(theme_count / rows)
|
|
382
|
+
y1 = ut.sin(periods=1)
|
|
383
|
+
y2 = ut.sin(periods=1, phase=-1)
|
|
298
384
|
figure.clf()
|
|
299
385
|
figure.subplots(rows, cols)
|
|
300
386
|
for row in range(1, rows + 1):
|
|
301
387
|
for col in range(1, cols + 1):
|
|
302
388
|
i = (row - 1) * cols + col - 1
|
|
303
|
-
if i <
|
|
389
|
+
if i < theme_count:
|
|
304
390
|
subplot = figure.subplot(row, col)
|
|
305
391
|
subplot.theme(themes[i])
|
|
306
392
|
subplot.title(themes[i])
|
|
307
|
-
subplot.scatter(y1)
|
|
393
|
+
subplot.scatter(y1)
|
|
394
|
+
subplot.plot(y2)
|
|
308
395
|
figure.show()
|
|
309
396
|
figure.clf()
|
|
310
397
|
|
|
398
|
+
|
|
311
399
|
##############################################
|
|
312
400
|
########### Test Function #############
|
|
313
401
|
##############################################
|
|
314
402
|
|
|
403
|
+
|
|
315
404
|
def test():
|
|
316
405
|
import random
|
|
317
|
-
|
|
318
|
-
figure.
|
|
406
|
+
|
|
407
|
+
figure.clf()
|
|
408
|
+
figure.clt()
|
|
409
|
+
figure.date_form("d/m/Y")
|
|
319
410
|
figure.take_min()
|
|
320
411
|
figure.plot_size(None, ut.terminal_height())
|
|
321
|
-
#figure.plot_size(108, 70)
|
|
322
|
-
|
|
412
|
+
# figure.plot_size(108, 70)
|
|
413
|
+
|
|
323
414
|
figure.plotsize(ut.tw(), ut.th() - 5)
|
|
324
415
|
figure.subplots(2, 2)
|
|
325
|
-
|
|
416
|
+
|
|
326
417
|
subplot = figure.subplot(1, 1)
|
|
327
418
|
subplot.title("Multiple Axes Plot")
|
|
328
|
-
subplot.canvas_color(66)
|
|
329
|
-
|
|
330
|
-
subplot.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
419
|
+
subplot.canvas_color(66)
|
|
420
|
+
subplot.axes_color(4)
|
|
421
|
+
subplot.ticks_color(216)
|
|
422
|
+
subplot.ticks_style("bold italic")
|
|
423
|
+
y = ut.sin(periods=1)
|
|
424
|
+
data_length = len(y)
|
|
425
|
+
subplot.scatter(y, label="lower left")
|
|
426
|
+
x = [figure.today_datetime() + dt.timedelta(days=i) for i in range(data_length)]
|
|
427
|
+
x = figure.datetimes_to_strings(x)
|
|
428
|
+
subplot.plot(x, x, label="upper right - all dates", xside=2, yside=2)
|
|
429
|
+
subplot.vline(data_length / 2, "red")
|
|
334
430
|
subplot.hline(0, 200)
|
|
335
|
-
subplot.text("origin",
|
|
336
|
-
subplot.xlabel(
|
|
337
|
-
subplot.
|
|
338
|
-
subplot.
|
|
339
|
-
subplot.
|
|
340
|
-
subplot.
|
|
341
|
-
|
|
431
|
+
subplot.text("origin", data_length // 2, 0, color="red", alignment="center")
|
|
432
|
+
subplot.xlabel("x lower")
|
|
433
|
+
subplot.xlabel("x upper", 2)
|
|
434
|
+
subplot.ylabel("y left", "left")
|
|
435
|
+
subplot.ylabel("y right", "right")
|
|
436
|
+
subplot.xfrequency(8)
|
|
437
|
+
subplot.xfrequency(5, 2)
|
|
438
|
+
subplot.yfrequency(3)
|
|
439
|
+
subplot.yfrequency(5, 2)
|
|
440
|
+
subplot.grid(1, 1)
|
|
441
|
+
|
|
342
442
|
subplot = figure.subplot(1, 2)
|
|
343
|
-
subplot.theme(
|
|
443
|
+
subplot.theme("innocent")
|
|
344
444
|
xb = ["Sausage", "Pepperoni", "Mushrooms", "Cheese", "Chicken", "Beef"]
|
|
345
445
|
y1 = [36, 14, 11, 8, 7, 4]
|
|
346
446
|
y2 = [20, 12, 35, 15, 4, 5]
|
|
347
|
-
subplot.stacked_bar(xb, [y1, y2], labels
|
|
447
|
+
subplot.stacked_bar(xb, [y1, y2], labels=["men", "women"])
|
|
348
448
|
|
|
349
449
|
subplot = figure.subplot(2, 1)
|
|
350
|
-
subplot.theme(
|
|
351
|
-
ld = 7 * 10
|
|
450
|
+
subplot.theme("dreamland")
|
|
451
|
+
ld = 7 * 10**4
|
|
352
452
|
data = [random.gauss(0, 1) for el in range(10 * ld)]
|
|
353
|
-
subplot.hist(data, bins
|
|
354
|
-
subplot.frame(1)
|
|
355
|
-
|
|
453
|
+
subplot.hist(data, bins=60, label="mean 0")
|
|
454
|
+
subplot.frame(1) # subplot.xaxes(1, 0); subplot.yaxes(1, 0)
|
|
455
|
+
|
|
356
456
|
subplot = figure.subplot(2, 2)
|
|
357
|
-
subplot.canvas_color(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
457
|
+
subplot.canvas_color("gray+")
|
|
458
|
+
subplot.axes_color("gray+")
|
|
459
|
+
ut.download(test_image_url, "cat.jpg")
|
|
460
|
+
subplot.image_plot("cat.jpg", grayscale=False)
|
|
461
|
+
ut.delete_file("cat.jpg")
|
|
462
|
+
subplot.title("A very Cute Cat")
|
|
362
463
|
subplot.frame(0)
|
|
363
464
|
|
|
364
|
-
#figure.plotsize(0, 0)
|
|
465
|
+
# figure.plotsize(0, 0)
|
|
365
466
|
figure.show()
|
|
366
467
|
figure._get_time()
|
|
367
|
-
figure.save_fig(
|
|
368
|
-
figure.save_fig(
|
|
369
|
-
#figure.clf()
|
|
370
|
-
|
|
468
|
+
figure.save_fig("test.txt")
|
|
469
|
+
figure.save_fig("test.html")
|
|
470
|
+
# figure.clf()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotext_plus
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.10
|
|
4
4
|
Summary: Modern terminal plotting library with enhanced visual features, themes, and AI integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/ccmitchellusa/plotext_plus
|
|
6
6
|
Project-URL: Repository, https://github.com/ccmitchellusa/plotext_plus.git
|
|
@@ -23,6 +23,7 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
24
|
Classifier: Topic :: Terminals
|
|
25
25
|
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: chuk-mcp-server>=0.3.5
|
|
26
27
|
Requires-Dist: chuk-term>=0.1.0
|
|
27
28
|
Requires-Dist: shtab>=1.7.2
|
|
28
29
|
Provides-Extra: completion
|
|
@@ -30,7 +31,7 @@ Requires-Dist: shtab; extra == 'completion'
|
|
|
30
31
|
Provides-Extra: image
|
|
31
32
|
Requires-Dist: pillow>=8.4; extra == 'image'
|
|
32
33
|
Provides-Extra: mcp
|
|
33
|
-
Requires-Dist: chuk-mcp-server>=0.
|
|
34
|
+
Requires-Dist: chuk-mcp-server>=0.3.5; extra == 'mcp'
|
|
34
35
|
Provides-Extra: video
|
|
35
36
|
Requires-Dist: ffpyplayer>=4.3.5; extra == 'video'
|
|
36
37
|
Requires-Dist: opencv-python>=4.5.5; extra == 'video'
|
|
@@ -301,3 +302,7 @@ plt.theme('professional')
|
|
|
301
302
|
plt.plot([1,2,3], [1,4,2])
|
|
302
303
|
plt.show()
|
|
303
304
|
```
|
|
305
|
+
|
|
306
|
+
## 🛠️ Development & Build System
|
|
307
|
+
|
|
308
|
+
Plotext+ includes a comprehensive build system with modern tooling. See **[Build Documentation](docs/build.md)** for complete setup, testing, publishing, and deployment instructions.
|