ユーザーが手紙を入力し、検索を容易にする特定のデータソース(ここにリスト)に基づいていくつかの提案を得る小さなユーザーインターフェイスを作成する方法を調べたいと思います。この目的のために私はQtのQCompleter
クラスを使用しています。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()
私の問題は解答https://stackoverflow.com/a/5443112/1504082でユーザーティモの提案である「ドキュメント.setHtml(options.text) 'の場合は、doc.setTextWidth(option.rect.width())も設定する必要があります。そうしないと、デリゲートは対象の描画領域に対して長いコンテンツを正しくレンダリングしません。たとえば、QListViewで単語をラップしません。
これは、コンプリータのポップアップで長いテキストを切り取らないようにするためです。しかし、私は次の出力を得ます:
この追加の垂直マージンはどこから来ますか?
私はこのビットを調査し、私はHTMLDelegate
の
sizeHint
方法は時々属性
(0, 0, 0, 0)
を持つ矩形が含まれてい
options
パラメータで呼び出されていることがわかります。そして、
doc.setTextWidth(options.rect.width())
の呼び出しの後、最終的に表示動作が変わります。しかし、私は最終的に誰がこのパラメータでそれを呼び出すか、そしてこれをいかに適切に修正できるかを知ることができませんでした。
これはどこから来て、どのように私はこれをうまく解決できるのか誰かが説明できますか?