gpt4 book ai didi

python - 如何正确格式化 QCompleter 弹出列表的列表项?

转载 作者:太空宇宙 更新时间:2023-11-04 05:28:28 25 4
gpt4 key购买 nike

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

在匹配元素中,键入的字母应使用 HTML 突出显示,如下面代码中的示例:Au<b>st</b>ria .最后,我将一些 SO 答案(参见 How to make item view render rich (html) text in Qt)和教程合并到一个小的独立模块中:

from PySide import QtCore, QtGui

class HTMLDelegate(QtGui.QStyledItemDelegate):
""" From: https://stackoverflow.com/a/5443112/1504082 """

def paint(self, painter, option, index):
options = QtGui.QStyleOptionViewItemV4(option)
self.initStyleOption(options, index)
if options.widget is None:
style = QtGui.QApplication.style()
else:
style = options.widget.style()

doc = QtGui.QTextDocument()
doc.setHtml(options.text)
doc.setTextWidth(option.rect.width())

options.text = ""
style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter)

ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()

# Highlighting text if item is selected
# if options.state & QtGui.QStyle.State_Selected:
# ctx.palette.setColor(QtGui.QPalette.Text,
# options.palette.color(QtGui.QPalette.Active,
# QtGui.QPalette.HighlightedText))

textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText,
options)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)
painter.restore()

def sizeHint(self, option, index):
options = QtGui.QStyleOptionViewItemV4(option)
self.initStyleOption(options, index)
doc = QtGui.QTextDocument()
doc.setHtml(options.text)
doc.setTextWidth(options.rect.width())
return QtCore.QSize(doc.size().width(), doc.size().height())


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 = HTMLDelegate()

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?
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']
editor = AutoCompleteEdit(values)
window = QtGui.QWidget()
hbox = QtGui.QHBoxLayout()
hbox.addWidget(editor)
window.setLayout(hbox)
window.show()

sys.exit(app.exec_())

demo()

我的问题是用户 Timo 在答案 https://stackoverflow.com/a/5443112/1504082 中的建议:

After line: 'doc.setHtml(options.text)', you need to set also doc.setTextWidth(option.rect.width()), otherwise the delegate wont render longer content correctly in respect to target drawing area. For example does not wrap words in QListView.

所以我这样做是为了避免在完成者的弹出窗口中裁剪长文本。但我得到以下输出: Where does this vertical margin come from?

这个额外的垂直边距从何而来?

我稍微调查了一下,发现 sizeHint HTMLDelegate的方法|有时用 options 调用包含一个矩形的参数 (0, 0, 0, 0) .在调用 doc.setTextWidth(options.rect.width()) 之后,显示行为最终发生了变化。 .但我无法最终找出谁用这个参数调用它以及我如何才能正确解决这个问题。

有人可以解释这是从哪里来的,我该如何解决这个问题?

最佳答案

最后我找到了另一种实现它的方法,使用 https://stackoverflow.com/a/8036666/1504082 的想法.没有这些我还不明白的自定义绘图对我来说更有意义:)

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()

关于python - 如何正确格式化 QCompleter 弹出列表的列表项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37943310/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com