QTableView单选行激活列编辑

Single selecting row in QTableView activates column edit

只要在表格视图中选择一行,我如何才能使第二列在编辑模式下处于活动状态,如下面的 gif 图像所示?我正在尝试在 python/pyside.

中重新创建它

理想情况下,我想使用某种项目委托,这样我就可以轻松处理列单元格中的 keyPressEvents 并添加自定义 (X) 清除按钮。但是,我不确定在使用 ItemModel 时如何使用这样的委托。因此,如果您能帮助我们完成这项任务,我们将不胜感激。

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

这是我的代码和截图:

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

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

在Qt5中已经实现了清除按钮,仅使用setClearButtonEnabled(True)激活,使用qss更改图标,但在Qt4中不存在,因此必须创建才能使用this answer.

最后还必须实现 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()