【问题标题】:How to extend Qt Widgets that do not have model support by default如何扩展默认不支持模型的 Qt Widget
【发布时间】:2015-03-25 16:13:27
【问题描述】:

CustomMenu 类继承自 QMenu。它的自定义setData() 方法接受一个参数并将其设置为一个类变量model。我这样做是因为QToolButtonQToolMenuQAction 不支持模型/视图框架。据我所知,除了所有QList-QTable-QTree Views and Trees,只有QComboBox 支持model。 那么问题来了:是否可以扩展其他non-model widgets 的功能,以便它们也可以用作driven-by-model 小部件?

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os

class CustomMenu(QMenu):
    def __init__(self):
        super(CustomMenu, self).__init__()
        self.model=None

    def setModel(self, model):
        self.model=model
        self.pupulate()
    def pupulate(self):
        for item in self.model.items:
            self.addAction(item)

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']

    def flags(self, index):
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
    def rowCount(self, QModelIndex):
        return len(self.items)
    def columnCount(self, QModelIndex):
        return 1

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        if role == Qt.DisplayRole:
            return QVariant(self.items[index.row()])
        return QVariant()
    def setData(self, index, value, role=Qt.EditRole):
        if index.isValid():            
            if role == Qt.EditRole:                
                self.items[index.row()]=value  
                return True
        return False
class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        layout=QVBoxLayout(self)
        self.setLayout(layout)

        tablemodel=Model(self)

        tableView=QTableView() 
        tableView.horizontalHeader().setStretchLastSection(True)
        tableView.setModel(tablemodel)         
        layout.addWidget(tableView)      

        combo=QComboBox()
        combo.setModel(tablemodel)
        layout.addWidget(combo)

        toolButton=QToolButton(self)
        toolButton.setText('Tool Button')
        toolButton.setPopupMode(QToolButton.InstantPopup)
        toolButton.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))

        menu=CustomMenu()
        menu.setModel(tablemodel)
        toolButton.setMenu(menu)
        layout.addWidget(toolButton)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

稍后编辑:尝试实现 QDataWidgetMapper():

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os

class CustomMenuButton(QWidget):
    def __init__(self, parent):
        super(CustomMenuButton, self).__init__(parent)
        self.dataMapper=QDataWidgetMapper()
        layout=QVBoxLayout()
        self.setLayout(layout)

        toolButton=QToolButton(self)
        toolButton.setText('Tool Button')
        toolButton.setPopupMode(QToolButton.InstantPopup)

        self.menu=QMenu(toolButton)
        toolButton.setMenu(self.menu)

    def setModel(self, model):
        self.dataMapper.setModel(model)

        for row in range(model.rowCount()):
            action=self.menu.addAction('Item')

            self.dataMapper.addMapping(action, row)

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']

    def flags(self, index):
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
    def rowCount(self, parent=QModelIndex()):
        return len(self.items)
    def columnCount(self, parent=QModelIndex()):
        return 1

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        if role == Qt.DisplayRole:
            return QVariant(self.items[index.row()])
        return QVariant()
    def setData(self, index, value, role=Qt.EditRole):
        if index.isValid():            
            if role == Qt.EditRole:                
                self.items[index.row()]=value  
                self.dataChanged.emit(index, index)
                return True
        return False

class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        layout=QVBoxLayout(self)
        self.setLayout(layout)

        tablemodel=Model(self)

        tableView=QTableView() 
        tableView.horizontalHeader().setStretchLastSection(True)
        tableView.setModel(tablemodel)         
        layout.addWidget(tableView)      

        combo=QComboBox()
        combo.setModel(tablemodel)
        layout.addWidget(combo)

        menuButton=CustomMenuButton(self)
        layout.addWidget(menuButton)

        menuButton.setModel(tablemodel)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

编辑 2:使用 dataChanged = pyqtSignal(QModelIndex,QModelIndex) 定义了一个类 CustomAction(QAction)...

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os

class CustomAction(QAction):
    dataChanged = pyqtSignal(QModelIndex,QModelIndex)
    def __init__(self, name, parent):
        super(CustomAction, self).__init__(name, parent)

class CustomMenuButton(QWidget):
    def __init__(self, parent):
        super(CustomMenuButton, self).__init__(parent)
        self.dataMapper=QDataWidgetMapper()
        layout=QVBoxLayout()
        self.setLayout(layout)

        toolButton=QToolButton(self)
        toolButton.setText('Tool Button')
        toolButton.setPopupMode(QToolButton.InstantPopup)

        self.menu=QMenu(toolButton)
        toolButton.setMenu(self.menu)

    def setModel(self, model):
        self.dataMapper.setModel(model)
        for row in range(model.rowCount()):
            index=model.index(row,0)
            itemName=model.data(index, Qt.DisplayRole).toPyObject()

            actn=CustomAction(itemName, self.menu)
            self.menu.addAction(actn)

            self.dataMapper.addMapping(actn, row)

class Model(QAbstractTableModel):
    def __init__(self, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.items = ['Row0_Column0','Row1_Column0','Row2_Column0']

    def flags(self, index):
        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
    def rowCount(self, parent=QModelIndex()):
        return len(self.items)
    def columnCount(self, parent=QModelIndex()):
        return 1

    def data(self, index, role):
        if not index.isValid(): return QVariant()
        if role == Qt.DisplayRole:
            return QVariant(self.items[index.row()])
        return QVariant()
    def setData(self, index, value, role=Qt.EditRole):
        if index.isValid():            
            if role == Qt.EditRole:                
                self.items[index.row()]=value  
                self.dataChanged.emit(index, index)
                return True
        return False

class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)
        layout=QVBoxLayout(self)
        self.setLayout(layout)

        tablemodel=Model(self)

        tableView=QTableView() 
        tableView.horizontalHeader().setStretchLastSection(True)
        tableView.setModel(tablemodel)         
        layout.addWidget(tableView)      

        combo=QComboBox()
        combo.setModel(tablemodel)
        layout.addWidget(combo)

        menuButton=CustomMenuButton(self)
        layout.addWidget(menuButton)

        menuButton.setModel(tablemodel)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python qt model pyqt qtableview


    【解决方案1】:

    这就是 QDataWidgetMapper 类的设计目的。以下博客有一个很棒的教程,详细介绍了如何实现它:http://www.yasinuludag.com/blog/?p=98

    编辑:我应该补充一点,QDataWidgetMapperQWidget 及其子类一起使用,所以QAction 不适用。对于不是QWidget 或其子类的类,您可能必须实现自己的自定义模型-视图绑定。

    【讨论】:

    • 这次我刚刚发布了相同代码的编辑版本,试图实现QDataWidgetMapper。获取QDataWidgetMapper.addMapping(QWidget, int, QByteArray): argument 1 has unexpected type 'QAction' Object::connect: No such signal QObject::dataChanged(QModelIndex,QModelIndex)
    • 正如我在帖子的编辑中提到的,QDataWidgetMapper 仅适用于 QWidgetQWidget 子类,例如 QLineEditQComboBox 等。如果你想使用其他类如QAction,不能使用QDataWidgetMapper;您需要实现自己的自定义模型视图绑定。我可以看到您已向您的 CustomAction 类添加了一个自定义 dataChanged 信号,但这永远不会发出,因为这些操作不属于您的表模型数据集 (items);另外,我不知道如何在操作中更改数据,因为它们是不可编辑的QAction 对象。
    猜你喜欢
    • 2023-03-08
    • 1970-01-01
    • 2020-10-16
    • 2020-09-19
    • 2017-08-15
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    • 2019-05-29
    相关资源
    最近更新 更多