QTableView中的单选行激活列编辑

时间:2018-09-17 02:16:15

标签: python pyside

每当在表格视图中选择一行时,如何使第二列在编辑模式下有效(如以下此gif所示)?我正在尝试在python / pyside中重新创建它。

理想情况下,我想使用某种项目委托,以便我可以轻松地处理column单元格中的keyPressEvents并添加自定义(X)清除按钮。但是我不确定在使用ItemModels时如何使用这样的委托。因此,对帮助实现此任务的任何帮助表示赞赏。

class ExampleDelegate(QtGui.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        line_edit = QtGui.QLineEdit(parent)
        return line_edit

enter image description here

这是我的代码和屏幕截图:

enter image description here

import os, sys
from PySide import QtGui, QtCore


class HotkeyItem():
    def __init__(self, command, shortcut):
        self.command = command
        self.shortcut = shortcut


class HotkeysModel(QtCore.QAbstractTableModel):

    def __init__(self):
        super(HotkeysModel, self).__init__()
        self.items = []
        self.headers = ['Command','Hotkey']

    def clear(self):
        self.beginResetModel()
        self.items = []
        self.endResetModel()

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return len(self.items)

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal:
            if role == QtCore.Qt.DisplayRole:
                cnt = len(self.headers)
                if section < cnt:
                    return self.headers[section]
        return None

    def columnCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return len(self.headers)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column, parent)

    def addItem(self, item):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self.items.append(item)
        self.endInsertRows()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return

        row = index.row()
        col = index.column()

        if 0 <= row < self.rowCount():
            item = self.items[row]

            if role == QtCore.Qt.DisplayRole:
                if col == 0:
                    return getattr(item, 'command', 'N/A')
                elif col == 1:
                    return getattr(item, 'shortcut', '')

            if role == QtCore.Qt.BackgroundRole:
                shortcuts = filter(None, [x.shortcut for x in self.items])
                dups = shortcuts.count(getattr(item, 'shortcut', ''))
                if dups > 1:
                    return QtGui.QBrush(QtGui.QColor(255, 50, 50, 255))

            elif role == QtCore.Qt.FontRole:
                shortcuts = filter(None, [x.shortcut for x in self.items])
                dups = shortcuts.count(getattr(item, 'shortcut', ''))
                if dups > 1:
                    fnt = QtGui.QFont()
                    fnt.setBold(True)
                    fnt.setItalic(True)
                    return fnt

        return None

    def flags(self, index):
        if not index.isValid():
            return 0
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable


class Example(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Example, self).__init__(parent)
        self.resize(600, 400)

        model = HotkeysModel()

        proxyModel = QtGui.QSortFilterProxyModel()
        proxyModel.setFilterKeyColumn(0)
        proxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        proxyModel.setSourceModel(model)

        self.uiView = QtGui.QTableView()
        self.uiView.setSortingEnabled(True)
        self.uiView.setModel(proxyModel)
        self.uiView.setAlternatingRowColors(True)
        self.uiView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.uiView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
        self.uiView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.uiView.verticalHeader().hide()
        self.uiView.horizontalHeader().show()

        lay = QtGui.QVBoxLayout()
        lay.addWidget(self.uiView)
        self.setLayout(lay)

        self.populate()

        # connections
        selection = self.uiView.selectionModel()
        selection.currentRowChanged.connect(self.selection_changed)


# ui->tableView->setCurrentIndex(index);
# ui->tableView->edit(index);

    def selection_changed(self, index):
        if index.isValid():
            row = index.row()
            self.uiView.setCurrentIndex(index)
            self.uiView.edit(index)


    def populate(self):
        model = self.uiView.model().sourceModel()
        model.clear()

        items = [
            HotkeyItem(command='Save', shortcut='Ctrl+S'),
            HotkeyItem(command='Open', shortcut='Ctrl+O'),
            HotkeyItem(command='Close', shortcut='Ctrl+Q'),
            HotkeyItem(command='Align Top', shortcut=''),
            HotkeyItem(command='Align Bottom', shortcut=''),
            HotkeyItem(command='Align Left', shortcut=''),
            HotkeyItem(command='Align Right', shortcut=''),
            HotkeyItem(command='Align Center', shortcut='Ctrl+O')
        ]

        for x in items:
            model.addItem(x)

        self.uiView.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self.uiView.resizeColumnsToContents()


def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:2)

部分问题在于,要使某个项目可编辑,必须激活标记QtCore.Qt.ItemIsEditable。另一部分是,通过selection_changed的索引可以来自第一列中的项目,而不是来自第二列,因此使用该索引应该从第二列中获取索引。

在Qt5中,已经实现了清除按钮,并且仅使用setClearButtonEnabled(True)激活了清除按钮,并使用qss更改了图标,但是对于Qt4,它不存在,因此必须使用{{ 3}}。

最后,您还必须实现setData()方法。

import os
import sys
from PySide import QtGui, QtCore


class LineEdit(QtGui.QLineEdit):
    def __init__(self, parent=None):
        super(LineEdit, self).__init__(parent)
        btnSize = self.sizeHint().height() - 5
        self.clearButton = QtGui.QToolButton(self)
        icon = QtGui.QIcon("clear.png")
        self.clearButton.setIcon(icon)
        self.clearButton.setCursor(QtCore.Qt.ArrowCursor)
        self.clearButton.setStyleSheet("QToolButton { border: none; padding: 2px}")
        self.clearButton.setFixedSize(btnSize, btnSize)
        self.clearButton.hide()
        frameWidth = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
        self.setStyleSheet("QLineEdit{{ padding-right: {}px }}".format(btnSize - frameWidth))
        self.setMinimumHeight(self.sizeHint().height())
        self.clearButton.clicked.connect(self.clear)
        self.textChanged.connect(self.onTextChanged)

    def resizeEvent(self, event):
        frameWidth = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
        self.clearButton.move(self.width() - self.clearButton.width() - frameWidth, 0)

    def onTextChanged(self, text):
        self.clearButton.setVisible(text != "")


class Delegate(QtGui.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = LineEdit(parent)
        font = index.data(QtCore.Qt.FontRole)
        editor.setFont(font)
        return editor

    def setEditorData(self, editor, index):
        text = index.data()
        editor.setText(text)

    def setModelData(self, editor, model, index):
        model.setData(index, editor.text())

class HotkeyItem():
    def __init__(self, command, shortcut):
        self.command = command
        self.shortcut = shortcut


class HotkeysModel(QtCore.QAbstractTableModel):
    def __init__(self):
        super(HotkeysModel, self).__init__()
        self.items = []
        self.headers = ['Command','Hotkey']

    def clear(self):
        self.beginResetModel()
        self.items = []
        self.endResetModel()

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return len(self.items)

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal:
            if role == QtCore.Qt.DisplayRole:
                cnt = len(self.headers)
                if section < cnt:
                    return self.headers[section]
        return None

    def columnCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return len(self.headers)

    def addItem(self, item):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self.items.append(item)
        self.endInsertRows()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return 

        row = index.row()
        col = index.column()

        if 0 <= row < self.rowCount():
            item = self.items[row]

            if role == QtCore.Qt.DisplayRole:
                if col == 0:
                    return getattr(item, 'command', 'N/A')
                elif col == 1:
                    return getattr(item, 'shortcut', '')

            if role == QtCore.Qt.BackgroundRole:
                shortcuts = filter(None, [x.shortcut for x in self.items])
                dups = shortcuts.count(getattr(item, 'shortcut', ''))
                if dups > 1:
                    return QtGui.QBrush(QtGui.QColor(255, 50, 50, 255))

            elif role == QtCore.Qt.FontRole:
                shortcuts = filter(None, [x.shortcut for x in self.items])
                dups = shortcuts.count(getattr(item, 'shortcut', ''))
                if dups > 1:
                    fnt = QtGui.QFont()
                    fnt.setBold(True)
                    fnt.setItalic(True)
                    return fnt

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            row = index.row()
            col = index.column()
            if 0 <= row < self.rowCount() and 0 <= col < self.columnCount():
                it = self.items[row]
                if col == 0:
                    it.command = value
                elif col == 1:
                    it.shortcut = value
                return True
        return False

    def flags(self, index):
        fl = QtCore.Qt.NoItemFlags
        if index.isValid():
            fl |= QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
            if index.column() == 1:
                fl |= QtCore.Qt.ItemIsEditable
        return fl


class Example(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Example, self).__init__(parent)
        self.resize(600, 400)

        model = HotkeysModel()

        proxyModel = QtGui.QSortFilterProxyModel()
        proxyModel.setFilterKeyColumn(0)
        proxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        proxyModel.setSourceModel(model)

        self.uiView = QtGui.QTableView()
        self.uiView.setSortingEnabled(True)
        self.uiView.setModel(proxyModel)
        self.uiView.setAlternatingRowColors(True)
        delegate = Delegate(self)
        self.uiView.setItemDelegateForColumn(1, delegate)
        self.uiView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.uiView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
        self.uiView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.uiView.verticalHeader().hide()
        self.uiView.horizontalHeader().show()

        lay = QtGui.QVBoxLayout()
        lay.addWidget(self.uiView)
        self.setLayout(lay)
        self.populate()

        # connections
        selection = self.uiView.selectionModel()
        selection.currentChanged.connect(self.openEditor)
        self.uiView.clicked.connect(self.openEditor)

    def openEditor(self, index):
        if index.isValid():
            ix = index.sibling(index.row(), 1)
            self.uiView.setCurrentIndex(ix)
            self.uiView.edit(ix)

    def populate(self):
        model = self.uiView.model().sourceModel()
        model.clear()

        items = [
            HotkeyItem(command='Save', shortcut='Ctrl+S'),
            HotkeyItem(command='Open', shortcut='Ctrl+O'),
            HotkeyItem(command='Close', shortcut='Ctrl+Q'),
            HotkeyItem(command='Align Top', shortcut=''),
            HotkeyItem(command='Align Bottom', shortcut=''),
            HotkeyItem(command='Align Left', shortcut=''),
            HotkeyItem(command='Align Right', shortcut=''),
            HotkeyItem(command='Align Center', shortcut='Ctrl+O')
        ]

        for x in items:
            model.addItem(x)

        self.uiView.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self.uiView.resizeColumnsToContents()


def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()