2013-10-09 5 views
5

Mam GUI, który składa się z wielu suwaków i zamiast aktualizować suwaki ręcznie, gdy zmieniają się dane podstawowe, chciałbym przechowywać dane w podklasie QAbstractListModel i automatycznie zmieniają pozycje suwaków. Moja podklasa wygląda następująco:Tworzenie interfejsu modelu/widoku z suwakami za pomocą 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 

Jak mogę podłączyć ten model do suwaków w moim GUI tak, że kiedy dane w modelu zostanie zmieniony, suwaki zmieniają, i vice versa?

Edit: Tutaj jest makieta podstawowego interfejsu pracuję na: enter image description here

Edycja: Wciąż nie udało się uzyskać to do pracy. Oto moja klasa model:

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 

Oto klasa Delegat:

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) 

A oto kod instalacyjny:

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

Niestety otrzymuję następujący błąd podczas toFirst() nazywa się:

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

Każda pomoc będzie doceniona.

+0

Czy możesz podać makietę wyglądu interfejsu (np. W QtDesigner)? Czy istnieje również zmienna liczba punktów danych (i tym samym suwaków) lub czy jest ona ustalona? –

+0

@ LegoStormtroopr Dodałem makietę, nad którą pracowałem w QtDesigner. Liczba suwaków zostanie ustalona w aplikacji, jest ich tylko DUŻO. Szczerze mówiąc, chciałbym móc przechowywać wszystkie ustawienia widżetów w aplikacji w modelu i aktualizować listy comboboxów podczas zmiany modelu, ale uruchomienie wszystkich suwaków byłoby dobrym początkiem i mam nadzieję, że pozwól mi ekstrapolować metodę do innych widżetów. – Bitrex

+0

Niestety, QAbstractListModel są najlepiej używane jako dane listy, które są przedstawione w formie listy. Może być łatwiej zrobić kilka niestandardowych widżetów (np. Zgrupowanie 6 suwaków po lewej i innej grupy po 8 po prawej z kombinacjami), a następnie promować je w QtDesigner i napisać niestandardowy kod dla promowanych klas? –

Odpowiedz

5

Więc nie użyłem QDataWidgetMapper. Wygląda interesująco, ale wygląda na bardziej użyteczny, gdy chcesz zaktualizować wiele widżetów do konkretnego wiersza w modelu (i móc łatwo przełączać się między wierszami), zamiast do każdego wiersza modelu odpowiadającego wartości widgetu (o którym myślę, że jesteś po).

To jest moja raczej surowa implementacja. Mamy nadzieję, że będziesz mógł go rozszerzyć do swojej aplikacji (może być potrzebna dodatkowa weryfikacja błędów i być może możliwość połączenia wielu suwaków z pojedynczym wierszem modelu, a następnie rozszerzenie na inne typy widżetów)

Po przeciągnięciu suwaka model jest aktualizowany do nowych wartości suwaków. Dodałem również pole tekstowe, w którym można wpisać liczbę, i kliknij przycisk, który ustawi model na określoną wartość. Zauważysz, że suwak zaktualizuje się do tej wartości!

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

Nadzieję, że pomaga!

+0

Bardzo ładne. Dzięki! – Bitrex

+0

Dobry przykład, ponieważ uczę się rozwoju MVC z Qt. Ale może połączenie powinno być wykonane wewnątrz klasy 'MainWindow' i poza' MyModel'? Na przykład 'self.slider1.valueChanged.connect (...)'? Z góry dziękuję za opinię. –

+0

@ Joël może, myślę, że to zależy od tego, czy spodziewasz się, że twój model będzie używany z widokiem Qt, czy nie. Jeśli tak, to zachowanie kodu modelu jest prawdopodobnie dobre. Ale jeśli używasz tylko modelu do przechowywania i nie implementujesz widoku Qt (tak jak zrobiłem) to prawdopodobnie nie ma to znaczenia! Jeśli wolisz zachować czystość kodu modelu, ponieważ pasuje on do twojego projektu, to idź do niego! –