基于Python的自定义编程语言编辑器多词变量自动补全实现咨询
Hey there! Let’s break down how to tackle your two core challenges—autocompleting those quirky quoted space-containing variables and managing them in a separate table window—using Python with PyQt/PySide6. I’ll cover two practical approaches based on the tools you’ve already explored.
Approach 1: QPlainTextEdit + QCompleter (Beginner-Friendly)
Since you’re still learning Python, this method uses more familiar PySide6 widgets and gives you full control over the autocomplete logic, which is perfect for your space-in-variable-names use case.
Step 1: Build the Variable Management Table
First, create a separate widget with a table to add/edit variables. Any changes here will automatically update the autocomplete list.
Step 2: Customize Autocomplete for Quoted Variables
We’ll use QCompleter to handle autocomplete, but tweak its behavior to trigger only when the user is inside unclosed single quotes, and insert variables with proper quoting.
Full Code Example
from PySide6.QtWidgets import (QApplication, QMainWindow, QPlainTextEdit, QCompleter, QTableWidget, QTableWidgetItem, QWidget, QVBoxLayout, QSplitter, QPushButton) from PySide6.QtCore import QStringListModel, Qt class VariableManager(QWidget): def __init__(self, completer_model, parent=None): super().__init__(parent) self.completer_model = completer_model layout = QVBoxLayout(self) # Table for variables self.table = QTableWidget() self.table.setColumnCount(1) self.table.setHorizontalHeaderLabels(["Variables"]) layout.addWidget(self.table) # Button to add new variable rows add_btn = QPushButton("Add New Variable") add_btn.clicked.connect(self.add_empty_variable) layout.addWidget(add_btn) # Update autocomplete whenever table changes self.table.itemChanged.connect(self.sync_completer) # Add a sample variable to start self.add_empty_variable() self.table.setItem(0, 0, QTableWidgetItem('THIS IS A VARIABLE')) def add_empty_variable(self): row = self.table.rowCount() self.table.insertRow(row) item = QTableWidgetItem("") item.setFlags(item.flags() | Qt.ItemIsEditable) self.table.setItem(row, 0, item) def sync_completer(self): # Collect all valid variables from the table var_list = [] for row in range(self.table.rowCount()): item = self.table.item(row, 0) if item and item.text().strip(): var_list.append(item.text()) self.completer_model.setStringList(var_list) class CustomTextEditor(QPlainTextEdit): def __init__(self, completer, parent=None): super().__init__(parent) self.completer = completer self.completer.setWidget(self) self.completer.activated.connect(self.insert_completed_variable) def insert_completed_variable(self, completion): cursor = self.textCursor() prev_text = self.toPlainText()[:cursor.position()] # Check if we're inside an unclosed single quote last_quote_idx = prev_text.rfind("'") if last_quote_idx != -1 and prev_text.count("'") % 2 == 1: # Replace partial text between quote and cursor with completion cursor.setPosition(last_quote_idx + 1) cursor.movePosition(cursor.End, cursor.KeepAnchor) cursor.insertText(completion) # Add closing quote if missing if not self.toPlainText()[cursor.position():].startswith("'"): cursor.insertText("'") else: # Insert full quoted variable if not in quote context cursor.insertText(f"'{completion}'") def keyPressEvent(self, event): # Let completer handle tab/enter when popup is visible if self.completer.popup().isVisible(): if event.key() in (Qt.Key_Tab, Qt.Key_Enter, Qt.Key_Return): event.ignore() return super().keyPressEvent(event) # Trigger autocomplete only inside unclosed single quotes cursor = self.textCursor() prev_text = self.toPlainText()[:cursor.position()] quote_count = prev_text.count("'") if quote_count % 2 == 1: # Get partial variable text under cursor to filter completer cursor.select(cursor.WordUnderCursor) self.completer.setCompletionPrefix(cursor.selectedText()) # Position popup next to cursor cr = self.cursorRect() cr.setWidth(self.completer.popup().sizeHintForColumn(0) + self.completer.popup().verticalScrollBar().sizeHint().width()) self.completer.complete(cr) else: self.completer.popup().hide() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Custom Language Editor") self.resize(800, 600) # Split window into editor and variable manager splitter = QSplitter(Qt.Horizontal) # Setup autocomplete model and completer var_model = QStringListModel() completer = QCompleter(var_model) completer.setCaseSensitivity(Qt.CaseInsensitive) # Add editor and variable manager to splitter self.editor = CustomTextEditor(completer) splitter.addWidget(self.editor) self.var_manager = VariableManager(var_model) splitter.addWidget(self.var_manager) self.setCentralWidget(splitter) if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec()
Approach 2: QScintilla (Powerful Editor Option)
If you want to stick with QScintilla for its advanced code editing features, you can override its default autocomplete behavior to support your variable format. The key is manually triggering autocomplete when in a quote context and feeding it your custom variable list.
Full Code Example
from PySide6.QtWidgets import (QApplication, QMainWindow, QSplitter, QTableWidget, QTableWidgetItem, QWidget, QVBoxLayout, QPushButton) from PySide6.QtCore import Qt import PySide6.Qsci as Qsci class VariableManager(QWidget): def __init__(self, editor, parent=None): super().__init__(parent) self.editor = editor layout = QVBoxLayout(self) self.table = QTableWidget() self.table.setColumnCount(1) self.table.setHorizontalHeaderLabels(["Variables"]) layout.addWidget(self.table) add_btn = QPushButton("Add New Variable") add_btn.clicked.connect(self.add_empty_variable) layout.addWidget(add_btn) self.table.itemChanged.connect(self.sync_autocomplete) self.add_empty_variable() self.table.setItem(0, 0, QTableWidgetItem('THIS IS A VARIABLE')) def add_empty_variable(self): row = self.table.rowCount() self.table.insertRow(row) item = QTableWidgetItem("") item.setFlags(item.flags() | Qt.ItemIsEditable) self.table.setItem(row, 0, item) def sync_autocomplete(self): var_list = [] for row in range(self.table.rowCount()): item = self.table.item(row, 0) if item and item.text().strip(): # Wrap variables in quotes so autocomplete inserts them correctly var_list.append(f"'{item.text()}'") self.editor.autoCompleteList(var_list) class CustomQsciEditor(Qsci.QsciScintilla): def __init__(self, parent=None): super().__init__(parent) # Basic editor setup self.setUtf8(True) self.setAutoCompletionThreshold(1) self.setAutoCompletionSource(Qsci.QsciScintilla.AcsAPIs) def keyPressEvent(self, event): super().keyPressEvent(event) # Check if we're inside unclosed single quotes cursor_pos = self.currentPosition() prev_text = self.text()[:cursor_pos] quote_count = prev_text.count("'") if quote_count % 2 == 1: # Trigger autocomplete when typing inside quotes self.autoComplete() else: # Cancel autocomplete if outside quotes self.autoCompleteCancel() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("QScintilla Custom Editor") self.resize(800, 600) splitter = QSplitter(Qt.Horizontal) self.editor = CustomQsciEditor() splitter.addWidget(self.editor) self.var_manager = VariableManager(self.editor) splitter.addWidget(self.var_manager) self.setCentralWidget(splitter) if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec()
Quick Tips for Your Workflow
- For the QCompleter approach, you can add validation to the table to prevent duplicate variables.
- With QScintilla, you can further customize syntax highlighting by creating a custom lexer for your old language.
- Both examples let you edit variables in the table and see changes reflected in autocomplete instantly.
内容的提问来源于stack exchange,提问作者InfiniteNopes

