Andreas Kling oznámil, že jelikož už se nevěnuje nezávislému operačnímu systému SerenityOS, ale výhradně jeho webovému prohlížeči Ladybird, přičemž vyvíjí primárně na Linuxu, SerenityOS opustí a Ladybird bude nově samostatný projekt (nový web, repozitář na GitHubu).
Po dvou měsících vývoje byla vydána nová verze 0.13.0 programovacího jazyka Zig (GitHub, Wikipedie). Přispělo 73 vývojářů. Přehled novinek v poznámkách k vydání.
Na čem aktuálně pracují vývojáři GNOME a KDE? Pravidelný přehled novinek v Týden v GNOME a Týden v KDE.
Před 70 lety, 7. června 1954, ve věku 41 let, zemřel Alan Turing, britský matematik, logik, kryptoanalytik a zakladatel moderní informatiky.
NiceGUI umožňuje používat webový prohlížeč jako frontend pro kód v Pythonu. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
Open source platforma Home Assistant (Demo, GitHub, Wikipedie) pro monitorování a řízení inteligentní domácnosti byla vydána ve verzi 2024.6. Z novinek lze vypíchnout lepší integraci LLM (OpenAI, Google AI, Ollama) nebo podporu Matter 1.3.
IKEA ve Spojeném království hledá zaměstnance do své nové pobočky. Do pobočky v počítačové hře Roblox. Nástupní mzda je 13,15 liber na hodinu.
Alyssa Rosenzweig se v příspěvku na svém blogu Vulkan 1.3 na M1 za 1 měsíc rozepsala o novém Vulkan 1.3 ovladači Honeykrisp pro Apple M1 splňujícím specifikaci Khronosu. Vychází z ovladače NVK pro GPU od Nvidie. V plánu je dále rozchodit DXVK a vkd3d-proton a tím pádem Direct3D, aby na Apple M1 s Asahi Linuxem běžely hry pro Microsoft Windows.
Byla vydána (𝕏) květnová aktualizace aneb nová verze 1.90 editoru zdrojových kódů Visual Studio Code (Wikipedie). Přehled novinek i s náhledy a animovanými gify v poznámkách k vydání. Ve verzi 1.90 vyjde také VSCodium, tj. komunitní sestavení Visual Studia Code bez telemetrie a licenčních podmínek Microsoftu.
Byla vydána (Mastodon, 𝕏) nová verze 2024.2 linuxové distribuce navržené pro digitální forenzní analýzu a penetrační testování Kali Linux (Wikipedie). Přehled novinek se seznamem nových nástrojů v oficiálním oznámení.
Po delší odmlce v seriálu o Qt4 jsme zpět. Doufám, že přestávka způsobená mou leností bude omluvena následujícími články, a zároveň doufám, že vážený čtenář snese dočasnou změnu autora.
Tento a příští článek seriálu bude poněkud jiný. Nebude obsahovat tolik kódu jako díly předchozí, ale přesto o oblíbený příklad nepřijdete.
Zároveň ale předpokládám, že tento článek bude pouze odrazovým můstkem ke studiu oficiální dokumentace, kde jsou popsány další metody, signály a závislosti.
V dávných dobách Qt3 se data do všelikých widgetů se seznamy, tabulkami a stromy plnila pomocí tzv. itemů. Qt4 sice takové plnění umožňuje také, ale jedná se pouze o obalené základní (a preferované) třídy view s vhodným modelem. Autoři Qt4 si od nového frameworku slibovali jednodušší práci a zásadní zrychlení interakce s datovými widgety. To druhé se podařilo beze zbytku, to první nechť si rozhodne každý sám dle osobních preferencí.
ukázka widgetů s modely: vlevo QListView, vpravo nahoře QTableView, dole QTreeView
Model-View-Controller (dále MVC) je klasický princip SW architektury. Je tak klasický, až prastarý, že budu dále předpokládat čtenářovu znalost, zkušenost anebo schopnost samostudia. Jen ve stručnosti:
Všechny tyto části by měly být nahraditelné jinými a zároveň na sobě nezávislé.
V terminologii Qt4 je všechno trošku zjednodušeno a roli Controlleru přebírá View, respektive se o práci dělí s objekty Delegate. Proto v Qt platí:
QStringModel * m = new QStringModel(); QStringList lst; lst << "foo" << "bar"; m->setStringList(lst); QListView * v = new QListView(); v->setModel(m); // oba view widgety budou zobrazovat stejna data QTableView * t = new QTableView(); t->setModel(m);
Všechny modely v Qt musí být potomkem třídy QAbstractItemModel. Data samotná nemusí být přímo agregována v instanci modelu, ale model je může na vyžádání načítat podle potřeby (např. modul QtSql).
Jakákoliv informace z modelu je z view odkazována přes QModelIndex, tj. dočasný odkaz na pozici v modelu. Tyto odkazy jsou platné pouze v aktuální okamžik a po změně hodnot dat (oznámeno signálem) se mohou změnit. QModelIndex určuje pozici dané položky svými atributy:
Všechny indexy daného modelu mají alespoň jednoho rodiče – tzv. Root Item. Parent indexu je zásadním údajem ve stromových strukturách, zatímco v jednoúrovňových seznamech není příliš podstatný. Řada a sloupec indexu je vždy uváděna relativně ke svému rodiči.
K přímému použití jsou už implementovány některé další třídy:
Subjektivní postřeh z praxe: QStandardItemModel jsem přestal používat poměrně brzy a začal si reimplementovat modely vlastní přímo z odpovídajících modelů abstratních. Přišlo mi, že spojovaly nevýhody MVC a Item Widgetů.
Každý prvek (item) modelu může obsahovat různá data, která se ve widgetech view zobrazují v různých rolích. View si pak sám přes metodu QAbstractItemModel::data() jednotlivé role načítá a zobrazuje je podle své implementace. Role definují, co bude pro daný index zobrazeno. Celou problematiku nejlépe osvětlí krátká ukázka kódu, popř. přiložený příklad (vytrženo z reálného programu):
QVariant CharTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); … // ctecky pro nevidome, nebo se casto pouziva jako zdroj dat pri drag&drop if (role == Qt::AccessibleTextRole && column == 0) return m_characters[ix]; // tooltip if (role == Qt::ToolTipRole) { QString tmp; tmp.sprintf("%04X", currentChar); return "Unicode:\n0x"+tmp; } // vygeneruj obrazek, ktery se zobrazi jako ikona if (role == Qt::DecorationRole && column == 1) { … generuj obrazek… return QVariant(QPixmap::fromImage(pix)); } // jestlize je to cislo, pak zarovnej doprava jako v tabulkovem procesoru if (role == Qt::TextAlignmentRole) { bool ok; curr.toDouble(&ok); if (ok) return QVariant(Qt::AlignRight | Qt::AlignTop); return QVariant(Qt::AlignTop); } …
V Qt se jako View označují widgety, které jsou schopné zobrazovat data, která jim předává asociovaný model. Přičemž jeden model může být napojen na několik view widgetů.
Všechny view widgety musí být potomkem třídy QAbstractItemView a implementovat alespoň minimální API. Zobrazení dat je plně v režii view widgetu, proto není problém data jednoho modelu jednou zobrazovat jako tabulku, zároveň jako graf anebo třeba stromovou strukturu.
rozdílná vizualizace jednoho modelu: vlevo tabulka (všechna data modelu), vpravo
koláčový graf (pouze „Quantity“)
Jednotlivé prvky modelu, identifikované svým QModelIndexem, může view zobrazovat sám anebo může použít zobrazení pomocí delegáta tak, jak je použito v příkladu – vizte kapitolu „Popis příkladu“.
Další velmi podstatnou úlohou view widgetů je správa označených prvků modelu (selections). Abychom byli přesní, tak výběry nezpracovává přímo view, ale další specializovaný model QItemSelectionModel, který je ale přístupný pouze z dané instance view (QAbstractItemView::selectionModel()).
Standardní widgety, které lze rovnou použít bez jakéhokoliv zásahu:
View widgety se také starají o uživatelský vstup. Proto je v Qt zaveden pojem delegate/delegát. Delegát je widget, kterým view:
Pokud uživatel aktivuje (závisí na jeho platformě/operačním systému) prvek view widgetu (model musí být editovatelný, viz metoda flags()), view vytvoří potřebný delegát a naplní jej odpovídajícími daty z modelu. Pokud programátor nenastavil nově vytvořeného delagáta, view použije jeden z předdefinovaných implicitních delegátů v závislosti na typu editovaných dat – delegáta založeného na QLineEdit, QSpinBox apod.
Na jednoduché editace tyto implicitní editory často stačí, ale komplexnější data je většinou potřeba editovat jinak. Typickým příkladem mohou být multimediální data, struktury geografických systémů atd.
Vlevo implicitní delegát založený na QLineEditu.
Vpravo reimplementovaný delegát s rozsáhlejší
funkcionalitou (editace v databázi, tlačítka
„vlož NULL hodnotu“ a „otevři editor jako modální dialog“).
Implementace specializovaných delegátů musí vycházet z API třídy QAbstractItemDelegate, což bude vyžadovat více ruční práce, anebo použít QStyledItemDelegate či starší QItemDelegate.
Modely samy o sobě neumí zobrazovaná data řadit a filtrovat. Existuje ale specializovaná třída QSortFilterProxyModel, také potomek QAbstractItemModelu, která filtrování a řazení dat umí, aniž by se tato data fyzicky měnila ve zdrojovém modelu.
Modelová situace:
MyModel * model = new MyModel(); // predpokladejme, ze konstruktor nacte data QTableView * view = new QTableView(); // view->setModel(model); nepouzijeme, protoze chceme filtrovat a tridit QSortFilterProxyModel * proxyModel = new QSortFilterProxyModel(); // model se zdrojovymi daty podstrcime proxy modelu proxyModel->setSourceModel(model) // a proxy model nasatvime do view view->setModel(proxyModel); … // uzivatel v nejakem widgetu nastavil filtr, // ten se pres signal a slot prenese do naseho proxy modelu. // Proxy model sam zablokuje nechtena data. Ta ale ve zdrojovem // modelu stale zustavaji proxyModel->setFilterWildcard("foo*");
Existující implementace QSortFilterProxyModel umí řadit pouze lexikograficky, tj. vše se pokusí převést na řetězce a ty pak porovná. Pokud chceme řadit data ve sloupci např. podle číselné hodnoty, anebo podle „ikonek“, musíme implementovat vlastní proxy model, který bude potomkem QSortFilterProxyModelu, a dokáže rozhodnout, které číslo nebo která QPixmapa je větší než druhá (metoda lessThan()).
Jedinou možností, jak plnit data do „seznamových“ widgetů v Qt3 a starších byly tzv. item widgety. V Qt4 je funkcionalita zachována, přičemž se jedná o specializované spojení modelu a odpovídajícího view widgetu.
Vezměme například jednoduchý seznam: Místo QListView a vlastnoručně implementovaného modelu je možné použít QListWidget a data plnit do instancí QListWidgetItem. Často používaný způsob práce je např.:
QListWidget * w = new QListWidget(); QString temp("%1"); for (int i = 0; i < 10; ++i) w->addItem(new QListWidgetItem(QIcon("foo.png"), temp.arg(i));
Z ukázky je patrná zřejmě největší výhoda item widgetů: práce s nimi je, alespoň na první pohled, jednodušší. Není třeba implementovat vlastní modely, vkládání dat je „klasické“.
Na druhý pohled, anebo časem v reálném nasazení, se ale objeví některé zápory daného řešení:
Proto je, stejně jako v jakémkoli jiném případě, velmi vhodné používat mozek a dopředu si zanalyzovat, jaká, kolik a jak často měněná data budu v daném případě zobrazovat. Na seznam tří ikonek v konfiguračním dialogu se mi opravdu psát vlatní model nevyplatí.
V přiloženém balíčku je ukázkový spustitelný program, který ukazuje tak trochu nestandardní použití model-view-delegate. Hlavní funkčnost spočívá v jakési „lupě“, tedy zobrazení fotografie v tabulce, kde každá buňka tabulky patří právě jednomu pixelu obrázku. Při změně aktuální buňky tabulky se mění další informace o buňce v externích widgetech.
Zdrojové kódy příkladu (qmake; make).
Co mne vedlo zrovna k takovému příkladu? Rád bych, aby si čtenář hned zpočátku (pokud je Qt začátečník) uvědomil, že popisovaná doména může mít celu řadu použití, a že jako programátor ji nemusí využívat na prosté seznamy a strohé tabulky.
Implementaci jsem zvolil pomocí výše uvedené techniky „delegát zobrazuje data“, protože je v tomto případě subjektivně snazší kreslit do samostatného widgetu, delegáta, než generovat bitmapu a ošetřovat její korektní umístění do view přes model::data() – není nutné řešit otázku zarovnávání apod.
Klasické použití model-view-delegate si ukážeme v díle následujícím, přičemž celou problematiku okořeníme trochou SQL a databázemi obecně.
Nástroje: Tisk bez diskuse
Tiskni Sdílej:
Skvělé. Děkuji.