QTableView mit riesigen Datenmengen

Alles rund um die Programmierung mit Qt
Antworten
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

QTableView mit riesigen Datenmengen

Beitrag von philistion »

Hallo!

Habt Ihr eine Idee, wie ich extrem große Datenmengen in einem QTableView darstellen kann, ohne dass der Rechner komplett in die Knie geht? Er lädt ja soweit ich das bis jetzt getestet habe, immer alle Daten rein, sollte aber in meinem Fall nur das laden, was gerade angezeigt wird.
Vielleicht ist dieses Steuerelement auch nicht das Richtige, ich wäre froh über alle Anregungen zum Thema "Riesige Datenmengen" und Tabellen-artige Darstellung in QT.

Muss ich am Ende vielleicht selbst eine eigene von QAbstractItem abgeleitete Klasse erstellen?

Die Daten verändern sich übrigens recht schnell, es kommen pro Sekunde etwa 10 Datensätze hinzu, das ganze sollte ohne Flackern und Performance-Probleme live darstellbar sein. Ich bin froh um jede Anregung!

Danke!
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Er lädt ja soweit ich das bis jetzt getestet habe, immer alle Daten rein, sollte aber in meinem Fall nur das laden, was gerade angezeigt wird.
Neee es sei denn du hasst nen anderes QT wie ich :-)
QTableview fragt nur die sichtbaren zeilen / spalten ab ...

Ok, aber mal zum Anfang. Du bist sicher das du das QTableview und nicht das QTableWidget benutzt ?

Wenn du das QTableview benutzt, also das Model View Controller konzept, was fuer ein Model verwendest du ? nen fertiges oder ein selbst geschriebenes (von QAbstractItemModel abgeleitet) ?

Das verhalten wird auch vom Model bestimmt ... wenn du bei funktionen wie rowcount, columncount, parent, index ... etc das ganze file durchlaeufst, iss klar das er ned hinnerherkommt ^^

wie machst du dem (QTable)View klar, das neue zielen hinzugekommen sind ? hoffentlich ned mit reset ?

wie aktualisierst du dein model und dein view, hoffentlich nicht 10 mal pro sekunde fuer jede zeile einzeln, sondern nur einmal pro sekunde und dann gleich fuer 10 oder 11 zeilen. Von allem anderen bekommt der user nur kranke augen ^^

Riesige Datenmengen, 10 datensaetze .... es ist ned zufaellig nen textfile (logfile) ? wenn doch, wie handelst du dann das du die zeilenanzahl rausbekommst ?

Ciao ...
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Hallo,

Danke für die Antwort, das ist schon mal beruhigend dass es nicht alles aufeinmal reinliest, da habe ich mich anscheinend geirrt.
Dass es sich um ein QTableView handelt, da bin ich mir hingegen ganz sicher.

Bei den Eingangsdaten handelt es sich um einen Netzwerkstream, den ich über Zugriff auf die libpcap einlese, dann ein auf UDP aufsetzendes internes Firmenprotokoll auswerte und diese ausgewerteten Daten nun anzeigen möchte. Pro Sekunde kommen zwischen 5 und 20 Pakete rein, manchmal auch etwas mehr.

Ich dachte erst daran, eine von QAbstractItemModel abgeleitete Model-Klasse zu erstellen, in welcher ich die ganze Auswertung implementieren kann. Dann müsste ich mich zumindest nicht mehr um das Anzeigefeld kümmern, sondern es hätte sich mit dem Model erledigt.

Mein erster Versuch war, die Daten laufend in eine SQLite-Datenbank zu schreiben und diese mit einem QSQLTableModel zu verknüpfen welches das ganze dann mit einem QTableView anzeigt.
Dem View habe ich bis jetzt per Aufruf von select() im QSQlTableModel aktualisiert, gibts da ne bessere Variante?
Vielleicht ist auch der ganze Ansatz mit der SQLite-Datenbank nicht so ideal, prinzipiell wäre aber so eine Lösung schon wichtig, da die Daten auch permanent abgespeichert werden sollten und nicht bei einem Absturz des GUI-Programms weg sein dürfen.

Das Problem was ich noch hatte, war dann allerdings, dass ich ja Live Pakete einfüge und ca. einmal pro Sekunde per Timer select() aufrufe. Der Benutzer möchte aber auch mal im Betrieb zu den älteren Paketen raufscrollen, ohne dass das QTableView durch den select()-Aufruf im Model wieder auf die aktuellste Zeile springt. Er kann praktisch immer nur die neuesten Pakete anschauen, da es immer wieder zurückspringt.
Wie kann ich das am Besten unterbinden?

Meinst du, ich sollte für meine Problemstellung View und Model (bzw. nur das View-Element wenn ich es item-basiert mache) ableiten und selbst schreiben?

(So ähnlich wie beim Netzwerksniffer Wireshark sollte es aussehen, also dass die Pakete laufend angezeigt werden und auch währenddessen gescrollt werden kann)

mfG
Zuletzt geändert von philistion am 8. Februar 2010 20:04, insgesamt 1-mal geändert.
upsala
Beiträge: 3946
Registriert: 5. Februar 2006 20:52
Wohnort: Landshut
Kontaktdaten:

Beitrag von upsala »

Ich würde mir ein eigenes Model schreiben, in der jede Zeile nur mit einem Verweis auf den Primär-Index des SQL-Tabelle hinterlegt ist und die Daten bei Bedarf aus der SQL-Engine nachladen. Ein kleiner Cache von 100-1000 Zeilen und das eine absolut Performante Sache mit kleinem Speicherbedarf.
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Danke upsala, der Ansatz klingt interessant.

Also du würdest das Model von QAbstractItemModel ableiten und bei jedem Scroll-Event die benötigten Daten aus der SQLite-Datenbank nachladen?
Das QTableView könnte ich dann belassen und müsste daran (abgesehen von dem Problem mit dem Zurückspringen) auch gar nichts mehr ändern, oder?

Gibt es in dieser Richtung noch etwas (Dokumentation, Schon existierende Software die sowas ähnlich realisiert?), was ich mir ansehen sollte, bevor ich dieses Model schreibe?

Hättest du für das Scroll-Problem auch einen Lösungsansatz?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ich wuerde vielleicht gar ned ueber SQL gehen.

SQL macht in dem context nur sinn, wenn man die daten vielleicht dauerhaft archivieren will, oder genormt generische Abfragen auf bestimmte informationen abrufen will. Ansonsten bläht SQL nur auf, und ist eigentlich "recht langsam"

So viel ich weiss, hat PCap nen eigenes genormtes fileformat, oder ?
Da kannst eigentlich "reinstreamen"

zum einlesen selber kannst wieder die libpcap nehmen.
problem iss eigentlich nur wenn deine blockgroessen unterschiedlich sind, du also nicht ohne weiteres auf deine "rowcount" kommst.

Aber dazu schreibt man sich neben dem pcap file ein (temporaeres)index file, welches die startpositionen der einzelnen bloecke hintereinander erhealt.

das model baust dann so, das es immer ueber das index file lauft.

Also rowcount z.b. = filegroesse vom indexfile / groesse des datentyps fuer die filepos

auf das 384te elemnt greiffst dann zu indem ....
filezeiger im index file auf pos 0 + 384* groesse des datentyps fuer die filepos setzen
filepos im pcap file ausm index file auslesen
im index file an ausgelesene pos springen
payload auslesen
gesamten Block mittels playoad auslesen
der libpcap zum interpretieren geben
interpretierte daten zum view durchgeben.

Das solte also alles in der data methode des models passieren.

wichtig, grad bei models mit sehr vielen events ...
entkoppeln.
Dein eingang, bzw die 2-10 bloecke pro sekunde sollten niemals das neuzeichnen, bzw die begininsertrows usw . funktionen im model triggern. Das sollt nen event tun was vom nem Applicationstimer getriggert wird, vielleicht user einstellbar von 200ms - 10 sek updateintervall, so haben wirs in ähnlichen faellen gemacht.
Das befuellen der strukturen passiert im hintergrund, und das event fragt die aenderungen immer ab (pollen).
Die routinen zwischen model und view sind alles andere als performant ... und alles 50ms irgendwas einfuegen bringt den rechner scho an die grenze. wobei es wenig unnerschied iss, ob du 1 zeile oder 5000 einfuegst. Das einfeugen allein erzeugt halt last.

Wir monitoren auf die weisse z.b. nachrichten aufn flexray Bus ... das sind so 8000 nachrichten pro sekunde ... und ja, es geht mit dem qtableview :-)

Ciao ...
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Vielen Dank! Auf die Idee gleich das .pcap Format zu nehmen bin ich gar nicht gekommen, wieder mal viel zu kompliziert gedacht ;)
Gleichzeitig hineinschreiben und herauslesen klingt aber etwas problematisch, ich muss ja alles was da drin steht Live anzeigen und trotzdem auf der Schnittstelle immer mithören.
Oder ist das gar kein Problem mit einem Thread reinzuschreiben und mit einem anderen (read-only) auslesen?

Wie müsste ich das QTableView umschreiben, damit es mir nicht immer bei jedem Update zur neuesten Zeile scrollt, sondern das Update quasi "still eingespielt" wird? Hat da jemand eine Idee?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Oder ist das gar kein Problem mit einem Thread reinzuschreiben und mit einem anderen (read-only) auslesen?
wenn die filemodis beim oeffnen der datei fuers lesen und schreiben gesetzt iss, iss das gar kein problem !
unter linux tun viele tools, wie z.b. tail, head ... nix anderes.
Gleichzeitiges lesen und schreiben in files iss quasi die simpelste Form von IPC, und wird grad unter unix noch haeufig benutzt.
Unter windows geht es genau so .... iss nur ned ganz so verbreitet.
Also das geht ueber prozesse ohne probleme, da isses ueber threads erst recht kein problem :-)
wobei bei threads die wahl hasst, ob jeder thread sein eigenes filehandle hat, oder du nen gemeinsames hasst.
Nimmst gemeinsamme, musst halt nur immer drann denken, dass das filehandle nur ein posizeiger fuers lesen, und einen fuers schreiben hat. Also beim threadwechsel die dinger sich veraendern koennen.



Wie müsste ich das QTableView umschreiben, damit es mir nicht immer bei jedem Update zur neuesten Zeile scrollt, sondern das Update quasi "still eingespielt" wird?
Normal macht das Tableview das doch gar ned ?
Das verhalten iss abhaengig wie die Daten eigefuegt werden.
Machst du nen reset = alle daten werden rausgeschmissen, das View holt sich alle Daten neu, es wird der sichtbare Bereich auf das erste element und folgende gesetzt.

informierst du das QTableview mittels beginInsertRows/endInsertRows bleibt der sichtbare bereich genau da stehen wo er ist. und die daten werden hinten angefuegt. War im sichtbaren bereich nach unten noch platz, werden die daten gleich danach vom view abgefragt, und unten angehangen. War der bereich voll, expandiert das view nur die scrollbar (nur daran siehst dann, das neue daten hinzugekommen sind).
Kann sein das man ne option setzen kann, das der automatisch immer das letzte eingefuegte anzeigt, bei uns isses aber ned so ...
Im gegenteil, wir haben per hand programmiert (user einstellbar) dass der view mit den neuen elementen mitlaeuft.

Ciao ...
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Hallo RHBaum!

Danke, du hast mir schon sehr geholfen.
Dann war das durch das Reset bedingt, alles klar. Ich werde nun mit der Programmierung unter Berücksichtigung deiner Tipps beginnen.

Ich verwende nun die Model/View Architektur, ist wohl in diesem Fall einem QTableWidget überlegen.

Weil du sagst, Ihr verwendet da eine manuelle Option zum Bestimmen ob es mitlaufen soll oder nicht. Ich füge ja nun per beginInsertRows und endInsertRows ein, somit verändert sich die Position nicht. Wie aber habt ihr es implementiert, wenn der Benutzer will dass es nun doch immer mit den neuen Daten mitlaufen soll? Scrollt ihr bei jedem Einfüge-Event manuell auf den neuesten Datensatz oder gibt es da einen eleganteren (built-in) Weg?

mfG
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Weiss ned, obs was elegenteres gibt ...

wir fangen nen event ab, (rowsinserted) setzen dann die selection aufs letzte element und stellen sicher das es im viewport liegt (scrollto).

Ciao ...
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Ich habe nun ein eigenes Model mit Zugriff auf SQLite entworfen, das ganze ist aber Performance-mäßig völlig funktionsunfähig. Vielleicht mache ich ja etwas grunsätzliches falsch.

Ich hoffe es ist jemand hier, der nicht nur per C auf Qt zugegriffen hat, sondern auch einmal per Python, also mit PyQt, ich muss nämlich bei diesem Projekt mit dieser Kombination arbeiten:

Ich arbeite mit verschiedenen Pipes gemäß http://code.activestate.com/recipes/496 ... ded-pipes/ angefangen bei einer Pipe bzw. einem Faucet welches die Netzwerkdaten per pypcap einliest, sie dann an eine Decodierpipe weitergibt, welche die Daten nach erfolgreichem Parsen wiederum an eine Datenbankpipe weitergibt und sie in die SQLite-DB einfügt.

Der Grund warum ich das gemacht habe war der, dass man bei pysqlite nur aus einem Thread auf ein Handle der Datenbank zugreifen kann, ich brauchte also einen Datenbankthread der alle Aufgaben erfüllen kann... deshalb die Pipe an die alles weitergereicht wird.

Das Problem ist nun, dass meine von QAbstractTableModel abgeleitete Klasse vor allem für die RowCount() und die data() Funktion viel zu lange braucht und dadurch das ganze Qt-Programm fast gänzlich blockiert wird.

Mein nächster Versuch war dann, nur alle paar Sekunden die Zeilenzahl auszulesen, sie statisch abzulegen und für die Daten einen Cache zu basteln, dass immer schon die angrenzenden Daten eingelesen werden, bevor überhaupt dorthin gescrollt wird.
Trotzdem braucht diese Funktion viel zulange und das ganze Programm hängt.
Ich denke langsam, dass mein Ansatz gänzlich falsch ist und ich die Model-View-Architektur falsch umsetze.

Das hier ist die Datei "packetmodel.py" die das Model enthält: http://paste.pocoo.org/show/186300/
Und hier noch die Datei "datamgr.py" welche die oben erwähnte Pipe enthält, welche die Daten in die Datenbank einfügen soll: http://paste.pocoo.org/show/186296/

Hat jemand eine Idee oder einen Alternativvorschlag wie ich möglichst effektiv gleichzeitig einen Socket-Zugriff (pypcap) der permanent Daten einliest und parallel dazu ein Model für ein QTableView realisieren kann, welches ohne Performance-Einbußen die Daten anzeigt.

Danke!
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Ich steige nun wahrscheinlich auf C/CPP um, da es doch aus Performance-Gründen besser geeignet zu sein scheint. Wäre trotzdem fein, wenn jemand noch einen Vorschlag bzw. ein paar Tipps bezüglich der grunsätzlichen Realisierung hätte.
Danke!
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ich glaub immer noch, das SQL ned das mittel deiner Wahl sein sollte.
Nen SQL Prozessor iss sicher bequem, kann aber nie so performant sein, wie das arbeiten mit diskreten typen.
miss mal die ticks die du brauchst um dein INSERT statement zu bauen,
und was brauchst um das einzufuegen
Das selbe mit den SELECTs .... damit du mal nen gefuehl bekommst, was wieviel zeit kostet ....

Ciao ....
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Du hast Recht, aber glaubst du das rechnet sich, wenn man die Daten auch filtern will? Also ich möchte ja diese Liste an Paketen auch eingrenzen, z.B. nur Pakete mit Quell-IP X und Quell-Port Y anzeigen. Um dies zu realisieren, hätte eine SQL-Datenbank natürlich Vorteile.

Nun wenn ich das mit diskreten Typen mache, glaubst du man kriegt das auch hin, performant zu filtern? Ich möchte ja ungern das Rad neu erfinden. Im Prinzip ist SQL viel zu viel für meine Anforderungen, ich brauche ja nur eine Filterfunktion.

Was hältst du von CouchDB oder MongoDB?
http://couchdb.apache.org
http://www.mongodb.org/display/DOCS/Home

Gerade weil meine Daten-Blobs im Paket auch mal bis zu einen Megabyte groß sein könnten, hätte dies dann vielleicht Vorteile?
So häte ich keine fixen Strukturen und die relationalen Features von SQL brauche ich sowieso nicht.

Ansonsten bin ich auf die Key-Value-Datenbank Tokyo Cabinet gestoßen. Sieht interessant aus: http://1978th.net/tokyocabinet/

Was hältst du davon?
philistion
Beiträge: 22
Registriert: 8. Februar 2010 09:37

Beitrag von philistion »

Hat irgendjemand noch eine Anregung zur Speicherung von ca. 6 Datenfelder pro Reihe, also eine nicht-relationale spaltenorientierte Datenbank wäre gesucht.

Oder wäre ein dokumenten-orientierter Ansatz wie bei CouchDB empfehlenswert?
Antworten