【发布时间】:2020-02-28 11:44:09
【问题描述】:
我目前正在使用 PyQt5 创建 SQL 编辑器和查询向导,但在尝试将 QCompleter 添加到向导中的所有 QPlainTextEdits 时遇到了问题。所有的 UI 都是用 Qt Designer 创建的,并存储在 QStackedWidget 中。
我已阅读将 Designer 中的小部件提升到我的自定义小部件 (TextEdit),这是一个带有 QCompleter 模型接口的 QTextEdit。
我的问题是,如何将完成模型设置为已创建的对象?有没有办法在完成者设置之后才初始化它?在程序的另一部分,由于功能的原因,我可以在设置后添加小部件,但必须有更好的方法。
任何帮助将不胜感激。下面是我用来设置工作方式的代码。
tab = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(tab)
self.completer = QCompleter(self)
self.all_autocomplete_words.extend(sql_words)
self.all_autocomplete_words = pd.Series(
self.all_autocomplete_words).sort_values().drop_duplicates().tolist()
#print(self.all_autocomplete_words)
completer_model = QStringListModel(self.all_autocomplete_words, self.completer)
self.completer.setModel(completer_model)
self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.completer.setWrapAround(False)
# print(self.auto_completion_words)
# print(self.all_autocomplete_words)
self.completingTextEdit = TextEdit(dict_words=self.auto_completion_words,
list_words=self.all_autocomplete_words, parent=tab)
self.completingTextEdit.setCompleter(self.completer)
self.completingTextEdit.setPlaceholderText('SQL Script')
horizontalLayout.addWidget(self.completingTextEdit)
#sql_ws.setPlaceholderText('SQL Script')
self.sql_tab.addTab(tab, key)
编辑:
这是最小示例的基类...
from min_example_fromui import Ui_MainWindow
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QTreeWidget, QTreeWidgetItem, QMenu, QAction, \
QMessageBox, QTreeWidgetItemIterator, QAbstractItemView, QDialog, QShortcut, QGridLayout, QLabel, QLineEdit,\
QPushButton, QWhatsThis, QCompleter, QTextEdit, QProgressDialog, QTableWidgetItem, QTableView
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QFile, QStringListModel, Qt, QThread, pyqtSignal, QObject, pyqtSlot, QTimer, QEventLoop
from PyQt5.QtGui import QCursor, QKeySequence, QTextCursor
import pandas as pd
class sqlWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(sqlWindow, self).__init__(parent)
self.setupUi(self)
self.sql_words = [
'ABORT'
]
self.all_autocomplete_words = []
self.auto_completion_words = {}
self.pushButton.clicked.connect(self.add_tab)
def add_tab(self):
tab = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(tab)
self.completer = QCompleter(self)
self.all_autocomplete_words.extend(self.sql_words)
self.all_autocomplete_words = pd.Series(
self.all_autocomplete_words).sort_values().drop_duplicates().tolist()
#print(self.all_autocomplete_words)
completer_model = QStringListModel(self.all_autocomplete_words, self.completer)
self.completer.setModel(completer_model)
self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.completer.setWrapAround(False)
# print(self.auto_completion_words)
# print(self.all_autocomplete_words)
self.completingTextEdit = TextEdit(dict_words=self.auto_completion_words,
list_words=self.all_autocomplete_words, parent=tab)
self.completingTextEdit.setCompleter(self.completer)
self.completingTextEdit.setPlaceholderText('SQL Script')
horizontalLayout.addWidget(self.completingTextEdit)
# sql_ws.setPlaceholderText('SQL Script')
self.sql_tab.addTab(tab, 'key')
self.sql_tab.setCurrentWidget(tab)
class TextEdit(QTextEdit):
def __init__(self, dict_words=dict, list_words=list, parent=None):
super(TextEdit, self).__init__(parent)
self._completer = None
self.auto_complete_dict = dict_words
self.all_autocomplete = list_words
#Class Instances
self.completion_prefix = ''
def setCompleter(self, c):
self._completer = c
c.setWidget(self)
c.setCompletionMode(QCompleter.PopupCompletion)
c.setCaseSensitivity(Qt.CaseInsensitive)
c.activated.connect(self.insertCompletion)
def insertCompletion(self, completion):
if self._completer.widget() is not self:
return
tc = self.textCursor()
extra = len(completion) - len(self._completer.completionPrefix())
tc.movePosition(QTextCursor.Left)
tc.movePosition(QTextCursor.EndOfWord)
if self.completion_prefix.lower() == completion[-extra:].lower():
pass
#print('You inserted the word after it was completed')
else:
tc.insertText(completion[-extra:])
#print('The text being inserted',completion[-extra:])
self.setTextCursor(tc)
self._completer.setModel(QStringListModel(self.all_autocomplete, self._completer))
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
#print('Here is the selected text under the cursor' , tc.selectedText())
return tc.selectedText()
def focusInEvent(self, e):
#Open the widget where you are at in the edit
if self._completer is not None:
self._completer.setWidget(self)
super(TextEdit, self).focusInEvent(e)
def keyPressEvent(self, e):
isShortcut = False
if self._completer is not None and self._completer.popup().isVisible():
#print('Popup is up')
# The following keys are forwarded by the completer to the widget.
if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab):
e.ignore()
#self._completer.setModel(QStringListModel(self.all_autocomplete, self._completer))
# Let the completer do default behavior.
return
if e.key() == Qt.Key_E and e.modifiers() == Qt.ControlModifier:
#Control + E was pressed.
words = self.all_autocomplete
self._completer.setModel(QStringListModel(words, self._completer))
isShortcut = True
if e.key() == Qt.Key_Period:
#This is how I will do the lookup functionality. Show when period is his, open the list of options.
self.textCursor().insertText('.')
self.moveCursor(QtGui.QTextCursor.PreviousWord)
self.moveCursor(QtGui.QTextCursor.PreviousWord, QtGui.QTextCursor.KeepAnchor)
dict_key = self.textCursor().selectedText().upper()
#print('Dict Key' , dict_key)
self.moveCursor(QtGui.QTextCursor.NextWord)
self.moveCursor(QtGui.QTextCursor.NextWord)
#print(dict_key)
words = self.auto_complete_dict[dict_key]
self._completer.setModel(QStringListModel(words, self._completer))
isShortcut = True
if self._completer is None or not isShortcut:
# Do not process the shortcut when we have a completer.
super(TextEdit, self).keyPressEvent(e)
ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
if self._completer is None or (ctrlOrShift and len(e.text()) == 0):
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
self.completion_prefix = completionPrefix
#print('CompletionPrefix :' , completionPrefix)
if not isShortcut and (hasModifier or len(e.text()) == 0 or len(completionPrefix) < 2 or e.text()[-1] in eow):
self._completer.popup().hide()
return
if completionPrefix != self._completer.completionPrefix():
#Puts the Prefix of the word youre typing into the Prefix
self._completer.setCompletionPrefix(completionPrefix)
self._completer.popup().setCurrentIndex(
self._completer.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self._completer.popup().sizeHintForColumn(0) + self._completer.popup().verticalScrollBar().sizeHint().width())
self._completer.complete(cr)
if __name__ == "__main__":
import sys
'''These excepthook lines are to catch errors when using pyqt5'''
sys._excepthook = sys.excepthook
def exception_hook(exctype, value, traceback):
print(exctype, value, traceback)
sys._excepthook(exctype, value, traceback)
sys.exit(1)
sys.excepthook = exception_hook
'''Error handling section to raise errors'''
app = QApplication(sys.argv)
main_window = sqlWindow()
main_window.show()
sys.exit(app.exec_())
这是我的用户界面代码:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
self.sql_tab = QtWidgets.QTabWidget(self.centralwidget)
self.sql_tab.setEnabled(True)
self.sql_tab.setTabsClosable(True)
self.sql_tab.setMovable(True)
self.sql_tab.setObjectName("sql_tab")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setObjectName("gridLayout_2")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_2.addItem(spacerItem, 0, 2, 1, 1)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.gridLayout_2.addItem(spacerItem1, 0, 0, 1, 1)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label_2 = QtWidgets.QLabel(self.tab)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
self.pushButton = QtWidgets.QPushButton(self.tab)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 1, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 1, 1, 1, 1)
self.label = QtWidgets.QLabel(self.tab)
self.label.setFrameShape(QtWidgets.QFrame.Box)
self.label.setLineWidth(0)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 0, 1, 1, 1)
self.sql_tab.addTab(self.tab, "")
self.verticalLayout.addWidget(self.sql_tab)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.sql_tab.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label_2.setText(_translate("MainWindow", "For Help, refer to the User Manual."))
self.pushButton.setText(_translate("MainWindow", "User Manual"))
self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-size:18pt;\">SQL Editor Welcome Sheet</span></p><p><br/></p><p>Only List Reports Can be Ran by Worksheet. </p><p>All Other Reports are Ran Through the Query Builder.</p></body></html>"))
self.sql_tab.setTabText(self.sql_tab.indexOf(self.tab), _translate("MainWindow", "SQL Editor Home"))
谢谢!
第二次编辑:
from min_example_fromui import Ui_MainWindow
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QTreeWidget, QTreeWidgetItem, QMenu, QAction, \
QMessageBox, QTreeWidgetItemIterator, QAbstractItemView, QDialog, QShortcut, QGridLayout, QLabel, QLineEdit,\
QPushButton, QWhatsThis, QCompleter, QTextEdit, QProgressDialog, QTableWidgetItem, QTableView
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QFile, QStringListModel, Qt, QThread, pyqtSignal, QObject, pyqtSlot, QTimer, QEventLoop
from PyQt5.QtGui import QCursor, QKeySequence, QTextCursor
import pandas as pd
class sqlWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(sqlWindow, self).__init__(parent)
self.setupUi(self)
self.sql_words = [
'ABORT'
]
self.all_autocomplete_words = []
self.auto_completion_words = {}
self.pushButton.clicked.connect(self.add_tab)
self.completer = QCompleter(self)
self.all_autocomplete_words.extend(self.sql_words)
self.all_autocomplete_words = pd.Series(
self.all_autocomplete_words).sort_values().drop_duplicates().tolist()
# print(self.all_autocomplete_words)
self.completer_model = QStringListModel(self.all_autocomplete_words, self.completer)
self.completer.setModel(self.completer_model)
self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.completer.setWrapAround(False)
self.my_sqlbox = TextEdit(dict_words=self.auto_completion_words,
list_words=self.all_autocomplete_words, parent=None)
self.my_sqlbox.setCompleter(self.completer)
self.my_sqlbox.setPlaceholderText('SQL Script')
def add_tab(self):
tab = QtWidgets.QWidget()
horizontalLayout = QtWidgets.QHBoxLayout(tab)
self.completingTextEdit = TextEdit(dict_words=self.auto_completion_words,
list_words=self.all_autocomplete_words, parent=tab)
self.completingTextEdit.setCompleter(self.completer)
self.completingTextEdit.setPlaceholderText('SQL Script')
horizontalLayout.addWidget(self.completingTextEdit)
# sql_ws.setPlaceholderText('SQL Script')
self.sql_tab.addTab(tab, 'key')
self.sql_tab.setCurrentWidget(tab)
class TextEdit(QTextEdit):
def __init__(self, dict_words=dict, list_words=list, parent=None):
super(TextEdit, self).__init__(parent)
self._completer = None
self.auto_complete_dict = dict_words
self.all_autocomplete = list_words
#Class Instances
self.completion_prefix = ''
def setCompleter(self, c):
self._completer = c
c.setWidget(self)
c.setCompletionMode(QCompleter.PopupCompletion)
c.setCaseSensitivity(Qt.CaseInsensitive)
c.activated.connect(self.insertCompletion)
def insertCompletion(self, completion):
if self._completer.widget() is not self:
return
tc = self.textCursor()
extra = len(completion) - len(self._completer.completionPrefix())
tc.movePosition(QTextCursor.Left)
tc.movePosition(QTextCursor.EndOfWord)
if self.completion_prefix.lower() == completion[-extra:].lower():
pass
#print('You inserted the word after it was completed')
else:
tc.insertText(completion[-extra:])
#print('The text being inserted',completion[-extra:])
self.setTextCursor(tc)
self._completer.setModel(QStringListModel(self.all_autocomplete, self._completer))
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
#print('Here is the selected text under the cursor' , tc.selectedText())
return tc.selectedText()
def focusInEvent(self, e):
#Open the widget where you are at in the edit
if self._completer is not None:
self._completer.setWidget(self)
super(TextEdit, self).focusInEvent(e)
def keyPressEvent(self, e):
isShortcut = False
if self._completer is not None and self._completer.popup().isVisible():
#print('Popup is up')
# The following keys are forwarded by the completer to the widget.
if e.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab):
e.ignore()
#self._completer.setModel(QStringListModel(self.all_autocomplete, self._completer))
# Let the completer do default behavior.
return
if e.key() == Qt.Key_E and e.modifiers() == Qt.ControlModifier:
#Control + E was pressed.
words = self.all_autocomplete
self._completer.setModel(QStringListModel(words, self._completer))
isShortcut = True
if e.key() == Qt.Key_Period:
#This is how I will do the lookup functionality. Show when period is his, open the list of options.
self.textCursor().insertText('.')
self.moveCursor(QtGui.QTextCursor.PreviousWord)
self.moveCursor(QtGui.QTextCursor.PreviousWord, QtGui.QTextCursor.KeepAnchor)
dict_key = self.textCursor().selectedText().upper()
#print('Dict Key' , dict_key)
self.moveCursor(QtGui.QTextCursor.NextWord)
self.moveCursor(QtGui.QTextCursor.NextWord)
#print(dict_key)
words = self.auto_complete_dict[dict_key]
self._completer.setModel(QStringListModel(words, self._completer))
isShortcut = True
if self._completer is None or not isShortcut:
# Do not process the shortcut when we have a completer.
super(TextEdit, self).keyPressEvent(e)
ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
if self._completer is None or (ctrlOrShift and len(e.text()) == 0):
return
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
self.completion_prefix = completionPrefix
#print('CompletionPrefix :' , completionPrefix)
if not isShortcut and (hasModifier or len(e.text()) == 0 or len(completionPrefix) < 2 or e.text()[-1] in eow):
self._completer.popup().hide()
return
if completionPrefix != self._completer.completionPrefix():
#Puts the Prefix of the word youre typing into the Prefix
self._completer.setCompletionPrefix(completionPrefix)
self._completer.popup().setCurrentIndex(
self._completer.completionModel().index(0, 0))
cr = self.cursorRect()
cr.setWidth(self._completer.popup().sizeHintForColumn(0) + self._completer.popup().verticalScrollBar().sizeHint().width())
self._completer.complete(cr)
if __name__ == "__main__":
import sys
'''These excepthook lines are to catch errors when using pyqt5'''
sys._excepthook = sys.excepthook
def exception_hook(exctype, value, traceback):
print(exctype, value, traceback)
sys._excepthook(exctype, value, traceback)
sys.exit(1)
sys.excepthook = exception_hook
'''Error handling section to raise errors'''
app = QApplication(sys.argv)
main_window = sqlWindow()
main_window.show()
sys.exit(app.exec_())
来自 ui 的静态 QPlainText
【问题讨论】:
-
@eyllanesc,我用一个小的工作示例和 ui 表单编辑了我上面的问题。在此示例中,我正在创建新选项卡。我想要做的是已经创建了一个文本框,我将这个 TextEdit 类和 QCompleter 功能添加到。
-
“文本框”是什么意思?我了解您想要一个所有 QTextEdit 共享的单个 QCompleter。我说的对吗?
-
对不起,当我说文本框时,我的意思是 QPlainText。而且我希望能够更新我的模型中的 QPlainText 框,以便为其分配 QCompleter。所以我可以添加一个新选项卡并分配它,但我也想要一个静态 QPlainText 并为其分配一个 QCompleter。
-
我不明白你,更好地解释你想要什么,但更详细,因为你的描述令人困惑。
标签: python pyqt5 qcompleter