Imagino que todos conheçam o padrão de projeto MVC, o famoso Model/View/Controller. A idéia é separar a visualização (interface com o usuário) do controle (os "comandos" que o usuário executa) e do modelo (os dados e a lógica de negócio).
Uma pequena variação disso é o model/view, quando não é necessário controle, ou o controle está em um outro lugar. O MVC completo é um padrão arquitetural, sendo que o "MV" é um padrão mais localizado. Acredito que isso fique mais claro ao final do post :-).
A partir do Qt 4, alguns widgets passaram a suportar o padrão MV. Os antigos QListWidget, QTableWidget, etc. foram substituídos pelo QListView, QTableView, etc. As classes de modelo foram adicionadas para completar o padrão, e para conectar ambos basta um simples someView.setModel(someModel).
Um exemplo usando algumas classes padrão:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
app = QApplication([])
view = QListView()
model = QStringListModel("item1 item2 item3 item4 item5".split())
view.setModel(model)
view.show()
sys.exit(app.exec_())
Qualquer mudança no model é refletida automaticamente na view, e é isso que torna o padrão interessante. É possível ter várias views para um model, e também existe o conceito dos delegates, que servem para dar flexibilidade à entrada de dados. Para não me alongar muito, não vou entrar nos detalhes, mas isso tudo pode ser visto com mais profundidade em Qt Model/View Programming.
model.setData(model.index(0), QVariant("aaaa"))
Agora um exemplo de classe que implementa um modelo abstrato. Supondo que eu queira um modelo de uma série de resultados numéricos, como proceder?
O primeiro passo é criar uma classe que implemente uma QAbstractTableModel:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class ResultTableModel(QAbstractTableModel):
def __init__(self, parent = None):
super(QAbstractTableModel, self).__init__(parent)
self.results = []
Como pode ser visto aqui, o meu modelo deve sobrescrever alguns métodos para que ele possa utilizado. Caso o modelo necessite apenas ser lido (read-only), os métodos flags(), data(), headerData() e rowCount() devem ser providos. Este quatro métodos são suficientes para um QAbstractListModel, mas como estou implementando uma tabela, columnCount() também deve ser provido.Uma implementação básica seria:
def rowCount(self, parent = QModelIndex()):
return len(self.results)
def columnCount(self, parent = QModelIndex()):
return 1 # por enquanto não quero mais de uma coluna
def flags(self, index):
# quero todos os itens habilitados e editáveis
return Qt.ItemIsEnabled | Qt.ItemIsEditable
# Recupera um dado de um índice (QModelIndex)
def data(self, index, role):
# índice não é válido, então retorno um dado vazio
if not index.isValid():
return QVariant()
# se a linha está fora dos bounds da minha lista, resultado vazio
if index.row() >= len(self.results):
return QVariant()
# se o role é o DisplayRole (ou seja, o valor que deve ser renderizado)
# retorno o valor que esta na linha lista
if role == Qt.DisplayRole:
return QVariant(self.results[index.row()])
else:
# podemos retornar valores específicos para outros roles,
# por exemplo ToolTipRole. Pela simplicidade, ignoramos isso.
return QVariant()
# Recupera os cabeçalhos da tabela
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return QVariant()
if orientation == Qt.Horizontal:
# Results vai aparecer no topo da linha
return QVariant("Results")
else:
# em cada linha, mostrar o numero dela no início
return QVariant("%s it." % (section + 1,))
Pronto, agora já podemos exibir o modelo em uma QTableView :-).
No próximo post irei mostrar como fazer para um model ser editável e redimensionável.
Outros materiais:
MVC
Model/View Programming
Model Subclassing
QTableView
QAbstractTableModel
Qt Documentation


