Skip to content

Commit 4d6590f

Browse files
committed
ADD: simple prototype
1 parent a13c276 commit 4d6590f

6 files changed

Lines changed: 111 additions & 2 deletions

File tree

examples/.gitkeep

Whitespace-only changes.

pymenu/__init__.py

Whitespace-only changes.

pymenu/menu.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from curses import wrapper, use_default_colors, curs_set
2+
from typing import List
3+
from pymenu.misc import Item, Keyboard
4+
5+
6+
class SelectorMenu:
7+
8+
def __init__(self, options: List[str], title: str = '', default_index: int = 0, indicator: str = "->"):
9+
if len(options) == 0:
10+
raise ValueError('must contains 1 element')
11+
if 0 <= default_index >= len(options):
12+
raise ValueError('default_index should be less than the length of options')
13+
14+
self.__options = options
15+
self.__title = title
16+
self.__index = default_index
17+
self.__indicator = indicator
18+
self.__scroll_top = 0
19+
20+
def get_selected(self) -> Item:
21+
return Item(name=self.__options[self.__index], index=self.__index)
22+
23+
def get_option_lines(self):
24+
lines = []
25+
for index, option in enumerate(self.__options):
26+
if index == self.__index:
27+
prefix = self.__indicator
28+
else:
29+
prefix = len(self.__indicator) * ' '
30+
lines.append(f'{prefix} {option}')
31+
return lines
32+
33+
def get_lines(self):
34+
title_lines = self.__title.split('\n')
35+
current_line_y = self.__index + len(title_lines) + 1
36+
lines = title_lines + self.get_option_lines()
37+
return lines, current_line_y
38+
39+
def go_up(self):
40+
self.__index = (self.__index - 1) % len(self.__options)
41+
42+
def go_down(self):
43+
self.__index = (self.__index + 1) % len(self.__options)
44+
45+
def draw(self, screen) -> None:
46+
screen.clear()
47+
48+
max_y, max_x = screen.getmaxyx()
49+
50+
lines, current_line = self.get_lines()
51+
52+
if current_line <= self.__scroll_top:
53+
self.__scroll_top = 0
54+
elif current_line - self.__scroll_top > max_y:
55+
self.__scroll_top = current_line - max_y
56+
57+
lines_to_draw = lines[self.__scroll_top:self.__scroll_top + max_y]
58+
59+
for y, line in enumerate(lines_to_draw):
60+
screen.addnstr(y, 0, line, max_x - 2)
61+
y += 1
62+
63+
screen.refresh()
64+
65+
def run_loop(self, screen):
66+
config = {
67+
Keyboard.UP: self.go_up,
68+
Keyboard.DOWN: self.go_down,
69+
}
70+
returnable_config = {
71+
Keyboard.ENTER: self.get_selected,
72+
}
73+
while True:
74+
self.draw(screen)
75+
key = screen.getch()
76+
for keys in config.keys():
77+
if key in keys:
78+
config[keys]()
79+
break
80+
for keys in returnable_config.keys():
81+
if key in keys:
82+
return returnable_config[keys]()
83+
84+
def __start(self, screen):
85+
use_default_colors()
86+
curs_set(0)
87+
return self.run_loop(screen)
88+
89+
def input(self):
90+
return wrapper(self.__start)

pymenu/misc.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from abc import ABC
2+
from curses import KEY_UP, KEY_DOWN, KEY_ENTER
3+
from typing import NamedTuple, Final
4+
5+
6+
class Item(NamedTuple):
7+
name: str
8+
index: int
9+
10+
11+
class Keyboard(ABC):
12+
UP: Final = (KEY_UP, ord('w'))
13+
DOWN: Final = (KEY_DOWN, ord('s'))
14+
ENTER: Final = (KEY_ENTER, ord('\n'))
15+
SELECT: Final = (ord(' '), )

requirements.txt

Whitespace-only changes.

run.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
from pymenu.menu import SelectorMenu
2+
3+
14
def main() -> None:
2-
pass
5+
ans = SelectorMenu(['С++', 'Python', 'Pascal'], title='Выбери ЯП мечты').input()
6+
print(ans)
37

48

59
if __name__ == "__main__":
6-
main()
10+
main()

0 commit comments

Comments
 (0)