QStandardItemModel: Inkrementelles nachladen von Datensätzen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich habe all meinen Mut zusammengefasst und mir das Thema fetchmore angenommen. Dazu gibt es einige Beispiele, die in von C++ in PyQt dargestellt werden: fetchmore.py, editabletreemodel und simpletreemodel.

In Anlehnung dieser Beispiele habe ich versucht die Logik zu verfolgen und diese umzusetzen. Mir ist aufgefallen, dass in all diesen Beispielen die QAbstractListModel()-Klasse implementiert werden. Da QStandardItemModel() von QAbstractListModel() erbt, habe ich mir gedacht, ich implementiere einfach QStandardItemModel(). Mein Beispiel, welches im Anschluss folgt, macht momentan noch nicht das was es soll. Ich möchte quasi den bisherigen Stand zeigen, wie weit ich gekommen bin und wo ich noch feststecke. Das Beispielprogramm ist lauffähig.

Stand der Dinge
In der StandardItemModel()-Klasse habe ich drei Methoden re-implementiert: canFetchMore(), fetchMore() und appendRow(). Die letzte Methode habe ich deshalb implementiert, weil ich die einzelnen Items in die Liste speichern möchte. Die die generierten Daten werden über Multithreading in das Model geladen - schließlich möchte ich nicht, dass die GUI »einfriert«.

Fazit
Die Daten werden leider noch nicht in Stapeln (Batch) geladen. Mir fehlt an dieser Stelle der Gedankenanstoß, wie ich jetzt vorgehen soll. Denn im TaskWorker(), welcher später zum QThread hinzugefügt wird, sehen wir, dass in der start_worker()-Methode, einen simulate_query()-Generator erstellt, damit ich einen Generator erhalte. In diesem Generator sollen später die Daten aus der Datenbank geholt werden. Der einfachhaltshalber habe ich für die Datenbank-Simulation einen xrange() eingesetzt, die 100 Daten generiert.

Was mache ich hier gedanklich falsch?

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding:utf-8 -*-
from sys import exit, argv
from os import path

from PyQt4.QtCore import QTimer, QObject, pyqtSignal, \
     QThread, Qt, QModelIndex, QAbstractItemModel
 
from PyQt4.QtGui import QDialog, QLabel, QPushButton, \
     QApplication, QVBoxLayout, QTreeWidget, QTreeWidgetItem, \
     QTreeView, QStandardItem, QStandardItemModel, qApp

def output(text):
    print "Message: {text}".format(text = text)
    return

def update_log(number):
    print "{number} items added".format(number = number)
    return
    
class StandardItemModel(QStandardItemModel):
    '''
        Re-implement QStandardItemModel for large data sets. This
        subclassed model will add items in batches, and preferably
        only when the items are needed by the view
        (i.e., when they are visible in the view
    '''
    number_populated = pyqtSignal(int)
    
    def __init__(self, parent = None):
        QStandardItemModel.__init__(self, parent)
        
        self.item_count = 0    
        self.item_list = []

    #   canFetchMore() and fetchMore()
    #   are called by the item view when it needs more items.
    def canFetchMore(self, index):
        return self.item_count < len(self.item_list)        

    def fetchMore(self, index):
        remainder = len(self.item_list) - self.item_count
        items_to_fetch = min(5, remainder)

        self.beginInsertRows(QModelIndex(), self.item_count,
                self.item_count + items_to_fetch)

        self.item_count += items_to_fetch

        self.endInsertRows()

        self.number_populated.emit(items_to_fetch)

    def appendRow(self, item):
        #   Reimplementing the appendRow() method, for storing items in a list
    
        self.item_list.append(item)
        
        QStandardItemModel.appendRow(self, item)

    
class TaskWorker(QObject):
 
    notify_progress = pyqtSignal(str)
    notify_item = pyqtSignal(object)
    finish_progress = pyqtSignal()
    fire_label = pyqtSignal(int)
   
    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        self.item_counter = 0
 
    def start_worker(self):
 
        self.element = self.simulate_query()
 
        self.timer = QTimer()
        self.timer.setSingleShot(False)
        self.timer.setInterval(1)
        self.timer.timeout.connect(self.increment)
        self.timer.start()
 
    def simulate_query(self):
               
        for name in xrange(100):          
            yield name
           
    def increment(self):
 
        try:
            self.notify_item.emit(next(self.element))
            self.item_counter += 1
            self.fire_label.emit(self.item_counter)
 
        except StopIteration:
 
            self.notify_progress.emit('Break the loop by raising StopIteration')
            self.finish_progress.emit()
            self.timer.stop()
        return
       
    def stop(self):
        self.notify_progress.emit('Stop the loop')
        self.timer.stop()
        return
 
       
class MyCustomDialog(QDialog):
 
    finish = pyqtSignal()
 
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self._standard_item_model = None
        
        self.tree = QTreeView(self)
        self.label = QLabel(self)
       
        self.pushButton_start = QPushButton("Start", self)
        self.pushButton_stopp = QPushButton("Stopp", self)
        self.pushButton_close = QPushButton("Close", self)

        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.tree)
        layout.addWidget(self.pushButton_start)
        layout.addWidget(self.pushButton_stopp)
        layout.addWidget(self.pushButton_close)
 
        self.pushButton_start.clicked.connect(self.on_start)
        self.pushButton_stopp.clicked.connect(self.on_finish)
        self.pushButton_close.clicked.connect(self.close)

        self.standard_item_model()

        self.tree.setModel(self._standard_item_model)
        self.tree.sortByColumn(1, Qt.AscendingOrder)

    def populate_model(self, model = None, item = None):
        two_columns_item = [QStandardItem(str(item))]
        model.appendRow(two_columns_item)
        return
        
    def standard_item_model(self, category=None, rows = None, columns = None, parent = None):

        if not self._standard_item_model is None:
            #   When model already exists only clear the model for adding new items
            self._standard_item_model.clear()
        else:
            #   The model doesn't exists, create a new one
            self._standard_item_model = StandardItemModel()
            self._standard_item_model.number_populated.connect(update_log)
        
    def on_label(self, i):
         self.label.setText("Result: {}".format(i))
         return
       
    def on_start(self):

        self.label.clear()

        self.standard_item_model(rows = 0, columns = 3, parent = self)

        task_thread = QThread(self)
        task_thread.work = TaskWorker()
        task_thread.work.moveToThread(task_thread)

        task_thread.work.fire_label.connect(self.on_label)
        task_thread.work.notify_progress.connect(output)

        task_thread.work.notify_item.connect(lambda item:
                                             self.populate_model(item = item, model = self._standard_item_model))

        task_thread.work.finish_progress.connect(task_thread.quit)
        self.finish.connect(task_thread.work.stop)

        task_thread.started.connect(task_thread.work.start_worker)
        task_thread.finished.connect(task_thread.deleteLater)
        task_thread.start()
        return
 
    def on_finish(self):
         self.finish.emit()
         return
     
def main():
    app = QApplication(argv)
    window = MyCustomDialog()
    window.resize(600, 400)
    window.show()
    exit(app.exec_())
 
if __name__ == "__main__":
    main()
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, ich habe mein Beispiel-Programm im Import etwas aufgeräumt und eine Methode hinzugefügt, die ich vergessen habe. Es handelt sich hierbei um die rowCount()-Methode der implementierten StandardItemModel()-Klasse. Allerdings verhält sich das Programm immer noch nicht wie gewünscht.

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding:utf-8 -*-
from sys import exit, argv
from os import path

from PyQt4.QtCore import QTimer, QObject, pyqtSignal, \
     QThread, Qt, QModelIndex
 
from PyQt4.QtGui import QDialog, QLabel, QPushButton, \
     QApplication, QVBoxLayout, QTreeView, QStandardItem, \
     QStandardItemModel, qApp

def output(text):
    print "Message: {text}".format(text = text)
    return

def update_log(number):
    print "{number} items added".format(number = number)
    return
    
class StandardItemModel(QStandardItemModel):
    '''
        Re-implement QStandardItemModel for large data sets. This
        subclassed model will add items in batches, and preferably
        only when the items are needed by the view
        (i.e., when they are visible in the view
    '''
    number_populated = pyqtSignal(int)
    
    def __init__(self, parent = None):
        QStandardItemModel.__init__(self, parent)
        
        self.item_count = 0    
        self.item_list = []

    def rowCount(self, parent = QModelIndex()):
        return self.item_count

    #   canFetchMore() and fetchMore()
    #   are called by the item view when it needs more items.
    def canFetchMore(self, index):
        return self.item_count < len(self.item_list)        

    def fetchMore(self, index):
        remainder = len(self.item_list) - self.item_count
        items_to_fetch = min(20, remainder)

        self.beginInsertRows(QModelIndex(), self.item_count,
                self.item_count + items_to_fetch)

        self.item_count += items_to_fetch

        self.endInsertRows()

        self.number_populated.emit(items_to_fetch)

    def appendRow(self, item):
        #   Reimplementing the appendRow() method, for storing items in a list
    
        self.item_list.append(item)
        
        QStandardItemModel.appendRow(self, item)

    
class TaskWorker(QObject):
 
    notify_progress = pyqtSignal(str)
    notify_item = pyqtSignal(object)
    finish_progress = pyqtSignal()
    fire_label = pyqtSignal(int)
   
    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        self.item_counter = 0
 
    def start_worker(self):
 
        self.element = self.simulate_query()
 
        self.timer = QTimer()
        self.timer.setSingleShot(False)
        self.timer.setInterval(1)
        self.timer.timeout.connect(self.increment)
        self.timer.start()
 
    def simulate_query(self):
               
        for name in xrange(500):          
            yield name
           
    def increment(self):
 
        try:
            self.notify_item.emit(next(self.element))
            self.item_counter += 1
            self.fire_label.emit(self.item_counter)
 
        except StopIteration:
 
            self.notify_progress.emit('Break the loop by raising StopIteration')
            self.finish_progress.emit()
            self.timer.stop()
        return
       
    def stop(self):
        self.notify_progress.emit('Stop the loop')
        self.timer.stop()
        return
 
       
class MyCustomDialog(QDialog):
 
    finish = pyqtSignal()
 
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self._standard_item_model = None
        
        self.tree = QTreeView(self)
        self.label = QLabel(self)
       
        self.pushButton_start = QPushButton("Start", self)
        self.pushButton_stopp = QPushButton("Stopp", self)
        self.pushButton_close = QPushButton("Close", self)

        layout = QVBoxLayout(self)
        layout.addWidget(self.label)
        layout.addWidget(self.tree)
        layout.addWidget(self.pushButton_start)
        layout.addWidget(self.pushButton_stopp)
        layout.addWidget(self.pushButton_close)
 
        self.pushButton_start.clicked.connect(self.on_start)
        self.pushButton_stopp.clicked.connect(self.on_finish)
        self.pushButton_close.clicked.connect(self.close)

        self.standard_item_model()

        self.tree.setModel(self._standard_item_model)
        self.tree.sortByColumn(1, Qt.AscendingOrder)

    def populate_model(self, model = None, item = None):
        two_columns_item = [QStandardItem(str(item))]
        model.appendRow(two_columns_item)
        return
        
    def standard_item_model(self, category=None, rows = None, columns = None, parent = None):

        if not self._standard_item_model is None:
            #   When model already exists only clear the model for adding new items
            self._standard_item_model.clear()
        else:
            #   The model doesn't exists, create a new one
            self._standard_item_model = StandardItemModel()
            self._standard_item_model.number_populated.connect(update_log)
        
    def on_label(self, i):
         self.label.setText("Result: {}".format(i))
         return
       
    def on_start(self):

        self.label.clear()

        self.standard_item_model(rows = 0, columns = 3, parent = self)

        task_thread = QThread(self)
        task_thread.work = TaskWorker()
        task_thread.work.moveToThread(task_thread)

        task_thread.work.fire_label.connect(self.on_label)
        task_thread.work.notify_progress.connect(output)

        task_thread.work.notify_item.connect(lambda item:
                                             self.populate_model(item = item, model = self._standard_item_model))

        task_thread.work.finish_progress.connect(task_thread.quit)
        self.finish.connect(task_thread.work.stop)

        task_thread.started.connect(task_thread.work.start_worker)
        task_thread.finished.connect(task_thread.deleteLater)
        task_thread.start()
        return
 
    def on_finish(self):
         self.finish.emit()
         return
     
def main():
    app = QApplication(argv)
    window = MyCustomDialog()
    window.resize(600, 400)
    window.show()
    exit(app.exec_())
 
if __name__ == "__main__":
    main()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst schon von QAbstractItemModel ableiten. Sonst kommen sich Standard- und deine eigene Datenhaltung in die Quere.

Des Weiteren ist deine Logik verquer: im Original Beispiel wird VOR dem setzen des Modells auf einem view schon der erste Schwung Daten gelesen. Dadurch ist canFetchMore True. Das ist bei dir False, und dein Modell ist eine Bleiente. Subsequentes hinzufügen von Daten durch appendRow ändert das nicht.

Eigentlich wäre es besser auf das Thread-gerödel zu verzichten, und den jeweils nächsten batch Daten dann bei Bedarf nachzuladen. Nur so wird ja überhaupt dein Ziel erreicht, nicht alles an Daten in den Speicher zu ziehen, sondern das vom View aus zu bestimmen.... wenn du im Hintergrund eh die ganze DB in den Speicher lädst, brauchst du dir den Aufriss erst gar nicht machen.

Wenn es aber erstmal darum geht das zum laufen zu bekommen (zusätzlich zur Ableitung die anders muss!), musst du in appendRow das Signal schmeißen, mit welchem dem view signalisiert wird “Daten haben sich geändert”. Ich bin jetzt zu faul das nachzulesen, aber das erkennt man.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Bist du kein Freund von Nebenläufigkeit? Ich frage nur, weil du abwertend von "Thread gerödel" redest. Ich möchte einfach nicht, dass die GUI einfriert und habe daher gehofft, dies irgendwie mit unter zu bringen. Denn was ist, wenn der Benutzer mit dem Scrollrad auf der Maus zu schnell scrollt? Dann ruckelt die GUI ganz schön. Oder was wenn man - spaßhaltshalber - die Größe der GUI verändert? Dann lädt das Model ständig nach und die Last liegt dann im Hauptthread. Ich werde mich morgen dransetzen und mich mut QAbstractItemModel auseinandersetzen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn man kann, sollte man Threads vermeiden. Sie verkomplizieren Probleme auf subtile Weise, die oft nicht verstanden und schwer zu debuggen sind.

Und meistens finden sich Lösungen auch ohne sie. Insofern: ja, ich bin da kein Freund von.

Für dein konkretes Problem zb habe ich große Schwierigkeiten mir vorzustellen, das die Zerteilung der Datenbankoperationen in kleinere Häppchen, wie sie für genau dieses Vorgehen eh gefragt ist, die Notwendigkeit für Threads nicht eliminiert.

Außerdem ist es durchaus fraglich, ob eine kurze Pause nicht erträglicher ist, als ein permanentes Update der GUI, welches zb die Scrollposition verhunzt etc. Da steht ein Ansatz dem anderen nicht überlegen gegenüber.

Und last but not least: du hast die Interaktion des Models mit dem view offensichtlich immer noch nicht völlig durchdrungen. Zb den Unterschied zwischen Standard (ist selbst eine Liste) und dem eigentlich wichtigeren abstract model (stellt nur eine Schnittstelle dar, die Daten auch in demand und transformiert darbietet). Solange du damit kämpfst, kommen dir die Thread-Geschichten oben drauf nur in die Quere.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

__deets__ hat geschrieben:Du musst schon von QAbstractItemModel ableiten. Sonst kommen sich Standard- und deine eigene Datenhaltung in die Quere.
Kannst du mir mal erklären, wieso? Denn in meinem derzeitigen Projekt arbeite ich ja auch mit QStandardItemModel() und dort klappt bisher alles wunderbar. Alles wird wie gewohnt korrekt über QTreeView() ausgegeben. Was und inwiefern kann in die Quere kommen? Und dann ist mir zudem aufgefallen, dass ein QAbstractItemModel () ziemlich nackig ist, und kaum Funktionalität aufweist und man daher sehr viel zu Fuß gehen muss, indem man viele fehlende Methoden implementiert. Dagegen bringt QStandardItemModel von Haus aus schon vieles mit. Ich will keine Grundsatzdiskussion, sondern nur verstehen, wer hier wann und wo in die Quere kommt und wieso es besser ist mit AbstractItemModel() zu arbeiten. Schließlich will ich ja was lernen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

StandartItemModel hat seine eigene Liste. Du hast ein deiner Ableitung eine zweite (item_list) eingeführt, die aber völlig egal ist, weil sie nicht die data-Methode befüttert. Und es unterstützt eben genau NICHT die inkrementelle Ansammlung von Daten.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ok. Klingt plausible. Danke. Dann muss ich wohl oder übel die furchteinflössende AbstractItemModel() verwenden. Wird wohl mein erstes Mal sein.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, ich habe das Model überarbeitet. Zunächst erbt die AbstractItemModel()-Klasse von QAbstractItemModel(). In dieser Klasse sind zur Zeit sieben Methoden re-implementiert worden. Die Klasse funktioniert soweit, dass ich in der Lage bin die Kopfzeilen im QTreeView() anzeigen zu lassen. Die TreeNodeItem()-Klasse dient dazu, falls später mit Kinder-Items gearbeitet wird. Zur Zeit wird in dieser Klasse nicht viel gemacht, außer mit der Anzahl von Spalten zu arbeiten.

Das Programm ist lauffähig, jedoch nicht vollständig. Nun stecke ich gedanklich fest, und frage mich, an welche Methoden ich noch denken sollte, und noch wichtiger ist, wie ich nun mit den Daten verfahren soll? Denn im fetchmore.py-Beispiel wird in der setDirPath()-Methode der FileListModel()-Klasse mit QDir() gearbeitet. Da ich mit der Datenbank arbeite, fällt dieses Konstrukt schon mal weg. Jemand eine Idee, wie ich nun weiter verfahren sollte?

Code: Alles auswählen

# -*- coding: utf-8 -*-
#!/usr/bin/env python

from sys import argv

from PyQt4.QtCore import Qt, QAbstractItemModel, QModelIndex, QVariant
from PyQt4.QtGui import QDialog, QApplication, QVBoxLayout, QTreeView

class WIndow(QDialog):
    def __init__(self):
        #   Initialize the application

        QDialog.__init__(self)

        #   Create attributes
        self.counter = 0
        self.horizontal_header = ["First Column", "Second Column"]
        
        # Make the GUI
        self.dbs_tree_view = QTreeView(self)
        self.setWindowTitle('TreeView')

        # Create Layout
        central_layout = QVBoxLayout(self)
        central_layout.addWidget(self.dbs_tree_view)

        # The tree of databases model/view
        self.dbs_tree_model = AbstractItemModel(self)
        self.dbs_tree_model.setHeaderData(section = len(self.horizontal_header), value = self.horizontal_header)
        
        self.dbs_tree_view.setModel(self.dbs_tree_model)

    def closeEvent(self, event):
        #   Handle close events.
        self.close

class TreeNodeItem(object):
    '''
    This subclass used to return row/column data,
    and keep note of it's parents and/or children.
    '''
    def __init__(self, item_data = None, parent = None):
        #   Initialize the class

        #   Create attributes
        self.parent_item = parent
        self._item_data = item_data
        self._child_items = []

    def __len__(self):
        return len(self._child_items)

    def childCount(self):
        return len(self._child_items)

    def data(self, column):
        try:
            return self._item_data[column]
        except IndexError:
            pass

    def setData(self, column, value):
        try:
            if column < 0 or column >= len(self._item_data):
                return False
        except TypeError:
            pass
        
        try:
            self._item_data[column] = value
        except (IndexError, TypeError):
            pass
        
        return True

    def row(self):
        '''
            NOTICE:
            =======
            The position of this node in the parent's list of children.
        '''

        if self.parent_item:
            return self.parent_item.children.index(self)

        return 0
        
class AbstractItemModel(QAbstractItemModel):
    """The tree of databases model."""

    def __init__(self, parent = None):
        #   Initialize the class for model

        QAbstractItemModel.__init__(self, parent)

        #   Create attributes
        self._root_data = None
        self._root_item = None

    def flags(self, index):
        """Returns the item flags for the given index. """
        return Qt.ItemIsEnabled|Qt.ItemIsSelectable


    def data(self, index, role):
        """Returns the data stored under the given role for the item
        referred to by the index."""

        if not index.isValid():
            return QVariant()
        node = self.nodeFromIndex(index)
        if role == Qt.DisplayRole:
            return QVariant(node.name)
        else:
            return QVariant()


    def setData(self, index, value, role=Qt.DisplayRole):
        """Sets the role data for the item at index to value."""

        if not index.isValid():
            return False
        node = self.nodeFromIndex(index)
        if role == Qt.DisplayRole:
            node.name = value
            self.emit(SIGNAL(
                'dataChanged(QModelIndex, QModelIndex)'), index, index)
            return True
        return False

    def headerData(self, section = None, orientation = Qt.Horizontal, role = Qt.DisplayRole):
        '''
            NOTICE:
            =======
            Returns the data for the given role and section in the header
            with the specified orientation.

            PARAMETERS:
            ===========
            :section        -
            :orientation    -
            :role           -

            RETURNS:
            ========
            All return values ​​are returned as QVariant for security,
            because Qt expects this type
        '''
        #   Check if the validity of a requested section for
        #   horizontal / vertical, otherwise returns `False` for invalid values
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QVariant(self._root_item.data(section))

        if orientation == Qt.Vertical and role == Qt.DisplayRole:
            return QVariant(self._root_item.data(section))

        return QVariant()

    def columnCount(self, parent = None):
        '''
            NOTICE:
            =======
            The number of columns for the children of the given index,
            otherwise return zero.
        '''
        if parent and parent.isValid():
            return parent.internalPointer().columnCount()
        else:
            if not self._root_data is None:
                return len(self._root_data)
            else:
                return 0

    
    def setHeaderData(self, section = None, orientation = Qt.Horizontal, value = None, role = Qt.DisplayRole):
        '''
            NOTICE:
            =======
            

            PARAMETERS:
            ===========
            :section        -
            :orientation    -
            :role           -

            RETURNS:
            ========
            
        '''
        
        created_value_list = []
        
        if role != Qt.DisplayRole or orientation == Qt.Vertical:
            return False
        else:
            #   We have to check when given value is a list or not.
            #   Because, it is possible that the user only wants one column,
            #   and therefore forgot to pass the header as a list.
            if not value is None:
                if isinstance(value, list):
                    self._root_data = [header for header in value]
                    self._root_item = TreeNodeItem(item_data = self._root_data)
                else:
                    created_value_list.append(value)
                    self._root_data = [header for header in created_value_list]
                    self._root_item = TreeNodeItem(item_data = self._root_data)
             
        if created_value_list:
            result = self._root_item.setData(section, value)
        else:
            result = self._root_item.setData(section, created_value_list)
        
        if result:
            self.headerDataChanged.emit(orientation, section, section)
        
           
        return QAbstractItemModel.setHeaderData(self, section, orientation, value)

    def rowCount(self, parent = QModelIndex()):
        """The number of rows of the given index."""
        if parent.column() > 0:
            return 0

        if not parent.isValid():
            parent_node = self._root_item
        else:
            parent_node = parent.internalPointer()

        if not parent_node is None:
            return parent_node.childCount()
        else:
            return 0


if __name__ == "__main__":
    app = QApplication(argv)
    tester = WIndow()
    tester.show()
    app.exec_()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du benutzt da eine nodeFromIndex Methode. Die sehe ich nicht. Und warum ist das ganze plötzlich ein Baum? Vorher hat dir doch eine Liste gereicht?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Tatsache, die nodeFromIndex ()-Methode ist nirgends bekannt. Hatte ich ein Dusel, dass das Programm bisher noch nicht angeeckt ist. Das mit dem Baum. Es ist ja kein Entprodukt. Mir geht es jetzt darum, dass ich mich frage, wo ich jetzt nun ansetze. Lade ich jetzt, ähnlich wie bei dem fetchmore-Beispiel die Datensätze zunächst in eine Liste? Ich ging nämlich davon aus, dass die benötigten Datensätze Häppchenweise aus der Datenbank geladen werden, und nicht aus der Liste? Deswegen stecke ich erst einmal fest.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest die schon in eine Liste laden. Du weißt ja nie, wann der view auf welches item zugreifen will. Da will man eine Cache.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ok, jetzt bin ich ein Bisschen verwirrt. Welchen Unterschied ergibt sich daraus, dass die gesamten Datensätze zunächst in eine Liste geladen werden oder ob die ganzen Datensätze, wie bei meinem derzeitigen Projekt, in QStandardItemModel() geladen werden? Ob Liste oder QStandardItemModel(), in beiden Fällen werden die gesamten Datensätze in ein Container geladen. Das verwirrt mich jetzt. Ich frage mich jetzt nun, worin jetzt da der entscheidende Vorteil liegt?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

der Vorteil ist, das du nur das lädst, was der User anfragt. Und damit weniger lange wartest. Ich bin mir selbst noch unsicher wann man ggf items aus der Liste rauswerfen kann, das fetchmore Beispiel macht es ja nicht.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also nachdem ich da nochmal ein paar StackOverflow Artikel zu gelesen habe, muss ich sagen: das ist ein unausgegorenes Feature. Deine Uralt-Qt-Version hat einen bug, wodurch das eh immer nur für Listen, nie für Bäume gilt. Und einen Weg, Daten wegzuwerfen sehe ich nicht.

Trotzdem könnte QAbstractItemModel die Lösung sein, weil du in data erst die Daten liefern musst, und das ggf eben lazy machen kannst. Mit einem LRU-Cache darunter. Das zumindest würde ich probieren.
Antworten