Seite 1 von 1
Model View Ansatz, nur wie?
Verfasst: Donnerstag 1. Juli 2010, 19:28
von anogayales
Hi Community,
ich arbeite gerade an einem kleinen Programm, bei dem man in einer Tabelle eine Zeile auswählen kann und man zu der ausgewählten Zeile in einem anderen Widget A mehr Informationen zu dieser einen Zeile bekommt, aber auch die Informationen, die in der Tabelle bereits vorhanden sind. (langeer Satz

)
Das Problem bei dem Ganzen ist, dass die Tabelle editierbar ist. Sprich, wenn ich eine Checkbox anwähle dann soll sich das ganze auch in der andere View ändern. Auch umgekehrt soll es möglich: Wenn ich was an Widget A ändern, so soll es die Tabelle mit editiern.
Das ganze habe ich schon in einer Richtung geschafft: Tabelle -> Widget A, indem ich das dataChanged Signal abgreife und darauf hin reagiere. Die andere Richtung ist zwar auch möglich, aber mit sehr viel Aufwand verbunden.
Gibt es für solche geschichten kein Framework?

Ja das gibt es, aber ich komm damit irgendwie nicht zurecht.
Eine QAbstractItemView ist ja nicht das was ich suche. Ich will ja nur ein Item anzeigen! Stellt das ein Problem da? Bei einer Implementierung von QAbstractItemView sieht mir die ganze Sache auch nach viel Arbeit aus, obwohl das ganze ja nicht kompliziert ist.
Kann mir jemand Tipps geben, wie ich das Problem lösen kann?
Grüße,
anogayales
Re: Model View Ansatz, nur wie?
Verfasst: Freitag 2. Juli 2010, 00:54
von jerch
Schau mal, ob Dir ProxyModels weiterhelfen. Versteh leider nicht ganz Dein Problem...
Re: Model View Ansatz, nur wie?
Verfasst: Freitag 2. Juli 2010, 01:07
von anogayales
Hier ein Screenshot meines Programms:
In der Tabelle wählt man einen Eintrag aus und will dazu auf der rechten Seite mehr Informationen erhalten.
Jetzt kann man aber in jeweiliger Ansicht (Tabelle oder Widget Rechts) Daten verändern, die sich in der jeweiligen View updaten.
Wählt man die Checkbox "Seen it" aus, so soll diese auch in der Tabelle geändert werden.
Das ganze habe ich schon hinbekommen, benutzte aber nicht das Model/View Framework, da das definieren einer eigen AbstractItemView nicht das ist was ich brauch, da ich ja nicht viele Items anzeigen will, sondern immer nur ein Spezielles.
Grüße,
anogayales
Re: Model View Ansatz, nur wie?
Verfasst: Freitag 2. Juli 2010, 08:39
von ichisich
Das Model View konzept hat aber nichts damit zu tun ob Du ein oder mehrere Items zeigen willst. Es geht nur darum EINE Datenquelle zu haben und MEHRERE Ansichten.
Jede Ansicht schreibt und liest die Daten aus dem, in das Model und alle Views werden darüber in Kenntnis gesetzt das sie was neues Anzeigen sollen wenn was geändert wurde.
In deinem Fall müssen Teile deiner Daten, bsp. die Checkboxen oder das Textfeld in zwei Views gezeigt werden und alles von einem Model verwaltet werden.
Wenn Du schon mal gemodelt und geviewt hast sollte das kein Problem sein. Geschenkt bekommt man nichts, also ein bisschen Aufwand ist sowas immer.
Beispiel gibts ja hier auch im Forum mein ich...
Gruß
Re: Model View Ansatz, nur wie?
Verfasst: Montag 5. Juli 2010, 11:41
von jerch
@anogayales:
Die Frage Model/View hat erstmal nichts mit der Menge der darzustellenden Items zu tun (wie ischisch schon geschrieben hat). So zeigt ein Model mit nur einem Eintrag im View eben nur einen Eintrag an
Zu Deinem Problem - hier führen mehrere Wege nach Rom.
Der unter Geschichtspunkten der Model/View-Programmierung sauberste Weg ist, Dein TableView um einen SubView zu erweitern und die jeweils aktive Zeile der Tabelle zum Model des Editorviews (rechtes Widget) zu erklären. Alles weitere geht dann wie gehabt über Delegates und Änderungen sollten nach einem setModelData() zu den Views durchtröpfeln. Der Trick ist, den View nicht auf das ganze Model abbilden zu wollen, sondern nur mit einem Subset der Daten zu agieren (einer Zeile Deines TableViews). Im Prinzip ist Dein Editorwidget nichts anderes als eine Liste, deren Einträge sehr verschieden zu behandeln sind. Das spricht für die Erstellung eines eigenen Views (also von QAbstractItemView abgeleitet). Alternativ könnte man es auch als Mapping verstehen (Key-Value) und mit einem angepassten TableView arbeiten.
Was auch geht:
Die Tabelle nicht editierbar machen und die Delegates in das rechte Widget umbiegen. Hätte den Vorteil eines "zentralen" Editors während die Tabelle eben nur zum Einsehen der Daten ist (Ist eine Frage der Usability, bin mir nicht sicher, ob eine stark formatierte Tabelle mit vielen verschiedenen Typen vom Benutzer überhaupt als inline editierbar erkannt wird.)
Weitere Möglichkeit:
Die Widgets rechts ohne Model/View-Unterstützung befüllen. Die Model/View-Abstraktion bringt genügend Signale etc. mit, um dies on-the-fly zu bewerkstelligen. Ist wahrscheinlich der schnellste aber eben auch unsauberste Weg. Bei einem immer gleich gearteten Ein-Element-Zugriff muss man sich halt fragen, ob es den Model/View-Aufwand wert ist. Allerdings solltest Du die Daten aus dem Model holen und nicht direkt auf der Datenquelle arbeiten. So sehen auch die Views des Models die geänderten Daten.
Ich würde diesen Weg nur für einfache Problemstellungen empfehlen, da das sehr schnell unübersichtlich werden kann.
Re: Model View Ansatz, nur wie?
Verfasst: Montag 5. Juli 2010, 15:09
von anogayales
Vielen Dank für deinen Input!
Wie würdest du aber der subView sagen, welche Zeile er gerade aus dem Model anzeigen soll? Die Selektion wird ja über das Selectionmodel geregelt, müsste escdann ein Selectionmodel für 2 verschiedene Views geben?
Grüße,
anogayales
Re: Model View Ansatz, nur wie?
Verfasst: Samstag 10. Juli 2010, 16:41
von jerch
Das SelektionModel sagt Dir, welche Items gerade selektiert sind. Daraus kannst Du das Model für den SubView ableiten.
Vereinfachtes Bsp. mit QSqlTableModel, QTableView und QListView:
Code: Alles auswählen
from PyQt4 import QtGui, QtCore, QtSql
from PyQt4.QtCore import Qt
class MyListModel(QtCore.QAbstractListModel):
def __init__(self, masterModel, indexes, parent=None):
QtCore.QAbstractListModel.__init__(self, parent)
self._masterModel = masterModel
self._masterModel.dataChanged.connect(self.triggerUpdate)
self._model = indexes
# update associated views when master model has new data
def triggerUpdate(self, topLeft, bottomRight):
self.dataChanged.emit(self.index(0, topLeft.column()),
self.index(0, bottomRight.column()))
def rowCount(self, index):
return len(self._model)
# implement display and edit role for now
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
return self._masterModel.data(self._model[index.row()])
# set appropriate flags of the items
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
# set data of the master model directly
def setData(self, index, value, role):
return self._masterModel.setData(self._model[index.row()], value, role)
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.query = QtSql.QSqlQuery('select * from person')
self.model = QtSql.QSqlTableModel()
self.model.setQuery(self.query)
self.table = QtGui.QTableView(self)
self.table.setSelectionBehavior(1)
self.table.setModel(self.model)
self.selectionModel = QtGui.QItemSelectionModel(self.model)
self.table.setSelectionModel(self.selectionModel)
self.gridLayout = QtGui.QGridLayout(self)
self.gridLayout.addWidget(self.table, 0,0)
self.listView = QtGui.QListView(self)
self.gridLayout.addWidget(self.listView, 0,1)
# change listview model on selection change
self.selectionModel.selectionChanged.connect(
lambda index : self.listView.setModel(
MyListModel(self.model, index.indexes())))
def prepareDB():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(":memory:")
db.open()
query = QtSql.QSqlQuery('CREATE TABLE person (forename char(50),surname char(50))')
query.prepare("INSERT INTO person (forename, surname) "
"VALUES (:forename, :surname)")
query.bindValue(":forename", "Bart")
query.bindValue(":surname", "Simpson")
query.exec_()
query.bindValue(":forename", "Hans")
query.bindValue(":surname", "Mustermann")
query.exec_()
return db
if __name__ == '__main__':
app= QtGui.QApplication([])
db = prepareDB()
win = MyWidget()
win.show()
app.exec_()
db.close()
del win, db
Für Deine verschiedenen Itemtypen mußt Du natürlich noch Hand an die Delegates legen. Der Modelunterbau sollte aber analog zu Diesem umsetzbar sein.
Nachtrag :
Mit QDataWidgetMapper kannst Du übrigens relativ einfach einen "Modeleditor" bauen und musst nicht den Weg über einen zweiten View gehen. Ist bei der Verschiedenheit Deiner Items vllt. einfacher umzusetzen.
Viel Spass beim Modeln
