【问题标题】:PyQt QSortFilterProxyModel index from wrong model passed to mapToSource?来自错误模型的PyQt QSortFilterProxyModel索引传递给mapToSource?
【发布时间】:2016-08-31 21:59:40
【问题描述】:

当我选择下拉自动完成项时,我想获取存储在[(1, 'cb'), (3, 'cd'), (7, 'ca'), (11, 'aa'), (22, 'bd')] 中的整数。

因为我使用的是QSortFilterProxyModel,所以当使用down键选择item时,索引来自代理模型。

我在文档中读到我应该使用mapToSource 来获取原始模型中的索引,但在这里我收到一条错误消息index from wrong model passed to mapToSource 并且index.row() 始终为-1。我错过了什么?谢谢!

错误是:

row in proxy model 0
QSortFilterProxyModel: index from wrong model passed to mapToSource 
row in original model -1

代码:

from PyQt4.QtCore import *
from PyQt4.QtGui import *


import sys
import re
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)


class MyModel(QStandardItemModel):

    def __init__(self, parent=None):
        super(MyModel, self).__init__(parent)

    def data(self, index, role):

        symbol = self.symbol_data[index.row()]
        if role == Qt.DisplayRole:
            return symbol[1]

        elif role == Qt.UserRole:
            return symbol[0]

    def setup(self, data):
        self.symbol_data = data
        for line, name in data:
            item = QStandardItem(name)
            self.appendRow(item)


class MyGui(QDialog):

    def __init__(self, parent=None):

        super(MyGui, self).__init__(parent)

        symbols = [(1, 'cb'), (3, 'cd'), (7, 'ca'), (11, 'aa'), (22, 'bd')]

        model = MyModel()
        model.setup(symbols)

        layout = QVBoxLayout(self)
        self.line = QLineEdit(self)

        layout.addWidget(self.line)

        self.setLayout(layout)

        completer = CustomQCompleter()

        completer.setModel(model)
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        completer.setWrapAround(False)

        self.line.setCompleter(completer)

        self.completer = completer

        self.completer.highlighted[QModelIndex].connect(self.test)

        # qApp.processEvents()
        # QTimer.singleShot(0, self.completer.complete)
        self.line.textChanged[QString].connect(self.pop)

    def pop(self, *x):
        text = x[0]
        self.completer.splitPath(text)
        QTimer.singleShot(0, self.completer.complete)

        self.line.setFocus()

    def test(self, index):
        print 'row in proxy model', index.row()
        print 'row in original model', self.completer.model().mapToSource(index).row()
        # print 'line in original model:',
        # self.completer.model().sourceModel().symbol_data[x[0].row()][0]


class CustomQCompleter(QCompleter):

    def __init__(self, parent=None):
        super(CustomQCompleter, self).__init__(parent)
        self.local_completion_prefix = ""
        self.source_model = None
        self.first_down = True

    def setModel(self, model):
        self.source_model = model
        self._proxy = QSortFilterProxyModel(
            self, filterCaseSensitivity=Qt.CaseInsensitive)
        self._proxy.setSourceModel(model)
        super(CustomQCompleter, self).setModel(self._proxy)

    def splitPath(self, path):
        self.local_completion_prefix = str(path)
        self._proxy.setFilterFixedString(path)
        return ""

    def eventFilter(self,  obj,  event):

        if event.type() == QEvent.KeyPress:
            'This is used to mute the connection to clear lineedit'
            if event.key() in (Qt.Key_Down, Qt.Key_Up):
                curIndex = self.popup().currentIndex()

                if event.key() == Qt.Key_Down:
                    if curIndex.row() == self._proxy.rowCount()-1:
                        print 'already last row', curIndex.row()
                        if self._proxy.rowCount() == 1:
                            pass
                        else:
                            return True
                else:
                    if curIndex.row() == 0:
                        print 'already first row'
                        return True

                if curIndex.row() == 0 and self.first_down:
                    print 'already row 0 first'
                    self.popup().setCurrentIndex(curIndex)
                    self.first_down = False
                    return True

        super(CustomQCompleter, self).eventFilter(obj,  event)
        return False


if __name__ == '__main__':

    app = QApplication(sys.argv)
    gui = MyGui()
    gui.show()
    sys.exit(app.exec_())

更新: 已解决,感谢 Avaris 在#pyqt 中的帮助。事实证明,我可以这样做将索引映射到原始模型

proxy_index= self.completer.completionModel().mapToSource(index)
print 'original row:', self.completer.model().mapToSource(proxy_index).row()

甚至更好:

print 'data:', index.data(Qt.UserRole).toPyObject()

因为:“ completionModel() 实际上是 .model() 上的代理模型

您不需要为此弄乱 mapToSource。 index.data(Qt.UserRole) 应该给你这个数字,不管返回哪个索引

仅供参考,您很少需要在(代理)模型之外使用 mapToSource。它主要用于内部使用。适当的代理应转发来自源的所有相关查询。所以你可以像使用源一样使用代理 -Avaris "

【问题讨论】:

    标签: python autocomplete pyqt qsortfilterproxymodel qcompleter


    【解决方案1】:

    在此处粘贴正确的代码以供参考

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    
    import sys
    import re
    import signal
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    
    
    class MyModel(QStandardItemModel):
    
        def __init__(self, parent=None):
            super(MyModel, self).__init__(parent)
    
        def data(self, index, role):
    
            symbol = self.symbol_data[index.row()]
            if role == Qt.DisplayRole:
                return symbol[1]
    
            elif role == Qt.UserRole:
                return symbol[0]
    
        def setup(self, data):
            self.symbol_data = data
            for line, name in data:
                item = QStandardItem(name)
                self.appendRow(item)
    
    
    class MyGui(QDialog):
    
        def __init__(self, parent=None):
    
            super(MyGui, self).__init__(parent)
    
            symbols = [(1, 'cb'), (3, 'cd'), (7, 'ca'), (11, 'aa'), (22, 'bd')]
    
            model = MyModel()
            model.setup(symbols)
    
            layout = QVBoxLayout(self)
            self.line = QLineEdit(self)
    
            layout.addWidget(self.line)
    
            self.setLayout(layout)
    
            completer = CustomQCompleter()
    
            completer.setModel(model)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
            completer.setWrapAround(False)
    
            self.line.setCompleter(completer)
    
            self.completer = completer
    
            self.completer.highlighted[QModelIndex].connect(self.test)
    
            # QTimer.singleShot(0, self.completer.complete)
            self.line.textChanged[QString].connect(self.pop)
    
        def pop(self, *x):
            text = x[0]
            self.completer.splitPath(text)
            QTimer.singleShot(0, self.completer.complete)
    
            self.line.setFocus()
    
        def test(self, index):
            print 'row in completion model', index.row()
            print 'data:', index.data(Qt.UserRole).toPyObject()
    
    class CustomQCompleter(QCompleter):
    
        def __init__(self, parent=None):
            super(CustomQCompleter, self).__init__(parent)
            self.local_completion_prefix = ""
            self.source_model = None
            self.first_down = True
    
        def setModel(self, model):
            self.source_model = model
            self._proxy = QSortFilterProxyModel(
                self, filterCaseSensitivity=Qt.CaseInsensitive)
            self._proxy.setSourceModel(model)
            super(CustomQCompleter, self).setModel(self._proxy)
    
        def splitPath(self, path):
            self.local_completion_prefix = str(path)
            self._proxy.setFilterFixedString(path)
            return ""
    
        def eventFilter(self,  obj,  event):
    
            if event.type() == QEvent.KeyPress:
                'This is used to mute the connection to clear lineedit'
                if event.key() in (Qt.Key_Down, Qt.Key_Up):
                    curIndex = self.popup().currentIndex()
    
                    if event.key() == Qt.Key_Down:
                        if curIndex.row() == self._proxy.rowCount()-1:
                            print 'already last row', curIndex.row()
                            if self._proxy.rowCount() == 1:
                                pass
                            else:
                                return True
                    else:
                        if curIndex.row() == 0:
                            print 'already first row'
                            return True
    
                    if curIndex.row() == 0 and self.first_down:
                        print 'already row 0 first'
                        self.popup().setCurrentIndex(curIndex)
                        self.first_down = False
                        return True
    
            super(CustomQCompleter, self).eventFilter(obj,  event)
            return False
    
    
    if __name__ == '__main__':
    
        app = QApplication(sys.argv)
        gui = MyGui()
        gui.show()
        sys.exit(app.exec_())
    

    【讨论】:

      猜你喜欢
      • 2019-04-25
      • 1970-01-01
      • 1970-01-01
      • 2014-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-31
      相关资源
      最近更新 更多