ConsoleFramework 1.0.0__tar.gz
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.
- consoleframework-1.0.0/ConsoleFramework/__init__.py +795 -0
- consoleframework-1.0.0/ConsoleFramework.egg-info/PKG-INFO +24 -0
- consoleframework-1.0.0/ConsoleFramework.egg-info/SOURCES.txt +8 -0
- consoleframework-1.0.0/ConsoleFramework.egg-info/dependency_links.txt +1 -0
- consoleframework-1.0.0/ConsoleFramework.egg-info/not-zip-safe +1 -0
- consoleframework-1.0.0/ConsoleFramework.egg-info/top_level.txt +1 -0
- consoleframework-1.0.0/LICENSE +21 -0
- consoleframework-1.0.0/PKG-INFO +24 -0
- consoleframework-1.0.0/setup.cfg +4 -0
- consoleframework-1.0.0/setup.py +20 -0
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ConsoleFramework - The best Console Library of the Python.
|
|
3
|
+
By: Suleiman
|
|
4
|
+
Copyright © Suleiman 2026
|
|
5
|
+
All rights are reserved.
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import math
|
|
11
|
+
import random
|
|
12
|
+
import socket
|
|
13
|
+
import zlib
|
|
14
|
+
import urllib.request
|
|
15
|
+
import json
|
|
16
|
+
import threading
|
|
17
|
+
import platform
|
|
18
|
+
import zipfile
|
|
19
|
+
import stat
|
|
20
|
+
import subprocess as CMD
|
|
21
|
+
import threading
|
|
22
|
+
import urllib.request
|
|
23
|
+
import urllib.parse
|
|
24
|
+
import base64
|
|
25
|
+
class Colors:
|
|
26
|
+
RESET = "\033[0m"
|
|
27
|
+
RED = "\033[31m"
|
|
28
|
+
GREEN = "\033[32m"
|
|
29
|
+
BLUE = "\033[34m"
|
|
30
|
+
CYAN = "\033[36m"
|
|
31
|
+
MAGENTA = "\033[35m"
|
|
32
|
+
YELLOW = "\033[33m"
|
|
33
|
+
WHITE = "\033[37m"
|
|
34
|
+
BLACK = "\033[30m"
|
|
35
|
+
BG_RED = "\033[41m"
|
|
36
|
+
BG_GREEN = "\033[42m"
|
|
37
|
+
BG_BLUE = "\033[44m"
|
|
38
|
+
BG_CYAN = "\033[46m"
|
|
39
|
+
BG_MAGENTA = "\033[45m"
|
|
40
|
+
BG_YELLOW = "\033[43m"
|
|
41
|
+
BG_WHITE = "\033[47m"
|
|
42
|
+
BG_BLACK = "\033[40m"
|
|
43
|
+
BOLD = "\033[1m"
|
|
44
|
+
DIM = "\033[2m"
|
|
45
|
+
ITALIC = "\033[3m"
|
|
46
|
+
UNDERLINE = "\033[4m"
|
|
47
|
+
BLINK = "\033[5m"
|
|
48
|
+
REVERSE = "\033[7m"
|
|
49
|
+
HIDDEN = "\033[8m"
|
|
50
|
+
STRIKE = "\033[9m"
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def rgb(r, g, b):
|
|
54
|
+
return f"\033[38;2;{r};{g};{b}m"
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def bg_rgb(r, g, b):
|
|
58
|
+
return f"\033[48;2;{r};{g};{b}m"
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def colorize(text, color, reset=True):
|
|
62
|
+
reset_code = Colors.RESET if reset else ""
|
|
63
|
+
return f"{color}{text}{reset_code}"
|
|
64
|
+
|
|
65
|
+
class Render:
|
|
66
|
+
@staticmethod
|
|
67
|
+
def DrawPic(points=None, width=80, height=25, clear_before_draw=False, default_char=' '):
|
|
68
|
+
if clear_before_draw:
|
|
69
|
+
os.system("cls" if os.name == "nt" else "clear")
|
|
70
|
+
field = [[default_char for _ in range(width)] for _ in range(height)]
|
|
71
|
+
if points:
|
|
72
|
+
for item in points:
|
|
73
|
+
if len(item) >= 3:
|
|
74
|
+
x, y, char = item[0], item[1], item[2]
|
|
75
|
+
if 0 <= x < width and 0 <= y < height:
|
|
76
|
+
if len(item) == 4:
|
|
77
|
+
color = item[3]
|
|
78
|
+
field[y][x] = f"{color}{char}{Colors.RESET}"
|
|
79
|
+
elif len(item) == 5:
|
|
80
|
+
color, bg = item[3], item[4]
|
|
81
|
+
field[y][x] = f"{color}{bg}{char}{Colors.RESET}"
|
|
82
|
+
else:
|
|
83
|
+
field[y][x] = char
|
|
84
|
+
for row in field:
|
|
85
|
+
print(''.join(row))
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _apply_color(points, color=None, bg=None):
|
|
88
|
+
if color is None and bg is None:
|
|
89
|
+
return points
|
|
90
|
+
result = []
|
|
91
|
+
for p in points:
|
|
92
|
+
if len(p) == 3:
|
|
93
|
+
x, y, char = p
|
|
94
|
+
if color and bg:
|
|
95
|
+
result.append((x, y, char, color, bg))
|
|
96
|
+
elif color:
|
|
97
|
+
result.append((x, y, char, color))
|
|
98
|
+
else:
|
|
99
|
+
result.append((x, y, char, Colors.BG_BLACK + bg))
|
|
100
|
+
else:
|
|
101
|
+
result.append(p)
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def get_line_points(x1, y1, x2, y2, char='#', color=None, bg=None):
|
|
106
|
+
points = []
|
|
107
|
+
dx = abs(x2 - x1)
|
|
108
|
+
dy = abs(y2 - y1)
|
|
109
|
+
sx = 1 if x1 < x2 else -1
|
|
110
|
+
sy = 1 if y1 < y2 else -1
|
|
111
|
+
err = dx - dy
|
|
112
|
+
while True:
|
|
113
|
+
points.append((x1, y1, char))
|
|
114
|
+
if x1 == x2 and y1 == y2:
|
|
115
|
+
break
|
|
116
|
+
e2 = err * 2
|
|
117
|
+
if e2 > -dy:
|
|
118
|
+
err -= dy
|
|
119
|
+
x1 += sx
|
|
120
|
+
if e2 < dx:
|
|
121
|
+
err += dx
|
|
122
|
+
y1 += sy
|
|
123
|
+
return Render._apply_color(points, color, bg)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def get_circle_points(cx, cy, r, char='#', color=None, bg=None):
|
|
127
|
+
points = []
|
|
128
|
+
for angle in range(0, 360, 5):
|
|
129
|
+
rad = angle * math.pi / 180
|
|
130
|
+
x = int(cx + r * math.cos(rad))
|
|
131
|
+
y = int(cy + r * math.sin(rad))
|
|
132
|
+
points.append((x, y, char))
|
|
133
|
+
return Render._apply_color(points, color, bg)
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def get_filled_circle_points(cx, cy, r, char='#', color=None, bg=None):
|
|
137
|
+
points = []
|
|
138
|
+
for y in range(cy - r, cy + r + 1):
|
|
139
|
+
for x in range(cx - r, cx + r + 1):
|
|
140
|
+
if (x - cx) ** 2 + (y - cy) ** 2 <= r ** 2:
|
|
141
|
+
points.append((x, y, char))
|
|
142
|
+
return Render._apply_color(points, color, bg)
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def get_rect_points(x1, y1, x2, y2, char='#', color=None, bg=None):
|
|
146
|
+
points = []
|
|
147
|
+
for x in range(x1, x2 + 1):
|
|
148
|
+
points.append((x, y1, char))
|
|
149
|
+
points.append((x, y2, char))
|
|
150
|
+
for y in range(y1, y2 + 1):
|
|
151
|
+
points.append((x1, y, char))
|
|
152
|
+
points.append((x2, y, char))
|
|
153
|
+
return Render._apply_color(points, color, bg)
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def get_fill_rect_points(x1, y1, x2, y2, char='#', color=None, bg=None):
|
|
157
|
+
points = []
|
|
158
|
+
for y in range(y1, y2 + 1):
|
|
159
|
+
for x in range(x1, x2 + 1):
|
|
160
|
+
points.append((x, y, char))
|
|
161
|
+
return Render._apply_color(points, color, bg)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def get_text_points(x, y, text, char=None, color=None, bg=None):
|
|
165
|
+
points = []
|
|
166
|
+
if char is None:
|
|
167
|
+
for i, ch in enumerate(text):
|
|
168
|
+
points.append((x + i, y, ch))
|
|
169
|
+
else:
|
|
170
|
+
for i in range(len(text)):
|
|
171
|
+
points.append((x + i, y, char))
|
|
172
|
+
return Render._apply_color(points, color, bg)
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
def get_triangle_points(x1, y1, x2, y2, x3, y3, char='#', color=None, bg=None):
|
|
176
|
+
points = []
|
|
177
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
178
|
+
points.extend(Render.get_line_points(x2, y2, x3, y3, char))
|
|
179
|
+
points.extend(Render.get_line_points(x3, y3, x1, y1, char))
|
|
180
|
+
return Render._apply_color(points, color, bg)
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def get_filled_triangle_points(x1, y1, x2, y2, x3, y3, char='#', color=None, bg=None):
|
|
184
|
+
points = []
|
|
185
|
+
min_x = min(x1, x2, x3)
|
|
186
|
+
max_x = max(x1, x2, x3)
|
|
187
|
+
min_y = min(y1, y2, y3)
|
|
188
|
+
max_y = max(y1, y2, y3)
|
|
189
|
+
for y in range(min_y, max_y + 1):
|
|
190
|
+
for x in range(min_x, max_x + 1):
|
|
191
|
+
if Render._point_in_triangle(x, y, x1, y1, x2, y2, x3, y3):
|
|
192
|
+
points.append((x, y, char))
|
|
193
|
+
return Render._apply_color(points, color, bg)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def get_heart_points(cx, cy, size=5, char='#', color=None, bg=None):
|
|
197
|
+
points = []
|
|
198
|
+
for y in range(-size, size + 1):
|
|
199
|
+
for x in range(-size, size + 1):
|
|
200
|
+
if ((x * x + y * y - size * size) ** 3 <= size * size * x * x * y * y * 0.5):
|
|
201
|
+
points.append((cx + x, cy - y, char))
|
|
202
|
+
return Render._apply_color(points, color, bg)
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def get_star_points(cx, cy, radius=5, points_count=5, char='*', color=None, bg=None):
|
|
206
|
+
points = []
|
|
207
|
+
angle = -math.pi / 2
|
|
208
|
+
step = math.pi / points_count
|
|
209
|
+
outer_points = []
|
|
210
|
+
inner_points = []
|
|
211
|
+
for i in range(points_count * 2):
|
|
212
|
+
r = radius if i % 2 == 0 else radius * 0.4
|
|
213
|
+
x = cx + r * math.cos(angle + i * step)
|
|
214
|
+
y = cy + r * math.sin(angle + i * step)
|
|
215
|
+
if i % 2 == 0:
|
|
216
|
+
outer_points.append((x, y))
|
|
217
|
+
else:
|
|
218
|
+
inner_points.append((x, y))
|
|
219
|
+
for i in range(points_count):
|
|
220
|
+
p1 = outer_points[i]
|
|
221
|
+
p2 = inner_points[i]
|
|
222
|
+
p3 = outer_points[(i + 1) % points_count]
|
|
223
|
+
points.extend(Render.get_line_points(int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1]), char))
|
|
224
|
+
points.extend(Render.get_line_points(int(p2[0]), int(p2[1]), int(p3[0]), int(p3[1]), char))
|
|
225
|
+
return Render._apply_color(points, color, bg)
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def get_bezier_points(x1, y1, x2, y2, x3, y3, steps=20, char='#', color=None, bg=None):
|
|
229
|
+
points = []
|
|
230
|
+
for t in range(steps + 1):
|
|
231
|
+
t = t / steps
|
|
232
|
+
x = (1 - t) ** 2 * x1 + 2 * (1 - t) * t * x2 + t ** 2 * x3
|
|
233
|
+
y = (1 - t) ** 2 * y1 + 2 * (1 - t) * t * y2 + t ** 2 * y3
|
|
234
|
+
points.append((int(x), int(y), char))
|
|
235
|
+
return Render._apply_color(points, color, bg)
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def get_wave_points(cx, cy, amplitude=5, length=20, char='~', color=None, bg=None):
|
|
239
|
+
points = []
|
|
240
|
+
for x in range(length):
|
|
241
|
+
y = cy + int(amplitude * math.sin(x * 0.5))
|
|
242
|
+
points.append((cx + x, y, char))
|
|
243
|
+
return Render._apply_color(points, color, bg)
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def get_spiral_points(cx, cy, turns=3, radius=10, step=0.1, char='*', color=None, bg=None):
|
|
247
|
+
points = []
|
|
248
|
+
angle = 0
|
|
249
|
+
r = 0
|
|
250
|
+
while r < radius:
|
|
251
|
+
x = int(cx + r * math.cos(angle))
|
|
252
|
+
y = int(cy + r * math.sin(angle))
|
|
253
|
+
points.append((x, y, char))
|
|
254
|
+
angle += step
|
|
255
|
+
r += step * 2
|
|
256
|
+
return Render._apply_color(points, color, bg)
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def get_grid_points(x1, y1, x2, y2, step=2, char='+', color=None, bg=None):
|
|
260
|
+
points = []
|
|
261
|
+
for x in range(x1, x2 + 1, step):
|
|
262
|
+
for y in range(y1, y2 + 1, step):
|
|
263
|
+
points.append((x, y, char))
|
|
264
|
+
return Render._apply_color(points, color, bg)
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def get_noise_points(cx, cy, radius, count=50, char='.', color=None, bg=None):
|
|
268
|
+
points = []
|
|
269
|
+
for _ in range(count):
|
|
270
|
+
angle = random.uniform(0, 2 * math.pi)
|
|
271
|
+
r = random.uniform(0, radius)
|
|
272
|
+
x = int(cx + r * math.cos(angle))
|
|
273
|
+
y = int(cy + r * math.sin(angle))
|
|
274
|
+
points.append((x, y, char))
|
|
275
|
+
return Render._apply_color(points, color, bg)
|
|
276
|
+
|
|
277
|
+
# ---- 3D ----
|
|
278
|
+
@staticmethod
|
|
279
|
+
def _point_in_triangle(px, py, x1, y1, x2, y2, x3, y3):
|
|
280
|
+
def sign(a, b, c):
|
|
281
|
+
return (a[0] - c[0]) * (b[1] - c[1]) - (b[0] - c[0]) * (a[1] - c[1])
|
|
282
|
+
p = (px, py)
|
|
283
|
+
d1 = sign(p, (x2, y2), (x1, y1))
|
|
284
|
+
d2 = sign(p, (x3, y3), (x2, y2))
|
|
285
|
+
d3 = sign(p, (x1, y1), (x3, y3))
|
|
286
|
+
has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
|
|
287
|
+
has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
|
|
288
|
+
return not (has_neg and has_pos)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def _rotx(x, y, z, angle):
|
|
292
|
+
rad = math.radians(angle)
|
|
293
|
+
cos_a = math.cos(rad)
|
|
294
|
+
sin_a = math.sin(rad)
|
|
295
|
+
y_new = y * cos_a - z * sin_a
|
|
296
|
+
z_new = y * sin_a + z * cos_a
|
|
297
|
+
return x, y_new, z_new
|
|
298
|
+
|
|
299
|
+
@staticmethod
|
|
300
|
+
def _roty(x, y, z, angle):
|
|
301
|
+
rad = math.radians(angle)
|
|
302
|
+
cos_a = math.cos(rad)
|
|
303
|
+
sin_a = math.sin(rad)
|
|
304
|
+
x_new = x * cos_a + z * sin_a
|
|
305
|
+
z_new = -x * sin_a + z * cos_a
|
|
306
|
+
return x_new, y, z_new
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def _rotz(x, y, z, angle):
|
|
310
|
+
rad = math.radians(angle)
|
|
311
|
+
cos_a = math.cos(rad)
|
|
312
|
+
sin_a = math.sin(rad)
|
|
313
|
+
x_new = x * cos_a - y * sin_a
|
|
314
|
+
y_new = x * sin_a + y * cos_a
|
|
315
|
+
return x_new, y_new, z
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def _project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy):
|
|
319
|
+
x, y, z = Render._rotx(x, y, z, rotx)
|
|
320
|
+
x, y, z = Render._roty(x, y, z, roty)
|
|
321
|
+
x, y, z = Render._rotz(x, y, z, rotz)
|
|
322
|
+
x2d = int(x * scale + cx)
|
|
323
|
+
y2d = int(-y * scale + cy)
|
|
324
|
+
return x2d, y2d
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def get_cube_3d_points(size=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
328
|
+
s = size / 2
|
|
329
|
+
vertices = [
|
|
330
|
+
(-s, -s, -s), (s, -s, -s), (s, s, -s), (-s, s, -s),
|
|
331
|
+
(-s, -s, s), (s, -s, s), (s, s, s), (-s, s, s)
|
|
332
|
+
]
|
|
333
|
+
edges = [
|
|
334
|
+
(0,1), (1,2), (2,3), (3,0),
|
|
335
|
+
(4,5), (5,6), (6,7), (7,4),
|
|
336
|
+
(0,4), (1,5), (2,6), (3,7)
|
|
337
|
+
]
|
|
338
|
+
points = []
|
|
339
|
+
projected = []
|
|
340
|
+
for v in vertices:
|
|
341
|
+
x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
|
|
342
|
+
projected.append((x2d, y2d))
|
|
343
|
+
for edge in edges:
|
|
344
|
+
x1, y1 = projected[edge[0]]
|
|
345
|
+
x2, y2 = projected[edge[1]]
|
|
346
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
347
|
+
return Render._apply_color(points, color, bg)
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def get_pyramid_3d_points(size=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
351
|
+
s = size / 2
|
|
352
|
+
vertices = [
|
|
353
|
+
(-s, -s, -s), (s, -s, -s), (s, -s, s), (-s, -s, s),
|
|
354
|
+
(0, s, 0)
|
|
355
|
+
]
|
|
356
|
+
edges = [
|
|
357
|
+
(0,1), (1,2), (2,3), (3,0),
|
|
358
|
+
(0,4), (1,4), (2,4), (3,4)
|
|
359
|
+
]
|
|
360
|
+
points = []
|
|
361
|
+
projected = []
|
|
362
|
+
for v in vertices:
|
|
363
|
+
x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
|
|
364
|
+
projected.append((x2d, y2d))
|
|
365
|
+
for edge in edges:
|
|
366
|
+
x1, y1 = projected[edge[0]]
|
|
367
|
+
x2, y2 = projected[edge[1]]
|
|
368
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
369
|
+
return Render._apply_color(points, color, bg)
|
|
370
|
+
|
|
371
|
+
@staticmethod
|
|
372
|
+
def get_cylinder_3d_points(radius=5, height=10, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
373
|
+
points = []
|
|
374
|
+
top_points = []
|
|
375
|
+
bottom_points = []
|
|
376
|
+
for i in range(segments):
|
|
377
|
+
rad = i / segments * 2 * math.pi
|
|
378
|
+
x = radius * math.cos(rad)
|
|
379
|
+
z = radius * math.sin(rad)
|
|
380
|
+
top_points.append((x, height/2, z))
|
|
381
|
+
bottom_points.append((x, -height/2, z))
|
|
382
|
+
all_vertices = top_points + bottom_points
|
|
383
|
+
projected = []
|
|
384
|
+
for v in all_vertices:
|
|
385
|
+
x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
|
|
386
|
+
projected.append((x2d, y2d))
|
|
387
|
+
for i in range(segments):
|
|
388
|
+
j = (i + 1) % segments
|
|
389
|
+
x1, y1 = projected[i]
|
|
390
|
+
x2, y2 = projected[j]
|
|
391
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
392
|
+
x1, y1 = projected[i + segments]
|
|
393
|
+
x2, y2 = projected[j + segments]
|
|
394
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
395
|
+
x1, y1 = projected[i]
|
|
396
|
+
x2, y2 = projected[i + segments]
|
|
397
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
398
|
+
return Render._apply_color(points, color, bg)
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def get_sphere_3d_points(radius=5, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
402
|
+
points = []
|
|
403
|
+
for i in range(segments):
|
|
404
|
+
theta = i / segments * math.pi
|
|
405
|
+
for j in range(segments):
|
|
406
|
+
phi = j / segments * 2 * math.pi
|
|
407
|
+
x = radius * math.sin(theta) * math.cos(phi)
|
|
408
|
+
y = radius * math.cos(theta)
|
|
409
|
+
z = radius * math.sin(theta) * math.sin(phi)
|
|
410
|
+
x2d, y2d = Render._project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy)
|
|
411
|
+
points.append((x2d, y2d, char))
|
|
412
|
+
return Render._apply_color(points, color, bg)
|
|
413
|
+
|
|
414
|
+
@staticmethod
|
|
415
|
+
def get_cone_3d_points(radius=5, height=10, segments=12, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
416
|
+
points = []
|
|
417
|
+
base_points = []
|
|
418
|
+
for i in range(segments):
|
|
419
|
+
rad = i / segments * 2 * math.pi
|
|
420
|
+
x = radius * math.cos(rad)
|
|
421
|
+
z = radius * math.sin(rad)
|
|
422
|
+
base_points.append((x, -height/2, z))
|
|
423
|
+
apex = (0, height/2, 0)
|
|
424
|
+
all_vertices = base_points + [apex]
|
|
425
|
+
projected = []
|
|
426
|
+
for v in all_vertices:
|
|
427
|
+
x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
|
|
428
|
+
projected.append((x2d, y2d))
|
|
429
|
+
for i in range(segments):
|
|
430
|
+
j = (i + 1) % segments
|
|
431
|
+
x1, y1 = projected[i]
|
|
432
|
+
x2, y2 = projected[j]
|
|
433
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
434
|
+
x1, y1 = projected[i]
|
|
435
|
+
x2, y2 = projected[-1]
|
|
436
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
437
|
+
return Render._apply_color(points, color, bg)
|
|
438
|
+
|
|
439
|
+
@staticmethod
|
|
440
|
+
def get_torus_3d_points(radius=8, tube=3, segments=16, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
441
|
+
points = []
|
|
442
|
+
for i in range(segments):
|
|
443
|
+
theta = i / segments * 2 * math.pi
|
|
444
|
+
for j in range(segments):
|
|
445
|
+
phi = j / segments * 2 * math.pi
|
|
446
|
+
x = (radius + tube * math.cos(phi)) * math.cos(theta)
|
|
447
|
+
y = tube * math.sin(phi)
|
|
448
|
+
z = (radius + tube * math.cos(phi)) * math.sin(theta)
|
|
449
|
+
x2d, y2d = Render._project_3d(x, y, z, rotx, roty, rotz, scale, cx, cy)
|
|
450
|
+
points.append((x2d, y2d, char))
|
|
451
|
+
return Render._apply_color(points, color, bg)
|
|
452
|
+
|
|
453
|
+
@staticmethod
|
|
454
|
+
def get_ico_sphere_3d_points(radius=5, rotx=0, roty=0, rotz=0, scale=10, cx=40, cy=12, char='#', color=None, bg=None):
|
|
455
|
+
points = []
|
|
456
|
+
phi = (1 + math.sqrt(5)) / 2
|
|
457
|
+
vertices = [
|
|
458
|
+
(-1, phi, 0), ( 1, phi, 0), (-1, -phi, 0), ( 1, -phi, 0),
|
|
459
|
+
( 0, -1, phi), ( 0, 1, phi), ( 0, -1, -phi), ( 0, 1, -phi),
|
|
460
|
+
( phi, 0, -1), ( phi, 0, 1), (-phi, 0, -1), (-phi, 0, 1)
|
|
461
|
+
]
|
|
462
|
+
vertices = [(x * radius / 1.618, y * radius / 1.618, z * radius / 1.618) for x, y, z in vertices]
|
|
463
|
+
edges = [
|
|
464
|
+
(0,1), (0,4), (0,5), (0,10), (0,11),
|
|
465
|
+
(1,2), (1,5), (1,7), (1,8), (1,9),
|
|
466
|
+
(2,3), (2,6), (2,7), (2,10), (2,11),
|
|
467
|
+
(3,4), (3,6), (3,8), (3,9), (3,11),
|
|
468
|
+
(4,5), (4,9), (4,11),
|
|
469
|
+
(5,7), (5,9),
|
|
470
|
+
(6,7), (6,10),
|
|
471
|
+
(7,8),
|
|
472
|
+
(8,9), (8,10),
|
|
473
|
+
(10,11)
|
|
474
|
+
]
|
|
475
|
+
projected = []
|
|
476
|
+
for v in vertices:
|
|
477
|
+
x2d, y2d = Render._project_3d(v[0], v[1], v[2], rotx, roty, rotz, scale, cx, cy)
|
|
478
|
+
projected.append((x2d, y2d))
|
|
479
|
+
for edge in edges:
|
|
480
|
+
x1, y1 = projected[edge[0]]
|
|
481
|
+
x2, y2 = projected[edge[1]]
|
|
482
|
+
points.extend(Render.get_line_points(x1, y1, x2, y2, char))
|
|
483
|
+
return Render._apply_color(points, color, bg)
|
|
484
|
+
|
|
485
|
+
class Animate:
|
|
486
|
+
@staticmethod
|
|
487
|
+
def clear_screen():
|
|
488
|
+
os.system("cls" if os.name == "nt" else "clear")
|
|
489
|
+
|
|
490
|
+
@staticmethod
|
|
491
|
+
def animate_print(text, delay=0.05, end="\n", flush=True, backspace_effect=False):
|
|
492
|
+
if backspace_effect:
|
|
493
|
+
for ch in text:
|
|
494
|
+
sys.stdout.write(ch)
|
|
495
|
+
sys.stdout.flush()
|
|
496
|
+
time.sleep(delay)
|
|
497
|
+
time.sleep(0.2)
|
|
498
|
+
for _ in text:
|
|
499
|
+
sys.stdout.write("\b \b")
|
|
500
|
+
sys.stdout.flush()
|
|
501
|
+
time.sleep(delay / 2)
|
|
502
|
+
for ch in text:
|
|
503
|
+
sys.stdout.write(ch)
|
|
504
|
+
sys.stdout.flush()
|
|
505
|
+
time.sleep(delay)
|
|
506
|
+
else:
|
|
507
|
+
for ch in text:
|
|
508
|
+
sys.stdout.write(ch)
|
|
509
|
+
if flush:
|
|
510
|
+
sys.stdout.flush()
|
|
511
|
+
time.sleep(delay)
|
|
512
|
+
sys.stdout.write(end)
|
|
513
|
+
if flush:
|
|
514
|
+
sys.stdout.flush()
|
|
515
|
+
|
|
516
|
+
@staticmethod
|
|
517
|
+
def spinning_cursor(message="Loading", duration=3.0, delay=0.1, frames=None, clear_before=False):
|
|
518
|
+
if clear_before:
|
|
519
|
+
Animate.clear_screen()
|
|
520
|
+
if frames is None:
|
|
521
|
+
frames = ["|", "/", "-", "\\"]
|
|
522
|
+
end_time = time.time() + duration
|
|
523
|
+
idx = 0
|
|
524
|
+
while time.time() < end_time:
|
|
525
|
+
frame = frames[idx % len(frames)]
|
|
526
|
+
sys.stdout.write(f"\r{message} {frame} ")
|
|
527
|
+
sys.stdout.flush()
|
|
528
|
+
time.sleep(delay)
|
|
529
|
+
idx += 1
|
|
530
|
+
sys.stdout.write("\r" + " " * (len(message) + 3) + "\r")
|
|
531
|
+
sys.stdout.flush()
|
|
532
|
+
|
|
533
|
+
@staticmethod
|
|
534
|
+
def countdown(seconds, message="Countdown", clear_before=False):
|
|
535
|
+
if clear_before:
|
|
536
|
+
Animate.clear_screen()
|
|
537
|
+
for remaining in range(seconds, 0, -1):
|
|
538
|
+
sys.stdout.write(f"\r{message}: {remaining:2d} seconds remaining ")
|
|
539
|
+
sys.stdout.flush()
|
|
540
|
+
time.sleep(1)
|
|
541
|
+
sys.stdout.write(f"\r{message}: 0 seconds remaining! Done!\n")
|
|
542
|
+
sys.stdout.flush()
|
|
543
|
+
|
|
544
|
+
class Keyboard:
|
|
545
|
+
@staticmethod
|
|
546
|
+
def GetKey():
|
|
547
|
+
try:
|
|
548
|
+
import termios
|
|
549
|
+
import tty
|
|
550
|
+
fd = sys.stdin.fileno()
|
|
551
|
+
old = termios.tcgetattr(fd)
|
|
552
|
+
try:
|
|
553
|
+
tty.setraw(fd)
|
|
554
|
+
return sys.stdin.read(1)
|
|
555
|
+
finally:
|
|
556
|
+
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
557
|
+
except:
|
|
558
|
+
import msvcrt
|
|
559
|
+
return msvcrt.getch().decode()
|
|
560
|
+
|
|
561
|
+
class ConsoleInfo:
|
|
562
|
+
@staticmethod
|
|
563
|
+
def GetConsole():
|
|
564
|
+
return sys.stdout
|
|
565
|
+
|
|
566
|
+
@staticmethod
|
|
567
|
+
def GetPipVer():
|
|
568
|
+
try:
|
|
569
|
+
result = CMD.run(['python', '-m', 'pip', '--version'], capture_output=True, text=True, check=True)
|
|
570
|
+
return result.stdout.strip()
|
|
571
|
+
except:
|
|
572
|
+
return "Error: pip not found"
|
|
573
|
+
|
|
574
|
+
class Process:
|
|
575
|
+
@staticmethod
|
|
576
|
+
def StartPyCode(code, wait=False, args=None, python="python"):
|
|
577
|
+
try:
|
|
578
|
+
cmd = [python, "-c", code]
|
|
579
|
+
if args:
|
|
580
|
+
cmd.extend(args)
|
|
581
|
+
if wait:
|
|
582
|
+
proc = CMD.run(cmd, capture_output=True, text=True)
|
|
583
|
+
return proc
|
|
584
|
+
else:
|
|
585
|
+
proc = CMD.Popen(cmd)
|
|
586
|
+
return proc
|
|
587
|
+
except Exception as e:
|
|
588
|
+
print(f"Error executing code: {e}")
|
|
589
|
+
return None
|
|
590
|
+
|
|
591
|
+
@staticmethod
|
|
592
|
+
def KillPyScript(pid):
|
|
593
|
+
try:
|
|
594
|
+
os.kill(pid, 15)
|
|
595
|
+
time.sleep(0.2)
|
|
596
|
+
return True
|
|
597
|
+
except ProcessLookupError:
|
|
598
|
+
print(f"Process {pid} not found")
|
|
599
|
+
return False
|
|
600
|
+
except PermissionError:
|
|
601
|
+
print(f"Permission denied to kill process {pid}")
|
|
602
|
+
return False
|
|
603
|
+
except Exception as e:
|
|
604
|
+
print(f"Error killing process: {e}")
|
|
605
|
+
return False
|
|
606
|
+
|
|
607
|
+
@staticmethod
|
|
608
|
+
def KillPyScriptForce(pid):
|
|
609
|
+
try:
|
|
610
|
+
os.kill(pid, 9)
|
|
611
|
+
return True
|
|
612
|
+
except ProcessLookupError:
|
|
613
|
+
print(f"Process {pid} not found")
|
|
614
|
+
return False
|
|
615
|
+
except PermissionError:
|
|
616
|
+
print(f"Permission denied to kill process {pid}")
|
|
617
|
+
return False
|
|
618
|
+
except Exception as e:
|
|
619
|
+
print(f"Error killing process: {e}")
|
|
620
|
+
return False
|
|
621
|
+
|
|
622
|
+
@staticmethod
|
|
623
|
+
def IsRunning(pid):
|
|
624
|
+
try:
|
|
625
|
+
os.kill(pid, 0)
|
|
626
|
+
return True
|
|
627
|
+
except ProcessLookupError:
|
|
628
|
+
return False
|
|
629
|
+
except PermissionError:
|
|
630
|
+
return True
|
|
631
|
+
|
|
632
|
+
@staticmethod
|
|
633
|
+
def GetProcessList():
|
|
634
|
+
try:
|
|
635
|
+
result = CMD.run(['ps', '-e', '-o', 'pid,comm'], capture_output=True, text=True)
|
|
636
|
+
lines = result.stdout.strip().split('\n')[1:]
|
|
637
|
+
processes = []
|
|
638
|
+
for line in lines:
|
|
639
|
+
parts = line.strip().split(' ', 1)
|
|
640
|
+
if len(parts) == 2:
|
|
641
|
+
pid = int(parts[0])
|
|
642
|
+
name = parts[1]
|
|
643
|
+
processes.append({'pid': pid, 'name': name})
|
|
644
|
+
return processes
|
|
645
|
+
except Exception:
|
|
646
|
+
return []
|
|
647
|
+
|
|
648
|
+
@staticmethod
|
|
649
|
+
def GetProcessInfo(pid):
|
|
650
|
+
try:
|
|
651
|
+
result = CMD.run(['ps', '-p', str(pid), '-o', 'pid,comm,%cpu,%mem,etime'], capture_output=True, text=True)
|
|
652
|
+
lines = result.stdout.strip().split('\n')
|
|
653
|
+
if len(lines) < 2:
|
|
654
|
+
return None
|
|
655
|
+
parts = lines[1].strip().split()
|
|
656
|
+
if len(parts) >= 5:
|
|
657
|
+
return {
|
|
658
|
+
'pid': int(parts[0]),
|
|
659
|
+
'name': parts[1],
|
|
660
|
+
'cpu': parts[2],
|
|
661
|
+
'memory': parts[3],
|
|
662
|
+
'time': parts[4]
|
|
663
|
+
}
|
|
664
|
+
return None
|
|
665
|
+
except Exception:
|
|
666
|
+
return None
|
|
667
|
+
|
|
668
|
+
@staticmethod
|
|
669
|
+
def WaitForProcess(pid, timeout=None):
|
|
670
|
+
start_time = time.time()
|
|
671
|
+
while Process.IsRunning(pid):
|
|
672
|
+
if timeout and (time.time() - start_time) > timeout:
|
|
673
|
+
return False
|
|
674
|
+
time.sleep(0.5)
|
|
675
|
+
return True
|
|
676
|
+
|
|
677
|
+
@staticmethod
|
|
678
|
+
def GetCurrentPID():
|
|
679
|
+
return os.getpid()
|
|
680
|
+
|
|
681
|
+
class Channel:
|
|
682
|
+
def __init__(self):
|
|
683
|
+
self.connection = None
|
|
684
|
+
self.host = None
|
|
685
|
+
self.port = None
|
|
686
|
+
self.is_server = False
|
|
687
|
+
|
|
688
|
+
@staticmethod
|
|
689
|
+
def _get(url, timeout=5):
|
|
690
|
+
try:
|
|
691
|
+
import urllib.request
|
|
692
|
+
with urllib.request.urlopen(url, timeout=timeout) as response:
|
|
693
|
+
return response.read().decode()
|
|
694
|
+
except Exception:
|
|
695
|
+
return None
|
|
696
|
+
|
|
697
|
+
@staticmethod
|
|
698
|
+
def GetPublicIP():
|
|
699
|
+
ip = Channel._get('https://api.ipify.org')
|
|
700
|
+
return ip
|
|
701
|
+
|
|
702
|
+
def Connect(self, host='127.0.0.1', port=54321):
|
|
703
|
+
self.host = host
|
|
704
|
+
self.port = port
|
|
705
|
+
try:
|
|
706
|
+
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
707
|
+
self.connection.connect((host, port))
|
|
708
|
+
return self.connection
|
|
709
|
+
except Exception as e:
|
|
710
|
+
print(f"[Error] Connection failed: {e}")
|
|
711
|
+
return None
|
|
712
|
+
|
|
713
|
+
def ConnectPublic(self, port=54321):
|
|
714
|
+
public_ip = Channel.GetPublicIP()
|
|
715
|
+
if not public_ip:
|
|
716
|
+
print("[Error] Could not get public IP")
|
|
717
|
+
return None
|
|
718
|
+
return self.Connect(host=public_ip, port=port)
|
|
719
|
+
|
|
720
|
+
def CreateServer(self, port=54321):
|
|
721
|
+
self.is_server = True
|
|
722
|
+
self.port = port
|
|
723
|
+
try:
|
|
724
|
+
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
725
|
+
server.bind(('0.0.0.0', port))
|
|
726
|
+
server.listen(1)
|
|
727
|
+
self.connection, addr = server.accept()
|
|
728
|
+
return self.connection
|
|
729
|
+
except Exception as e:
|
|
730
|
+
print(f"[Error] Server failed: {e}")
|
|
731
|
+
return None
|
|
732
|
+
|
|
733
|
+
def Disconnect(self):
|
|
734
|
+
if self.connection:
|
|
735
|
+
self.connection.close()
|
|
736
|
+
self.connection = None
|
|
737
|
+
|
|
738
|
+
def Send(self, message):
|
|
739
|
+
if not self.connection:
|
|
740
|
+
return False
|
|
741
|
+
try:
|
|
742
|
+
if isinstance(message, str):
|
|
743
|
+
message = message.encode()
|
|
744
|
+
self.connection.send(message + b'\n')
|
|
745
|
+
return True
|
|
746
|
+
except Exception:
|
|
747
|
+
return False
|
|
748
|
+
|
|
749
|
+
def Receive(self):
|
|
750
|
+
if not self.connection:
|
|
751
|
+
return None
|
|
752
|
+
try:
|
|
753
|
+
data = self.connection.recv(1024).decode().strip()
|
|
754
|
+
return data
|
|
755
|
+
except Exception:
|
|
756
|
+
return None
|
|
757
|
+
|
|
758
|
+
class Web:
|
|
759
|
+
def UploadPaste(text, title="My Paste"):
|
|
760
|
+
def _get_api_key():
|
|
761
|
+
decoded = base64.b64decode("eJyzNLY0sTAxsTQtyymzNK+IDC/JztM1yA82N8zTDcv2TQ6NCnQ2igxPAwDwCwxD".encode("utf-8"))
|
|
762
|
+
decompressed = zlib.decompress(decoded).decode("utf-8")
|
|
763
|
+
if decompressed.startswith("93948449"):
|
|
764
|
+
return decompressed[len("93948449"):]
|
|
765
|
+
return None
|
|
766
|
+
api_key = _get_api_key()
|
|
767
|
+
data = {
|
|
768
|
+
'api_dev_key': api_key,
|
|
769
|
+
'api_option': 'paste',
|
|
770
|
+
'api_paste_code': text,
|
|
771
|
+
'api_paste_name': title,
|
|
772
|
+
'api_paste_private': '0',
|
|
773
|
+
'api_paste_expire_date': 'N',
|
|
774
|
+
}
|
|
775
|
+
encoded_data = urllib.parse.urlencode(data).encode('utf-8')
|
|
776
|
+
req = urllib.request.Request(
|
|
777
|
+
"https://pastebin.com/api/api_post.php",
|
|
778
|
+
data=encoded_data,
|
|
779
|
+
method="POST",
|
|
780
|
+
headers={
|
|
781
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
782
|
+
}
|
|
783
|
+
)
|
|
784
|
+
try:
|
|
785
|
+
with urllib.request.urlopen(req) as response:
|
|
786
|
+
result = response.read().decode('utf-8')
|
|
787
|
+
if result.startswith("https://pastebin.com/"):
|
|
788
|
+
paste_id = result.split("/")[-1]
|
|
789
|
+
return f"https://pastebin.com/raw/{paste_id}"
|
|
790
|
+
else:
|
|
791
|
+
return f"Pastebin error: {result}"
|
|
792
|
+
except urllib.error.HTTPError as e:
|
|
793
|
+
return f"HTTP Error {e.code}: {e.reason}"
|
|
794
|
+
except Exception as e:
|
|
795
|
+
return f"Error: {e}"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ConsoleFramework
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: The best Console Library of the Python.
|
|
5
|
+
Author: Suleiman
|
|
6
|
+
Author-email: steal.apet@mail.ru
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/plain
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: license
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
23
|
+
|
|
24
|
+
ConsoleFramework - Pure Python library for console rendering, animations, processes and more. In this module ONLY standart python, no other modules.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ConsoleFramework
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suleiman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ConsoleFramework
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: The best Console Library of the Python.
|
|
5
|
+
Author: Suleiman
|
|
6
|
+
Author-email: steal.apet@mail.ru
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/plain
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: license
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: requires-python
|
|
22
|
+
Dynamic: summary
|
|
23
|
+
|
|
24
|
+
ConsoleFramework - Pure Python library for console rendering, animations, processes and more. In this module ONLY standart python, no other modules.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="ConsoleFramework",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
description="The best Console Library of the Python.",
|
|
7
|
+
long_description="ConsoleFramework - Pure Python library for console rendering, animations, processes and more. In this module ONLY standart python, no other modules.",
|
|
8
|
+
long_description_content_type="text/plain",
|
|
9
|
+
author="Suleiman",
|
|
10
|
+
author_email="steal.apet@mail.ru",
|
|
11
|
+
license="MIT",
|
|
12
|
+
packages=find_packages(),
|
|
13
|
+
python_requires=">=3.6",
|
|
14
|
+
zip_safe=False,
|
|
15
|
+
classifiers=[
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
],
|
|
20
|
+
)
|