ttykit 0.2.2__tar.gz → 0.3.5__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.
- {ttykit-0.2.2/src/ttykit.egg-info → ttykit-0.3.5}/PKG-INFO +7 -1
- {ttykit-0.2.2 → ttykit-0.3.5}/README.md +5 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/pyproject.toml +3 -3
- ttykit-0.3.5/src/ttykit/__init__.py +69 -0
- ttykit-0.3.5/src/ttykit/console/TUI.py +148 -0
- ttykit-0.3.5/src/ttykit/console/console.py +235 -0
- ttykit-0.3.5/src/ttykit/console/const.py +18 -0
- {ttykit-0.2.2 → ttykit-0.3.5/src/ttykit.egg-info}/PKG-INFO +7 -1
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit.egg-info/SOURCES.txt +4 -1
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit.egg-info/requires.txt +1 -0
- ttykit-0.2.2/src/ttykit/__init__.py +0 -22
- {ttykit-0.2.2 → ttykit-0.3.5}/LICENSE +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/setup.cfg +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit/_state.py +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit/progress.py +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit/spinner.py +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit/status.py +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit.egg-info/dependency_links.txt +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit.egg-info/entry_points.txt +0 -0
- {ttykit-0.2.2 → ttykit-0.3.5}/src/ttykit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ttykit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Helping made beatiful out in terminal
|
|
5
5
|
Author: He-STALIN
|
|
6
6
|
Author-email: hene.stalin@gmail.com
|
|
@@ -32,6 +32,7 @@ Keywords: tty,terminal,formatting
|
|
|
32
32
|
Requires-Python: >=3.13
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
|
+
Requires-Dist: keyboard
|
|
35
36
|
Provides-Extra: test
|
|
36
37
|
Requires-Dist: pytest; extra == "test"
|
|
37
38
|
Dynamic: license-file
|
|
@@ -47,18 +48,23 @@ Dynamic: license-file
|
|
|
47
48
|
[](https://github.com/He-STALIN/ttykit)
|
|
48
49
|
|
|
49
50
|
</div>
|
|
51
|
+
|
|
50
52
|
---
|
|
51
53
|
|
|
52
54
|
## 📦 For Start
|
|
55
|
+
### Manually:
|
|
53
56
|
|
|
54
57
|
```bash
|
|
55
58
|
git clone https://github.com/He-STALIN/ttykit.git
|
|
56
59
|
```
|
|
57
60
|
|
|
58
61
|
- Change dir and exec
|
|
62
|
+
|
|
59
63
|
```bash
|
|
60
64
|
pip install .
|
|
61
65
|
```
|
|
66
|
+
### Auto
|
|
67
|
+
- exec `pip install ttykit`
|
|
62
68
|
|
|
63
69
|
- And use in your projects!
|
|
64
70
|
|
|
@@ -9,18 +9,23 @@
|
|
|
9
9
|
[](https://github.com/He-STALIN/ttykit)
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
|
+
|
|
12
13
|
---
|
|
13
14
|
|
|
14
15
|
## 📦 For Start
|
|
16
|
+
### Manually:
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
19
|
git clone https://github.com/He-STALIN/ttykit.git
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
- Change dir and exec
|
|
23
|
+
|
|
21
24
|
```bash
|
|
22
25
|
pip install .
|
|
23
26
|
```
|
|
27
|
+
### Auto
|
|
28
|
+
- exec `pip install ttykit`
|
|
24
29
|
|
|
25
30
|
- And use in your projects!
|
|
26
31
|
|
|
@@ -8,17 +8,17 @@ include = ["ttykit*"]
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "ttykit"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.3.5"
|
|
12
12
|
description = "Helping made beatiful out in terminal"
|
|
13
13
|
requires-python = ">=3.13"
|
|
14
14
|
authors = [
|
|
15
|
-
{
|
|
15
|
+
{name = "He-STALIN"},
|
|
16
16
|
{email = "hene.stalin@gmail.com"}
|
|
17
17
|
]
|
|
18
18
|
license = {file = "LICENSE"}
|
|
19
19
|
readme = "README.md"
|
|
20
20
|
keywords = ['tty', 'terminal', 'formatting']
|
|
21
|
-
dependencies = []
|
|
21
|
+
dependencies = ["keyboard"]
|
|
22
22
|
|
|
23
23
|
[project.scripts]
|
|
24
24
|
ttykit-spinner = "ttykit.spinner:main"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Helping made beatiful out in terminal"""
|
|
2
|
+
|
|
3
|
+
from .progress import Progress
|
|
4
|
+
from .status import Status
|
|
5
|
+
from ._state import TaskState, Colors, Styles, RESET
|
|
6
|
+
from .console.console import Console
|
|
7
|
+
from .console.TUI import TUI
|
|
8
|
+
import traceback
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
__colors__ = [
|
|
12
|
+
Colors,
|
|
13
|
+
Styles,
|
|
14
|
+
RESET
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'Progress',
|
|
19
|
+
'Status',
|
|
20
|
+
'TaskState',
|
|
21
|
+
'Console',
|
|
22
|
+
'TUI',
|
|
23
|
+
'get_console',
|
|
24
|
+
'set_custom_hook'
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
def get_console() -> 'Console':
|
|
28
|
+
"""Return a instance of `Console`"""
|
|
29
|
+
console = Console()
|
|
30
|
+
return console
|
|
31
|
+
|
|
32
|
+
def custom_excepthook(exc_type, exc_value, exc_tb):
|
|
33
|
+
tb_lines = traceback.format_exception(exc_type, exc_value, exc_tb)
|
|
34
|
+
|
|
35
|
+
# Самая длинная строка
|
|
36
|
+
error_line = f"Error: {exc_type.__name__}"
|
|
37
|
+
msg_line = f"Message: {exc_value}"
|
|
38
|
+
max_len = max(len(error_line), len(msg_line), 40)
|
|
39
|
+
|
|
40
|
+
# Рамка
|
|
41
|
+
print(f"╔{'═' * (max_len + 4)}╗")
|
|
42
|
+
print(f"║ {Colors.RED}{error_line.ljust(max_len)}{RESET} ║")
|
|
43
|
+
print(f"║ {Colors.RED}{msg_line.ljust(max_len)}{RESET} ║")
|
|
44
|
+
print(f"╠{'═' * (max_len + 4)}╣")
|
|
45
|
+
|
|
46
|
+
for line in tb_lines[-3:]:
|
|
47
|
+
clean = line.strip()
|
|
48
|
+
if len(clean) > max_len:
|
|
49
|
+
clean = clean[:max_len-3] + "..."
|
|
50
|
+
print(f"║ {clean.ljust(max_len)} ║")
|
|
51
|
+
|
|
52
|
+
print(f"╚{'═' * (max_len + 4)}╝")
|
|
53
|
+
|
|
54
|
+
def set_custom_hook(Traceback: bool= False) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Set a custom methods
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
Traceback (bool): replace Traceback on custom or not. Default `False`
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
if Traceback:
|
|
63
|
+
print('set custom excepthook...')
|
|
64
|
+
sys.excepthook = custom_excepthook
|
|
65
|
+
|
|
66
|
+
__name__ = 'ttykit'
|
|
67
|
+
__author__ = 'He-STALIN'
|
|
68
|
+
__version__ = '0.3.5'
|
|
69
|
+
__license__ = 'MIT'
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import keyboard as kb
|
|
2
|
+
import os, sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TUI:
|
|
6
|
+
"""
|
|
7
|
+
Create TUI (Text User Interface) in terminal with user menus
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
title (str): title of the `TUI`
|
|
11
|
+
|
|
12
|
+
### Methods:
|
|
13
|
+
`addMenu(name, callback)`: add menu to UI render.
|
|
14
|
+
`Run()`: exec and start render the UI.
|
|
15
|
+
`UpdateUI()`: forced update UI Layer.
|
|
16
|
+
|
|
17
|
+
### Examples
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from ttykit import TUI
|
|
21
|
+
|
|
22
|
+
ui = TUI("this example UI!")
|
|
23
|
+
|
|
24
|
+
def someAction():
|
|
25
|
+
# do something
|
|
26
|
+
|
|
27
|
+
ui.addMenu("Us menu", someAction) # adding menu in UI
|
|
28
|
+
|
|
29
|
+
ui.Run() # and start render UI
|
|
30
|
+
```
|
|
31
|
+
Out in Terminal
|
|
32
|
+
```
|
|
33
|
+
===[ this example UI! ]===
|
|
34
|
+
|
|
35
|
+
> Us menu
|
|
36
|
+
|
|
37
|
+
> Exit
|
|
38
|
+
|
|
39
|
+
For control use arrows Up/Down and Enter to select
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self, title: str):
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.TITLE: str = title if title else "Text UI"
|
|
45
|
+
self.FOOTER: str = "For control use arrows Up/Down and Enter to select"
|
|
46
|
+
self.DEFAULT_SPACE = " "
|
|
47
|
+
self._requestClosing: bool = False
|
|
48
|
+
self.action: int = 1
|
|
49
|
+
self._max_menus: int = 0
|
|
50
|
+
self._menus: list = []
|
|
51
|
+
kb.add_hotkey("Up", self.UpMenu)
|
|
52
|
+
kb.add_hotkey("Down", self.DownMenu)
|
|
53
|
+
kb.add_hotkey("enter", self._selectAction)
|
|
54
|
+
|
|
55
|
+
def _clearTerminal(self):
|
|
56
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
57
|
+
|
|
58
|
+
def _selectAction(self):
|
|
59
|
+
if self.action == self._max_menus:
|
|
60
|
+
self._requestClosing = True
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
menu = self._menus[self.action - 1]
|
|
64
|
+
menu["callback"]()
|
|
65
|
+
|
|
66
|
+
def _UpMenu(self):
|
|
67
|
+
self.action -= 1
|
|
68
|
+
|
|
69
|
+
if self.action < 1:
|
|
70
|
+
self.action = self._max_menus
|
|
71
|
+
|
|
72
|
+
self.UpdateUI()
|
|
73
|
+
|
|
74
|
+
def _DownMenu(self):
|
|
75
|
+
self.action += 1
|
|
76
|
+
|
|
77
|
+
if self.action > self._max_menus:
|
|
78
|
+
self.action = 1
|
|
79
|
+
|
|
80
|
+
self.UpdateUI()
|
|
81
|
+
|
|
82
|
+
def UpdateUI(self):
|
|
83
|
+
"""Update UI Layer in Terminal"""
|
|
84
|
+
TUI = ""
|
|
85
|
+
TUI += f"===[ {self.TITLE} ]==="
|
|
86
|
+
TUI += "\n\n"
|
|
87
|
+
|
|
88
|
+
_current_menu = 1
|
|
89
|
+
for menu in self._menus:
|
|
90
|
+
if self.action == _current_menu:
|
|
91
|
+
TUI += f"{self.DEFAULT_SPACE}\033[34m> {menu["name"]}\033[0m\n"
|
|
92
|
+
else:
|
|
93
|
+
TUI += f"{self.DEFAULT_SPACE}> {menu["name"]}\033[0m\n"
|
|
94
|
+
|
|
95
|
+
TUI += "\n"
|
|
96
|
+
_current_menu += 1
|
|
97
|
+
|
|
98
|
+
if len(self._menus) == self._max_menus:
|
|
99
|
+
self._max_menus += 1 #? add Exit menu in UI
|
|
100
|
+
|
|
101
|
+
if self.action == self._max_menus: #? exit menu always is last
|
|
102
|
+
TUI += f"{self.DEFAULT_SPACE}\033[31m> Exit\033[0m\n"
|
|
103
|
+
else:
|
|
104
|
+
TUI += f"{self.DEFAULT_SPACE}> Exit\033[0m\n"
|
|
105
|
+
|
|
106
|
+
TUI += "\n" + self.FOOTER + "\n"
|
|
107
|
+
|
|
108
|
+
sys.stdout.write("\033[H") #? set cursor to pos (0, 0)
|
|
109
|
+
sys.stdout.write(TUI)
|
|
110
|
+
sys.stdout.flush()
|
|
111
|
+
|
|
112
|
+
def addMenu(self, name: str, callback):
|
|
113
|
+
"""
|
|
114
|
+
Adding menu to UI.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name (str): name of the menu
|
|
118
|
+
callback: what need to call, when menu selected
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
self._menus.append({
|
|
122
|
+
"name": name,
|
|
123
|
+
"callback": callback
|
|
124
|
+
})
|
|
125
|
+
self._max_menus += 1
|
|
126
|
+
except Exception as e:
|
|
127
|
+
raise Exception(e)
|
|
128
|
+
|
|
129
|
+
def Run(self):
|
|
130
|
+
if len(self._menus) < 1:
|
|
131
|
+
raise ValueError("UI can't has been created without menus")
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
self._clearTerminal()
|
|
135
|
+
self.UpdateUI()
|
|
136
|
+
while True:
|
|
137
|
+
if self._requestClosing:
|
|
138
|
+
sys.exit()
|
|
139
|
+
break
|
|
140
|
+
else:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
except KeyboardInterrupt:
|
|
144
|
+
sys.stdout.write("[!] Exited by user\n")
|
|
145
|
+
sys.stdout.flush()
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
raise Exception(e)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
from typing import Optional, Literal, Mapping, Any
|
|
4
|
+
from ttykit import Colors, Styles
|
|
5
|
+
|
|
6
|
+
from .const import ColorSystem, WINDOWS
|
|
7
|
+
|
|
8
|
+
class Console:
|
|
9
|
+
"""
|
|
10
|
+
Class for controling terminal events
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
color_system (str, Optional): Color system of the terminal. Either `standard`, `256` or `truecolor`. Leave as `auto` to autodetect.
|
|
14
|
+
width (int): width of the terminal. If state is `None`, calculates it automatically.
|
|
15
|
+
height (int): height of the terminal. If state is `None`, calculates it automatically.
|
|
16
|
+
stderr (bool): use `stderr` for errors instead of stdout. Default `False`.
|
|
17
|
+
no_color (bool): if `True`, the terminal be without colors, only monochrome (white and black). Default `False`.
|
|
18
|
+
force_terminal (bool): if `True` use terminal control codes in any situation.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
_environ: Mapping[str, str] = os.environ
|
|
22
|
+
|
|
23
|
+
def __init__(self,
|
|
24
|
+
color_system: Optional[Literal["auto", "standard", "256", "truecolor", "windows"]] = "auto",
|
|
25
|
+
width: int = None,
|
|
26
|
+
height: int = None,
|
|
27
|
+
stderr: bool = False,
|
|
28
|
+
no_color: bool = False,
|
|
29
|
+
force_terminal: Optional[bool] = False
|
|
30
|
+
):
|
|
31
|
+
super().__init__()
|
|
32
|
+
|
|
33
|
+
#? preparing to avoid any errors
|
|
34
|
+
self._color_system = Optional[ColorSystem]
|
|
35
|
+
self._force_terminal = None
|
|
36
|
+
self.stderr = stderr
|
|
37
|
+
self.no_color = no_color
|
|
38
|
+
self.height = None
|
|
39
|
+
self.width = None
|
|
40
|
+
|
|
41
|
+
if color_system is None:
|
|
42
|
+
self._color_system = None
|
|
43
|
+
elif color_system == "auto":
|
|
44
|
+
self._color_system = self._detect_colorSystem()
|
|
45
|
+
|
|
46
|
+
if force_terminal is not None:
|
|
47
|
+
self._force_terminal = force_terminal
|
|
48
|
+
|
|
49
|
+
if width is None:
|
|
50
|
+
try:
|
|
51
|
+
size = os.get_terminal_size()
|
|
52
|
+
width = size.columns
|
|
53
|
+
except OSError:
|
|
54
|
+
print('Error calculating width of the terminal. Use default value (80)')
|
|
55
|
+
width = 80
|
|
56
|
+
|
|
57
|
+
if height is None:
|
|
58
|
+
try:
|
|
59
|
+
size = os.get_terminal_size()
|
|
60
|
+
height = size.lines
|
|
61
|
+
except OSError:
|
|
62
|
+
print('Error calculating height of the terminal. Use default value (24)')
|
|
63
|
+
height
|
|
64
|
+
|
|
65
|
+
def _print_error(self, msg: str=None):
|
|
66
|
+
if msg is None:
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if self.stderr:
|
|
70
|
+
sys.stderr.write(msg)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def isTerminal(self) -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Check if the console writing to a terminal
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
state (bool): return `True` if console is understanding escape sequences, otherwise `False`
|
|
79
|
+
"""
|
|
80
|
+
if self._force_terminal is not None:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
if hasattr(sys.stdin, "__module__") and sys.stdin.__module__.startswith("idlelib"):
|
|
84
|
+
return False #? return False for idle which claims to be a tty but can't handle ANSI codes
|
|
85
|
+
|
|
86
|
+
ttyCompatyble = self._environ.get('TTY_COMPATIBLE', '')
|
|
87
|
+
if ttyCompatyble == '0': #? 0 = device is not tty compatible
|
|
88
|
+
return False
|
|
89
|
+
elif ttyCompatyble =='1': #? 1 = device is tty compatible
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
if not sys.stdout.isatty():
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def is_dumb_terminal(self) -> bool:
|
|
98
|
+
"""Detect dumb terminal.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
state (bool): `True` if writing to a dumb terminal, otherwise `False`.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
is_dumb = self._environ.get("TERM", "").lower() in ("dumb", "unknown")
|
|
105
|
+
return self.isTerminal and is_dumb
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def color_system(self):
|
|
110
|
+
return self._detect_colorSystem()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _detect_colorSystem(self) -> Optional[ColorSystem]:
|
|
114
|
+
"""Autodetect the supported color theme fo the terminal"""
|
|
115
|
+
|
|
116
|
+
if self.no_color:
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
if WINDOWS:
|
|
120
|
+
# TODO: add check for legacy windows
|
|
121
|
+
|
|
122
|
+
if self._environ.get('COLORTERM') in ('truecolor', '24bit'):
|
|
123
|
+
return ColorSystem.TRUECOLOR
|
|
124
|
+
return ColorSystem.WINDOWS
|
|
125
|
+
else:
|
|
126
|
+
if self._environ.get('COLORTERM') in ('truecolor', '24bit'):
|
|
127
|
+
return ColorSystem.TRUECOLOR
|
|
128
|
+
|
|
129
|
+
if self._environ.get("TERM") in ("xterm-256color", "screen-256color"):
|
|
130
|
+
return ColorSystem.EIGHT_BIT
|
|
131
|
+
return ColorSystem.STANDARD
|
|
132
|
+
|
|
133
|
+
def _get_style_code(self, style: str) -> str:
|
|
134
|
+
styles = {
|
|
135
|
+
'bold': Styles.BOLD,
|
|
136
|
+
'dim': Styles.DIM,
|
|
137
|
+
'italic': Styles.ITALIC,
|
|
138
|
+
'underline': Styles.UNDERLINE,
|
|
139
|
+
'blink': Styles.BLINK,
|
|
140
|
+
'rapid_blink': Styles.RAPID_BLINK,
|
|
141
|
+
'reverse': Styles.REVERSE
|
|
142
|
+
}
|
|
143
|
+
return styles.get(style, '')
|
|
144
|
+
|
|
145
|
+
def _get_color_code(self, color: str) -> str:
|
|
146
|
+
colors = {
|
|
147
|
+
"black": Colors.BLACK,
|
|
148
|
+
"red": Colors.RED,
|
|
149
|
+
"green": Colors.GREEN,
|
|
150
|
+
"yellow": Colors.YELLOW,
|
|
151
|
+
"blue": Colors.BLUE,
|
|
152
|
+
"purple": Colors.PURPLE,
|
|
153
|
+
"cyan": Colors.CYAN,
|
|
154
|
+
"white": Colors.WHITE,
|
|
155
|
+
"dark_grey": Colors.DARK_GREY,
|
|
156
|
+
"light_red": Colors.LIGHT_RED,
|
|
157
|
+
"light_green": Colors.LIGHT_GREEN,
|
|
158
|
+
"light_yellow": Colors.LIGHT_YELLOW,
|
|
159
|
+
"light_blue": Colors.LIGHT_BLUE,
|
|
160
|
+
"light_purple": Colors.LIGHT_PURPLE,
|
|
161
|
+
"light_cyan": Colors.LIGHT_CYAN,
|
|
162
|
+
"light_white": Colors.LIGHT_WHITE
|
|
163
|
+
}
|
|
164
|
+
return colors.get(color, "")
|
|
165
|
+
|
|
166
|
+
def bell(self):
|
|
167
|
+
sys.stdout.write("\a")
|
|
168
|
+
sys.stdout.flush()
|
|
169
|
+
|
|
170
|
+
def print(self,
|
|
171
|
+
*args,
|
|
172
|
+
sep: str | None=" ",
|
|
173
|
+
end: str | None ="\n",
|
|
174
|
+
style: str | None=None,
|
|
175
|
+
color: str | None=None) -> None:
|
|
176
|
+
"""
|
|
177
|
+
Print the args to a stream
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
*args (Any): args to write to a stream
|
|
181
|
+
sep (str | None): string inserted between values, default a space.
|
|
182
|
+
end (str | None): string appended after the last value, default a newline.
|
|
183
|
+
style: style of args write to a steam, default `None`.
|
|
184
|
+
color: color of args write to a stream, default `None`.
|
|
185
|
+
Returns:
|
|
186
|
+
None: write in `stdout`
|
|
187
|
+
"""
|
|
188
|
+
if not self.isTerminal or self.no_color:
|
|
189
|
+
#? Just out, without colors or styles
|
|
190
|
+
sys.stdout.write(sep.join(str(a) for a in args) + end)
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
#TODO: realize print logic with color style replace
|
|
194
|
+
|
|
195
|
+
#? Apply the style with color to args
|
|
196
|
+
text = sep.join(str(a) for a in args)
|
|
197
|
+
if color and self.color_system:
|
|
198
|
+
color_code = self._get_color_code(color.lower())
|
|
199
|
+
text = f"{color_code}{text}{Colors.RESET}"
|
|
200
|
+
|
|
201
|
+
if style:
|
|
202
|
+
style_code = self._get_style_code(style.lower())
|
|
203
|
+
text = f"{style_code}{text}"
|
|
204
|
+
|
|
205
|
+
sys.stdout.write(text + end)
|
|
206
|
+
sys.stdout.flush()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def input(self,
|
|
210
|
+
prompt = '',
|
|
211
|
+
*,
|
|
212
|
+
style: str | None = None,
|
|
213
|
+
color: str | None = None,
|
|
214
|
+
password: bool = False) -> str:
|
|
215
|
+
"""
|
|
216
|
+
Displays a prompt if have and waits for input from user.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
prompt: text to render in prompt
|
|
220
|
+
style (str | None): style for prompt render. Required prompt
|
|
221
|
+
color (str | None): color for prompt render. Required prompt
|
|
222
|
+
password (bool): if `True`, hide typed text. Default `False`
|
|
223
|
+
Returns:
|
|
224
|
+
result (str): typed text from user from stdin
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
if prompt:
|
|
228
|
+
self.print(prompt, style=style, color=color, end='')
|
|
229
|
+
|
|
230
|
+
if password:
|
|
231
|
+
# TODO: realize text hidding
|
|
232
|
+
Warning("This attribute in developing. Please, set 'password=False'")
|
|
233
|
+
else:
|
|
234
|
+
result = input()
|
|
235
|
+
return result
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
WINDOWS = sys.platform == "win32"
|
|
5
|
+
|
|
6
|
+
class ColorSystem(IntEnum):
|
|
7
|
+
"""One of the 3 color system supported by terminals."""
|
|
8
|
+
|
|
9
|
+
STANDARD = 1
|
|
10
|
+
EIGHT_BIT = 2
|
|
11
|
+
TRUECOLOR = 3
|
|
12
|
+
WINDOWS = 4
|
|
13
|
+
|
|
14
|
+
def __repr__(self) -> str:
|
|
15
|
+
return f"ColorSystem.{self.name}"
|
|
16
|
+
|
|
17
|
+
def __str__(self) -> str:
|
|
18
|
+
return repr(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ttykit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Helping made beatiful out in terminal
|
|
5
5
|
Author: He-STALIN
|
|
6
6
|
Author-email: hene.stalin@gmail.com
|
|
@@ -32,6 +32,7 @@ Keywords: tty,terminal,formatting
|
|
|
32
32
|
Requires-Python: >=3.13
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
|
+
Requires-Dist: keyboard
|
|
35
36
|
Provides-Extra: test
|
|
36
37
|
Requires-Dist: pytest; extra == "test"
|
|
37
38
|
Dynamic: license-file
|
|
@@ -47,18 +48,23 @@ Dynamic: license-file
|
|
|
47
48
|
[](https://github.com/He-STALIN/ttykit)
|
|
48
49
|
|
|
49
50
|
</div>
|
|
51
|
+
|
|
50
52
|
---
|
|
51
53
|
|
|
52
54
|
## 📦 For Start
|
|
55
|
+
### Manually:
|
|
53
56
|
|
|
54
57
|
```bash
|
|
55
58
|
git clone https://github.com/He-STALIN/ttykit.git
|
|
56
59
|
```
|
|
57
60
|
|
|
58
61
|
- Change dir and exec
|
|
62
|
+
|
|
59
63
|
```bash
|
|
60
64
|
pip install .
|
|
61
65
|
```
|
|
66
|
+
### Auto
|
|
67
|
+
- exec `pip install ttykit`
|
|
62
68
|
|
|
63
69
|
- And use in your projects!
|
|
64
70
|
|
|
@@ -11,4 +11,7 @@ src/ttykit.egg-info/SOURCES.txt
|
|
|
11
11
|
src/ttykit.egg-info/dependency_links.txt
|
|
12
12
|
src/ttykit.egg-info/entry_points.txt
|
|
13
13
|
src/ttykit.egg-info/requires.txt
|
|
14
|
-
src/ttykit.egg-info/top_level.txt
|
|
14
|
+
src/ttykit.egg-info/top_level.txt
|
|
15
|
+
src/ttykit/console/TUI.py
|
|
16
|
+
src/ttykit/console/console.py
|
|
17
|
+
src/ttykit/console/const.py
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""Helping made beatiful out in terminal"""
|
|
2
|
-
|
|
3
|
-
from .progress import Progress
|
|
4
|
-
from .status import Status
|
|
5
|
-
from ._state import TaskState, Colors, Styles, RESET
|
|
6
|
-
|
|
7
|
-
__colors__ = [
|
|
8
|
-
Colors,
|
|
9
|
-
Styles,
|
|
10
|
-
RESET
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
'Progress',
|
|
15
|
-
'Status',
|
|
16
|
-
'TaskState'
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
__name__ = 'ttykit'
|
|
20
|
-
__author__ = 'He-STALIN'
|
|
21
|
-
__version__ = '0.2.2'
|
|
22
|
-
__license__ = 'MIT'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|