持久排序选择QCombobox-QCompleter弹出窗口

时间:2018-02-19 19:52:57

标签: python qt pyside qsortfilterproxymodel qcompleter

我有一个从QCombobox派生的自定义类,它带有一个基础自定义列表模型(派生自QAbstractListModel)。该模型允许用户从选项中进行多项选择(通过检查成员)。组合框是从委托(从QItemDelegate派生)创建的,该委托在QAbstractTableModel派生实例上工作。这个想法是,从选择中,表模型将选择存储为包含选择成员的列表,并将其显示为列表的字符串表示。

到目前为止,我的实施工作正常,但我还没有完成两件事:

  1. 用户在完成者上插入文本后,显示的结果列表不按字母顺序排序(在完成者的弹出窗口中)。
  2. 每次点击完成者的输出列表(弹出窗口)时,视图会隐藏/关闭(我不确定哪一个),这会阻止一次选择多个项目。
  3. 以下是我的实施的简短(pyside)示例:

    from PySide.QtCore import *
    from PySide.QtGui import *
    
    class MyCombobox(QComboBox):
        def __init__(self, parent = None):
            super(MyCombobox, self).__init__(parent)
    
            self.setFocusPolicy(Qt.StrongFocus)
            self.setEditable(True)
    
            self.filter = QSortFilterProxyModel(self)
            self.filter.setFilterCaseSensitivity(Qt.CaseInsensitive)
    
            self.completer = QCompleter(self.filter, self)
            self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
            self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
            self.setCompleter(self.completer)
    
    
            #signals
            self.activated.connect(self._comboActivated)
            self.lineEdit().textEdited[unicode].connect(self.filter.setFilterFixedString)
            self.completer.activated['QModelIndex'].connect(self._completerActivated)
    
            self._pressed = True
    
        def _completerActivated(self, index):
            if index.isValid():
               self._itemPressed(index)
    
    
        def setModel(self, model):
            super(MyCombobox, self).setModel(model)
            self.filter.setSourceModel(model)
            self.completer.setModel(self.filter)
    
        def _itemPressed(self, index):
            row = index.row()
            col = index.column()
            index2 = self.filter.index(row, col)
            index = self.filter.mapToSource(index2)
            model = self.model()
            state = model.data(index, role = Qt.CheckStateRole)
            if state == Qt.Checked:
                model.setData(index, Qt.Unchecked, Qt.CheckStateRole)
                self._pressed = True
                return
            elif state == Qt.Unchecked:
                model.setData(index, Qt.Checked, Qt.CheckStateRole)
                self._pressed = True
                return
            else:
                self._pressed = False
                return
    
          def _comboActivated(self, pos):
             model  = self.model()
             index = model.index(pos, 0)
             state = model.data(index, role = Qt.CheckStateRole)
             if state == Qt.Checked:
                 model.setData(index, Qt.Unchecked, Qt.CheckStateRole)
                 self._pressed = True
                 return
             elif state == Qt.Unchecked:
                 model.setData(index, Qt.Checked, Qt.CheckStateRole)
                 self._pressed = True
                 return
             else:
                 self._pressed = False
                 return
    
    
        def hidePopup(self):
            if not self._pressed:
                super(MyCombobox, self).hidePopup()
                self._pressed = True
            else:
                self._pressed = False
    
    class MyDelegate(QItemDelegate):
        def __init__(self, parent = None):
            super(MyDelegate, self).__init__(parent)
    
        def createEditor(self, parent, option, index):
            cb = MyCombobox(parent)
            tableModel = index.model()
            sel = tableModel._table[0][0]
            model = MyModel(parent)
    
            for k, name in enumerate(model._base):            
                model._checked[k] = name in sel 
            cb.setModel(model)
            return cb
    
        def setModelData(self, editor, model, index):
            mymodel = editor.model()
            sel = mymodel._checked
            base = mymodel._base
            myselection = [ name for k, name in enumerate(base) if sel[k] ]
            model.setData(index, myselection, role = Qt.DisplayRole)
    
    class MyModel(QAbstractListModel):
        def __init__(self, parent = None):
            super(MyModel, self).__init__(parent)
            self._base = ["one", "two", "three", "four", "five"]
            self._checked = [False for k in range(len(self._base))]
    
        def rowCount(self, index = None):
            return len(self._base)
    
        def data(self, index, role = Qt.DisplayRole):
            i = index.row()
            if 0 <= i < self.rowCount():
                if role == Qt.DisplayRole:
                    return self._base[i]
                elif role == Qt.CheckStateRole:
                    if self._checked[i]:
                        return Qt.Checked
                    else:
                        return Qt.Unchecked
                else:
                    pass
            else:
                pass
    
        def setData(self, index, value, role = Qt.CheckStateRole):
            i = index.row()
            if 0 <= i < self.rowCount():
                if role == Qt.CheckStateRole:
                    self._checked[i] = value == Qt.Checked
                    self.dataChanged.emit(index, index)
                    return True
                else:
                    return super(MyModel, self).setData(index, value, role)
            else:
                return False
    
    
        def flags(self, index):
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
    
    class MyTableModel(QAbstractTableModel):
        def __init__(self, table, parent = None):
            super(MyTableModel, self).__init__(parent)
            self._table = table
    
        def rowCount(self, index = None):
            return len(self._table)
    
        def columnCount(self, index = None):
            if self._table: return len(self._table[0])
            return 0
    
        def data(self, index, role = Qt.DisplayRole):
            i, j = index.row(), index.column()
            if role == Qt.DisplayRole:
                return unicode(self._table[i][j])
    
        def setData(self, index, value, role = Qt.DisplayRole):
            i, j = index.row(), index.column()
            if role == Qt.DisplayRole:
                self._table[i][j] = value
    
        def flags(self, index):
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
    
    if __name__ == '__main__':
    
        app = QApplication("test")
        tableView = QTableView()
        model = MyTableModel([[["numbers"]]])
        tableView.setModel(model)
        delegate = MyDelegate(tableView)
        tableView.setItemDelegate(delegate)
        tableView.show();
        app.exec_()
    

1 个答案:

答案 0 :(得分:0)

好的,所以我找到了解决这两个问题的方法。

首先,对于完成输出的排序:我将QLineEdit.textEdited信号连接到组合框中的自定义插槽。此插槽强制QSortFilterProxyModel实例按列0对基础模型进行排序。不是很好,但有效。

第二,点击&#34;点击&#34;完成弹出窗口的问题:这是一个头脑。在玩了几个小时的事件和过滤器后,我意识到视图上的鼠标重新发布事件(弹出窗口)发出了点击的信号(感谢文档),我相信,这个信号已连接到&#34;隐藏& #34;视图的插槽。所以我的解决方案是一个丑陋的黑客,但同样,它的工作原理。我编写了自己的QListView视图,并覆盖mouseReleaseEvent方法,我阻止所有信号然后处理事件。为了实现这一点,我不得不将组合框实例传递给我的视图类。

以下是为了工作而修改或添加到初始发布代码的类:

class MyCombobox(QComboBox):
    def __init__(self, parent=None):
        super(MyCombobox, self).__init__(parent)

        self.setFocusPolicy(Qt.StrongFocus)
        self.setEditable(True)

        self.filter = QSortFilterProxyModel(self)
        self.filter.setFilterCaseSensitivity(Qt.CaseInsensitive)

        self.completer = QCompleter(self.filter, self)
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
        self.setCompleter(self.completer)

        # signals
        self.activated.connect(self._comboActivated)
        self.lineEdit().textEdited[unicode].connect(self.filter.setFilterFixedString)
        self.lineEdit().textEdited[unicode].connect(self._sort)
        self.completer.activated['QModelIndex'].connect(self._completerActivated)

        mlview = MyListView(self)
        mlview.setEditTriggers(self.view().editTriggers())
        self.completer.setPopup(mlview)

        self._pressed = True

    def _completerActivated(self, index):
        if index.isValid():
            self._itemPressed(index)


    def setModel(self, model):
        super(MyCombobox, self).setModel(model)
        self.filter.setSourceModel(model)
        self.completer.setModel(self.filter)


    def _itemPressed(self, index):
        row = index.row()
        col = index.column()
        index2 = self.filter.index(row, col)
        index = self.filter.mapToSource(index2)
        model = self.model()
        state = model.data(index, role = Qt.CheckStateRole)
        if state == Qt.Checked:
            model.setData(index, Qt.Unchecked, Qt.CheckStateRole)
            self._pressed = True
            return
        elif state == Qt.Unchecked:
            model.setData(index, Qt.Checked, Qt.CheckStateRole)
            self._pressed = True
            return
        else:
            self._pressed = False
            return

    def _comboActivated(self, pos):
        model  = self.model()
        index = model.index(pos, 0)
        state = model.data(index, role = Qt.CheckStateRole)
        if state == Qt.Checked:
            model.setData(index, Qt.Unchecked, Qt.CheckStateRole)
            self._pressed = True
            return
        elif state == Qt.Unchecked:
            model.setData(index, Qt.Checked, Qt.CheckStateRole)
            self._pressed = True
            return
        else:
            self._pressed = False
            return

    def _sort(self, arg = None):
        self.filter.sort(0)

    def hidePopup(self):
        if not self._pressed:
            super(MyCombobox, self).hidePopup()
            self._pressed = True
        else:
            self._pressed = False

class MyListView(QListView):
    def __init__(self, widget, parent = None):
        super(MyListView, self).__init__(parent)
        self._widget = widget
        self.setSelectionMode(QListView.SingleSelection)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setSelectionBehavior(QListView.SelectItems)


    def mouseReleaseEvent(self, event):
        self.blockSignals(True)
        super(MyListView, self).mouseReleaseEvent(event)
        self.blockSignals(False)
        index = self.currentIndex()
        if not index.isValid(): return
        self._widget._itemPressed(index)