Skip to content

Commit 117527f

Browse files
committed
Undo changes to diagnostic_interface (1)
1 parent dea7213 commit 117527f

File tree

2 files changed

+113
-128
lines changed

2 files changed

+113
-128
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import re
2+
import shlex
3+
from PyQt5.QtCore import Qt, pyqtSignal, QProcess
4+
from PyQt5.QtWidgets import QTextEdit, QMessageBox
5+
6+
7+
class CommandOutputWidget(QTextEdit):
8+
"""A QTextEdit that runs a command in a QProcess and renders ANSI output (incl. spinners)."""
9+
_chunk_ready = pyqtSignal(str)
10+
11+
def __init__(self, parent=None, max_lines=10):
12+
super().__init__(parent)
13+
self.setReadOnly(True)
14+
self.setAcceptRichText(False)
15+
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
16+
self.max_lines = max_lines
17+
18+
self._active = False
19+
20+
self._proc = QProcess(self)
21+
self._proc.setProcessChannelMode(QProcess.MergedChannels)
22+
self._proc.readyReadStandardOutput.connect(self._on_ready_read)
23+
self._chunk_ready.connect(self._append_chunk)
24+
25+
def deactivate(self):
26+
self._active = False
27+
28+
def activate(self):
29+
self._active = True
30+
31+
def start_cmd(self, cmd: str):
32+
args = shlex.split(cmd)
33+
if args:
34+
self._proc.start(args[0], args[1:])
35+
36+
def stop(self):
37+
"""Terminate the process."""
38+
self._proc.terminate()
39+
40+
def _on_ready_read(self):
41+
if self._active:
42+
raw = self._proc.readAllStandardOutput()
43+
text = bytes(raw).decode("utf-8", errors="replace")
44+
self._chunk_ready.emit(text)
45+
46+
def _append_chunk(self, text: str):
47+
# Normalize spinner resets and restores to carriage returns
48+
text = re.sub(r'\x1b7|\x1b\[s', '\r', text)
49+
text = re.sub(r'\x1b8|\x1b\[u', '', text)
50+
51+
parts = re.split(r'(\r\n|\r|\n)', text)
52+
cursor = self.textCursor()
53+
cursor.beginEditBlock()
54+
self.setUpdatesEnabled(False)
55+
56+
for part in parts:
57+
if part == '\r':
58+
# Remove the previous line for spinner animation
59+
self._remove_last_line()
60+
else:
61+
cursor.insertText(part)
62+
63+
cursor.endEditBlock()
64+
self.setUpdatesEnabled(True)
65+
66+
self._prune_old_lines()
67+
self._scroll_to_bottom()
68+
69+
# Handle confirmation prompts ending with '(y/N) >'
70+
if text.rstrip().endswith('(y/N) >'):
71+
self._ask_and_respond('(y/N)')
72+
73+
def _prune_old_lines(self):
74+
doc = self.document()
75+
overflow = doc.blockCount() - self.max_lines
76+
if overflow > 0:
77+
block = doc.findBlockByNumber(overflow)
78+
cursor = self.textCursor()
79+
cursor.setPosition(0)
80+
cursor.setPosition(block.position(), cursor.KeepAnchor)
81+
cursor.removeSelectedText()
82+
83+
def _remove_last_line(self):
84+
cursor = self.textCursor()
85+
cursor.movePosition(cursor.End)
86+
cursor.select(cursor.BlockUnderCursor)
87+
cursor.removeSelectedText()
88+
cursor.deletePreviousChar()
89+
90+
def _ask_and_respond(self, prompt: str):
91+
answer = QMessageBox.question(
92+
self,
93+
"Confirmation Required",
94+
prompt,
95+
QMessageBox.Yes | QMessageBox.No,
96+
QMessageBox.Yes
97+
)
98+
resp = ("y\n" if answer == QMessageBox.Yes else "n\n").encode('utf-8')
99+
self._proc.write(resp)
100+
101+
def _scroll_to_bottom(self):
102+
sb = self.verticalScrollBar()
103+
sb.setValue(sb.maximum())
104+
105+
# Disable wheel scrolling
106+
def wheelEvent(self, ev):
107+
pass
108+
109+
# Disable arrow/page scrolling
110+
def keyPressEvent(self, ev):
111+
if ev.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown):
112+
return
113+
super().keyPressEvent(ev)

diagnostic_interface/widgets/comnand_output.py

Lines changed: 0 additions & 128 deletions
This file was deleted.

0 commit comments

Comments
 (0)