Пример #1
0
class Completer(QGraphicsProxyWidget, object):
    ''' Class for handling text autocompletion in the SDL scene '''
    def __init__(self, parent):
        ''' Create an autocompletion list popup '''
        widget = QListWidget()
        super(Completer, self).__init__(parent)
        self.setWidget(widget)
        self.string_list = QStringListModel()
        self._completer = QCompleter()
        self.parent = parent
        self._completer.setCaseSensitivity(Qt.CaseInsensitive)
        # For some reason the default minimum size is (61,61)
        # Set it to 0 so that the size of the box is not taken
        # into account when it is hidden.
        self.setMinimumSize(0, 0)
        self.prepareGeometryChange()
        self.resize(0, 0)
        self.hide()

    def set_completer_list(self):
        ''' Set list of items for the autocompleter popup '''
        compl = [item.replace('-', '_') for item in
                 self.parent.parentItem().completion_list]
        self.string_list.setStringList(compl)
        self._completer.setModel(self.string_list)

    def set_completion_prefix(self, completion_prefix):
        '''
            Set the current completion prefix (user-entered text)
            and set the corresponding list of words in the popup widget
        '''
        self._completer.setCompletionPrefix(completion_prefix)
        self.widget().clear()
        count = self._completer.completionCount()
        for i in xrange(count):
            self._completer.setCurrentRow(i)
            self.widget().addItem(self._completer.currentCompletion())
        self.prepareGeometryChange()
        if count:
            self.resize(self.widget().sizeHintForColumn(0) + 40, 70)
        else:
            self.resize(0, 0)
        return count

    # pylint: disable=C0103
    def keyPressEvent(self, e):
        super(Completer, self).keyPressEvent(e)
        if e.key() == Qt.Key_Escape:
            self.parentItem().setFocus()
        # Consume the event so that it is not repeated at EditableText level
        e.accept()

    # pylint: disable=C0103
    def focusOutEvent(self, event):
        ''' When the user leaves the popup, return focus to parent '''
        super(Completer, self).focusOutEvent(event)
        self.hide()
        self.resize(0, 0)
        self.parentItem().setFocus()
Пример #2
0
class LineEditWithComplete(QLineEdit):
    def __init__(self,
                 parent,
                 inputList,
                 configPath='./config/LineEditWithHistory.ini',
                 qssFilePath="./Resources/stylesheet/style.qss"):
        super(LineEditWithComplete, self).__init__(parent)
        self.qssFilePath = qssFilePath
        self.configPath = configPath
        # 用于存放历史记录的List
        self.inputList = inputList

        self.__widgetInit()

    def Exit(self):
        pass

    def __widgetInit(self):
        # LineEdit设置QCompleter,用于显示历史记录
        self.completer = QCompleter(self)
        self.listModel = QStringListModel(self.inputList, self)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setModel(self.listModel)
        self.completer.activated.connect(self.Slot_completer_activated,
                                         type=Qt.QueuedConnection)

        # try:
        #     with open(self.qssFilePath, "r") as fh:
        #         self.completer.popup().setStyleSheet(fh.read())
        # except Exception as e:
        #     logger.info('读取QSS文件失败' + str(e))

        self.setCompleter(self.completer)

    # 按下回车或单击后恢复显示模式  https://doc.qt.io/qt-5/qcompleter.html#activated
    def Slot_completer_activated(self, text):
        self.completer.setCompletionMode(QCompleter.PopupCompletion)

    def event(self, event):
        # 按下Tab键时弹出所有记录
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Tab:
                # 设置不过滤显示  https://doc.qt.io/qt-5/qcompleter.html#completionMode-prop
                if self.completer.completionCount() > 0:
                    self.completer.setCompletionMode(
                        QCompleter.UnfilteredPopupCompletion)
                    self.listModel.setStringList(self.inputList)
                    self.completer.complete()
                    self.completer.popup().show()
                return True
            elif event.key() == Qt.Key_Delete:
                self.deleteEle(self.text())

        return super().event(event)
Пример #3
0
class TextStatusEditComplete(TextStatusEdit):
    """ Adds Completion functions to the base class

    This class extends 'TextStatusEdit' by:

    1.  providing a QCompleter to validate lines for the
        'fixupText' and 'lineChanged' signals
    2.  providing a popup for suggested completions as
        the user is typing
    3.  auto-completing the line when the user selects
        a suggestion.

    The task of auto completion and providing suggestions
    is provided directly by this class.

    The task validating and cleaning up text is provided by
    the PluginFinder.
    """
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        self._dataModel = None
        self._monitorDbChanges = False
        self._enableAutoCompletion = False
        self._completedAndSelected = False
        self._completer = QCompleter(self)

        self._completer.setWidget(self)
        self._completer.setWrapAround(False)
        self._completer.setCompletionMode(QCompleter.PopupCompletion)
        self._completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._completer.setFilterMode(Qt.MatchStartsWith)
        self._completer.setModelSorting(
            QCompleter.CaseInsensitivelySortedModel)
        self._completer.activated.connect(self.replaceLine)

        self._pluginFinder = PluginFinder(self._completer, self)
        self.fixupText.connect(self._pluginFinder.fixupText)
        self.lineChanged.connect(self._pluginFinder.setRowForLine)

        QShortcut(Qt.CTRL + Qt.Key_E, self, self.toggleAutoCompletion)
        QShortcut(Qt.CTRL + Qt.Key_T, self, self.suggestCompletions)

    # --- Methods related to the completer's underlying data model

    def setModel(self, model: QAbstractItemModel):
        self._completer.setModel(model)

    def _updateModelSignals(self):
        """ We do not need to check for column changes due to
        the way our PluginModel is structured. """

        if self._dataModel is not None:
            self._dataModel.rowsMoved.disconnect(self.resetData)
            self._dataModel.rowsInserted.disconnect(self.resetData)
            self._dataModel.rowsRemoved.disconnect(self.resetData)
            self._dataModel.modelReset.disconnect(self.resetData)
            self._dataModel.dataChanged.disconnect(self.resetData)
            self._dataModel.layoutChanged.disconnect(self.resetData)

        if self._monitorDbChanges:
            self._dataModel = self._completer.model()
            if self._dataModel is not None:
                self._dataModel.rowsMoved.connect(self.resetData)
                self._dataModel.rowsInserted.connect(self.resetData)
                self._dataModel.rowsRemoved.connect(self.resetData)
                self._dataModel.modelReset.connect(self.resetData)
                self._dataModel.dataChanged.connect(self.resetData)
                self._dataModel.layoutChanged.connect(self.resetData)
        else:
            self._dataModel = None

    def monitorDbChanges(self, enable: bool):
        """ Enable invalidating line status when
        the data model changes.

        Depending on the underlying data model, it may
        be unnecessary to monitor these changes, or, a
        higher level class can monitor specific signals
        more efficiently.  So, this is not enabled
        by default.  """

        if self._monitorDbChanges == enable:
            return

        self._monitorDbChanges = enable
        if enable:
            self._dataModel = self._completer.model()
            self._completer.completionModel().sourceModelChanged.connect(
                self._updateModelSignals)
        else:
            self._completer.completionModel().sourceModelChanged.disconnect(
                self._updateModelSignals)
        self._updateModelSignals()

    # ---- Methods related to line completion

    def completer(self):
        return self._completer

    def enableAutoCompletion(self, enable: bool):
        self._enableAutoCompletion = enable

    def toggleAutoCompletion(self):
        self.enableAutoCompletion(not self._enableAutoCompletion)

    def _textUnderCursor(self):
        tc = self.textCursor()
        if tc.positionInBlock() == 0 and len(tc.block().text()) > 1:
            tc.movePosition(QTextCursor.NextCharacter)
        tc.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor)
        return tc.selectedText().lstrip()

    def suggestCompletions(self):
        if self.isLineInvalid(self.textCursor().blockNumber()):
            self._suggestCompletionsForText(self._textUnderCursor())

    def _suggestCompletionsForText(self, prefix: str):
        if not prefix:
            return
        if prefix != self._completer.completionPrefix():
            self._completer.setCompletionPrefix(prefix)
            self._completer.popup().setCurrentIndex(
                self._completer.completionModel().index(0, 0))
        if self._completer.completionCount() == 1:
            self._insertSuggestion(self._completer.currentCompletion())
        else:
            rect = self.cursorRect()
            rect.moveRight(self.statusAreaWidth())
            rect.setWidth(
                self._completer.popup().sizeHintForColumn(
                    self._completer.completionColumn()) +
                self._completer.popup().verticalScrollBar().sizeHint().width())
            self._completer.complete(rect)

    def _insertSuggestion(self, text: str):
        """ Only one suggestion matched, prefill line """

        cursor = self.textCursor()
        # handle when cursor is in middle of line
        if not cursor.atBlockEnd():
            cursor.beginEditBlock()
            cursor.select(QTextCursor.LineUnderCursor)
            cursor.removeSelectedText()
            cursor.insertText(text)
            cursor.movePosition(QTextCursor.StartOfLine)
            cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
            self._completedAndSelected = True
            self.setTextCursor(cursor)
            cursor.endEditBlock()
            return

        # handle when cursor at end of line
        cursor.beginEditBlock()
        numCharsToComplete = len(text) - len(
            self._completer.completionPrefix())
        insertionPosition = cursor.position()
        cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
        cursor.removeSelectedText()
        cursor.insertText(text[-numCharsToComplete:])
        cursor.setPosition(insertionPosition)
        cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
        self._completedAndSelected = True
        self.setTextCursor(cursor)
        cursor.endEditBlock()

    def keyPressEvent(self, event: QKeyEvent):
        if self._completedAndSelected and self.handledCompletedAndSelected(
                event):
            return

        self._completedAndSelected = False
        if self._completer.popup().isVisible():
            ignoredKeys = [
                Qt.Key_Up,
                Qt.Key_Down,
                Qt.Key_Enter,
                Qt.Key_Return,
                Qt.Key_Tab,
                Qt.Key_Escape,
            ]
            if event.key() in ignoredKeys:
                event.ignore()
                return
            self._completer.popup().hide()

        super().keyPressEvent(event)
        if not self._enableAutoCompletion:
            return

        ctrlOrShift = (event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier
                       or event.modifiers() & Qt.ControlModifier
                       == Qt.ControlModifier)

        if ctrlOrShift and not event.text():
            return

        if self.textCursor().atBlockEnd():
            self.suggestCompletions()

    def mousePressEvent(self, event: QMouseEvent):
        if self._completedAndSelected:
            self._completedAndSelected = False
            self.document().undo()
        super().mousePressEvent(event)

    def handledCompletedAndSelected(self, event: QKeyEvent):
        """ The line is prefilled when only one completion matches. The user
        can accept the suggestion by pressing 'Enter'. The user can reject
        the suggestion by pressing 'Esc' or by continuing to type. """

        self._completedAndSelected = False
        cursor = self.textCursor()
        acceptKeys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab]
        if event.key() in acceptKeys:
            self.replaceLine(self._completer.currentCompletion())
        elif event.key() == Qt.Key_Escape:
            self.document().undo()
        else:
            self.document().undo()
            return False

        self.setTextCursor(cursor)
        event.accept()
        return True

    def replaceLine(self, text: str):
        cursor = self.textCursor()
        cursor.beginEditBlock()
        cursor.select(QTextCursor.LineUnderCursor)
        cursor.removeSelectedText()
        cursor.insertText(text)
        cursor.movePosition(QTextCursor.EndOfLine)
        self.setTextCursor(cursor)
        cursor.endEditBlock()

    # ---- Methods related to Context Menu

    def createStandardContextMenu(self, pos: QPoint):
        menu = super().createStandardContextMenu(pos)
        menu.addSeparator()
        autoCompletionAction = menu.addAction(
            QIcon(),
            self.tr("Enable Auto Complete"),
            self.toggleAutoCompletion,
            QKeySequence(Qt.CTRL + Qt.Key_E),
        )
        autoCompletionAction.setCheckable(True)
        autoCompletionAction.setChecked(self._enableAutoCompletion)

        completionAction = menu.addAction(
            QIcon(),
            self.tr("Suggest Completions"),
            self.suggestCompletions,
            QKeySequence(Qt.CTRL + Qt.Key_T),
        )
        completionAction.setEnabled(
            self.isLineInvalid(self.textCursor().blockNumber()))
        return menu
Пример #4
0
class LineEditWithHistory(QLineEdit):
    elementSelected = pyqtSignal(str)
    nowTime = 0
    oldTime = 0

    def __init__(self,
                 parent,
                 title='历史记录',
                 configPath='',
                 qssFilePath="./Resources/stylesheet/style.qss"):
        super(LineEditWithHistory, self).__init__(parent)
        self.qssFilePath = qssFilePath
        if "" == configPath:
            self.configPath = "./config/{parentName}/LineEditWithHistory.ini".format(
                parentName=type(parent).__name__)
        else:
            if 0 <= os.path.basename(configPath).find("."):
                self.configPath = configPath
            else:
                self.configPath = "{basePath}/LineEditWithHistory.ini".format(
                    basePath=configPath)
        self.title = title
        self.sectionName = '{title}'.format(title=self.title)
        self.HistoryListChanged = False
        self.commandHasSent = False
        # 用于存放历史记录的List
        self.inputList = []

        self.__loadHistory()
        self.__widgetInit()

    def Exit(self):
        self.__saveHistory()

    def __widgetInit(self):
        # LineEdit设置QCompleter,用于显示历史记录
        self.completer = QCompleter(self)
        self.listModel = QStringListModel(self.inputList, self)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setModel(self.listModel)
        self.completer.activated.connect(self.Slot_completer_activated,
                                         type=Qt.QueuedConnection)

        try:
            with open(self.qssFilePath, "r") as fh:
                self.completer.popup().setStyleSheet(fh.read())
        except Exception as e:
            logger.info('读取QSS文件失败' + str(e))

        self.setCompleter(self.completer)
        # 输入完成按下回车后去重添加到历史记录中
        self.returnPressed.connect(self.Slot_updateHistoryModule)

    def __loadHistory(self):
        historyList = self.inputList
        historyOp = Public.Public_ConfigOp(self.configPath)
        rt = historyOp.ReadAllBySection(self.sectionName)
        if True is rt[0]:
            for item in rt[1]:
                if (item[1] not in historyList) and ("" != item[1]):
                    historyList.append(item[1])
        else:
            logger.info(rt[1])

    def __saveHistory(self):
        ipOp = Public.Public_ConfigOp(self.configPath)
        if True is self.HistoryListChanged:
            ipOp.RemoveSection(self.sectionName)
            ipOp.SaveAll()
            for index, item in enumerate(self.inputList):
                ipOp.SaveConfig(self.sectionName, str(index), item)

    def updateHistory(self):
        content = self.text()
        if content != "":
            if content not in self.inputList:
                self.inputList.append(content)
                self.listModel.setStringList(self.inputList)
                self.completer.setCompletionMode(QCompleter.PopupCompletion)
                self.HistoryListChanged = True
        self.__sendElement()

    def Slot_updateHistoryModule(self):
        self.updateHistory()

    # 按下回车或单击后恢复显示模式  https://doc.qt.io/qt-5/qcompleter.html#activated
    def Slot_completer_activated(self, text):
        self.completer.setCompletionMode(QCompleter.PopupCompletion)
        self.__sendElement()

    def __sendElement(self):
        self.elementSelected.emit(self.text())

    def deleteEle(self, ele):
        if ele in self.inputList:
            self.inputList.remove(ele)
            self.listModel.setStringList(self.inputList)
            self.completer.setCompletionMode(QCompleter.PopupCompletion)
            self.HistoryListChanged = True

    def event(self, event):
        # 按下Tab键时弹出所有记录
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Tab:
                # 设置不过滤显示  https://doc.qt.io/qt-5/qcompleter.html#completionMode-prop
                if self.completer.completionCount() > 0:
                    self.completer.setCompletionMode(
                        QCompleter.UnfilteredPopupCompletion)
                    self.listModel.setStringList(self.inputList)
                    self.completer.complete()
                    self.completer.popup().show()
                return True
            elif event.key() == Qt.Key_Delete:
                self.deleteEle(self.text())

        return super().event(event)