data_process_ui/app/widget_term.py

174 lines
5.9 KiB
Python
Raw Normal View History

2026-01-12 09:21:42 +08:00
import re
from PyQt6.QtCore import Qt, QPoint, QStringListModel
from PyQt6.QtWidgets import (
QDockWidget, QTextEdit, QPlainTextEdit,
QSplitter, QMenu, QCompleter,
)
from PyQt6.QtGui import (
QFont, QTextCursor, QKeySequence,
QAction, QPalette, QColorConstants,
)
from IPython.core.completer import (
provisionalcompleter, cursor_to_position,
_deduplicate_completions
)
from text_term import text_term
class completer_py(QCompleter):
def __init__(self, widget, *args):
super().__init__(*args)
model = QStringListModel([], self)
self.setWidget(widget)
self.setModel(model)
self.setModelSorting(QCompleter.ModelSorting.CaseInsensitivelySortedModel)
self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
self.setWrapAround(False)
self.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
self.activated.connect(widget.insertCompletion)
class text_input(QPlainTextEdit):
def __init__(self, window, *arg):
super().__init__(*arg)
self.window = window
self.setTabStopDistance(40)
self.setPlaceholderText(
"%s 发送命令\n"
"%s 自动补全\n"
"%s%s 浏览命令历史\n"
% (
QKeySequence("Ctrl+Enter").toString(QKeySequence.SequenceFormat.NativeText),
QKeySequence("Ctrl+E").toString(QKeySequence.SequenceFormat.NativeText),
QKeySequence("Ctrl+Up").toString(QKeySequence.SequenceFormat.NativeText),
QKeySequence("Ctrl+Down").toString(QKeySequence.SequenceFormat.NativeText),
)
)
self.completer_py = completer_py(self)
self.history = [""]
self.history_idx = 0
def insertCompletion(self, completion):
tc = self.textCursor()
extra = len(completion) - len(self.completer_py.completionPrefix())
tc.movePosition(QTextCursor.MoveOperation.Left)
tc.movePosition(QTextCursor.MoveOperation.EndOfWord)
tc.insertText(completion[-extra:])
self.setTextCursor(tc)
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QTextCursor.SelectionType.WordUnderCursor)
return tc.selectedText()
def focusInEvent(self, event):
if self.completer_py is not None:
self.completer_py.setWidget(self)
super().focusInEvent(event)
def keyPressEvent(self, event):
if(
event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return)
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
):
s = self.toPlainText()
if not s:
return
self.window.process(s)
self.history[-1] = s
if len(self.history) >= 2 and self.history[-1] == self.history[-2]:
self.history.pop()
self.history_idx = len(self.history)
self.history.append("")
self.clear()
return
if(
event.key() == Qt.Key.Key_Up
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
and self.history_idx > 0
):
if self.history_idx == len(self.history) - 1:
self.history[-1] = self.toPlainText()
self.history_idx -= 1
self.setPlainText(self.history[self.history_idx])
return
if(
event.key() == Qt.Key.Key_Down
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
and self.history_idx < len(self.history) - 1
):
self.history_idx += 1
self.setPlainText(self.history[self.history_idx])
return
if self.completer_py is not None and self.completer_py.popup().isVisible():
# The following keys are forwarded by the completer to the widget.
if event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return, Qt.Key.Key_Escape, Qt.Key.Key_Tab, Qt.Key.Key_Backtab):
event.ignore()
# Let the completer do default behavior.
return
isShortcut = (bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier) and event.key() == Qt.Key.Key_E)
if self.completer_py is None or not isShortcut:
# Do not process the shortcut when we have a completer.
super().keyPressEvent(event)
ctrlOrShift = event.modifiers() & (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
if self.completer_py is None or (ctrlOrShift and len(event.text()) == 0):
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
hasModifier = (event.modifiers() != Qt.KeyboardModifier.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
if not isShortcut and (hasModifier or len(event.text()) == 0 or len(completionPrefix) < 3 or event.text()[-1] in eow):
self.completer_py.popup().hide()
return
pos = self.textCursor().position()
value = self.document().toRawText()
hint = re.search(r"[_0-9a-zA-Z.%]*$", value[:pos]).group()
pos_hint = pos - len(hint)
hint += re.search(r"^[_0-9a-zA-Z.%]*", value[pos:]).group()
with provisionalcompleter():
complete_list = _deduplicate_completions(hint, self.window.ipshell.Completer.completions(hint, pos - pos_hint))
complete_list = list(complete_list)
complete_list = [i.text for i in complete_list]
# complete_list = self.window.ipshell.complete(hint, cursor_pos = pos - pos_hint)
# print(complete_list)
self.completer_py.model().setStringList(complete_list)
if completionPrefix != self.completer_py.completionPrefix():
self.completer_py.setCompletionPrefix(completionPrefix)
self.completer_py.popup().setCurrentIndex(
self.completer_py.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self.completer_py.popup().sizeHintForColumn(0) + self.completer_py.popup().verticalScrollBar().sizeHint().width())
self.completer_py.complete(cr)
class widget_term(QDockWidget):
def __init__(self, window, *arg):
super().__init__(*arg)
self.window = window
self.setAllowedAreas(
Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea
| Qt.DockWidgetArea.TopDockWidgetArea | Qt.DockWidgetArea.BottomDockWidgetArea
)
font = QFont("consolas", 12)
font.setFamilies(["consolas", "黑体"])
self.setFont(font)
split = QSplitter(Qt.Orientation.Horizontal)
self.setWidget(split)
self.text_term = text_term(window, limit = 2 ** 16)
split.addWidget(self.text_term)
self.text_input = text_input(window)
split.addWidget(self.text_input)