mwe full cycle form input

click-textual
Maveno 1 year ago
parent 24e85d7005
commit 37786f56b5

@ -2,7 +2,7 @@
import os, sys, subprocess, json, click, time, re import os, sys, subprocess, json, click, time, re
from ffx.modes_app import ModesApp from ffx.ffx_app import FfxApp
VERSION='0.1.0' VERSION='0.1.0'
@ -391,7 +391,7 @@ def unmux(ctx,
def shows(ctx): def shows(ctx):
app = ModesApp(ctx.obj) app = FfxApp(ctx.obj)
app.run() app.run()

@ -0,0 +1,67 @@
import os
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from ffx.model.show import Base, Show
from ffx.model.pattern import Pattern
from .shows_screen import ShowsScreen
from .warning_screen import WarningScreen
from .dashboard_screen import DashboardScreen
from .settings_screen import SettingsScreen
from .help_screen import HelpScreen
class FfxApp(App):
TITLE = "FFX"
BINDINGS = [
("q", "quit()", "Quit"),
# ("d", "switch_mode('dashboard')", "Dashboard"),
# ("s", "switch_mode('settings')", "Settings"),
("h", "switch_mode('help')", "Help"),
]
# MODES = {
# "shows": ShowsScreen,
# "warning": WarningScreen,
# "dashboard": DashboardScreen,
# "settings": SettingsScreen,
# "help": HelpScreen,
# }
def __init__(self, context = {}):
super().__init__()
# Data 'input' variable
self.context = context
# Initialize DB
homeDir = os.path.expanduser("~")
ffxVarDir = os.path.join(homeDir, '.local', 'var', 'ffx')
if not os.path.exists(ffxVarDir):
os.makedirs(ffxVarDir)
self.context['database_url'] = f"sqlite:///{os.path.join(ffxVarDir, 'ffx.db')}"
self.context['database_engine'] = create_engine(self.context['database_url'])
self.context['database_session'] = sessionmaker(bind=self.context['database_engine'])
Base.metadata.create_all(self.context['database_engine'])
def on_mount(self) -> None:
# Show Shows Screen
#self.switch_mode("shows")
self.push_screen(ShowsScreen())
def getContext(self):
"""Data 'output' method"""
return self.context

@ -1,2 +0,0 @@
class FilePattern():
pass

@ -0,0 +1,10 @@
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from .show import Base
class Pattern(Base):
__tablename__ = 'patterns'
id = Column(Integer, primary_key=True)
pattern = Column(String)

@ -0,0 +1,13 @@
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Show(Base):
__tablename__ = 'shows'
id = Column(Integer, primary_key=True)
name = Column(String)
year = Column(Integer)

@ -1,39 +0,0 @@
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label
from .shows_screen import ShowsScreen
from .warning_screen import WarningScreen
from .dashboard_screen import DashboardScreen
from .settings_screen import SettingsScreen
from .help_screen import HelpScreen
class ModesApp(App):
TITLE = "FFX"
BINDINGS = [
("q", "quit()", "Quit"),
# ("d", "switch_mode('dashboard')", "Dashboard"),
# ("s", "switch_mode('settings')", "Settings"),
# ("h", "switch_mode('help')", "Help"),
]
MODES = {
"shows": ShowsScreen,
"warning": WarningScreen,
"dashboard": DashboardScreen,
"settings": SettingsScreen,
"help": HelpScreen,
}
def __init__(self, context = {}):
super().__init__()
self.context = context
def on_mount(self) -> None:
self.switch_mode("shows")
def getContext(self):
return self.context

@ -1,2 +0,0 @@
class Show():
pass

@ -0,0 +1,166 @@
import click
from textual import events
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
from textual.containers import Grid, Horizontal
from ffx.model.show import Show
# class IdInput(Input):
# def on_key(self, event: events.Key) -> None:
# if event == "enter":
# quit()
class ShowDetailsScreen(Screen):
CSS = """
Grid {
grid-size: 2;
grid-rows: 2 auto;
grid-columns: 30 auto;
height: 100%;
width: 100%;
padding: 1;
}
Input {
border: none;
}
Button {
border: none;
}
#toplabel {
height: 1;
column-span: 2;
}
#two {
column-span: 2;
row-span: 2;
tint: magenta 40%;
}
.box {
height: 100%;
border: solid green;
}
"""
#BINDINGS = [
# #("q", "quit()", "Quit"),
# ("h", "switch_mode('help')", "Help")
#]
#def action_quit(self):
# quit()
def __init__(self, show_id = None):
super().__init__()
self.context = self.app.getContext()
self.Session = self.context['database_session'] # convenience
self.show_id = show_id
def action_submit(self):
quit()
def compose(self):
yield Header()
with Grid():
yield Static("New Show" if self.show_id is None else "Show", id="toplabel")
yield Static("ID")
yield Input(type="text", id="id_input")
yield Static("Name")
yield Input(type="text", id="name_input")
yield Static("Year")
yield Input(type="text", id="year_input")
yield Static("")
yield Static("")
yield Static("")
yield Static("")
yield Button("Save", id="save_button")
yield Button("Cancel", id="cancel_button")
yield Static("", id="output_static")
yield Footer()
def getValues(self):
showId = int(self.query_one("#id_input", Input).value)
showName = self.query_one("#name_input", Input).value
showYear = self.query_one("#year_input", Input).value
return showId, showName, showYear
def on_input_submitted(self, event: Input.Submitted) -> None:
showId, showName, showYear = self.getValues()
#self.query_one("#output_static", Static).update(f"{showId} - {showName} ({showYear})")
# if event.input.id == "id_input":
# # Retrieve the entered text
# name = event.value
# # Display the result in the output label
# self.query_one("#output_static", Static).update(f"Hello, {name}!")
# self.set_focus(self.query_one("#name_input", Input))
#
# if event.input.id == "name_input":
# # Retrieve the entered text
# name = event.value
# # Display the result in the output label
# self.query_one("#output_static", Static).update(f"Yo, {name}!")
# self.set_focus(self.query_one("#year_input", Input))
#
# if event.input.id == "year_input":
# # Retrieve the entered text
# name = event.value
# # Display the result in the output label
# self.query_one("#output_static", Static).update(f"Ya, {name}!")
# self.set_focus(None)
# Event handler for button press
def on_button_pressed(self, event: Button.Pressed) -> None:
# Check if the button pressed is the one we are interested in
if event.button.id == "save_button":
showId, showName, showYear = self.getValues()
self.query_one("#output_static", Static).update(f"{showId} - {showName} ({showYear})")
self.addShow(showId, showName, showYear)
#TODO: Validation
# self.app.pop_screen()
self.dismiss(showId)
if event.button.id == "cancel_button":
self.app.pop_screen()
def addShow(self, show_id, show_name, show_year):
try:
s = self.Session()
show = Show(id = show_id,
name = show_name,
year = show_year)
s.add(show)
s.commit()
except Exception as ex:
click.ClickException(f"ShowDetailsScreen.addShow(): {repr(ex)}")
finally:
s.close()

@ -0,0 +1,60 @@
import click
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button
from textual.containers import Grid, Horizontal
from ffx.model.show import Show
class ShowNewScreen(Screen):
CSS = """
Grid {
grid-size: 2;
grid-rows: 2 auto;
height: 100%;
width: 100%;
padding: 1;
}
#top {
height: 1;
}
#two {
column-span: 2;
row-span: 2;
tint: magenta 40%;
}
.box {
height: 100%;
border: solid green;
}
"""
BINDINGS = [
("q", "quit()", "Quit"),
("h", "switch_mode('help')", "Help")
]
def __init__(self):
super().__init__()
self.context = self.app.getContext()
self.Session = self.context['database_session'] # convenience
def compose(self):
yield Header()
with Grid():
yield Static("New Show")
yield Footer()

@ -1,9 +1,16 @@
import click
from textual.app import App, ComposeResult from textual.app import App, ComposeResult
from textual.screen import Screen from textual.screen import Screen
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button
from textual.containers import Grid, Horizontal from textual.containers import Grid, Horizontal
from ffx.model.show import Show
from .show_details_screen import ShowDetailsScreen
from .show_new_screen import ShowNewScreen
from .help_screen import HelpScreen
class ShowsScreen(Screen): class ShowsScreen(Screen):
@ -35,51 +42,95 @@ class ShowsScreen(Screen):
""" """
BINDINGS = [ BINDINGS = [
("q", "quit()", "Quit"), #("q", "quit()", "Quit"),
("e", "switch_mode('settings')", "Edit Show"), ("e", "switch_mode('show_details')", "Edit Show"),
("n", "switch_mode('dashboard')", "New Show") ("n", "new_show", "New Show"),
# ("h", "switch_mode('help')", "Help"), #("h", "switch_mode('help')", "Help")
] ]
# MODES = {
# "show_details": ShowDetailsScreen,
# "show_new": ShowNewScreen,
# "help": HelpScreen,
# }
def action_new_show(self):
self.app.push_screen(ShowDetailsScreen(), self.handle_new_screen)
def handle_new_screen(self, showId):
showList = self.loadShows()
for show in showList:
if show[0] == showId:
self.table.add_row(*map(str, show))
def __init__(self): def __init__(self):
super().__init__() super().__init__()
context = self.app.getContext() self.context = self.app.getContext()
context['dashboard'] = 'dashboard'
self.Session = self.context['database_session'] # convenience
def loadShows(self):
try:
s = self.Session()
q = s.query(Show)
return [(int(s.id), s.name, s.year) for s in q.all()]
except Exception as ex:
click.ClickException(f"ShowsScreen(): {repr(ex)}")
finally:
s.close()
def on_mount(self) -> None:
for show in self.loadShows():
self.table.add_row(*map(str, show)) # Convert each element to a string before adding
#def on_resume(self) -> None:
# quit()
# showList = self.loadShows()
#
# tableIdSet = set({int(r[0]) for r in self.table.rows})
# databaseIdSet = set({int(s[0]) for s in showList})
#
# missingIdSet = databaseIdSet - tableIdSet
# removedIdSet = tableIdSet - databaseIdSet
#
# for show in showList:
# if show[0] in missingIdSet:
# self.table.add_row(*map(str, show))
def compose(self): def compose(self):
# Create the DataTable widget # Create the DataTable widget
table = DataTable() self.table = DataTable()
# Define the columns with headers # Define the columns with headers
table.add_column("ID", width=10) self.table.add_column("ID", width=10)
table.add_column("Name", width=20) self.table.add_column("Name", width=50)
table.add_column("Age", width=10) self.table.add_column("Year", width=10)
table.add_column("Country", width=20)
table.cursor_type = 'row'
# Sample data for rows
data = [
(1, "John Doe", 28, "USA"),
(2, "Jane Smith", 34, "Canada"),
(3, "Alice Johnson", 29, "UK"),
(4, "Bob Brown", 45, "Australia"),
(5, "Charlie Davis", 23, "USA"),
]
# Add rows to the table self.table.cursor_type = 'row'
for i in range(20):
for row in data:
table.add_row(*map(str, row)) # Convert each element to a string before adding
yield Header() yield Header()
with Grid(): with Grid():
yield Static("Shows") # , classes="box" yield Static("Shows")
yield table yield self.table
yield Footer() yield Footer()

Loading…
Cancel
Save