Initial Codes

This commit is contained in:
2026-05-21 15:46:41 -04:00
commit b01ad5ea40
40 changed files with 9102 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
"""
Process list viewer — shows running server processes with kill capability.
Auto-refresh support with configurable interval.
"""
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QTableWidget, QTableWidgetItem, QHeaderView, QCheckBox,
QMessageBox,
)
from PyQt6.QtCore import Qt, pyqtSignal, QTimer
from app.utils.worker import SchemaWorker
class ProcessListPanel(QWidget):
status_message = pyqtSignal(str)
def __init__(self, driver, connection_name: str = "", parent=None):
super().__init__(parent)
self._driver = driver
self._connection_name = connection_name
self._refresh_timer = QTimer(self)
self._refresh_timer.timeout.connect(self._refresh)
self._build_ui()
self._refresh()
# ── UI ────────────────────────────────────────────────────────────────────
def _build_ui(self):
root = QVBoxLayout(self)
root.setContentsMargins(0, 0, 0, 0)
# Header bar
hdr = QHBoxLayout()
hdr.setContentsMargins(8, 6, 8, 4)
title = QLabel(f"⚙️ Process List — {self._connection_name}")
title.setObjectName("structureTitle")
f = title.font()
f.setBold(True)
f.setPointSize(12)
title.setFont(f)
self._auto_cb = QCheckBox("Auto-refresh (5s)")
self._auto_cb.toggled.connect(self._toggle_auto_refresh)
self._refresh_btn = QPushButton("🔄 Refresh")
self._refresh_btn.setFixedWidth(90)
self._refresh_btn.clicked.connect(self._refresh)
self._kill_btn = QPushButton("🛑 Kill Process")
self._kill_btn.setObjectName("deleteBtn")
self._kill_btn.setEnabled(False)
self._kill_btn.clicked.connect(self._kill_selected)
hdr.addWidget(title)
hdr.addStretch()
hdr.addWidget(self._auto_cb)
hdr.addWidget(self._refresh_btn)
hdr.addWidget(self._kill_btn)
root.addLayout(hdr)
# Process table
self._table = QTableWidget(0, 0)
self._table.setAlternatingRowColors(True)
self._table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self._table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
self._table.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.ResizeToContents
)
self._table.horizontalHeader().setStretchLastSection(True)
self._table.verticalHeader().setDefaultSectionSize(24)
self._table.verticalHeader().setVisible(False)
self._table.itemSelectionChanged.connect(self._on_selection_changed)
root.addWidget(self._table)
# ── Data loading ──────────────────────────────────────────────────────────
def _refresh(self):
self._refresh_btn.setEnabled(False)
w = SchemaWorker(self._driver.get_process_list, parent=self)
w.result.connect(self._populate)
w.error.connect(self._on_error)
w.finished.connect(lambda: self._refresh_btn.setEnabled(True))
w.start()
def _populate(self, data):
cols, rows = data
self._table.setColumnCount(len(cols))
self._table.setHorizontalHeaderLabels([c.upper() for c in cols])
self._table.setRowCount(0)
for row in rows:
r = self._table.rowCount()
self._table.insertRow(r)
for c, val in enumerate(row):
item = QTableWidgetItem("" if val is None else str(val))
item.setTextAlignment(
Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft
)
self._table.setItem(r, c, item)
self.status_message.emit(
f"Process list: {len(rows)} process(es)"
)
def _on_error(self, msg: str):
self._table.setRowCount(1)
self._table.setColumnCount(1)
self._table.setHorizontalHeaderLabels(["Error"])
self._table.setItem(0, 0, QTableWidgetItem(msg))
self.status_message.emit(f"Process list error: {msg}")
# ── Kill ──────────────────────────────────────────────────────────────────
def _on_selection_changed(self):
self._kill_btn.setEnabled(bool(self._table.selectedItems()))
def _kill_selected(self):
row = self._table.currentRow()
if row < 0:
return
# Process ID is always the first column
pid_item = self._table.item(row, 0)
if not pid_item or not pid_item.text():
return
try:
pid = int(pid_item.text())
except ValueError:
self.status_message.emit("Could not determine process ID.")
return
btn = QMessageBox.warning(
self, "Kill Process",
f"Kill process {pid}?\n\nThis will immediately terminate the running query.",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if btn != QMessageBox.StandardButton.Yes:
return
w = SchemaWorker(self._driver.kill_process, pid, parent=self)
w.result.connect(lambda _: (
self.status_message.emit(f"Process {pid} killed."),
self._refresh(),
))
w.error.connect(
lambda e: QMessageBox.warning(self, "Kill Error",
f"Could not kill process {pid}:\n{e}")
)
w.start()
# ── Auto-refresh ──────────────────────────────────────────────────────────
def _toggle_auto_refresh(self, checked: bool):
if checked:
self._refresh_timer.start(5000)
else:
self._refresh_timer.stop()
def closeEvent(self, event):
self._refresh_timer.stop()
super().closeEvent(event)