如何对项目的复选框与其文本的点击事件进行不同的处理?(PyQt/PySide/Qt) [英] How to treat click events differently for an item's checkbox versus its text? (PyQt/PySide/Qt)

查看:53
本文介绍了如何对项目的复选框与其文本的点击事件进行不同的处理?(PyQt/PySide/Qt)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 QStandardItemModel,其中每个项目都是可检查的.一方面,当我单击项目的复选框时,我希望调用不同的插槽,另一方面,当我单击其文本时.我的最终目标是进行文本编辑和复选框状态的更改,分别进入 QUndoStack.

I have a QStandardItemModel in which each item is checkable. I want different slots to be called when I click on the item's checkbox, on one hand, versus when I click on its text, on the other. My ultimate goal is to have text edits, and changes in checkbox state, go onto the QUndoStack separately.

在我重新实现 clicked 时,我想区别对待复选框点击和文本点击.到目前为止,我在文档中找不到区分这些事件的方法 用于 QCheckBoxQStandardItem. 虽然 QCheckBox 有一个我可以使用的 toggled 信号,但我没有确定如何专门监听文本区域的点击.

In my reimplementation of clicked I want to treat checkbox clicks and text clicks differently. So far, I have found no way to differentiate these events in the documentation for QCheckBox or QStandardItem. While QCheckBox has a toggled signal that I can use, I am not sure how to specifically listen for clicks on the text area.

我试图避免手动设置坐标,然后在项目视图的不同区域监听点击.

I am trying to avoid having to set up coordinates manually and then listen for clicks in the different regions of the view of the item.

这似乎不像调用 itemChanged 那样简单,因为这只会为您提供项目的 new 状态,而不是之前的状态.基于之前的问题,我相信您需要某种方式将之前的状态打包到撤消堆栈中,以便您知道要恢复到什么状态.这就是我对 clicked 的目标,但可能有更好的方法.

It doesn't seem this will be as simple as calling something like itemChanged, because that only gives you the new state of the item, not the previous state. Based on previous questions, I believe you need some way to pack the previous state into the undo stack, so you know what to revert to. That's what I am aiming to do with clicked, but there might be a better way.

这个问题在本系列前两个问题的基础上,我试图弄清楚如何在模型中撤消:

This question piggybacks on the previous two in this series, in which I'm trying to figure out how to undo things in models:

推荐答案

基于 ekhumoro 的建议和代码块,我构建了一个 QStandardItemModel 的树视图,当项目更改时会发出自定义信号.代码通过 setData 中的角色来区分文本与复选框的变化(对于文本,使用 Qt.EditRole 和复选框状态更改使用 Qt.CheckStateRole) :

Based on ekhumoro's suggestion and code nuggets, I built a treeview of a QStandardItemModel that emits a custom signal when an item changes. The code differentiates the text versus the checkbox changing via the role in setData (for text, use Qt.EditRole and for checkbox state changes use Qt.CheckStateRole) :

# -*- coding: utf-8 -*-

from PySide import QtGui, QtCore
import sys

class CommandTextEdit(QtGui.QUndoCommand):
    def __init__(self, tree, item, oldText, newText, description):
        QtGui.QUndoCommand.__init__(self, description)
        self.item = item
        self.tree = tree
        self.oldText = oldText
        self.newText = newText

    def redo(self):      
        self.item.model().itemDataChanged.disconnect(self.tree.itemDataChangedSlot) 
        self.item.setText(self.newText)
        self.item.model().itemDataChanged.connect(self.tree.itemDataChangedSlot) 

    def undo(self):
        self.item.model().itemDataChanged.disconnect(self.tree.itemDataChangedSlot) 
        self.item.setText(self.oldText)
        self.item.model().itemDataChanged.connect(self.tree.itemDataChangedSlot) 


class CommandCheckStateChange(QtGui.QUndoCommand):
    def __init__(self, tree, item, oldCheckState, newCheckState, description):
        QtGui.QUndoCommand.__init__(self, description)
        self.item = item
        self.tree = tree
        self.oldCheckState = QtCore.Qt.Unchecked if oldCheckState == 0 else QtCore.Qt.Checked
        self.newCheckState = QtCore.Qt.Checked if oldCheckState == 0 else QtCore.Qt.Unchecked

    def redo(self): #disoconnect to avoid recursive loop b/w signal-slot
        self.item.model().itemDataChanged.disconnect(self.tree.itemDataChangedSlot) 
        self.item.setCheckState(self.newCheckState)
        self.item.model().itemDataChanged.connect(self.tree.itemDataChangedSlot) 

    def undo(self):
        self.item.model().itemDataChanged.disconnect(self.tree.itemDataChangedSlot)
        self.item.setCheckState(self.oldCheckState)
        self.item.model().itemDataChanged.connect(self.tree.itemDataChangedSlot) 


class StandardItemModel(QtGui.QStandardItemModel):
    itemDataChanged = QtCore.Signal(object, object, object, object)


class StandardItem(QtGui.QStandardItem):
    def setData(self, newValue, role=QtCore.Qt.UserRole + 1):
        if role == QtCore.Qt.EditRole:
            oldValue = self.data(role)
            QtGui.QStandardItem.setData(self, newValue, role)
            model = self.model()
            #only emit signal if newvalue is different from old
            if model is not None and oldValue != newValue:
                model.itemDataChanged.emit(self, oldValue, newValue, role)
            return True
        if role == QtCore.Qt.CheckStateRole:
            oldValue = self.data(role)
            QtGui.QStandardItem.setData(self, newValue, role)
            model = self.model()
            if model is not None and oldValue != newValue:
                model.itemDataChanged.emit(self, oldValue, newValue, role)
            return True
        QtGui.QStandardItem.setData(self, newValue, role)


class UndoableTree(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent = None)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.view = QtGui.QTreeView()
        self.model = self.createModel()
        self.view.setModel(self.model)
        self.view.expandAll()
        self.undoStack = QtGui.QUndoStack(self)
        undoView = QtGui.QUndoView(self.undoStack)
        buttonLayout = self.buttonSetup()
        mainLayout = QtGui.QHBoxLayout(self)
        mainLayout.addWidget(undoView)
        mainLayout.addWidget(self.view)
        mainLayout.addLayout(buttonLayout)
        self.setLayout(mainLayout)
        self.makeConnections()

    def makeConnections(self):
        self.model.itemDataChanged.connect(self.itemDataChangedSlot)
        self.quitButton.clicked.connect(self.close)
        self.undoButton.clicked.connect(self.undoStack.undo)
        self.redoButton.clicked.connect(self.undoStack.redo)

    def itemDataChangedSlot(self, item, oldValue, newValue, role):
        if role == QtCore.Qt.EditRole:
            command = CommandTextEdit(self, item, oldValue, newValue,
                "Text changed from '{0}' to '{1}'".format(oldValue, newValue))
            self.undoStack.push(command)
            return True
        if role == QtCore.Qt.CheckStateRole:
            command = CommandCheckStateChange(self, item, oldValue, newValue, 
                "CheckState changed from '{0}' to '{1}'".format(oldValue, newValue))
            self.undoStack.push(command)
            return True

    def buttonSetup(self):
        self.undoButton = QtGui.QPushButton("Undo")
        self.redoButton = QtGui.QPushButton("Redo")
        self.quitButton = QtGui.QPushButton("Quit")
        buttonLayout = QtGui.QVBoxLayout()
        buttonLayout.addStretch()
        buttonLayout.addWidget(self.undoButton)
        buttonLayout.addWidget(self.redoButton)
        buttonLayout.addStretch()
        buttonLayout.addWidget(self.quitButton)
        return buttonLayout

    def createModel(self):
        model = StandardItemModel()
        model.setHorizontalHeaderLabels(['Titles', 'Summaries'])
        rootItem = model.invisibleRootItem()
        item0 = [StandardItem('Title0'), StandardItem('Summary0')]
        item00 = [StandardItem('Title00'), StandardItem('Summary00')]
        item01 = [StandardItem('Title01'), StandardItem('Summary01')]
        item0[0].setCheckable(True)
        item00[0].setCheckable(True)
        item01[0].setCheckable(True)
        rootItem.appendRow(item0)
        item0[0].appendRow(item00)
        item0[0].appendRow(item01)
        return model


def main():
    app = QtGui.QApplication(sys.argv)
    newTree = UndoableTree()
    newTree.show()
    sys.exit(app.exec_())    

if __name__ == "__main__":
    main()

这篇关于如何对项目的复选框与其文本的点击事件进行不同的处理?(PyQt/PySide/Qt)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆