2013-10-09 3 views
5

У меня есть графический интерфейс, состоящий из нескольких слайдеров, и вместо того, чтобы обновлять ползунки вручную при изменении базовых данных, я хотел бы сохранить данные в подклассе QAbstractListModel и обновить ползунки. Мой подкласс выглядит следующим образом:Создание интерфейса модели/представления с помощью ползунков с использованием PyQt

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, так что, когда данные в модели изменяются, ползунки изменятся, и наоборот?

Edit: Вот макет основного интерфейса я работал на: enter image description here

Edit: я до сих пор не удалось получить эту работу. Вот моя модель класса:

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 

Вот класс делегата:

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' 

Любая помощь будет оценена по достоинству.

+0

Можете ли вы представить макет того, как вы хотите, чтобы этот интерфейс выглядел (например, в QtDesigner)? Кроме того, существует ли переменное количество точек данных (и, следовательно, ползунков) или оно фиксировано? –

+0

@LegoStormtroopr Я добавил макет, над которым я работал в QtDesigner. Количество слайдеров будет зафиксировано в приложении, их всего лишь много. Честно говоря, я хотел бы иметь возможность хранить все настройки виджета в приложении в модели, а также иметь обновления comboboxes и т. Д., Когда модель также будет изменена, но получение всех рабочих слайдеров будет хорошим началом и, надеюсь, позвольте мне экстраполировать метод на другие виджеты. – Bitrex

+0

К сожалению, QAbstractListModel лучше всего использовать в качестве данных списка, которые представлены в виде списка. Может быть проще сделать несколько пользовательских виджетов (например, группировать 6 ползунков слева и другую группу для 8 справа с помощью комбо), а затем продвигать их в QtDesigner и писать собственный код для продвигаемых классов? –

ответ

5

Так что я не использовал 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_()) 

Надеюсь, что это поможет!

+0

Очень приятно. Благодаря! – Bitrex

+0

Хороший пример, поскольку я изучаю разработку MVC с помощью Qt. Но, возможно, соединение должно выполняться внутри класса «MainWindow» и вне «MyModel»? Например, 'self.slider1.valueChanged.connect (...)'? Заранее благодарим за отзыв. –

+0

@ Joël Возможно, я думаю, это зависит от того, ожидаете ли вы, что ваша модель будет использоваться с Qt View или нет. Если да, то сохранить код модели чистым, вероятно, хорошо. Но если вы просто используете модель для хранения и не реализуете Qt-представление (например, я сделал), то это, вероятно, не имеет значения! Если вы предпочитаете, чтобы код модели был чистым, потому что он лучше подходит для вашего дизайна, а затем идите! –

Смежные вопросы