Probleme beim Erstellen von editierbaren Models

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Hier mein Code: http://paste.pocoo.org/show/119209/

Entscheidend ist die Methode setData, bei der ich mir nicht sicher bin, ob sie von mir richtig implementiert wurde. Das Problem ist, dass ich in den Zellen Werte einfügen kann, aber sie verschwinden sofort nach dem Drücken der Eingabetaste. Habe ich das mit dem emitten von dataChanged so richtig gemacht? Wie kann ich es erreichen, dass die Werte nach der Eingabe in der Zelle bleiben?
Benutzeravatar
Traggger
User
Beiträge: 27
Registriert: Mittwoch 17. Dezember 2008, 11:33
Wohnort: Regensburg

Guten morgen,

da nur ein Code Stück von dir da is konnt ich es net probieren...

Nimm ma das Signal

Code: Alles auswählen

layoutChanged ()
Soweit ich das verstanden habe wird dieses Signal genutz um den View neu zu zeichnen, während

Code: Alles auswählen

dataChanged (const QModelIndex&,const QModelIndex&)
wohl nur signaliesiert das etwas geändert wurde...

>> Beschreibung auf Riverbank
There are 10 kinds of people. Those who understand binary notation, and those who do not.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Leider bewirkte es bei mir nichts, vielleicht habe ich die Methoden auch nur falsch eingesetzt. Damit ihr mein Problem nachvollziehen könnt, hier alle benötigten Dateien:

main_window.py: http://paste.pocoo.org/show/119402/
model.py: http://paste.pocoo.org/show/119403/
main.ui: http://paste.pocoo.org/show/119404/
Benutzeravatar
Traggger
User
Beiträge: 27
Registriert: Mittwoch 17. Dezember 2008, 11:33
Wohnort: Regensburg

Also, da du ja dein eigenes Model gebaut hast must du auch eine Delegateklasse dazu basteln, sonst weiß ja der View nicht wie er mit deinen selbst eingeführten Daten arbeiten soll...

Sind noch einige andre nützliche Funktionen drinnen die man evtl noch integrieren kann... www.riverbankcomputing.co.uk

Code: Alles auswählen

# model.py

from __future__ import with_statement
from os import path
import pickle
import sys

from PyQt4 import QtCore, QtGui
from babel.dates import get_day_names

################# CHANGES ###########################

class TimeTableDelegate(QtGui.QItemDelegate):
    
    def __init__(self):
        QtGui.QItemDelegate.__init__(self)
        
    def setModelData(self, editor, model, index):
        """Funtion holt die Daten aus dem Editorfenster ab und schickt sie ans Model zum aendern/speichern"""
        txt = editor.text()
        model.setData(index, QtCore.QVariant(txt))
    
    def setEditorData(self, editor, index):
        """ Funktion setzt den Editortext auf den Text der Zelle"""
        text = # TODO: hier den text irgendwie über den index holen
        editor.setText(text)

############## END CHANGES ###########################
        
class TimeTable(QtCore.QAbstractTableModel):
    def __init__(self, filename, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)

        with open(filename, 'rb') as fileobj:
            try:
                self.lessons = pickle.load(fileobj)
            except EOFError:
                # create an empty timetable with 7 days and 8 lessons each day
                self.lessons = [
                    [QtCore.QVariant() for _ in xrange(7)] for _ in xrange(8)
                ]

    [ ... ]

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        
################# CHANGES ###########################

        # FIXED
        if index.isValid() and role == QtCore.Qt.EditRole:
            self.lessons[index.row()][index.column()] = value
            self.emit(QtCore.SIGNAL("layoutChanged ()"))
            return True
        else:
            return False

############### END CHANGES  #########################
und in der main mußt du das Delegate noch zuweißen:

Code: Alles auswählen

#main_window.py

#!/usr/bin/env python

import sys
from os import path

from PyQt4 import QtCore, QtGui, uic

from model import *

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        uic.loadUi('main.ui', self)

        # file where the data is stored
        filename = path.join(path.dirname(
            path.abspath(__file__)), 'data.pickle'
        )
        # table view is the central widget
        view = QtGui.QTableView(self)
        model = TimeTable(filename, view)
        
        self.delegate = TimeTableDelegate()
        view.setItemDelegate(self.delegate)
        
################# CHANGES ###########################

        view.setModel(model)
        self.setCentralWidget(view)

############## END CHANGES ###########################

        self.connect(
            self.actionQuit,
            QtCore.SIGNAL('triggered()'),
            self.close
        )

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()
There are 10 kinds of people. Those who understand binary notation, and those who do not.
lunar

Traggger hat geschrieben:Also, da du ja dein eigenes Model gebaut hast must du auch eine Delegateklasse dazu basteln, sonst weiß ja der View nicht wie er mit deinen selbst eingeführten Daten arbeiten soll...
Unsinn, ein eigenes Modell impliziert noch lange nicht die Notwendigkeit eines eigenen Delegates, Qt4 kann mit Standarddaten (Text, Bilder, Zahlen) alleine umgehen. Ein Delegate wird nur und ausschließlich dann benötigt, wenn man "exotische" Daten anzeigen oder die Anzeige von Standard-Datentypen verändern möchte (z.B. eine Combobox für die Eingabe verwenden möchte).

Das eigentliche Problem ist, dass der Code des OP das übergebene QVariant-Exemplar direkt speichert. Ändert man den Code so, dass Zeichenketten gespeichert werden, und übergebene Werte mittels "QVariant.toString()" konvertiert werden, funktioniert die Anzeige.

Ich weiß nicht, wo die Ursache liegt, müsste ich raten, würde ich sagen, dass QVariant sinnvollerweise nur eine schwache Referenz auf das enthaltene Objekt hält. Die Eingabe wird im Delegate offenbar auf dem Stack alloziert, und daher gelöscht, sobald die Methode des Delegate, die auf die Eingabe reagiert, beendet ist. Normalerweise ist das kein Problem, da QStrings und andere elementare Datentypen "implicitly shared" sind. In diesem Fall existiert aber keine direkte Referenz auf dieses Datum, sondern lediglich eine indirekte, schwache Referenz über QVariant. Daher sieht Qt4 keine Referenz mehr und löscht das Objekt.
Benutzeravatar
Traggger
User
Beiträge: 27
Registriert: Mittwoch 17. Dezember 2008, 11:33
Wohnort: Regensburg

ein eigenes Modell impliziert noch lange nicht die Notwendigkeit eines eigenen Delegates
Ok, dann habe ich das aus der Hilfe und dem Beispiel falsch interpretiert... Sorry!!
Ich habe bisher immer mit eignen Datentypen sowas erstellt, deswegen habe ich da wohl ein bissl in die falsche Richtung gedacht...
There are 10 kinds of people. Those who understand binary notation, and those who do not.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

lunar hat geschrieben:Das eigentliche Problem ist, dass der Code des OP das übergebene QVariant-Exemplar direkt speichert. Ändert man den Code so, dass Zeichenketten gespeichert werden, und übergebene Werte mittels "QVariant.toString()" konvertiert werden, funktioniert die Anzeige.
Dankeschön, das wars! Ich hätte nicht gedacht, dass mein Problem so knifflig war.

Edit: Zum besseren Verständnis hier nochmal die model.py (die anderen Dateien blieben unverändert): http://paste.pocoo.org/show/119572/
Antworten