使用PyQt创建带滑块的模型/视图界面

时间:2013-10-09 14:56:02

标签: python model-view-controller pyqt

我有一个由多个滑块组成的GUI,而不是在底层数据发生变化时手动更新滑块,我想将数据存储在QAbstractListModel的子类中,并自动更新滑块位置。我的子类看起来像这样:

from PyQt4 import QtCore

class myDataModel(QtCore.QAbstractListModel):
    def __init__(self, initData, parent=None):
        super(myDataModel, self).__init__(parent)
        self.__data = initData

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

        if index.row() > len(self.__data):
            return None

        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            return self.__data[index.row()]

        return None

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.__data)

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if not index.isValid() or role != QtCore.Qt.EditRole:
            return False

        self.__data[index.row()] = value
        self.dataChanged.emit(index, index)
        return True

如何将此模型连接到GUI中的滑块,以便在模型中的数据更改时,滑块更改,反之亦然?

编辑:这是我一直在研究的基本界面的模型: enter image description here

编辑:我仍然无法让这个工作。这是我的模型类:

class dataModel(QtCore.QAbstractListModel):

    def __init__(self, initData, parent=None):
        super(dataModel, self).__init__(parent)
        self.__data = initData

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

        if index.row() > len(self.__data):
            return None

        if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
            return self.__data[index.row()]

        return None

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.__data)

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if not index.isValid() or role != QtCore.Qt.EditRole:
            return False

        self.__data[index.row()] = value
        self.dataChanged.emit(index, index)
        return True

这是Delegate类:

class sliderDelegate(QtGui.QItemDelegate):
    '''
    classdocs
    '''
    def __init__(self, parent=None):
        '''
        Constructor
        '''
        super(sliderDelegate, self).__init__(parent)

    def setEditorData(self, editor, index):
        editor.setValue(index.model().data(index, QtCore.Qt.EditRole))

    def setModelData(self, editor, model, index):
        model.setData(index, editor.value(), QtCore.Qt.EditRole)

这是设置代码:

self._model = dataModel([0 for i in xrange(20)])
self._parameterMapper = QtGui.QDataWidgetMapper(mainWindowInstance)
self._parameterMapper.setModel(self._model)
self._parameterMapper.setItemDelegate(sliderDelegate(mainWindowInstance))
self._parameterMapper.addMapping(self._mainWindowInstance.ui.mySlider, 0)
self._parameterMapper.toFirst()

不幸的是,调用toFirst()时出现以下错误:

editor.setValue(index.model().data(index, QtCore.Qt.EditRole))
AttributeError: 'NoneType' object has no attribute 'data'

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:6)

所以我没有使用QDataWidgetMapper。它确实看起来很有趣,但是当您希望将多个窗口小部件更新到模型中的特定行(并且能够轻松地在行之间切换)时,看起来更有用,而不是模型的每一行对应于窗口小部件的值(我认为你就是这样)。

所以这是我相当粗略的实现。希望您能够将其扩展到您的应用程序(可能需要添加更多错误检查,并且可能将多个滑块链接到单个模型行,然后可能扩展到其他类型的小部件)

拖动滑块时,模型将更新为滑块新值。我还添加了一个文本框,您可以在其中键入数字,然后单击按钮,将模型设置为特定值。您会注意到滑块将更新为此值!

import sys

from PyQt4 import QtGui
from PyQt4 import QtCore

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        main_layout = QtGui.QVBoxLayout()

        # Create the model
        self.model = MyModel()

        # Create a slider and link it to the model
        self.slider1 = QtGui.QSlider()
        self.model.add_slider(self.slider1)        
        main_layout.addWidget(self.slider1)

        # Add a lineEdit and button to force update the model
        # Note that the LineEdit is not linked to the model, so won't update with the slider
        self.edit = QtGui.QLineEdit()
        button = QtGui.QPushButton('update model')
        button.clicked.connect(self.on_clicked)        
        main_layout.addWidget(self.edit)
        main_layout.addWidget(button)

        self.setLayout(main_layout)

    def on_clicked(self):
        self.model.update_model(int(self.edit.text()),self.slider1)

class MyModel(QtGui.QStandardItemModel):
    def __init__(self,*args,**kwargs):
        super(MyModel,self).__init__(*args,**kwargs)
        self._slider_list = {}
        self.itemChanged.connect(self.on_item_changed)

    def add_slider(self,slider):
        if slider in self._slider_list:
            raise Exception('You cannot link a slider to the model twice')

        item = QtGui.QStandardItem(str(slider.value()))
        self._slider_list[slider] = item
        self.appendRow(item)
        slider.valueChanged.connect(lambda value: self.update_model(value,slider))

    def update_model(self,value,slider):
        if str(value) != self._slider_list[slider].text():
            self._slider_list[slider].setText(str(value))
            print 'update_model: %d'%value

    def on_item_changed(self,item):
        slider = self._slider_list.keys()[self._slider_list.values().index(item)]
        if slider.value() != int(item.text()):
            slider.setValue(int(item.text()))
            print 'on_item_changed: %s'%item.text()

app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()

sys.exit(app.exec_())

希望有所帮助!