Add Kill Job button to Running Tasks screen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
10
tui/jobs.py
10
tui/jobs.py
@@ -77,6 +77,16 @@ class JobManager:
|
|||||||
app.post_message(JobFinished(job.id, rc))
|
app.post_message(JobFinished(job.id, rc))
|
||||||
return job.return_code if job.return_code is not None else 1
|
return job.return_code if job.return_code is not None else 1
|
||||||
|
|
||||||
|
def kill_job(self, job_id: str) -> bool:
|
||||||
|
job = self._jobs.get(job_id)
|
||||||
|
if not job or job._proc is None:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
job._proc.terminate()
|
||||||
|
return True
|
||||||
|
except ProcessLookupError:
|
||||||
|
return False
|
||||||
|
|
||||||
def kill_running(self) -> None:
|
def kill_running(self) -> None:
|
||||||
for job in self._jobs.values():
|
for job in self._jobs.values():
|
||||||
if job._proc is not None:
|
if job._proc is not None:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from textual.widgets import Header, Footer, Static, Button, DataTable
|
|||||||
from textual.containers import Vertical, Horizontal
|
from textual.containers import Vertical, Horizontal
|
||||||
|
|
||||||
from tui.jobs import job_manager
|
from tui.jobs import job_manager
|
||||||
from tui.widgets import OperationLog
|
from tui.widgets import ConfirmDialog, OperationLog
|
||||||
|
|
||||||
|
|
||||||
class RunningTasksScreen(Screen):
|
class RunningTasksScreen(Screen):
|
||||||
@@ -20,6 +20,7 @@ class RunningTasksScreen(Screen):
|
|||||||
yield DataTable(id="rt-table")
|
yield DataTable(id="rt-table")
|
||||||
with Horizontal(id="rt-buttons"):
|
with Horizontal(id="rt-buttons"):
|
||||||
yield Button("View Log", variant="primary", id="btn-rt-view")
|
yield Button("View Log", variant="primary", id="btn-rt-view")
|
||||||
|
yield Button("Kill Job", variant="error", id="btn-rt-kill")
|
||||||
yield Button("Clear Finished", variant="warning", id="btn-rt-clear")
|
yield Button("Clear Finished", variant="warning", id="btn-rt-clear")
|
||||||
yield Button("Back", id="btn-rt-back")
|
yield Button("Back", id="btn-rt-back")
|
||||||
yield Footer()
|
yield Footer()
|
||||||
@@ -66,6 +67,8 @@ class RunningTasksScreen(Screen):
|
|||||||
elif event.button.id == "btn-rt-clear":
|
elif event.button.id == "btn-rt-clear":
|
||||||
job_manager.remove_finished()
|
job_manager.remove_finished()
|
||||||
self._refresh_table()
|
self._refresh_table()
|
||||||
|
elif event.button.id == "btn-rt-kill":
|
||||||
|
self._kill_selected()
|
||||||
elif event.button.id == "btn-rt-view":
|
elif event.button.id == "btn-rt-view":
|
||||||
table = self.query_one("#rt-table", DataTable)
|
table = self.query_one("#rt-table", DataTable)
|
||||||
if table.row_count == 0:
|
if table.row_count == 0:
|
||||||
@@ -82,5 +85,29 @@ class RunningTasksScreen(Screen):
|
|||||||
)
|
)
|
||||||
self.app.push_screen(log_screen)
|
self.app.push_screen(log_screen)
|
||||||
|
|
||||||
|
def _kill_selected(self) -> None:
|
||||||
|
table = self.query_one("#rt-table", DataTable)
|
||||||
|
if table.row_count == 0:
|
||||||
|
self.notify("No jobs to kill", severity="warning")
|
||||||
|
return
|
||||||
|
row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate)
|
||||||
|
job_id = str(row_key)
|
||||||
|
job = job_manager.get_job(job_id)
|
||||||
|
if not job:
|
||||||
|
return
|
||||||
|
if job.status != "running":
|
||||||
|
self.notify("Job is not running", severity="warning")
|
||||||
|
return
|
||||||
|
self.app.push_screen(
|
||||||
|
ConfirmDialog(f"Kill job '{job.label}'?", "Confirm Kill"),
|
||||||
|
callback=lambda ok: self._do_kill(job_id) if ok else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _do_kill(self, job_id: str) -> None:
|
||||||
|
if job_manager.kill_job(job_id):
|
||||||
|
self.notify("Job killed")
|
||||||
|
else:
|
||||||
|
self.notify("Could not kill job", severity="error")
|
||||||
|
|
||||||
def action_go_back(self) -> None:
|
def action_go_back(self) -> None:
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|||||||
Reference in New Issue
Block a user