Portál AbcLinuxu, 1. května 2025 09:39

Implementácia QAbstractItemModel-u

9.8.2010 17:30 | Přečteno: 1317× | Linux | Výběrový blog | poslední úprava: 9.8.2010 17:28

Tento článok nie žiaden návod ako správne implementovať QAbstractItemModel. Je len malým zamyslením MVC architektúrou v Qt. Text bude obsahovať niekoľko otázok, na ktoré hľadám odpoveď, ak teda viete na niečo z toho odpovedať nehanbite sa a píšte.

Blog začnem malým úvodom do fungovania modelov v Qt (pre tých, ktorý s tým zatiaľ nepracovali). Som samozrejme človek a niečo z toho som možno zle pochopil, ak tu mám teda chybu môžte kopať (aj do hlavy ;) ).

Pri prechode na Qt 4 som čítal rôzne články o tom, aká je nová MVC (model-view-controller) architektúra pokroková. Dokumentácia sa tvári, že primárnym účelom je jednotný spôsob prístupu k dátam a teda aj možnosť zobraziť tie isté dáta v rôznych pohľadoch (tabuľka, strom, zoznam …).

Architektúra MVC umožňuje prezentovať dáta z modelu prakticky ľubovoľným spôsobom, ale implementácia týchto pohľadov nie je práve jednoduchá. Ako sa hovorí starý QTreeView novým kúskom nenaučíš a vlastný pohľad je príliš zložitý. Z tohto dôvodu pár developerov začalo projekt ItemviewsNG, ktorý využíval QGraphicsView. Myslím, že príchod QML tento projekt pochová nadobro (čo je myslím aj celkom dobré, lebo QML je o triedu jednoduchšie a dostatočne flexibilné). Snahy o zlepšenie vizuálnej časti tú sú, ale ako je na tom model?

Čo je to model

Model je rozhranie pre jednotný prístup k dátam. Umožňuje získavanie dát a voliteľne aj ich modifikáciu. Okrem všeobecného modelu (QAbstractItemModel) Qt poskytuje aj jednoduché modely pre zoznamy (QAbstractListModel) a tabuľky (QAbstractTableModel).

Prvá vec, na ktorú musíme pri návrhu modelu myslieť je jeho rýchlosť. Filozofia modelov v Qt je taká, že Qt neuchováva žiadnu cache, pre použiteľnosť je životne dôležité aby nebol príliš pomalý. Cachovanie sa dá dosiahnuť použitím proxy modelu (stačí napísať raz a použiť na akýkoľvek existujúci model, takže tá práca sa celkom aj vyplatí).

Návrh typu "ani byte navyše" je vidieť hlavne u triedy QModelIndex, čo je jednoznačný identifikátor položky v modeli. Implementácia tejto triedy si vystačí s číslom riadku, stĺpca, smerníkom na model a vecou, ktorú asi nikdy nepochopím (ale snažím sa o to) - interným pointrom. K tomuto myslím pomerne zbastlenému (Jardík by o tom vedel písať) riešeniu sa vrátim v ďalšom texte.

Implementácia virtuálnych metód

Poradie metód, ktoré budem popisovať je moje obvyklé poradie implementácie (od najjednoduchšej po najzložitejšiu). Takže začneme oddychovkami.

int QAbstractItemModel::columnCount(const QModelIndex &parent = QModelIndex())
Funkcia jednoducho vráti počet stĺpcov. Také malé drobné + za argument parent, ktorý umožní vytvoriť povedzme stromový model, ktorý má rôzny počet stĺpcov na rôznych úrovniach. Možno by som to aj niekedy použil keby to niektorý z pohľadov korektne podporoval ;). Ale ako model má za to u mňa +.


int QAbstractItemModel::rowCount(const QModelIndex &parent = QModelIndex())
V podstate táto metóda je podobná predchádzajúcej, ale tu už musíme v prípade stromových modelov využiť argument parent. Index ako som už spomínal musí byť unikátny a je čiste na programátorovi ako využije index na zistenie počtu položiek. Celkom užitočná môže byť vlastná dátová rola (Qt::UserRole) ale väčšinou sa dá v tejto funkcii vykľučkovať obyčajným volaním parent na indexe, nájdením hĺbky a trasovaním podľa čísla riadku. V praxi to vyzerá tak, že voláte parent() až po prvú úroveň a odtiaľ zisťujete čísla riadkov a postupne prechádzate do hlbších úrovní.


QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole)
Táto metóda slúži ako už názov napovedá na získanie dát z modelu. Dátovú položku jednoznačne identifikuje index, takže podľa indexu sa dostaneme ku konkrétnej položke a získame jej dáta. Stratégiu nechávam na fantázii čitateľa, časté je použitie interného pointera indexu, alebo interného id prípadne sledovanie od nadradených indexov. Je možné vracať aj vlastné polia (Qt::UserRole + n, n >= 0) a tak si trochu zjednodušiť život.


QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex &parent = QModelIndex())
Tak táto metóda mi obvykle robí dosť starostí. Jej účelom je teda previesť číslo riadku a stĺpca na unikátny index. Pre vnorené položky je tu samozrejme argument parent. Unikátny index je vtedy, keď má unikátne číslo riadku, stĺpca a interný pointer / interné id (interné id a interný pointer zdieľajú to isté miesto v pamäti, koho to trápi, že id je 32 bitové a pointer podľa platformy ;) ).


QModelIndex QAbstractItemModel::parent(const QModelIndex &index)
Pri tejto metóde mám taký pocit, že teóriu relativity bolo celkom jednoduché objaviť. Ak má niekto tip ako túto funkciu v zložitejších štruktúrach jednoducho implementovať sem s nim. Inak netuším aký má toto účel.


Prečo je zložité implementovať toto všetko vysvetlím radšej na aplikačnom príklade.

Aplikačný príklad

Tento príklad je čiste praktický a reálne som ho riešil pre jedného zákazníka. Nemôžem písať podrobnosti, ani to nie je potrebné, berte to len ako možnú a nie až tak nepravdepodobnú situáciu.

Cieľom mojej práce bolo vytvoriť trojúrovňový (nie viac, nie menej, jednoducho 3 úrovne) model, ktorý by používal prvky z databázy.

Štruktúrou uloženia v databáze sa nemusíme zaoberať, postačí nám poznať, že dáta sú uložené tak, aby sa s nimi dobre manipulovalo, indexy na správnych stĺpcov, podľa možnosti odstránené duplicity. Databáza je otvorená vo výlučnom režime, takže je jediný program, ktorý k nej môže pristupovať a nemusíme sa tak zaoberať možnosťou manipulácie s databázou cudzím procesom.

Asi najjednoduchším riešením je vytvoriť select, ktorý by vybral všetky požadované údaje a z neho vytvoriť v pamäti stromovú štruktúru. Štruktúra by sa skladala z pointra na nadradenú položku, zoznamu pointrov na podpoložky a dáta. Implementácie metód data, index a parent by urobili jednoduchý static_cast na položku stromu. Získanie dát položky, alebo vytvorenie indexu je v takomto prípade veľmi jednoduchá záležitosť. Takéto riešenie by ale s väčšou databázou, alebo u embedded zariadení rozhodne neprešlo. Zaoberajme sa teda ďalej možnosťou, že dáta ponecháme pekne v databáze a budeme k nim pristupovať len v prípade potreby (samozrejme aj s cachovaním výsledkov).

Prvé riešenie nám zaručilo, že indexy budú unikátne pretože pointre na rôzne objekty majú rôznu hodnotu. Nie je možné však vytvoriť pointer na dočasný objekt (objekt v cache). Namiesto pointra musíme teda vymyslieť iný jednoznačný identifikátor. V mojom prípade som robil stromovú štruktúru z viacerých tabuliek v databáze. Kľúčové id bolo pre každú úroveň z inej tabuľky. V prípade jedinej tabuľky bolo možné zabezpečiť unikátnosť, ale v prípade rôznych tabuliek je to už horšie (nie nemožné, ale rozhodne by to nebolo správne a ani by to nejako moc nezlepšilo našu situáciu).

Kritickými metódami, ktoré nás budú zaujímať sú index a parent, telo zvyšných metód bude závislé na týchto. Vytvorenie indexu znamená v prvom rade zistenie bodu, na ktorý ukazuje argument parent. Ciest ako sa k nemu dostať je viacero. Ja pre tento účel využijem hodnotu interného id. Ak vieme, že v tabuľke nebude > 2^30 hodnôt môžeme vrchné 2 bity obetovať na identifikátor tabuľky, z ktorej pochádza id. Kód bude teda vyzerať nejako takto:

if (internalId < 0x3FFFFFFF) {
}
else if (internalId < 0x7FFFFFFF) {
}
else {
}

Pri vytváraní nadradeného indexu (parent) zistíme teda hĺbku stromu podľa hodnoty internalId. Na vytvorenie indexu potrebujem číslo riadku, číslo stĺpca a interné id nadradeného prvku. V prípade, že vieme z databázy zistiť id nadradeného prvku a jeho riadok je všetko v poriadku (teda až na malú drobnosť, že je to príliš pomalé). Číslo stĺpca sa vždy musí rovnať 0 (nepochopil som prečo, v dokumentácii som o tom nič nenašiel).

V prípadoch keď sa ten istý list môže nachádzať vo viacerých vetvách (áno sú také prípady) už id na nájdenie nadradenej položky nemôže fungovať. Žiaľ ja ako pozemšťan by som v takomto prípade absolútne rezignoval na nejaký QAbstractItemModel a prehlásil by som ho za absolútne nevhodný na daný (pomerne jednoduchý) účel.

Záver

V dnešnom blogu som teda trochu predstavil MVC architektúru v Qt 4. Na jej návrhu je vidieť, že autori sa snažili maximálne šetriť prostriedkami či už bude mať stromová štruktúra 1 000 000 riadkov, alebo vnorenie hĺbky 1 000 000.

O rýchlosti ich návrhu nepochybujem, ale skúsil som sa zamyslieť nad tým, či je možné implementovať model tak, aby bol skutočne rýchly a elegantný. Nebolo by lepšie, aby QModelIndex mal priamo v sebe odkaz na nadradený index a tak by sa odstránila aj podmienka duplicity metóda parent? Je skutočne potrebné mať teoretickú zložitosť prechádzania do hĺbky O(1) napriek tomu, že pri zložitejších modeloch nebude zložitosť lepšia než O(n) (kvôli zložitej implementácii metódy parent a index)?

Čo s ďalej?

Týmto blogom by som Vás čitateľov chcel poprosiť aby ste sa zamysleli nad MVC architektúrou v Qt. Ak máte nejaké konštruktívne nápady sem s nimi. Ak viete ako implementovať model lepšie sem s tým. Snáď by sa podarilo niečo pretlačiť aj do Qt.

       

Hodnocení: 100 %

        špatnédobré        

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

Komentáře

Nástroje: Začni sledovat (2) ?Zašle upozornění na váš email při vložení nového komentáře. , Tisk

Vložit další komentář

9.8.2010 19:25 N/A
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
K metodě QModelIndex QAbstractItemModel::index(...) se manuál staví tak, že by se měla volat metoda QAbstractItemModel::createIndex(). Takže bych se asi držel tohoto doporučení ;-)
mirec avatar 9.8.2010 19:42 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Nie som až taká lama ;) Samozrejme vo svojom blogu predpokladám, že čitateľ pozná createIndex. Iná možnosť vytvorenia validného indexu vlastne ani neexistuje. To, čo ma na tej metóde trápi je posledný parameter - interný pointer, alebo interné id.
LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
mkoubik avatar 9.8.2010 20:22 mkoubik | skóre: 5 | blog: lorem_ipsum | Praha 8 - Bohnice
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Architekturu Qt bych doporučoval nastudovat všem "lepičům webíků", aby pochopili jak to MVC soudruzi z Xeroxu před těma 40 lety mysleli. Pak by stěží tvrdili "Proč tu instanci modelu cpeš do view? To je strašná prasárna." Z toho pak pramení, že 90% php frameworků není MVC, ale MCV. Osobně mi přijde bližší ten smalltalkovský přístup.

To jen takový OT povzdech, Qt jsem pár let neviděl a tohle mi rozjasnilo den, asi se na to podivám blíž.
Plesk! | Population | Industry
9.8.2010 21:55 void
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Já model vždy předávám viewu (sice ne u webíků, ale obecně), podle tebe je to dobře, nebo špatně? To jsem úplně nepochopil, jak jsi to myslel.
Luboš Doležel (Doli) avatar 9.8.2010 22:31 Luboš Doležel (Doli) | skóre: 98 | blog: Doliho blog | Kladensko
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Můžu říct, že implementace QAbstractItemModel byla ta největší pakárna, co jsem kdy s Qt měl. Pamatuju si, že jsem pár věcí ohledně ubírání a další manipulace s řádky tipoval a odhadoval, protože ta dokumentace moc detailní nebyla. Možná se to už zlepšilo.
mirec avatar 9.8.2010 22:45 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Ja tipujem, že stále rovnaká, len už teraz viem ako implementovať. Ale stále sa nájdu veci, ktoré ma jednoducho zaskočia a neviem ako implementovať. Celkovo mám z toho zmiešané pocity. Na jednej strane je to skvelá myšlienka, na druhej strane sú tu veci, ako parent, ktoré ma vytáčajú. Napr. Cocoa (NSOutlineViewDataSource) nevyžaduje pri stromoch implementovať parent.
LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
10.8.2010 07:48 s0 | skóre: 32 | blog: nejchytřejší kecy | prágl
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Poslechnou, Mirec, nechtěj to nějak učesat, dát tomu nějakou formu, začátek a konec a poslat to jako článek, který by navazoval na totok: Grafické programy v Qt 4 – 9 (prezentace dat – architektura model/view)?
Kuolema Kaikille (Paitsi Meille).
10.8.2010 10:17 lok
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
+1, take se primlouvam za clanek. Bylo by to moc fajn.
mirec avatar 10.8.2010 13:48 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u

No článkov o MVC v Qt je pomerne dosť. Neviem, či by som bol schopný priniesť tu niečo nové o čom ešte nikto nepísal.

Všetky články, ktoré som o implementácii vlastných modelov v Qt čítal sa zaoberali jednoducho riešiteľnými problémami. Buď sa vôbec nezaoberali stromovými štruktúrami, alebo stromy boli vždy priamo v pamäti a nebol teda problém urobiť cast na interný pointer. Existuje ale celá trieda problémov pri ktorých nie je možné všetky dáta udržiavať v pamäti a práve s takýmto problémom som sa ja stretol.

Rád by som tento blog upravil a urobil z neho článok, ale chýba mi naň pár zásadných vecí. V prvom rade hovorím o probléme, ktorý viem vyriešiť len veľmi mizerne nehovoriac o tom, že toto moje "riešenie" nie je vôbec univerzálne a na mnoho problémov vôbec fungovať nemôže. Nechcel by som si vziať na svoju zodpovednosť to, že naučím nových potenciálnych programátorov robiť zlé rýchlo zbastlené riešenia.

Stále ale verím, že sa nájde niekto, kto tomu rozumie viacej ako ja, napíše jeho správne rieštenie do diskusie, ja sa chytím za hlavu a pôjdem sa rovno zakopať ;)

LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
10.8.2010 16:46 volvox | skóre: 16
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Teda, nevím, jestli úplně rozumím popisu problému, ale co např. implementace QFileSystemModel? Ta nenačítá ihned všechna data náležící modelu a mimo jiné si drží vlastní keš:
QFileSystemModel keeps a cache with file information. The cache is automatically kept up to date using the QFileSystemWatcher.
mirec avatar 10.8.2010 17:02 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Ja nemôžem načítať niekoľko miliónov prvkov. Nevojdú sa do RAM a aj keby trvalo by to pridlho. Môžem akurát udržiavať v cache pár prvkov a zvyšok načítať na požiadanie. QFileSystemModel má stálu cache, takže to, čo raz načíta už neodstraňuje, takže problém so zmenou pointrov tam nenastane. Snáď sa nikto nenahnevá za časť zdrojáku:
static_cast<QFileSystemModelPrivate::QFileSystemNode*>(index.internalPointer());
LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
10.8.2010 17:51 volvox | skóre: 16
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Popravdě, stále mi ten problém nějak uniká. Přijde mi, že je to spíš otázka návrhu, než implementace. Proč podobně jako QFileSystemModel nenačítat data na vyžádání (canFetchMore() a fetchMore()) a nepoužívaná data (tj. jedn. větvě stromu) neodmazávat z modelu dle potřeby. Proto, že by znovu načtená data měla jiný identifikátor/ukazatel? Čemu to ale vadí - k datům přistupuje přímo pouze model a ten už se postará, aby jedn. položky měly platné indexy. Co se týče fragmentu kódu, tak přesně pro tento účel se internalPointer ve stromové struktuře používá, nevidím na tom nic špatného - IMHO ho ale klidně můžete použít pro ukazatel na rodiče.
mirec avatar 10.8.2010 18:12 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Proč podobně jako QFileSystemModel nenačítat data na vyžádání (canFetchMore() a fetchMore()) a nepoužívaná data (tj. jedn. větvě stromu) neodmazávat z modelu dle potřeby.

Vymazanie by zneplatnilo indexy a program by padol. Preto nemôžem použiť internalPointer, ale musím mať identifikátor, pomocou ktorého sa jednoznačne dajú nájsť dáta v databáze (internalId).

Čemu to ale vadí - k datům přistupuje přímo pouze model a ten už se postará, aby jedn.

Ak by sa už cache vymazala a ja by som dostal požiadavku na vrátenie dát k položke QModelIndex s neplatným interným pointrom tak môžem maximálne tak zhodiť aplikáciu. Nemám absolútne žiadnu informáciu o tom, ktoré dáta by som mal vrátiť. QModelIndex má akurát riadok, stĺpec a už neexistujúci interný pointer, takže v stromovej štruktúre neexistuje spôsob nájdenia správnych dát.

IMHO ho ale klidně můžete použít pro ukazatel na rodiče.

To viem, ale pre zjednodušenie som popisoval ako vytvoriť index z aktuálneho prvku. Bežne je ale pre mňa praktickejšie keď na prvej úrovni mám internalId 0 a zvyšné podľa rodičov. Stále to ale nie je riešenie, lebo ani rodičia sa do RAM nezmestia ;). S už zmazaným rodičom si teda tiež nijako nepomôžem.

LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
11.8.2010 17:13 volvox | skóre: 16
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Aha, tak už jsem to konečně pochopil.
Vymazanie by zneplatnilo indexy a program by padol. Preto nemôžem použiť internalPointer, ale musím mať identifikátor, pomocou ktorého sa jednoznačne dajú nájsť dáta v databáze (internalId).
Ano, vymazání položek by zneplatnilo indexy - o tom by se ale napojené pohledy a případné proxy modely dověděly. Při použití indexů si model musí sám hlídat, jestli je index platný (a mimo model by se internalPointer, resp. internalId IMHO používat nemělo - proto už to internal v názvu). Pokud se přesto někde používají, tak ochranou proti dangling pointers by bylo použití některé z smart pointer tříd v Qt.

Použití internalId tak, jak ho používáte teď, je IMHO v rozporu s určením třídy QModelIndex. Ta by měla sloužit jako index pro model, ne jako index pro data.

Co se týče řešení problému, napadají mě tyto možnosti:
  1. napojit všechny objekty, které komunikují s modelem pomocí indexů tak, aby se dozvěděly, že došlo k zneplatnění indexu, ať už se postarají sami, co s tím - to zřejmě vyhovovat nebude
  2. nemazat již vytvořené položky stromy, ale pouze jejich data - polovičaté řešení, ale v určitých případech by mohlo stačit
  3. implementovat vlastní model index s informací, jak se dobrat k rodiči - tady je ale třeba mít na mysli, že indexy celkem masivně vznikají a zanikají a zásadně tím ovlivňují výkon a IMHO je to celkově v rozporu s návrhem indexů
  4. mazat pouze nepoužívané položky (ve smyslu, že ve zbytku aplikace na ně nejsou indexy) - to by snad šlo implementovat pomocí QExplicitlySharedDataPointer a QSharedData
Případně nějaký mix, ale věřím, že s tím něco půjde dělat ;) Na druhou stranu s Qt (a vlastně i s c++) jsem začal teprve před půl rokem a napsal jedinou aplikaci, takže na nějakou odbornou diskusi nemám a popravdě jsem se už úplně vyčerpal ;)
mirec avatar 11.8.2010 17:44 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u

Ja si tie indexy moc neviem ustrážiť v rámci modelu, inak mimo internalPointer nepoužívam.

Takže k dynamickému vymazaniu položiek ... no je to síce teoreticky pekné (a je na to zopár tried, takže stálo by to minimálnu námahu). Takýto postup som neskúšal, možno, že by navonok aj vyzeral ako funkčný ... ale nemám rád ani teoretickú možnosť zhodenia aplikácie takouto trivialitou.

Uvažujme situáciu, že mám model, view si z neho nejako berie dáta. V cache udržiavam pár položiek + cestu k nim. Cesta sa vyhodí z cache až vtedy keď sa odstránia aj položky cache, takže nejakým hrabnutím na parent nič nezhodím. Uvažujem situáciu, že užívateľ dosť brutálne scrolluje v zozname. Udalosti sa ukladajú do fronty. Povedzme, že vyhodím časť cache, ale vo fronte bude ešte požiadavka na volanie parent s parametrom index odkazujúcim na už odstránený prvok. Viem, že situácia, že toto nastane je pomerne zriedkavá, ale nie nemožná a mať aplikáciu, ktorá sa dá zhodiť obyčajným scrollovaním práve nechcem.

implementovat vlastní model index s informací, jak se dobrat k rodiči - tady je ale třeba mít na mysli, že indexy celkem masivně vznikají a zanikají a zásadně tím ovlivňují výkon a IMHO je to celkově v rozporu s návrhem indexů

V podstate je tu viacej zaujímavých nápadov, ale u tohto mi nedá nereagovať. Situácia je skutočne taká, že indexy dosť masívne vznikajú a zanikajú. Rozmýšľal som tak nad indexom, ktorý by mal odkaz na nadradený index a tie odkazy automaticky spravovať (automatické pointre a tak). Určite je tam veľa technických prekážok, ale to je asi tak moja predstava ideálneho indexu. Pomocou indexu by sa dala získať celá cesta. Len neviem ako by som to reálne implementoval ;)

LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
Jardík avatar 10.8.2010 12:23 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Ty vole, oni indexujou intama a já ve svém modelu potřebuju 3 miliardy položek a na mém systému to má rozsah jen -+2mld+-autobus ...
QModelIndex je akorát zpomalování a zabírá místo. Zlatý size_t/ptrdiff_t. Neustálé vytváření a ničení QVariant, to musí být opět rychlé. Já teda neříkám, že to je úplně špatné, třeba pro snadnost ano, ale pokud potřebuju napsat listview bez stromové struktury, tak je mnohem lepší mít pro indexaci nějaký POD typ (nejlépe něco stejně velikého jako pointer, třeba i znaménkový ptrdiff_t, ale pořád lepší než int).
Ja pre tento účel využijem hodnotu interného id. Ak vieme, že v tabuľke nebude > 2^30 hodnôt môžeme vrchné 2 bity obetovať na identifikátor tabuľky
Zase něco předpokládáme? Třeba velikost intu? Nebo jste použil něco jako qint32 (jestli to existuje)?
Věřím v jednoho Boha.
mirec avatar 10.8.2010 13:10 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u

Tak od tohto komentáru som čakal viacej, takže pekne poporiadku ;)

Ty vole, oni indexujou intama a já ve svém modelu potřebuju 3 miliardy položek a na mém systému to má rozsah jen -+2mld+-autobus

Áno aj nie, je možné vytvárať model priamo z pointra a vtedy je veľkosť podľa platformy. Je možné vytvoriť index aj z ich 32-bitového int-u a ten zdieľa rovnaké pamäťové miesto ako pointer (klasický union, žiadna mágia). Klasický zoznam teda v žiadnom prípade nebude slabinou ich MVC architektúry. Ale stačí môj príklad s 3-úrovňovou štruktúrou a už neviem nájsť elegantné riešenie (to, ktoré som použil je len núdzové).

Zase něco předpokládáme? Třeba velikost intu? Nebo jste použil něco jako qint32 (jestli to existuje)?
createIndex(int row, int column, quint32 id) const

Ako alternatívu som mohol použiť interný pointer a správať sa k nemu nie ako k pointru, ale ako k číslu.

createIndex(int row, int column, void * ptr = 0)
zdroj
LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
Jardík avatar 10.8.2010 15:11 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
createIndex(int row, int column, quint32 id) const
Ok, vždyť já se ptal. Oni v Qt rádi používají inty či longy k přetypování z pointeru, tak jsem se chtěl ujistit, jestli tu opravdu je typ s pevnou velikostí. Jinak to s tím indexováním intem souvisí i s tím int QAbstractItemModel::rowCount(const QModelIndex &parent = QModelIndex()) - vrací to int, takže počet řádků je daný intem - je to sice pro toho rodiče a tak by jich teoreticky mohlo víc, ale kdo ví, jak to pak Qt sčítá.
Věřím v jednoho Boha.
Jardík avatar 10.8.2010 15:12 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Jinak s Qt nedělám, takže skoro nic o tom nevím, tak jestli plácám blbosti, tak sry.
Věřím v jednoho Boha.
10.8.2010 15:51 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Oni v Qt rádi používají inty či longy k přetypování z pointeru
Opravdu?
Jardík avatar 10.8.2010 15:55 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Mám to hledat? Jestli o to opravdu stojíš, tak ti to najdu.
Věřím v jednoho Boha.
Jardík avatar 10.8.2010 16:03 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Prosim. Projděte si commity a koukněte, co tam bylo za prasárny. Díky projektu mingw-w64 se je daří eliminovat, protože vývojáři chytře opatchovali či nastavili mingw, aby ve výchozím nastavení explicitní přetypování pointeru na číselný typ o jiné velikosti hlásil jako error a né jen obyčejný warning. MSVC build 64bit Qt (naposledy jsem zkoušel 4.6) byl právě plný náhodných pádů kvůli takovým kravinám - nahlásí warning, ale ten se v té smršti výpisů snadno ztratí.
Věřím v jednoho Boha.
10.8.2010 17:13 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Ok, díky za link.
Co se týče MSVC na 64-bitech tak to je demence největší. Qt se mi nepodařilo zkompilovat ve verzích 4.6.2 a 4.6.3 v žádné z možných konfigurací...
Takže musím mingw. Je to s mingw-w64 už funkční?
Jardík avatar 10.8.2010 17:47 Jardík | skóre: 40 | blog: jarda_bloguje
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Někde jsem tu kdysi dával 64bit Aroru (prohlížeč), tuším, že i s Qt knihovnama. Bylo to zkompilovaný s MSVC, ale občas to padalo (právě kvůli těm úchylným přetypováním). Jinak MSVC to zkompilovalo hned (s warningama), s mingw-w64 se mi podařilo (ručně jsem tam opravoval typecasty podle patchů pro jinou verzi) zkompilovat akorát debug build, release hlásil nedefinované symboli při linkování. Jinak už je to dýl, Qt 4.7 možná půjde v poho, netuším, možná zkusím.
Věřím v jednoho Boha.
16.8.2010 12:17 Ivan
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
To kopirovani QVariant-u je fakt opruz. Zrovna ted jsem neco fixoval v jednom OS projektu a nestacil jsem se divit kolikrat se musi QVariant zkopirovat mezi nactenim dat z DB a jejich zobrazenim na obrazovce. V pripade ze QVariant obsahuje cislo nebo string tak to tak moc nevadi(QString-y jsou sdilene), ale QVariant umoznuje drizet "libovolny" datovy typ ktery je zaregistrovan pres Q_METATYPE a ktery ma copy-construktor.

Prislo mi, ze cely kod by byl efektivnejsi, kdyby byl napsany v JAVE nebo Pythonu, kde se vsechno predava referenci.
Rezza avatar 10.8.2010 15:52 Rezza | skóre: 25 | blog: rezza | Brno
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Jak to cele vznikalo a to same i na videu.
11.8.2010 11:05 Jan Švec | skóre: 2 | blog: honzas | Plzeň
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Odpovědět | Sbalit | Link | Blokovat | Admin
Poněvadž jsem už několik QtModelů implementoval (sice v PyQt, ale sémanticky to je totéž), dovolím si vložit kód metod index() a parent():
    def index(self, row, column, parent):
        if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent):
            return QtCore.QModelIndex()

        if not parent.isValid():
            # koren
            return self.createIndex(row, column, self.items[row])
        else:
            # deti
            item = parent.internalPointer()
            if item not in self._records_cache:
                self._records_cache[item] = list(item.records.values())
            records = self._records_cache[item]
            return self.createIndex(row, column, records[row])

    def parent(self, child):
        data = child.internalPointer()
        if isinstance(data, archive.Item):
            # koren
            return QtCore.QModelIndex()
        elif isinstance(data, archive.Record):
            # deti
            record = data
            item = record.item
            row = self.items.index(item)
            column = 0
            parent = QtCore.QModelIndex() # takto se ziska index pro ktery isValid() je False, tj. rodic je korenova polozka
            return self.index(row, column, parent)
        else:
            return QtCore.QModelIndex()
internalPointer slouží čistě k vašim potřebám. Dá se do něj uložit třeba primary key zobrazované položky, která se na tom indexu (buňce) nachází, cesta k elementu v XML, cokoli se vám zachce. V příkladu výše se do internalPointeru ukládá odkaz na SQLAlchemy položku z databáze.

Ještě jsem v komentářích viděl zmínku o neustálém vytváření a rušení QVariant - věřte, že view volá data() pouze pro položky, které má zobrazit. A na obrazovku se najednou více než pár stovek buněk nevejde. Jediné, kde to zpomaluje je pokud nastavíte přizpůsobení šířky sloupce view podle obsahu, pak to musí projít všechny indexy v daném sloupci, aby zjistilo ten nejdelší.
mirec avatar 11.8.2010 11:57 mirec | skóre: 32 | blog: mirecove_dristy | Poprad
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u

Tiež takto riešim implementáciu index a parent ak je to možné. V mojom príklade by to ale padlo na tomto:

 self._records_cache[item] = list(item.records.values())

U mňa totiž po určitej dobe nepoužívania (neberte to ako časovo, ale počet prístupov) vymažem položku item, takže internalPointer by už ukazoval na neplatnú pamäť. Preto to riešim znásilňovaním interného id vrchnými bitmi, ktoré v tomto prípade moc nevyužijem. To, čo zostalo dole je id v databáze.

Ještě jsem v komentářích viděl zmínku o neustálém vytváření a rušení QVariant - věřte, že view volá data() pouze pro položky, které má zobrazit.

Toto tvrdenie trochu poopravím pretože nie je to celkom pravda. Pri QTreeView-u ešte musí byť nastavené uniformRowHeights na true. No a keď už o tom píšem záťaž spôsobená QVariant-om je zanedbateľná.

LinuxOS.sk | USE="-fotak -zbytocnosti -farebne_lcd +vydrz +odolnost +java" emerge telefon
25.8.2010 13:18 Jan Kundrát (jkt) | skóre: 27 | blog: jkt | Praha - Bohnice
Rozbalit Rozbalit vše Re: Implementácia QAbstractItemModel-u
Clanek i diskusi pod nim jsem cetl pomerne zbezne, ale mam pocit, ze problemy s "uz zneplatnenym internalid/ptr" jsou zpusobene tim, ze si aplikace "nekde" necha QModelIndex. V dokumentaci se dost jasne pise, ze se to nema delat, ze QModelIndex muze byt pomerne rychle zneplatnen (resp. explicitne se tam poukazuje na zneplatneni po modifikacich modelu). Verim tomu, ze pokud si aplikace potrebuje index pamatovat o neco dele, ma pouzit QPersistentModelIndex. Nejsem si ovsem jisty, jestli to zcela odpovida na autorovy dotazy.

Založit nové vláknoNahoru

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.