1+ from textual .app import App , ComposeResult
2+ from textual .containers import ScrollableContainer , Container
3+ from textual .widgets import Header , Footer , DataTable , Static , Button
4+ from textual .reactive import reactive
5+ from textual .message import Message
6+ from textual .screen import Screen
7+ from textual .widget import Widget
8+ from textual import on
9+ from textual import events
10+ import systemctl_python
11+
12+
13+ class Devices_list (Static ):
14+ dev_type_g = ""
15+ devices = reactive ([])
16+
17+ def __init__ (self , dev_type ):
18+ self .dev_type = dev_type
19+ super ().__init__ ()
20+ def update_devices (self ) -> None :
21+ self .devices = systemctl_python .list_working_units ("rpi-sb-" + self .dev_type + "*" )
22+ def watch_devices (self , devices ) -> None :
23+ """Called when the devices variable changes"""
24+ text = ""
25+ for i in range (len (devices )):
26+ text += devices [i ] + "\n "
27+ self .styles .height = len (devices )
28+ self .update (text )
29+
30+ def on_mount (self ) -> None :
31+ self .set_interval (1 / 20 , self .update_devices )
32+
33+ ROWS = [
34+ ("Serial Number" ,),
35+ ]
36+
37+ class CompletedDevicesList (Widget ):
38+ dev_type_g = ""
39+ devices = reactive ([])
40+ def compose (self ) -> ComposeResult :
41+ yield DataTable ()
42+ def __init__ (self , dev_type ):
43+ self .dev_type = dev_type
44+ super ().__init__ ()
45+ def update_devices (self ) -> None :
46+ self .devices = systemctl_python .list_completed_devices ()
47+ def watch_devices (self , devices : list [str ]) -> None :
48+ """Called when the devices variable changes"""
49+ table = self .query_one (DataTable )
50+ table_devices = []
51+ for device in self .devices :
52+ table_devices .append ((device , ))
53+ table .clear ()
54+ table .add_rows (table_devices )
55+
56+ def on_mount (self ) -> None :
57+ table = self .query_one (DataTable )
58+ table .add_columns (* ROWS [0 ])
59+ table .add_rows (ROWS [1 :])
60+ self .set_interval (1 / 20 , self .update_devices )
61+
62+ class Failed_devices_list (Static ):
63+ dev_type_g = ""
64+ devices = reactive ([])
65+ def compose (self ) -> ComposeResult :
66+ yield DataTable ()
67+ def __init__ (self , dev_type ):
68+ self .dev_type = dev_type
69+ super ().__init__ ()
70+ def update_devices (self ) -> None :
71+ self .devices = systemctl_python .list_failed_devices ()
72+ def watch_devices (self , devices : list [str ]) -> None :
73+ """Called when the devices variable changes"""
74+ table = self .query_one (DataTable )
75+ table_devices = []# [("TEST",), ("TEST",)]
76+ for device in self .devices :
77+ table_devices .append ((device , ))
78+ table .clear ()
79+ table .add_rows (table_devices )
80+
81+ def on_mount (self ) -> None :
82+ table = self .query_one (DataTable )
83+ table .add_columns (* ROWS [0 ])
84+ table .add_rows (ROWS [1 :])
85+ self .set_interval (1 / 20 , self .update_devices )
86+
87+ class Triage_Box (Static ):
88+ def compose (self ) -> ComposeResult :
89+ yield ScrollableContainer (Static ("Triaging \n ----------------" ), Devices_list (dev_type = "triage" ))
90+
91+ class Keywrite_Box (Static ):
92+ def compose (self ) -> ComposeResult :
93+ yield ScrollableContainer (Static ("Keywriting \n ----------------" ), Devices_list (dev_type = "keywriter" ))
94+
95+ class Provision_Box (Static ):
96+ def compose (self ) -> ComposeResult :
97+ yield ScrollableContainer (Static ("Provisioning \n ----------------" ), Devices_list (dev_type = "provision" ))
98+
99+
100+ class Completed_Box (Static ):
101+ def compose (self ) -> ComposeResult :
102+ yield ScrollableContainer (Static ("Completed \n ----------------" ), CompletedDevicesList (dev_type = "provision" ))
103+
104+ class Failed_Box (Static ):
105+ def compose (self ) -> ComposeResult :
106+ yield ScrollableContainer (Static ("Failed \n ----------------" ), Failed_devices_list (dev_type = "provision" ))
107+
108+ class Processing (Static ):
109+ def compose (self ) -> ComposeResult :
110+ yield Triage_Box ("1" , classes = "box2" )
111+ yield Keywrite_Box ("2" , classes = "box2" )
112+ yield Provision_Box ("3" , classes = "box2" )
113+
114+ class Ended (Static ):
115+ def compose (self ) -> ComposeResult :
116+ yield Completed_Box ("1" , classes = "box2" )
117+ yield Failed_Box ("2" , classes = "box2" )
118+
119+ class FileSelector (Container ):
120+ def __init__ (self , filelist ):
121+ self .filelist = filelist
122+ self .selected_file = None
123+ self .id_to_filename = {}
124+ super ().__init__ ()
125+ def compose (self ) -> ComposeResult :
126+ """Create child widgets for the app."""
127+ # List files in the directory
128+ for file in self .filelist :
129+ self .id_to_filename .update ([(file .replace ("." , "" ), file )])
130+ yield Button (file , id = file .replace ("." , "" ), classes = "fileselectorbutton" )
131+ def get_filename_from_id (self , id ) -> str :
132+ return self .id_to_filename [id ]
133+
134+ class MainScreen (Screen ):
135+ def compose (self ) -> ComposeResult :
136+ """Create child widgets for the app."""
137+ yield Header ()
138+ yield Footer ()
139+ yield Processing ("Processing" , classes = "box" )
140+ yield Ended ("Completed" , classes = "box" )
141+ def action_goto_log (self ) -> None :
142+ self .dismiss (self .query_one (Ended ).get_device ())
143+
144+ @on (DataTable .CellSelected )
145+ def on_cell_selected (self , event : DataTable .CellSelected ) -> None :
146+ self .dismiss (event .value )
147+
148+ class LogScreen (Screen ):
149+ def __init__ (self , device_name ):
150+ self .device_name = device_name
151+ super ().__init__ ()
152+
153+ def compose (self ) -> ComposeResult :
154+ """Create child widgets for the app."""
155+ yield Header ()
156+ yield Footer ()
157+ yield Static ("This is the log screen for device: " + self .device_name , id = "header_string" )
158+ yield FileSelector (filelist = systemctl_python .list_device_files (self .device_name ))
159+ yield ScrollableContainer (Static (" " , id = "file_contents" ))
160+
161+ def on_button_pressed (self , event : Button .Pressed ) -> None :
162+ static = self .query_one ("#file_contents" )
163+ fileselector = self .query_one ("FileSelector" )
164+ # Need to read the file into this container now!
165+ contents = systemctl_python .read_device_file (self .device_name , fileselector .get_filename_from_id (event .button .id ))
166+ static .update (contents )
167+
168+ def on_screen_resume (self ) -> None :
169+ static = self .query_one ("#header_string" )
170+ static .update (self .device_name )
171+
172+
173+ class App (App ):
174+ """A Textual app to manage stopwatches."""
175+ CSS_PATH = "layout.css"
176+ BINDINGS = [("m" , "mainscreen" , "Main Screen" ), ("q" , "quit" , "Quit" )]
177+ SCREENS = {"MainScreen" : MainScreen (), "LogScreen" : LogScreen ("unknown-serial" )}
178+
179+ def on_mount (self ) -> None :
180+ self .title = "rpi-sb-provisioner"
181+ self .push_screen (LogScreen (device_name = "INIT" ))
182+ self .push_screen (MainScreen (), self .action_logscreen )
183+
184+ def action_mainscreen (self ):
185+ self .pop_screen ()
186+ self .push_screen (MainScreen (), self .action_logscreen )
187+
188+ def action_logscreen (self , device : str ):
189+ self .push_screen (LogScreen (device ))
190+
191+ if __name__ == "__main__" :
192+ app = App ()
193+ app .run ()
0 commit comments