2013-10-09 22 views
5

Tôi có GUI bao gồm một số thanh trượt và thay vì cập nhật thanh trượt theo cách thủ công khi dữ liệu cơ bản thay đổi, tôi muốn lưu trữ dữ liệu trong phân lớp QAbstractListModel và có các vị trí thanh trượt cập nhật tự động. lớp con của tôi trông như thế này:Tạo giao diện model/view với thanh trượt bằng 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 

Làm thế nào tôi có thể kết nối mô hình này để các thanh trượt trong GUI của tôi vì vậy mà khi dữ liệu trong mô hình được thay đổi, các thanh trượt thay đổi, và ngược lại?

Edit: Đây là một mockup của giao diện cơ bản tôi đã làm việc trên: enter image description here

Edit: Tôi vẫn chưa thể có được điều này để làm việc. Đây là mô hình lớp học của tôi:

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 

đây là lớp đại biểu:

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) 

Và đây là đoạn code thiết lập:

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

Đáng tiếc là tôi nhận được lỗi sau khi toFirst() được gọi là:

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

Bất kỳ trợ giúp nào sẽ được đánh giá cao.

+0

Bạn có thể cung cấp mô hình về cách bạn muốn giao diện này trông như thế nào (ví dụ: trong QtDesigner) không? Ngoài ra, có một số điểm dữ liệu biến (và do đó trượt) hoặc là nó cố định? –

+0

@LegoStormtroopr Tôi đã thêm mockup mà tôi đã làm việc trên QtDesigner. Số lượng thanh trượt sẽ được cố định trong ứng dụng, chỉ có một LOT của chúng. Thành thật mà nói, tôi muốn có thể lưu trữ tất cả các thiết lập widget trong ứng dụng trong một mô hình, và có comboboxes, vv cập nhật khi mô hình được thay đổi, nhưng nhận được tất cả các thanh trượt làm việc sẽ là một khởi đầu tốt và hy vọng hãy để tôi ngoại suy phương pháp cho các tiện ích khác. – Bitrex

+0

Thật không may, QAbstractListModel được sử dụng tốt nhất làm dữ liệu danh sách được trình bày dưới dạng danh sách. Nó có thể dễ dàng hơn để tạo một vài widget tùy chỉnh (ví dụ, nhóm 6 thanh trượt bên trái và một nhóm khác cho 8 bên phải với combo), sau đó quảng bá chúng trong QtDesigner và viết mã tùy chỉnh cho các lớp được quảng bá? –

Trả lời

5

Vì vậy, tôi chưa sử dụng QDataWidgetMapper. Nó trông thú vị, nhưng có vẻ hữu ích hơn khi bạn muốn có nhiều widget cập nhật cho một hàng cụ thể trong một mô hình (và có thể chuyển đổi giữa các hàng một cách dễ dàng), thay vì mỗi hàng của một mô hình tương ứng với giá trị của một widget (mà tôi nghĩ là những gì bạn đang có sau).

Vì vậy, đây là triển khai khá thô ráp của tôi. Hy vọng rằng bạn sẽ có thể mở rộng ứng dụng của mình (có thể cần kiểm tra lỗi nhiều hơn một chút và có thể liên kết nhiều thanh trượt với một hàng mẫu duy nhất và sau đó có thể mở rộng sang các loại tiện ích khác)

Khi bạn kéo thanh trượt, mô hình sẽ được cập nhật thành giá trị mới của thanh trượt. Tôi cũng đã thêm một hộp văn bản nơi bạn có thể nhập vào một số và nhấp vào nút, điều này sẽ đặt mô hình thành một giá trị cụ thể. Bạn sẽ thấy thanh trượt sẽ cập nhật giá trị này!

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

Hy vọng điều đó sẽ hữu ích!

+0

Rất đẹp. Cảm ơn! – Bitrex

+0

Ví dụ hay, khi tôi đang học phát triển MVC với Qt. Nhưng có lẽ kết nối nên được thực hiện bên trong lớp 'MainWindow', và bên ngoài' MyModel'? Ví dụ, 'self.slider1.valueChanged.connect (...)'? Cảm ơn trước vì phản hồi của bạn. –

+0

@ Joël có lẽ, tôi đoán nó phụ thuộc vào việc bạn mong đợi mô hình của bạn sẽ được sử dụng với một Qt View hay không. Nếu bạn đang có, sau đó giữ mã mô hình sạch sẽ có lẽ là tốt. Nhưng nếu bạn chỉ sử dụng một mô hình để lưu trữ và không triển khai chế độ xem Qt (như tôi đã làm) thì có thể nó không quan trọng! Nếu bạn muốn giữ mã mô hình sạch sẽ vì nó phù hợp hơn với thiết kế của bạn, sau đó đi cho nó! –

Các vấn đề liên quan