Python 如何格式化QCompleter';弹出列表是否正确?

Python 如何格式化QCompleter';弹出列表是否正确?,python,qt,pyside,qstyleditemdelegate,Python,Qt,Pyside,Qstyleditemdelegate,我想研究如何制作一个小型用户界面,用户可以在其中键入一些字母,并根据给定的数据源(此处列出)获得一些建议,从而使搜索更容易。为此,我使用Qt的QCompleter类 在匹配元素中,应使用HTML突出显示键入的字母,如下面代码中的示例:Austria。 最后,我将一些SO答案(请参阅)和教程合并到一个独立的小模块中: 从PySide导入QtCore、QtGui 类HTMLDelegate(QtGui.QStyledItemDelegate): “”“发件人:https://stackoverflo

我想研究如何制作一个小型用户界面,用户可以在其中键入一些字母,并根据给定的数据源(此处列出)获得一些建议,从而使搜索更容易。为此,我使用Qt的
QCompleter

在匹配元素中,应使用HTML突出显示键入的字母,如下面代码中的示例:
Austria
。 最后,我将一些SO答案(请参阅)和教程合并到一个独立的小模块中:

从PySide导入QtCore、QtGui
类HTMLDelegate(QtGui.QStyledItemDelegate):
“”“发件人:https://stackoverflow.com/a/5443112/1504082 """
def油漆(自身、油漆工、选项、索引):
options=QtGui.QStyleOptionViewItemV4(选项)
self.initStyleOption(选项,索引)
如果options.widget为无:
style=QtGui.QApplication.style()
其他:
style=options.widget.style()
doc=QtGui.QTextDocument()
doc.setHtml(options.text)
doc.setTextWidth(option.rect.width())
options.text=“”
style.drawControl(QtGui.QStyle.CE_ItemViewItem、选项、画师)
ctx=QtGui.QAbstractTextDocumentLayout.PaintContext()
#选中项目时高亮显示文本
#如果选择了options.state和QtGui.QStyle.state\u:
#ctx.palete.setColor(QtGui.qpalete.Text,
#选项.调色板.颜色(QtGui.qpalete.Active,
#QtGui.qpalete.HighlightedText)
textRect=style.subElementRect(QtGui.QStyle.SE_ItemViewItemText,
选项)
保存
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(画师,ctx)
恢复
def sizeHint(自身、选项、索引):
options=QtGui.QStyleOptionViewItemV4(选项)
self.initStyleOption(选项,索引)
doc=QtGui.QTextDocument()
doc.setHtml(options.text)
doc.setTextWidth(options.rect.width())
返回QtCore.QSize(doc.size().width(),doc.size().height())
类CustomQCompleter(QtGui.QCompleter):
“Implement”包含“筛选器模式”,因为筛选器模式“contains”不是
Qt<5.2时可用
发件人:https://stackoverflow.com/a/7767999/1504082 """
def uuu init uuu(self,parent=None):
超级(自定义QCompleter,self)。\uuuu初始化\uuuu(父级)
self.local_completion_prefix=“”
self.source\u model=无
self.delegate=htmlelegate()
def setModel(自我,型号):
self.source\u model=模型
super(CustomQCompleter,self).setModel(self.source\u model)
def updateModel(自我):
本地完成前缀=self.local完成前缀
#见:http://doc.qt.io/qt-4.8/model-view-programming.html#proxy-模型
类InnerProxyModel(QtGui.QSortFilterProxyModel):
def filterAcceptsRow(自身、源行、源父级):
#按行的模型索引映射,1d模型=>列始终为0
index=self.sourceModel().index(sourceRow,0,sourceParent)
source_data=self.sourceModel().data(索引,QtCore.Qt.DisplayRole)
#执行不区分大小写的匹配
#如果项目应保留在返回的过滤数据中,则返回True
#返回False以拒绝项目
返回源\u数据中的本地\u完成\u前缀.lower()
proxy_model=InnerProxyModel()
proxy_model.setSourceModel(self.source_model)
super(CustomQCompleter,self).setModel(proxy\u model)
#@todo:为什么又要放在这里?
self.popup().setItemDelegate(self.delegate)
def拆分路径(自身,路径):
self.local\u completion\u prefix=路径
self.updateModel()
返回“”
类自动完成编辑(QtGui.QLineEdit):
“基本上来自:
http://doc.qt.io/qt-5/qtwidgets-tools-customcompleter-example.html
"""
def uuu init uuuu(self,list_数据,分隔符=“”,addSpaceAfterCompleting=True):
超级(自动完成编辑,自我)。\uuu初始化
#背景
self.\u separator=分隔符
self.\u addSpaceAfterCompleting=addSpaceAfterCompleting
#完成者
self.\u completer=CustomQCompleter(self)
self.\u completer.setCaseSensitivity(QtCore.Qt.CaseSensitivious)
self.\u completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
self.model=QtGui.QStringListModel(列表数据)
self.\u completer.setModel(self.model)
#将完成符连接到行编辑
self.\u completer.setWidget(self)
#激活所选完成时触发插入
self.connect(self.\u completer,
QtCore.信号(“已激活(QString)”,
自我(插入完成)
self.\u忽略\u键=[QtCore.Qt.Key\u输入,
QtCore.Qt.Key\u返回,
QtCore.Qt.Key\u转义,
QtCore.Qt.Key\u选项卡]
def_insertCompletion(自我,完成):
"""
这是QCompleter.activated(QString)信号的事件处理程序,
当用户在completer弹出窗口中选择一个项目时,将调用它。
它将删除已键入的字符串和一个完成字符串。
"""
stripped_text=self.text()[:-len(self._completer.completionPrefix())]
额外文本=完成[-额外:]
如果是self.\u addspace完成后:
额外文本+=''
self.setText(剥离文本+额外文本)
def textUnderseror(自身):
text=self.text()
textUndersersor=“”
i=self.cursorPosition()-1
而i>=0和文本[i]!=自选分离器:
textUndersersor=text[i]+textUndersersor
i-=1
返回文本und
from PySide import QtCore, QtGui


class TaskDelegate(QtGui.QItemDelegate):
    # based on https://stackoverflow.com/a/8036666/1504082
    # https://doc.qt.io/archives/qt-4.7/qitemdelegate.html#drawDisplay
    # https://doc.qt.io/archives/qt-4.7/qwidget.html#render
    margin_x = 5
    margin_y = 3

    def drawDisplay(self, painter, option, rect, text):
        label = self.make_label(option, text)
        # calculate render anchor point
        point = rect.topLeft()
        point.setX(point.x() + self.margin_x)
        point.setY(point.y() + self.margin_y)

        label.render(painter, point, renderFlags=QtGui.QWidget.DrawChildren)

    def sizeHint(self, option, index):
        # get text using model and index
        text = index.model().data(index)
        label = self.make_label(option, text)
        return QtCore.QSize(label.width(), label.height() + self.margin_y)

    def make_label(self, option, text):
        label = QtGui.QLabel(text)

        if option.state & QtGui.QStyle.State_Selected:
            p = option.palette
            p.setColor(QtGui.QPalette.WindowText,
                       p.color(QtGui.QPalette.Active,
                               QtGui.QPalette.HighlightedText)
                       )

            label.setPalette(p)

        label.setStyleSheet("border: 1px dotted black")

        # adjust width according to widget's target width
        label.setMinimumWidth(self.target_width - (2 * self.margin_x))
        label.setMaximumWidth(self.target_width - self.margin_x)
        label.setWordWrap(True)
        label.adjustSize()
        return label


class CustomQCompleter(QtGui.QCompleter):
    """ Implement "contains" filter mode as the filter mode "contains" is not
    available in Qt < 5.2
    From: https://stackoverflow.com/a/7767999/1504082 """

    def __init__(self, parent=None):
        super(CustomQCompleter, self).__init__(parent)
        self.local_completion_prefix = ""
        self.source_model = None
        self.delegate = TaskDelegate()
        # widget not set yet
        # self.delegate.target_width = self.widget().width()

    def setModel(self, model):
        self.source_model = model
        super(CustomQCompleter, self).setModel(self.source_model)

    def updateModel(self):
        local_completion_prefix = self.local_completion_prefix

        # see: http://doc.qt.io/qt-4.8/model-view-programming.html#proxy-models
        class InnerProxyModel(QtGui.QSortFilterProxyModel):
            def filterAcceptsRow(self, sourceRow, sourceParent):
                # model index mapping by row, 1d model => column is always 0
                index = self.sourceModel().index(sourceRow, 0, sourceParent)
                source_data = self.sourceModel().data(index, QtCore.Qt.DisplayRole)
                # performs case insensitive matching
                # return True if item shall stay in th returned filtered data
                # return False to reject an item
                return local_completion_prefix.lower() in source_data.lower()

        proxy_model = InnerProxyModel()
        proxy_model.setSourceModel(self.source_model)
        super(CustomQCompleter, self).setModel(proxy_model)
        # @todo: Why to be set here again?
        # -> rescale popup list items to widget width
        self.delegate.target_width = self.widget().width()
        self.popup().setItemDelegate(self.delegate)

    def splitPath(self, path):
        self.local_completion_prefix = path
        self.updateModel()
        return ""


class AutoCompleteEdit(QtGui.QLineEdit):
    """ Basically from:
    http://doc.qt.io/qt-5/qtwidgets-tools-customcompleter-example.html
    """

    def __init__(self, list_data, separator=' ', addSpaceAfterCompleting=True):
        super(AutoCompleteEdit, self).__init__()
        # settings
        self._separator = separator
        self._addSpaceAfterCompleting = addSpaceAfterCompleting
        # completer
        self._completer = CustomQCompleter(self)
        self._completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self._completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)

        self.model = QtGui.QStringListModel(list_data)
        self._completer.setModel(self.model)

        # connect the completer to the line edit
        self._completer.setWidget(self)
        # trigger insertion of the selected completion when its activated
        self.connect(self._completer,
                     QtCore.SIGNAL('activated(QString)'),
                     self._insertCompletion)

        self._ignored_keys = [QtCore.Qt.Key_Enter,
                              QtCore.Qt.Key_Return,
                              QtCore.Qt.Key_Escape,
                              QtCore.Qt.Key_Tab]

    def _insertCompletion(self, completion):
        """
        This is the event handler for the QCompleter.activated(QString) signal,
        it is called when the user selects an item in the completer popup.
        It will remove the already typed string with the one of the completion.
        """
        stripped_text = self.text()[:-len(self._completer.completionPrefix())]

        extra_text = completion  # [-extra:]
        if self._addSpaceAfterCompleting:
            extra_text += ' '
        self.setText(stripped_text + extra_text)

    def textUnderCursor(self):
        text = self.text()
        textUnderCursor = ''
        i = self.cursorPosition() - 1
        while i >= 0 and text[i] != self._separator:
            textUnderCursor = text[i] + textUnderCursor
            i -= 1
        return textUnderCursor

    def keyPressEvent(self, event):
        if self._completer.popup().isVisible():
            if event.key() in self._ignored_keys:
                event.ignore()
                return
        super(AutoCompleteEdit, self).keyPressEvent(event)
        completionPrefix = self.textUnderCursor()
        if completionPrefix != self._completer.completionPrefix():
            self._updateCompleterPopupItems(completionPrefix)
        if len(event.text()) > 0 and len(completionPrefix) > 0:
            self._completer.complete()
        if len(completionPrefix) == 0:
            self._completer.popup().hide()

    def _updateCompleterPopupItems(self, completionPrefix):
        """
        Filters the completer's popup items to only show items
        with the given prefix.
        """
        self._completer.setCompletionPrefix(completionPrefix)
        # self._completer.popup().setCurrentIndex(
        #     self._completer.completionModel().index(0, 0))


if __name__ == '__main__':
    def demo():
        import sys
        app = QtGui.QApplication(sys.argv)
        values = ['Germany',
                  'Au<b>st</b>ria',
                  'Switzerland',
                  'Hungary',
                  'The United Kingdom of Great Britain and Northern Ireland',
                  'USA']
        editor = AutoCompleteEdit(values)
        window = QtGui.QWidget()
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(editor)
        window.setLayout(hbox)
        window.show()

        sys.exit(app.exec_())

    demo()