add: первый экран TUI
This commit is contained in:
parent
ec1b585fb2
commit
e6dd940d8a
@ -6,6 +6,8 @@ authors = ["erjemin <erjemin@gmail.com>"]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
packages = [{ include = "pganec", from = "src" }]
|
packages = [{ include = "pganec", from = "src" }]
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
pganec = "pganec.main:run"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.8.1,<4.0.0"
|
python = ">=3.8.1,<4.0.0"
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# src/pganec/_init__.py
|
||||||
"""
|
"""
|
||||||
PGanec - TUI для резервного копирования и восстановления баз данных PostgreSQL.
|
PGanec - TUI для резервного копирования и восстановления баз данных PostgreSQL.
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
# src/pganec/config.py
|
||||||
import logging
|
import logging
|
||||||
import yaml
|
import yaml
|
||||||
from typing import Optional, Dict # Для Python < 3.9 используйте Dict, для Python 3.9+ можно просто dict
|
from typing import Optional, Dict # Для Python < 3.9 используйте Dict, для Python 3.9+ можно просто dict
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
# src/pganec/config.py
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from config import load_config, ConfigError, ConfigNotFoundError, InvalidConfigFormatError
|
from config import load_config, ConfigError, ConfigNotFoundError, InvalidConfigFormatError
|
||||||
|
from tui import PGanecApp
|
||||||
|
|
||||||
# --- Настройки и инициирование логирования ---
|
# --- Настройки и инициирование логирования ---
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -57,12 +59,13 @@ if __name__ == '__main__':
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except ConfigError as e: # Общая ошибка конфигурации (должна быть последней в цепочке)
|
except ConfigError as e: # Общая ошибка конфигурации (должна быть последней в цепочке)
|
||||||
print(f"Ошибка при загрузке конфигурации: {e}", file=sys.stderr)
|
print(f"Ошибка при загрузке конфигурации: {e}", file=sys.stderr)
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
except Exception as e: # Для совсем непредвиденных, не ConfigError
|
||||||
|
logger.critical(f"Необработанная ошибка во время инициализации: {e}", exc_info=True)
|
||||||
|
print(f"Произошла критическая и непредвиденная ошибка: {e}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
print_hi('PyCharm')
|
# Запуск главного меню TUI
|
||||||
|
PGanecApp().run()
|
||||||
# with open("config.yaml", "r") as f:
|
|
||||||
# config = yaml.safe_load(f)
|
|
||||||
|
|
||||||
|
|
||||||
|
97
src/pganec/tui.py
Normal file
97
src/pganec/tui.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# src/pganec/tui.py
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.containers import Vertical, Horizontal
|
||||||
|
from textual.widgets import Button, Header, Footer, Static
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MainMenu(Static):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
|
||||||
|
yield Horizontal(
|
||||||
|
Button(label="Backup (DB -> Dump)", id="backup", variant="primary"),
|
||||||
|
Button(label="Restore (Dump -> DB)", id="restore", variant="primary"),
|
||||||
|
Button(label="Copy (DB -> DB)", id="copy", variant="error"),
|
||||||
|
Button(label="Quit", id="quit", variant="error"),
|
||||||
|
id="menu",
|
||||||
|
)
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
class PGanecApp(App):
|
||||||
|
CSS = """
|
||||||
|
#menu {
|
||||||
|
width: 100%;
|
||||||
|
align: left top;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
align: left middle;
|
||||||
|
text-align: left;
|
||||||
|
width: 25%;
|
||||||
|
color: orange;
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button.-primary {
|
||||||
|
color: orange;
|
||||||
|
border: round orange;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button.-error {
|
||||||
|
color: grey;
|
||||||
|
border: round orange;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button:focus {
|
||||||
|
border: heavy green;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
BINDINGS = [
|
||||||
|
("b", "backup", "Backup"),
|
||||||
|
("r", "restore", "Restore"),
|
||||||
|
("с", "copy", "Copy"),
|
||||||
|
("q", "quit", "Quit"),
|
||||||
|
("right", "focus_next"), ("down", "focus_next"),
|
||||||
|
("left", "focus_previous"), ("up", "focus_previous"),
|
||||||
|
("enter", "activate"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
yield Static("PGanec", id="title")
|
||||||
|
yield MainMenu()
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
button_id = event.button.id
|
||||||
|
if button_id == "backup":
|
||||||
|
await self.push_screen(BackupScreen()) # пока заглушка
|
||||||
|
elif button_id == "restore":
|
||||||
|
await self.push_screen(RestoreScreen()) # пока заглушка
|
||||||
|
elif button_id == "copy":
|
||||||
|
await self.action_quit()
|
||||||
|
elif button_id == "quit":
|
||||||
|
await self.action_quit()
|
||||||
|
|
||||||
|
async def action_activate(self) -> None:
|
||||||
|
focused = self.focused
|
||||||
|
if isinstance(focused, Button):
|
||||||
|
await self.on_button_pressed(Button.Pressed(focused))
|
||||||
|
|
||||||
|
# Заглушки для других экранов:
|
||||||
|
class BackupScreen(Static):
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.update("🛠 Здесь будет экран выбора сервера для бэкапа.")
|
||||||
|
|
||||||
|
class RestoreScreen(Static):
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
self.update("♻️ Здесь будет экран выбора сервера и бэкапа для восстановления.")
|
Loading…
x
Reference in New Issue
Block a user