<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Gryt</title><link>https://gryt.io/posts/</link><description>Recent content in Posts on Gryt</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>robin@gryt.io (Robin Penea)</managingEditor><webMaster>robin@gryt.io (Robin Penea)</webMaster><lastBuildDate>Thu, 02 Apr 2026 17:27:05 +0200</lastBuildDate><atom:link href="https://gryt.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>The miracle of AI agent-assisted learning</title><link>https://gryt.io/posts/miracle-of-ai-agent-assisted-learning/</link><pubDate>Thu, 02 Apr 2026 17:27:05 +0200</pubDate><author>robin@gryt.io (Robin Penea)</author><guid>https://gryt.io/posts/miracle-of-ai-agent-assisted-learning/</guid><description>&lt;p>Everybody&amp;rsquo;s talking about the impact of AI code generation. It&amp;rsquo;s a big deal. But I found incredible value on the other side: &lt;strong>AI assisted learning&lt;/strong>. Here&amp;rsquo;s my blueprint.&lt;/p></description><content>&lt;p>Everybody&amp;rsquo;s talking about the impact of AI code generation. It&amp;rsquo;s a big deal. But I found incredible value on the other side: &lt;strong>AI assisted learning&lt;/strong>. Here&amp;rsquo;s my blueprint.&lt;/p>
&lt;h2 id="the-situation">The situation&lt;/h2>
&lt;p>There&amp;rsquo;s a specific category of code where I&amp;rsquo;m happy to delegate the implementation to an AI: I&amp;rsquo;m proficient with the language and framework, the feature is well-scoped, edge cases are discussed, and the plan is solid. For any other situation, I strongly disagree with the complete AI delegation. For a toy-project, sure. For a product&amp;hellip; Not so sure.&lt;/p>
&lt;p>To put it simply: by delegating a non-trivial/non-mastered code, I&amp;rsquo;m losing touch with the project. More specifically, &lt;strong>I cannot feel the rough edges of the structure anymore&lt;/strong>. Refactoring becomes a prayer to the LLM gods. I hope it doesn&amp;rsquo;t fuck up, because I won&amp;rsquo;t be able to unfuck it.&lt;/p>
&lt;p>The situation I needed to resolve is the following:&lt;/p>
&lt;blockquote>
&lt;p>When I&amp;rsquo;m still actively learning, how can I leverage the incredible power of an AI agent?&lt;/p>
&lt;/blockquote>
&lt;p>This approach emerged over months of learning new languages, tech stacks, and more.&lt;/p>
&lt;h2 id="tldr">TLDR&lt;/h2>
&lt;p>You can download the &lt;a href="https://gist.github.com/synapticvoid/cc0d61003d968a365fa5564fdba5127d">SKILL.md&lt;/a> and start developing with this approach by typing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/guide I want to implement feature X
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-setup">The setup&lt;/h2>
&lt;p>I&amp;rsquo;ll implement an LRU (Least Recently Used) cache in Python to illustrate my approach. Keep in mind that &lt;strong>it can be applied to virtually any situation&lt;/strong>. The setup is the following, in split windows:&lt;/p>
&lt;ul>
&lt;li>Open an AI Agent (Claude Code, Codex, OpenCode, amp, etc)&lt;/li>
&lt;li>Open your favorite code editor&lt;/li>
&lt;/ul>
&lt;p>In the agent window, write the following prompt:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">I&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">want&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">develop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">an&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LRU&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cache&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Your&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">role&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">guide&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">me&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">through&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">implementation&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">Let&lt;/span>&lt;span class="s1">&amp;#39;s discuss the plan, I will confirm when it&amp;#39;&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ready&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">### Keywords
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">**`&lt;/span>&lt;span class="n">hint&lt;/span>&lt;span class="o">`**&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">—&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">I&lt;/span>&lt;span class="s1">&amp;#39;m stuck. Give one targeted nudge: a single pointer, help.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">Do not review the full implementation.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">**`check`** — I want a review of my work on the current step:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">1. Read the changed files.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2. Critique: correctness, idioms, edge cases.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">3. Suggest specific improvements if needed.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">4. If the step is complete and solid: confirm and present the next step.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">5. If not: give focused feedback and wait for another `check`.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">## Hard constraints:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- **Never write, edit, or create code files** unless the user explicitly asks.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">- **Never give a full implementation** unless explicitly asked — guide, don&amp;#39;&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">solve&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">You&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">may&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">read&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">search&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">the&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">codebase&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tests&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">CLI&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">commands&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>I DO 100% OF THE IMPLEMENTATION&lt;/strong>. The goal of the AI is to provide guidance, not replace me&lt;/li>
&lt;li>&lt;strong>The discussion of the plan is absolutely fundamental&lt;/strong>. This improves the quality of the Agent output, and clarifies your ideas. More on this below&lt;/li>
&lt;li>I keep the &lt;em>&amp;ldquo;unless the user explicitly asks&lt;/em>&amp;rdquo; to still delegate some mechanical refactoring&lt;/li>
&lt;li>&lt;code>hint&lt;/code> and &lt;code>check&lt;/code> make it fast to type, and act as checkpoints&lt;/li>
&lt;/ul>
&lt;h2 id="the-conversation">The conversation&lt;/h2>
&lt;p>Here&amp;rsquo;s what the conversation looked like after my initial prompt (edited for brevity). I intentionally added clarifying questions to show you how we can shape the plan:&lt;/p>
&lt;h3 id="-agent">🤖 Agent&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">LRU Cache — Implementation Plan
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Before we start, what language do you want to implement this in?
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Here&amp;#39;s the plan we&amp;#39;ll follow. Review and confirm:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 1 — Data structures: hashmap for O(1) lookup + doubly linked list for usage order. Define Node and LRUCache with sentinel head/tail.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 2 — List helpers: remove(node) and insert_front(node).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 3 — get(key): hit → move to front + return value. Miss → return -1.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 4 — put(key, value): update or insert at front; if at capacity, evict the tail sentinel&amp;#39;s predecessor first.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 5 — Edge cases: capacity 1, key overwrite, get after eviction.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Does this plan look good, or do you want to adjust anything (language, structure, scope)?
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="-me">👨🏻‍🦱 Me&lt;/h3>
&lt;blockquote>
&lt;p>I will do the implementation in Python. We can use dict for the hashmap, but what could we we use for the
doubly linked list?&lt;/p>
&lt;/blockquote>
&lt;h3 id="-agent-1">🤖 Agent&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Python doesn&amp;#39;t have a built-in doubly linked list, so you have two options:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 1. Roll your own Node class — [...] gives you full control.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 2. Use collections.OrderedDict — it hides all the interesting mechanics.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> For learning purposes, I&amp;#39;d recommend option 1. It forces you to implement the list operations yourself, which
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> is the whole point of the exercise.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Ready to start with Step 1?
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="-me-1">👨🏻‍🦱 Me&lt;/h3>
&lt;blockquote>
&lt;p>I&amp;rsquo;d rather start with &lt;code>collections.OrderedDict&lt;/code> to have a good idea of the general shape, write the tests, and
then implement the custom &lt;code>Node&lt;/code>. We will use those test for non regression. Update the plan.&lt;/p>
&lt;/blockquote>
&lt;h3 id="-agent-2">🤖 Agent&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Good approach. Updated plan:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 1 — Implement with OrderedDict
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Build LRUCache using collections.OrderedDict. Methods: get(key) and put(key, value).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - move_to_end() handles the &amp;#34;mark as recently used&amp;#34; logic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - Eviction is just popping from one end
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 2 — Write tests
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cover: basic get/put, capacity enforcement, eviction order, overwrite existing key, capacity 1, get after
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> eviction.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 3 — Re-implement with custom Node
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Replace the OrderedDict internals with your own doubly linked list + plain dict. The class interface stays
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> identical — tests should pass unchanged.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Step 4 — Verify non-regression
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Run the same test suite against the new implementation.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ---
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Confirm and I&amp;#39;ll walk you through Step 1.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="-me-2">👨🏻‍🦱 Me&lt;/h3>
&lt;blockquote>
&lt;p>Plan confirmed. Write it to &lt;code>plan.md&lt;/code> with the instructions and checklist that you maintain for each step. Add a notes section that will be updated with learning I had along the way.&lt;/p>
&lt;/blockquote>
&lt;h3 id="-agent-3">🤖 Agent&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Step 1 — Implement with OrderedDict
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Create a file lru_cache.py with a class LRUCache that has:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - __init__(self, capacity: int)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - get(self, key: int) -&amp;gt; int — return the value or -1 if not found
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - put(self, key: int, value: int) -&amp;gt; None — insert or update; evict LRU if over capacity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Two hints to get you started:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - OrderedDict.move_to_end(key) moves a key to the right (most recent) by default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> - OrderedDict.popitem(last=False) pops from the left (least recent)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Give it a go — use check when ready.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="-me-3">👨🏻‍🦱 Me&lt;/h3>
&lt;blockquote>
&lt;p>check&lt;/p>
&lt;/blockquote>
&lt;p>And repeat until the feature is done!&lt;/p>
&lt;h2 id="unlocking-the-potential">Unlocking the potential&lt;/h2>
&lt;p>This approach has several benefits:&lt;/p>
&lt;ol>
&lt;li>You still develop, but the Agent is present all along to guide/respond to your questions.&lt;/li>
&lt;li>If the task spans more than a couple of hours, you will likely be interrupted. &lt;strong>&lt;code>plan.md&lt;/code> is the shared state with the LLM&lt;/strong>. Resume tomorrow by prompting &lt;code>Read plan.md and let's continue&lt;/code>.&lt;/li>
&lt;li>You can switch models without starting from scratch.&lt;/li>
&lt;li>Commit the &lt;code>plan.md&lt;/code> file! When you&amp;rsquo;re finished, delete it and carry on.&lt;/li>
&lt;li>If you start a new feature, reset the context window (&lt;code>/new&lt;/code>, &lt;code>/clear&lt;/code> or whatever) and start from scratch. Same process.&lt;/li>
&lt;/ol>
&lt;p>You &lt;strong>absolutely&lt;/strong> should adapt the plan granularity to your level of proficiency. I identified the following levels:&lt;/p>
&lt;h3 id="level-1">&lt;code>Level 1&lt;/code>&lt;/h3>
&lt;p>You have no idea of what&amp;rsquo;s going on. You&amp;rsquo;re discovering something totally new. The best example is when I follow a tutorial, and I&amp;rsquo;m totally unable to predict what should be done next or how. &lt;strong>Here the plan should be very detailed, to guide you almost line by line.&lt;/strong> It&amp;rsquo;s OK to be dumb, the goal is to level up.&lt;/p>
&lt;h3 id="level-2">&lt;code>Level 2&lt;/code>&lt;/h3>
&lt;p>You&amp;rsquo;re starting to &lt;em>feel&lt;/em> the direction of the development. It&amp;rsquo;s still foggy in your head, but there&amp;rsquo;s something. If the Agent tells you to implement X, Y and you have a (vague) idea of how&amp;hellip; &lt;strong>Share it with the agent!&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>To implement X, Y, I&amp;rsquo;m guessing that I should refactor &lt;code>foo.py&lt;/code> and reorder &lt;code>bar()&lt;/code>. Is it right?&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>This is where real mastery happens&lt;/strong>. Form a hypothesis, test it, reassess. That&amp;rsquo;s the key to reach level 3.&lt;/p>
&lt;h3 id="level-3">&lt;code>Level 3&lt;/code>&lt;/h3>
&lt;p>Your opinions about what and how things should be done are getting stronger. You start to challenge the Agent&amp;rsquo;s guidance. This is a great place to be. The plan should start to be more abstract. If it&amp;rsquo;s too detailed, it will rob you of the hard mental training needed to level up. &lt;strong>I&amp;rsquo;ve found myself discussing on a tangent with the Agent for 2h&lt;/strong> because I noticed something in the implementation while coding.&lt;/p>
&lt;p>If you&amp;rsquo;re not already a master of the tech, this step is impossible to explore without writing the code yourself.&lt;/p>
&lt;h3 id="level-4">&lt;code>Level 4&lt;/code>&lt;/h3>
&lt;p>The stack is your home. The goal is only to have a copilot to delegate some mechanical tasks, but the implementation is still yours. The plan should only serve as a global reminder, but it should almost fit in your head. The Agent must also have it with &lt;code>plan.md&lt;/code> :).&lt;/p>
&lt;h2 id="whats-next">What&amp;rsquo;s next?&lt;/h2>
&lt;p>I tested this approach on several topics where I wanted to learn:&lt;/p>
&lt;ul>
&lt;li>Containers orchestration&lt;/li>
&lt;li>Learning a new programming language (Zig)&lt;/li>
&lt;li>Learning advanced Programming Language concepts: type checker, compilers, etc&lt;/li>
&lt;/ul>
&lt;p>It felt like a boon. In a day, I absorbed a week&amp;rsquo;s worth of Docker Swarm concepts. In a couple of weeks, I was writing idiomatic Zig, something that would have taken months on my own. But the real point is this:&lt;/p>
&lt;h3 id="i-learned">I learned.&lt;/h3>
&lt;h3 id="im-now-able-to-understand-and-do-more-the-ai-only-made-it-possible-faster">I&amp;rsquo;m now able to understand and do more. The AI only made it possible &lt;em>faster&lt;/em>.&lt;/h3>
&lt;p>I did not delegate the effort of writing, understanding, forming a hypothesis. The Agent gave me instant access to a &lt;strong>personalized, always available&lt;/strong> teacher. And as a teacher myself, this is so gratifying to continue my learning journey.&lt;/p>
&lt;p>You can download the &lt;a href="https://gist.github.com/synapticvoid/cc0d61003d968a365fa5564fdba5127d">SKILL.md&lt;/a> and start developing with this approach by typing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">/guide I want to implement feature X
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></content></item><item><title>My Favorite (Tech) Talks</title><link>https://gryt.io/posts/favorite-tech-talks/</link><pubDate>Fri, 03 Oct 2025 13:19:48 +0200</pubDate><author>robin@gryt.io (Robin Penea)</author><guid>https://gryt.io/posts/favorite-tech-talks/</guid><description>&lt;p>Watching (tech) talks is a hobby of mine. It puts me in a kind of meditative mood while watching the explanation unfolding.&lt;/p>
&lt;p>Here&amp;rsquo;s a list of my all-time favorite talks. Very few of them are about a specific language / framework. The majority is to make you think and re-assess your mental models.&lt;/p></description><content>&lt;p>Watching (tech) talks is a hobby of mine. It puts me in a kind of meditative mood while watching the explanation unfolding.&lt;/p>
&lt;p>Here&amp;rsquo;s a list of my all-time favorite talks. Very few of them are about a specific language / framework. The majority is to make you think and re-assess your mental models.&lt;/p>
&lt;h3 id="updated---02102025">&lt;em>Updated - 02/10/2025&lt;/em>&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=wo84LFzx5nI">The Big OOPs - Casey Muratori&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=JNCVj_RtdZw">Dynamic Phase Alignment in Audio - Sander J. Skjegstad&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=bUOOaXf9qIM">File Pilot: Inside the Engine - Vjekoslav Krajačić&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=hFMaT9oRbs4">Terence Tao continuing history’s cleverest cosmological measurements - 3Blue1Brown&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="updated---01032021">&lt;em>Updated - 01/03/2021&lt;/em>&lt;/h3>
&lt;p>Here are the new talks (Description in the following tables):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=ShEez0JkOFw">Programming Clojure With Hand Tools - Tim Ewald&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://youtu.be/6avJHaC3C2U">Art of code - Dylan Beattie&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=NGFhc8R_uO4">Indistinguishable From Magic - Todd Fernandez&lt;/a>&lt;/li>
&lt;/ul>
&lt;div style="text-align: center; margin: 2em 0; clear: both;">
&lt;span style="font-size: 1.5em; color: #666;">• • •&lt;/span>
&lt;/div>
&lt;h2 id="general">General&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Title&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://www.destroyallsoftware.com/talks/boundaries">Boundaries - Gary Bernhardt&lt;/a>&lt;/td>
&lt;td>Thoughts on architecture of a project and how to define the boundaries between modules&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=_ahvzDzKdB0">Growing a Language - Guy Steel&lt;/a>&lt;/td>
&lt;td>Deep thinking about the emergence of a programming language and the paradigm that stems from it&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=f84n5oFoZBc">Hammock Driven Development - Rich Hickey&lt;/a>&lt;/td>
&lt;td>How to tackle complex problems&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=aAb7hSCtvGw">How To Design a Good API - Joshua Block&lt;/a>&lt;/td>
&lt;td>A gold mine of advices to build a solid architecture&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=PUv66718DII">Inventing On Principle - Bret Victor&lt;/a>&lt;/td>
&lt;td>A complete different take on how programming is a tool to create&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=8_UoDmJi7U8">Livable Code - Sarah Mei&lt;/a>&lt;/td>
&lt;td>What does software archicture mean today? What is going with this building metaphore?&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=oytL881p-nQ">Simple Made Easy - Rich Hickey&lt;/a>&lt;/td>
&lt;td>The difference between simple and easy. Simple can become complexe, easy becomes complicated&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=wo84LFzx5nI">The Big OOPs - Casey Muratori&lt;/a>&lt;/td>
&lt;td>Where does OOP come from? How is it used and mis-used? 2h30, and every minute is worth it&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=WELBnE33dpY">What We Know We Don't Know - Hillel Wayne&lt;/a>&lt;/td>
&lt;td>We pride ourselves with our &amp;ldquo;scientific thinking&amp;rdquo;. We are however deeply driven by beliefs. What works? what doesn&amp;rsquo;t? What can we really prove?&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="low-level">Low Level&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Title&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=VUT386_GKI8">A talk near the future of Python - David Bazley&lt;/a>&lt;/td>
&lt;td>1h of LiveCoding to build a complete machine, compiled in WASM. Bonkers.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=WDIkqP4JbkE">CPU Caches Why it Matters - Scott Meyers&lt;/a>&lt;/td>
&lt;td>How does the CPU works and how can we leverage its power&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=8fi7uSYlOdc">Containers From Scratch - Liz Rice&lt;/a>&lt;/td>
&lt;td>Build Docker from scratch in 30mins in Go to understand what Docker is about&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=rX0ItVEVjHc">Data-Oriented Design and C++&lt;/a>&lt;/td>
&lt;td>Technical architecture to yield the maximum performances with your data structures&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=JNCVj_RtdZw">Dynamic Phase Alignment in Audio - Sander J. Skjegstad&lt;/a>&lt;/td>
&lt;td>How to compensate phase-shifting when recording 2 mics with incredible graphics&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=bUOOaXf9qIM">File Pilot: Inside the Engine - Vjekoslav Krajačić&lt;/a>&lt;/td>
&lt;td>What&amp;rsquo;s more common than a FileExplorer? Watch and see how incredibly optimized it can get&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=NGFhc8R_uO4">Indistinguishable From Magic - Todd Fernandez&lt;/a>&lt;/td>
&lt;td>Todd worked on CPU production and explains the process of building one. So much complexity&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://vimeo.com/341115830">JSON Explained - Jesse Wilson&lt;/a>&lt;/td>
&lt;td>Deep dive on how JSON parsing works and why it works that way&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="functional-programming">Functional programming&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Title&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=ShEez0JkOFw">Programming Clojure With Hand Tools - Tim Ewald&lt;/a>&lt;/td>
&lt;td>An elegant analogy of programming and woodworking. It starts slowly, but so satisfying&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=vK1DazRK_a0">Solving Problems The Clojure Way - Raffal Dittwald&lt;/a>&lt;/td>
&lt;td>3 ways to solve the same problem: procedural, object oriented, functional (written in JavaScript)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=QyJZzq0v7Z4">Why isn&amp;rsquo;t Functional Programming the Norm ? - Richard Feldman&lt;/a>&lt;/td>
&lt;td>History of programming language to understand why the functional paradigm is adopted (or not) by the industry&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="fun--crazy">Fun &amp;amp; Crazy&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Title&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://youtu.be/6avJHaC3C2U">Art of code - Dylan Beattie&lt;/a>&lt;/td>
&lt;td>An exploration of every form of art using code. Fractals, music, Conway&amp;rsquo;s game of life, you name it&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.deconstructconf.com/2018/tom-murphy-vii-improper-hierarchy">Improper Hierarchy - Tom Murphy&lt;/a>&lt;/td>
&lt;td>This dude reverse engineers a Nintendo console in a peculiar way. Mind blowing.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.destroyallsoftware.com/talks/wat">Wat - Gary Bernhardt&lt;/a>&lt;/td>
&lt;td>Lightning talk about JavaScript quirkness&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=5R9eywArFTE">How to Hack a Painting - Tyler Hobbs&lt;/a>&lt;/td>
&lt;td>Mixing art and programming to (re)produce abstract paintings&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="non-tech-talks">Non Tech Talks&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Title&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=Pb5oIIPO62g">Creativity - John Cleese&lt;/a>&lt;/td>
&lt;td>Monty Python&amp;rsquo;s John Cleese explains how he sees and ritualizes creativity. Fun and insightful&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=so2nCu2Jkbc">Higgs Explained (CERN)&lt;/a>&lt;/td>
&lt;td>A multi-part CERN serie that explains the Higgs boson in layman terms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=hFMaT9oRbs4">Terence Tao continuing history’s cleverest cosmological measurements - 3Blue1Brown&lt;/a>&lt;/td>
&lt;td>The famous mathematician Terence Tao explains how we measure astronomical distances&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=vtIzMaLkCaM">The Craft of Writing Effectively - Larry McEnerney&lt;/a>&lt;/td>
&lt;td>Mr McEnerney deconstructs the non-fiction writing process to be the most effective&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://www.youtube.com/watch?v=RjD1aLm4Thg">What Bodies Think About: Bioelectric Computation Outside the Nervous System&lt;/a>&lt;/td>
&lt;td>An incredible biotech talk that explores how the body might be linked to eletrical signals&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table></content></item><item><title>What Unit Tests tells about Human Nature</title><link>https://gryt.io/posts/unit-tests-human-nature/</link><pubDate>Mon, 17 Jun 2019 10:35:32 +0100</pubDate><author>robin@gryt.io (Robin Penea)</author><guid>https://gryt.io/posts/unit-tests-human-nature/</guid><description>It&amp;rsquo;s Friday night. after an intense week of work, you&amp;rsquo;re about to take off to enjoy your week-end. Your boss storms in the office:
&amp;ldquo;You&amp;rsquo;re still here! Great, I need you to add a hot fix in our Phronabulax System. We expect a demo Monday morning very early, everything has to be ready!&amp;rdquo;
Well. That was unexpected. You know your boss, he/she wouldn&amp;rsquo;t ask if it wasn&amp;rsquo;t really urgent. You grumble a bit but still go dig into the code to do the work.</description><content>&lt;p>It&amp;rsquo;s Friday night. after an intense week of work, you&amp;rsquo;re about to take off to enjoy your week-end. Your boss storms in the office:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;You&amp;rsquo;re still here! Great, I need you to add a hot fix in our Phronabulax System. We expect a demo Monday morning very early, everything has to be ready!&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Well. That was unexpected. You know your boss, he/she wouldn&amp;rsquo;t ask if it wasn&amp;rsquo;t really urgent. You grumble a bit but still go dig into the code to do the work.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/bored.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/bored.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >My enthusiasm and fervor to do this on a Friday night cannot be overstated. Thank you boss.&lt;/figcaption>
&lt;/figure>
&lt;p>30 minutes later, you commit your work and breathe a sigh of relief. &lt;strong>Wait! You completely forgot the tests! You didn&amp;rsquo;t add a new test covering the modification&lt;/strong>. There might be unintended side effects. What shall you do?&lt;/p>
&lt;p>&lt;em>Without a doubt, you shutdown your computer and leave. This can wait Monday, and the fix was trivial!&lt;/em>&lt;/p>
&lt;p>&lt;strong>Are you a bad developer? An impostor? Nope, I certainly hope not. I think you&amp;rsquo;re just a Human.&lt;/strong>&lt;/p>
&lt;h1 id="what-is-a-unit-test">What is a Unit Test?&lt;/h1>
&lt;p>I won&amp;rsquo;t delve into the details of Unit Testing, TDD (Test Driven Development) or any &lt;em>purely&lt;/em> technical aspect. &lt;strong>I&amp;rsquo;d like to scratch what is hidden beneath Unit Tests. More precisely, what it reveals about us, about our Human Nature.&lt;/strong>&lt;/p>
&lt;p>The formal definition of a Unit test by &lt;a href="https://en.wikipedia.org/wiki/Unit_testing">Wikipedia&lt;/a> states:&lt;/p>
&lt;blockquote>
&lt;p>In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use&lt;/p>
&lt;/blockquote>
&lt;p>As any proper formal definition, it&amp;rsquo;s absolutely opaque for anyone who doesn&amp;rsquo;t already know what is a Unit Test.&lt;/p>
&lt;p>One might ask &lt;em>&amp;ldquo;What&amp;rsquo;s the point of the definition then?&amp;rdquo;&lt;/em>. To look educated and unreachable my dear!&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/bridge-blueprint.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/bridge-blueprint.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >When you ask why the bridge is red and your friend starts explaining.&lt;/figcaption>
&lt;/figure>
&lt;p>A Unit Tests is a piece of code that you write that will execute a subset of your real program to verify its behavior.&lt;/p>
&lt;p>If I input &lt;code>5&lt;/code>, it should spit &lt;code>&amp;quot;Hello&amp;quot;&lt;/code>, If I input &lt;code>42&lt;/code>, it should crash, and so on.&lt;/p>
&lt;h1 id="i-dont-have-the-time-to-write-tests">I don&amp;rsquo;t have the time to write tests&lt;/h1>
&lt;p>And I agree with you, me neither. &lt;em>Writing tests requires time, the structure of the test is a craft in itself&lt;/em>. Having a decent test coverage is a real effort. Still, I have a question for you:&lt;/p>
&lt;h3 id="do-you-test-what-you-develop">Do you test what you develop?&lt;/h3>
&lt;p>I mean it in the simplest sense: &lt;strong>you write some code, run the program and check if it works as intended. Chances are, you are doing it all the time&lt;/strong>. You might even yell at your computer because the compilation / startup time to test it takes more than 2,4 seconds.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/harakiri.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/harakiri.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Compilation took 4 seconds, my mug is empty and the Coffee Machine is broken. This is unbearable.&lt;/figcaption>
&lt;/figure>
&lt;p>&lt;strong>Unit Tests are merely an automated way of doing what you already do by hand&lt;/strong>. But I understand, it&amp;rsquo;s time consuming and the feature has to be delivered yesterday. So why write tests?&lt;/p>
&lt;h2 id="human-after-all">Human After All&lt;/h2>
&lt;p>&lt;em>Because you are just a Human! I believe testing reveals something very interesting about us, as Humans: we&amp;rsquo;re absolutely fallible, and even more while coding.&lt;/em>&lt;/p>
&lt;p>Don&amp;rsquo;t believe me?&lt;/p>
&lt;p>What is the quality of you work when you slept 3 hours ? When there&amp;rsquo;s a disturbing noise in the office? When you neighbor keeps chewing that dreadful, juicy candy?&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/terminator.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/terminator.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >My sleep routine is tight, my work is perfect. Any question?&lt;/figcaption>
&lt;/figure>
&lt;p>Let&amp;rsquo;s assume you always sleep 8 hours each night and that your noise canceling headphones does wonder.&lt;/p>
&lt;p>&lt;strong>What about &lt;em>that&lt;/em> dark corner of the project?&lt;/strong> The one that makes everyone look at their feet when you need to modify it? It was written ages ago by a dark ninja-shaman on a moonless night? Nobody understands it and even less dares to touch it?&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/project-octopus.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/project-octopus.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >I don't want to make a fix below water level. PLEASE GOD NO!&lt;/figcaption>
&lt;/figure>
&lt;p>Yes, it makes you cringe every time you approach it.&lt;/p>
&lt;p>That&amp;rsquo;s exactly why Unit Tests exist: because you&amp;rsquo;re just a Human. You can be tired, angry, not concentrated. &lt;strong>Because you don&amp;rsquo;t have the time nor the energy, the Unit Test will execute the 526 cases that you don&amp;rsquo;t want to check yet another time.&lt;/strong>&lt;/p>
&lt;h2 id="down-the-rabbit-hole">Down the Rabbit Hole&lt;/h2>
&lt;p>What I find incredible is not that the discovery that we are deeply flawed. The Atlantic Ocean could be filled with the ink spilled on this topic for the last 3000 years.&lt;/p>
&lt;p>&lt;strong>What is amazing is our resourcefulness&lt;/strong>. As soon as we admit that eating every dish of that damn Chinese self-serve buffet turns us into brainless oysters for the rest of the afternoon, we start constructing countermeasures.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/oyster.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/oyster.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >I shall open at the afternoon break. Until then, let me die in my soy sauce.&lt;/figcaption>
&lt;/figure>
&lt;p>Unit Test are for me the product of humble yet very sharp minds, who acknowledge their weakness but still want to get the job done, in the best possible way.&lt;/p>
&lt;p>&lt;strong>See it that way: we are tool builders. We don&amp;rsquo;t have claws or big furs, so we build tools to compensate. Writing tests is an answer to our daily stupidity.&lt;/strong>&lt;/p>
&lt;h2 id="the-beatitude-of-the-fool">The Beatitude Of The Fool&lt;/h2>
&lt;p>When I started doing tests, I was closer to the attitude of the rebel Teenager than the excited newbie: &lt;em>&amp;ldquo;Yeaaaah! Whatever. I still ship good code. Meh&amp;rdquo;&lt;/em>.&lt;/p>
&lt;p>But, as the test coverage grew, I started to notice an interesting pattern:&lt;/p>
&lt;ol>
&lt;li>Customer requests feature X&lt;/li>
&lt;li>I implement feature X&lt;/li>
&lt;li>I don&amp;rsquo;t write any test, feeling confident. The day was sunny, my focus was Zen-like while writing the code&lt;/li>
&lt;li>I ship the feature X&lt;/li>
&lt;li>Customer gets back to me &lt;em>&amp;ldquo;Thanks Robin! X works great, however Y is broken!&amp;rdquo;&lt;/em>&lt;/li>
&lt;li>&lt;em>I&amp;rsquo;m sorry what?&lt;/em> I dig in, and see the regression&lt;/li>
&lt;li>I (finally) write a test, the test fails. I fix the bug and ship again&lt;/li>
&lt;/ol>
&lt;p>Rince and repeat. That feeling, between the &lt;em>&amp;ldquo;I&amp;rsquo;m sorry what?&amp;rdquo;&lt;/em> and the understanding that by simply adding a couple of tests, the bug could have been prevented is priceless. It&amp;rsquo;s a mix of shame and anger.&lt;/p>
&lt;p>After a couple of iterations, I &lt;em>systematically&lt;/em> wrote a test for any feature. And that my friends, was a pure Dopamine shot.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/parachute-hole.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/parachute-hole.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >We have a great Tests Coverage. The project is bulletproof!&lt;/figcaption>
&lt;/figure>
&lt;p>&lt;strong>Now, I see my tests as a handcrafted parachute. I can code and feel the bliss of the Fool. I can be Human, stupid, tired, and my tests have my back. Oh, what a feeling!&lt;/strong>&lt;/p>
&lt;h2 id="side-effects-and-cult">Side Effects and Cult&lt;/h2>
&lt;p>A beautiful side-effect of testing your program, &lt;em>is that it puts you in the shoes of the user of your API&lt;/em>. You write an incredibly efficient program, great! How usable it is?&lt;/p>
&lt;p>Remember, you don&amp;rsquo;t have the time to write tests, &lt;strong>do you think your colleague will have the time to understand your crazy Logorrhea of functions, variables and convoluted classes? Of course not!&lt;/strong>&lt;/p>
&lt;p>Testing gives you a feedback on how your API &lt;em>feels&lt;/em>. This is extremely useful, because you won&amp;rsquo;t be able to know that before having to modify it again.&lt;/p>
&lt;p>However, I want to add a big nuance:&lt;/p>
&lt;h3 id="not-everything-should-be-tested">Not everything should be tested&lt;/h3>
&lt;p>You know how it&amp;rsquo;s called when someone tells you &lt;em>&amp;ldquo;If you don&amp;rsquo;t test X, you&amp;rsquo;re worthless&amp;rdquo;&lt;/em>? &lt;strong>Religion Fever&lt;/strong>.&lt;/p>
&lt;p>&lt;em>In my humble opinion, it&amp;rsquo;s as stupid to refuse to test anything than to insist on testing absolutely everything.&lt;/em>&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/unit-test-human-nature/prayer.jpg"
onclick="openLightbox('\/img\/unit-test-human-nature\/prayer.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Our brother Robin did not write a test for the project POC-to-be-dropped-and-rewritten-anyway. He shall be excommunicated from the Church Of The Right Software.&lt;/figcaption>
&lt;/figure>
&lt;p>Some situations makes the tests impossibly hard to write or maintain.&lt;/p>
&lt;p>In many cases, I don&amp;rsquo;t write any UI test. Why? Because I have a limited time, and I honestly care little about UI testing. It makes perfect sense for huge projects, with very strict UI designs.&lt;/p>
&lt;p>&lt;strong>I see testing more as a state of mind, a humbling affirmation of our imperfection and a celebration of our imagination to face it.&lt;/strong>&lt;/p>
&lt;p>&lt;em>As with any craft, it has to be practiced and repeated&lt;/em>. Sometimes your tests will be useless and badly written. Other times it will fit perfectly to your project.&lt;/p>
&lt;p>&lt;strong>It takes time, but it&amp;rsquo;s worthwhile. You&amp;rsquo;ll thank yourself for doing it, trust me.&lt;/strong>&lt;/p></content></item><item><title>Powerpoint and Chickens</title><link>https://gryt.io/posts/powerpoint-and-chickens/</link><pubDate>Thu, 20 Dec 2018 15:32:12 +0100</pubDate><author>robin@gryt.io (Robin Penea)</author><guid>https://gryt.io/posts/powerpoint-and-chickens/</guid><description>Do you remember that day — probably a rainy one — when you sat in a conference room? Your heart was filled with eagerness to dive into the speaker&amp;rsquo;s mind. You even prepared by documenting about the topic, to fully comprehend the genius striding in front of you.
The common purpose And then, as the drama unfolded in the room and the rain kept pouring outside, this slide came up:</description><content>&lt;p>&lt;strong>Do you remember that day — probably a rainy one — when you sat in a conference room? Your heart was filled with eagerness to dive into the speaker&amp;rsquo;s mind. You even prepared by documenting about the topic, to fully comprehend the genius striding in front of you.&lt;/strong>&lt;/p>
&lt;h2 id="the-common-purpose">The common purpose&lt;/h2>
&lt;p>And then, as the drama unfolded in the room and the rain kept pouring outside, this slide came up:&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/powerpoint-presentation.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/powerpoint-presentation.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Here's a short summary of my ideas, in 5 small bullet points.&lt;/figcaption>
&lt;/figure>
&lt;p>&lt;em>&amp;ldquo;Oh dear&amp;rdquo;&lt;/em>, you whispered, &lt;em>&amp;ldquo;Not again&amp;hellip; Not this Powerpoint presentation&amp;hellip;&amp;rdquo;&lt;/em>. You know it when you see it. The Powerpoint to end all Powerpoints. The one with slides so dense, that a pair of opera-binoculars should be provided on each chair.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/opera-binoculars.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/opera-binoculars.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >The mandatory tool to brain numbing presentations.&lt;/figcaption>
&lt;/figure>
&lt;p>Yes my friend, you&amp;rsquo;re about to spend a long, excruciating hour. You&amp;rsquo;ll watch the speaker spit 3 zillion data per minute, while your brain turns into a mishmash of tears, agony, and forgotten eastern curses inherited from your mother side. Who would have guessed that these memories could be retrieved? Bonkers!&lt;/p>
&lt;p>I call this phenomenon the &lt;strong>Chicken Hypnosis&lt;/strong>, or the &lt;em>Gallus Gallus Apathy&lt;/em> — Latin makes things so &lt;em>chic&lt;/em> —. We have all seen dozens of Powerpoint presentations where you ask yourself: &lt;strong>What is the point of this Powerpoint? To help the speaker or to hypnotize chickens, namely, the audience?&lt;/strong>&lt;/p>
&lt;h2 id="the-real-purpose">The &lt;em>real&lt;/em> purpose&lt;/h2>
&lt;p>Much ink has been spilled on the Powerpoint issue: Back in 2003, the NASA pointed that &lt;a href="https://www.nytimes.com/2003/12/14/magazine/2003-the-3rd-annual-year-in-ideas-powerpoint-makes-you-dumb.html">Powerpoint makes you dumb&lt;/a>, some physicists chose to &lt;a href="https://www.symmetrymagazine.org/article/march-2014/physics-by-hand">ban Powerpoint altogether&lt;/a> and searching for &amp;ldquo;Powerpoint hate&amp;rdquo; on Google yields &lt;a href="https://www.google.com/search?q=powerpoint+hate">24 400 000 results&lt;/a>.&lt;/p>
&lt;p>But still, (almost) everybody uses a form of Powerpoint to do a presentation. It can&amp;rsquo;t be for nothing! &lt;strong>Indeed, I believe that a Powerpoint has a great purpose: to &lt;em>support&lt;/em> the speaker. To provide some anchor, in the turmoil of a long explanation.&lt;/strong>&lt;/p>
&lt;p>A Powerpoint purpose is 2-folded:&lt;/p>
&lt;ul>
&lt;li>&lt;em>&lt;strong>Condense information&lt;/strong>&lt;/em> in a digestible format to sum important points&lt;/li>
&lt;li>&lt;em>&lt;strong>Provide multimedia content&lt;/strong>&lt;/em> to animate the presentation&lt;/li>
&lt;/ul>
&lt;p>But, what &lt;strong>really&lt;/strong> matters is the explanation of the speaker. Filling slides up to death is an innocent act of laziness, not malevolence.&lt;/p>
&lt;h2 id="how-did-we-get-there">How did we get there?&lt;/h2>
&lt;h3 id="tool--user">Tool &amp;amp; User&lt;/h3>
&lt;p>I think Powerpoint is a very efficient tool. Without any prior knowledge, you can put together titles, bullet points and incredible-yet-utterly-useless animations.&lt;/p>
&lt;p>&lt;strong>However, who is using Powerpoint? For the vast majority, people who are not professional speakers (me too!)&lt;/strong>. They are business men, developers, physicists, or anything that is not directly centered on a Speaker / User Experience. For them, &lt;em>making a presentation is mandated by their job, but they surely didn&amp;rsquo;t study to do presentations or animate conferences.&lt;/em>&lt;/p>
&lt;p>Mix the ease of use of Powerpoint with the lack of interest of its users, and you get this new Super-Villain: &lt;strong>The Destroyer Of Minds (DOM for the closest friends)&lt;/strong>.&lt;/p>
&lt;h3 id="misuse-of-the-tool">Misuse of the Tool&lt;/h3>
&lt;p>From here, the resulting presentation has many chances of being failed. We already covered the densely-packed slides, but there&amp;rsquo;s another killer hiding in plain sight: &lt;strong>The lifeless presentation&lt;/strong>.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/deck-alone.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/deck-alone.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Will he finish the talk? Will he jump? Will the restaurant still have my favorite Cheesecake?&lt;/figcaption>
&lt;/figure>
&lt;p>Everything &lt;em>feels&lt;/em> empty. There can be a lot on each slide&amp;hellip; Or not. The speaker is slow and monotonous. Add a last slide, and the abyss will swallow you in the eternal bliss of infinite boredom.&lt;/p>
&lt;p>In this case, the &lt;em>Powerpoint has taken the driver seat&lt;/em>. The trigger to speak is the &lt;em>&amp;ldquo;Next Slide Button&amp;rdquo;&lt;/em> — not what the speaker intends to say.&lt;/p>
&lt;h3 id="down-the-rabbit-hole">Down the Rabbit Hole&lt;/h3>
&lt;p>It gets even worse. To be so easy to use, Powerpoint has a killer-feature: &lt;strong>The Bullet Point&lt;/strong>.&lt;/p>
&lt;p>It might seem innocuous, but I think it&amp;rsquo;s exactly the reason for which presentations get such a bad press: &lt;strong>it transforms complex ideas into a sequence of bullet points, waiting to be unstacked, one by one&lt;/strong>.&lt;/p>
&lt;p>A presentation can have multiple purposes: make you think, spark a heated debate, provide a different perspective on a known issue.&lt;/p>
&lt;p>A set of bullet points is a drowning of creativity. &lt;strong>Said plainly, you cannot express complex ideas with bullet points&lt;/strong>. &lt;em>It&amp;rsquo;s like describing a car with karate moves: it&amp;rsquo;s not meant for that&lt;/em>.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/karate.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/karate.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >"Here are the seats, and you can lower the windows like so." — Damn, this picture could almost make the opposite point! Note to self: choose a worse karate picture.&lt;/figcaption>
&lt;/figure>
&lt;p>Still, why do we cram so much data in our slides? &lt;em>Because we have the illusion that the audience must know everything&lt;/em>. &lt;strong>WRONG&lt;/strong>! 90% of the time, you are presenting your deep work to people who might not have the slightest idea of your passion.&lt;/p>
&lt;p>They are just curious / forced to be here / bored / sitting in the wrong conference room.&lt;/p>
&lt;blockquote>
&lt;p>Oh dear, this is about the Java programming language? I&amp;rsquo;m just a Peruvian Coffee Beans aficionado!&lt;/p>
&lt;/blockquote>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/coffee-bag.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/coffee-bag.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >As you can see, the Abstract Factory Pattern makes it so easy to spawn new objects.&lt;/figcaption>
&lt;/figure>
&lt;h2 id="how-should-we-approach-it">How should we approach it?&lt;/h2>
&lt;p>You might yell at me:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;Very well then, should I empty my slides? Fill them? Simplify everything? Put only complex diagrams?&amp;rdquo;&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>Breath. I understand. You think about &lt;em>that&lt;/em> presentation you already hated even before finishing it. We all have been culprit of such an abomination.&lt;/p>
&lt;p>I will calmly answer with 2 questions: &lt;strong>Who is your audience? What are your constraints?&lt;/strong>&lt;/p>
&lt;p>&lt;em>If you can&amp;rsquo;t answer, the presentation is already doomed.&lt;/em> Call it a day and start doing some improv class, you&amp;rsquo;ll have better chances.&lt;/p>
&lt;p>&lt;em>Is your audience young children, discovering science? International astrophysists waiting for your Boson revelation? Do you have 20 mins or 2 hours? Can you draw something while talking? Is it online or in a real room, with real people?&lt;/em>&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/speaker.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/speaker.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >They told me that it will be in Sydney's Opera with Kung-Fu Senseis. Something's Wrong.&lt;/figcaption>
&lt;/figure>
&lt;p>&lt;strong>This is going to determine the shape and content of your presentation&lt;/strong>. Who cares if you used a distributed micro-service architecture if your audience can&amp;rsquo;t tell the difference between Internet and Google Chrome?&lt;/p>
&lt;p>If the answer to these questions keeps Powerpoint in the race, I can see 2 possible presentation general &amp;ldquo;shapes&amp;rdquo;.&lt;/p>
&lt;h3 id="tbp-aka-the-big-presentation">TBP aka The Big Presentation&lt;/h3>
&lt;p>You give an important talk, on a topic you have been working on for months / years. &lt;strong>By God, reduce the load per slice, add schemas and animations to make it easier to grasp the idea&lt;/strong>. Yes, use bullets point to sum up and the end of a section, but not to articulate a complex topic.&lt;/p>
&lt;p>Your hard work deserves a proper presentation. It would be a shame to flush your years-long sweat in the toilet of &amp;ldquo;Slides Mediocrity&amp;rdquo;. Your audience &lt;em>will probably not&lt;/em> have the same depth as you do. And if they do, the questions is the perfect time to dig in.&lt;/p>
&lt;p>If you already know you&amp;rsquo;re dealing with specialists, add references, data, but remember: &lt;strong>YOU&lt;/strong> are the reason they&amp;rsquo;re wasting their life in this small room for an hour. Not your slides. Not your fancy animations.&lt;/p>
&lt;h3 id="machine-gun-slides">Machine-gun Slides&lt;/h3>
&lt;p>If you have (like myself in recent instances) have to produce hundreds of slides on a topic, you have to be efficient rather than beautiful.&lt;/p>
&lt;p>Getting your point across without spending 2 hours on each Powerpoint can make a difference. &lt;strong>Again, know your constraints and deal with it&lt;/strong>.&lt;/p>
&lt;p>Some people have such a mastery of the content that the Powerpoint becomes a character of the presentation. For example, Larry Lessig, with his &lt;em>&amp;ldquo;Lessig Method&amp;rdquo;&lt;/em> is known to &lt;a href="https://www.ted.com/talks/larry_lessig_says_the_law_is_strangling_creativity">fire many, many slides&lt;/a> for a single presentation. And it works!&lt;/p>
&lt;h2 id="a-tool--nothing-more-nothing-less-">A tool — nothing more, nothing less —&lt;/h2>
&lt;p>Everyone has a love / hate relationship with Powerpoint. In the end, it&amp;rsquo;s just a tool. Like any tool, it can be properly used and misused.&lt;/p>
&lt;p>Because it&amp;rsquo;s easy to start with, you can dump your Ph.D thesis in your slides. Job done, back to &lt;em>real&lt;/em> work. Not so fast!&lt;/p>
&lt;p>Your presentation &lt;strong>is a part of your work&lt;/strong>. Don&amp;rsquo;t spoil it by doing a cheap stunt. You deserve better.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/powerpoint-chicken/powerpoint-presentation-chicken.jpg"
onclick="openLightbox('\/img\/powerpoint-chicken\/powerpoint-presentation-chicken.jpg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >The Gallus Gallus Apathy can, and should be prevented! To arms my fellow Powerpointers!&lt;/figcaption>
&lt;/figure>
&lt;p>Powerpoint is &lt;em>a&lt;/em> tool, not &lt;em>the&lt;/em> tool. &lt;strong>When you only have a hammer, every problem looks like a nail.&lt;/strong>&lt;/p>
&lt;p>Surrendering is transforming yourself into a nail, while Powerpoint is happily smashing on you.&lt;/p>
&lt;p>Surrendering, is transforming your audience into chickens.&lt;/p>
&lt;p>&lt;strong>You&amp;rsquo;re not a Nail nor a Chicken. You&amp;rsquo;re a Human. Embrace it.&lt;/strong>&lt;/p></content></item><item><title>Qt Model / View</title><link>https://gryt.io/posts/understanding-qt-modelview-read/</link><pubDate>Thu, 25 Oct 2018 15:00:12 +0200</pubDate><author>robin@gryt.io (Robin Penea)</author><guid>https://gryt.io/posts/understanding-qt-modelview-read/</guid><description>The Shock The first time I&amp;rsquo;ve read the Qt Model / View documentation, I was shocked. Flabbergasted. Blown away. I started with a vague idea of the purpose of the Model / View, and finished convinced that I understood even less. My first implementation of the pattern was an utter failure: it looked like a Frankenstein implementation of a boilerplate code written by a crazy monkey. This was not right, the Qt documentation is known to be of quality — detailed, explanatory and coherent.</description><content>&lt;h2 id="the-shock">The Shock&lt;/h2>
&lt;p>&lt;strong>The first time I&amp;rsquo;ve read the &lt;a href="http://doc.qt.io/qt-5/model-view-programming.html">Qt Model / View documentation&lt;/a>, I was shocked. Flabbergasted. Blown away. I started with a vague idea of the purpose of the Model / View, and finished convinced that I understood even less. My first implementation of the pattern was an utter failure: it looked like a Frankenstein implementation of a boilerplate code written by a crazy monkey. This was not right, the Qt documentation is known to be of quality — detailed, explanatory and coherent&lt;/strong>.&lt;/p>
&lt;p>After multiple attempts and many hours reading and re-reading the doc, I started to grasp the concept. The deep understanding came when I had to write about this topic. Heck, I&amp;rsquo;ve written &lt;a href="https://www.amazon.com/Mastering-Qt-stunning-cross-platform-applications/dp/1786467127/">whole chapters&lt;/a> on the Model / View!&lt;/p>
&lt;p>I entertain myself imagining how this marathon-length documentation came to be:&lt;/p>
&lt;blockquote>
&lt;p>A Qt framework developer glances over the Model / View documentation and utters to himself &lt;strong>&amp;ldquo;This is not right. It makes no sense at all&amp;rdquo;&lt;/strong>. He then pounds on his keyboard in a sweated rush of the documentation-muse inspiration. He looks at his masterpiece and proudly says &lt;strong>&amp;ldquo;It&amp;rsquo;s now much clearer! Let&amp;rsquo;s add my stone to the cathedral and update the official doc&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;/blockquote>
&lt;p>After several iterations of this episode, the current doc now takes longer to read than The Lord Of the Rings. And boy, it&amp;rsquo;s a long story! Don&amp;rsquo;t get me wrong, the documentation is exhaustive in the deepest sense of exhaustiveness. But, it&amp;rsquo;s also exhausting when you discover the Model / View pattern. The underlying principle is drown in a plethora of details which are useless at the beginning.&lt;/p>
&lt;p>This article aims for exactly that, &lt;strong>understanding the Model / View pattern with a simple example&lt;/strong>. I won&amp;rsquo;t cover all the bells and whistles that you can implement with Model / View.&lt;/p>
&lt;h2 id="tldr--show-me-the-code">TL;DR — Show me the code!&lt;/h2>
&lt;p>You just want the source code to understand all by yourself? Everything is available on &lt;a href="https://github.com/synapticvoid/sample-qt-model-view">GitHub&lt;/a>. Don&amp;rsquo;t worry, it&amp;rsquo;s less than 150 lines of C++.&lt;/p>
&lt;h2 id="the-goal">The Goal&lt;/h2>
&lt;p>The goal is simple: build a simple Model / View example of a list of countries. It will be a read-only Model, no editing to keep things really simple (the edition will be covered in a separate post). We&amp;rsquo;ll display a list of countries and upon a country selection, a country details widget will be updated.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/qt-modelview-read/model-view-read-countries.png"
onclick="openLightbox('\/img\/qt-modelview-read\/model-view-read-countries.png')"
style="cursor: pointer;" />
&lt;figcaption class="center" >The final Model / View application&lt;/figcaption>
&lt;/figure>
&lt;p>A country is composed of 3 pieces of data:&lt;/p>
&lt;ul>
&lt;li>Name&lt;/li>
&lt;li>Flag&lt;/li>
&lt;li>Capital&lt;/li>
&lt;/ul>
&lt;h2 id="the-principle">The Principle&lt;/h2>
&lt;p>In my mind, the Model / View has 2 main goals:&lt;/p>
&lt;ol>
&lt;li>Cleanly separate the model&amp;rsquo;s data source from the display. This makes the update of the data source less prone to impact the View side.&lt;/li>
&lt;li>Leverage the Qt signal / slot mechanism to update multiple views from a unified data source.&lt;/li>
&lt;/ol>
&lt;p>Here&amp;rsquo;s a simplified diagram of the Model / View architecture:&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/qt-modelview-read/model-view.svg"
onclick="openLightbox('\/img\/qt-modelview-read\/model-view.svg')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Model / View architecture diagram&lt;/figcaption>
&lt;/figure>
&lt;ul>
&lt;li>&lt;strong>You feed the Model with your data source&lt;/strong>. It can be a CSV file, rows from a database query or the result of an HTTP request. The &lt;em>Model&lt;/em> name is misleading, it actually handles your &lt;em>model classes&lt;/em>. In our example, we&amp;rsquo;ll create a &lt;code>CountryModel&lt;/code> that will be the owner of a &lt;code>Country&lt;/code> list. The real data is in the &lt;code>Country&lt;/code> class, the &lt;code>CountryModel&lt;/code> just loads it and makes it available for potential clients.&lt;/li>
&lt;li>&lt;strong>The data is provided to the View through the Model&lt;/strong>. The View can be a Qt type : &lt;code>QListView&lt;/code>, or &lt;code>QTableView&lt;/code>. It really depends on what your model can offer (rows and/or columns) and what type of display your want. To enforce the separation, the format is completely domain agnostic. Here, there&amp;rsquo;s no &lt;code>Country&lt;/code> class at all, only a generic bundle. We&amp;rsquo;ll come back soon to this.&lt;/li>
&lt;li>&lt;strong>The user interacts with the View to produce events&lt;/strong> — select a row, delete a row, etc. Each event triggers an exchange between the View and the Model. The user wants to delete a row? Tell the Model about it and wait for the result with a slot.&lt;/li>
&lt;/ul>
&lt;p>The beauty of this architecture is that it fulfills our 2 goals:&lt;/p>
&lt;ol>
&lt;li>The data source can be completely changed from CSV values to Database queries, the View would not have any idea about it.&lt;/li>
&lt;li>Because the Model provides signals and slots, we can even connect multiple Views on a single Model. Each View will connect to the Model&amp;rsquo; slots and get any update.&lt;/li>
&lt;/ol>
&lt;h2 id="the-model">The Model&lt;/h2>
&lt;p>The code is done inside a Qt Widget project. We&amp;rsquo;ll start the basic build block — the &lt;code>Country.h&lt;/code> class:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;QString&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Country&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QString&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QString&lt;/span> &lt;span class="n">capital&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QString&lt;/span> &lt;span class="n">flagIcon&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Country&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QString&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">QString&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">capital&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">QString&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">flagIcon&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">capital&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">capital&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">flagIcon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">flagIcon&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The &lt;code>flagIcon&lt;/code>is the path to the PNG icon stored in a &lt;code>.qrc&lt;/code> file, nothing fancy. The real deal starts with &lt;code>CountryModel.h&lt;/code>, the owner of a &lt;code>Country&lt;/code> list:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CountryModel&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">QAbstractListModel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Q_OBJECT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">enum&lt;/span> &lt;span class="nc">CountryRoles&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CapitalRole&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Qt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UserRole&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CountryModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QObject&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">parent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">CountryModel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="nf">rowCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">parent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">override&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QVariant&lt;/span> &lt;span class="nf">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">role&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="k">override&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QVector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Country&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span> &lt;span class="n">countries&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let&amp;rsquo;s break it down. &lt;code>CountryModel&lt;/code>extends &lt;a href="http://doc.qt.io/qt-5/qabstractlistmodel.html">QAbstractListModel&lt;/a>, which is a kind of Model that provides rows of data. The View displaying &lt;code>CountryModel&lt;/code>&amp;rsquo;s data will configure itself by calling:&lt;/p>
&lt;ul>
&lt;li>&lt;code>rowCount()&lt;/code> to measure its widget size according to the number of rows.&lt;/li>
&lt;li>&lt;code>data()&lt;/code> to retrieve the actual data. It returns a &lt;a href="http://doc.qt.io/qt-5/qvariant.html">QVariant&lt;/a>, the generic Qt union type to transfer data back and forth without being restricted by a strong C++ type.&lt;/li>
&lt;/ul>
&lt;p>These functions come from &lt;code>QAbstractListModel&lt;/code>, so we have to override them to provide the real data size and content. For our purpose, the data is simply a &lt;code>QVector&lt;/code> of &lt;code>Country*&lt;/code>.&lt;/p>
&lt;p>Now, &lt;code>rowCount()&lt;/code> and &lt;code>data()&lt;/code> both take a special argument: a &lt;code>QModelIndex&lt;/code>. We&amp;rsquo;ll dive in greater length about this topic in the post about the editable Model / View. For now, just consider it as an opaque object that handles data indexes from the Model&amp;rsquo;s perspective. To retrieve a &lt;code>Country&lt;/code> from &lt;code>countries&lt;/code> at a specific index, we&amp;rsquo;ll do an &lt;code>index.row()&lt;/code>. Things get hairy when we update the data — adding, deleting lines — because this will mess up the index. That&amp;rsquo;s why Qt uses an internal index handler to keep track of what moved with &lt;code>QModelIndex&lt;/code>.&lt;/p>
&lt;p>Finally, the &lt;code>enum CountryRoles&lt;/code>. We&amp;rsquo;ll skip it for now. It will come to light in the implementation of the function &lt;code>data()&lt;/code>.&lt;/p>
&lt;div style="text-align: center; margin: 2em 0; clear: both;">
&lt;span style="font-size: 1.5em; color: #666;">• • •&lt;/span>
&lt;/div>
&lt;p>Let&amp;rsquo;s switch the &lt;code>CountryModel.cpp&lt;/code>, with &lt;code>CountryModel&lt;/code>&amp;rsquo;s contructor &amp;amp; destructor:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;QIcon&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CountryModel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CountryModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QObject&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QAbstractListModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">countries&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">countries&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Country&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;France&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Paris&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;:/flags/fr.png&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Country&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Great Britain&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;London&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;:/flags/gb.png&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Country&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Japan&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Tokyo&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;:/flags/jp.png&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Country&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;United States of America&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Washington, D.C&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;:/flags/us.png&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CountryModel&lt;/span>&lt;span class="o">::~&lt;/span>&lt;span class="n">CountryModel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qDeleteAll&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">countries&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>countries&lt;/code> is initialized in the constructor. You can replace it with whatever your data source is: database, CSV file, etc. The country&amp;rsquo;s icon is a path pointing to a &lt;code>.qrc&lt;/code> resource stored in the project. The destructor just cleans up the &lt;code>Country*&lt;/code>stored in countries.&lt;/p>
&lt;p>The meat of the class lies in the following functions, starting with &lt;code>rowCount()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">CountryModel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">rowCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">const&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Q_UNUSED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">countries&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The function takes a &lt;code>QModelIndex&amp;amp; parent&lt;/code>, because you data source could be tree (think of a directory / files structure). Each directory would be a &lt;code>parent&lt;/code>, and its size would be different from its neighbors. As our model is a flat list, we can safely ignore the &lt;code>parent&lt;/code> and return &lt;code>countries.size()&lt;/code>.&lt;/p>
&lt;p>Next, the implementation of &lt;code>data()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">QVariant&lt;/span> &lt;span class="n">CountryModel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">role&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">const&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">Country&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">country&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">countries&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">at&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">row&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">role&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="n">Qt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nl">DisplayRole&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">country&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="n">Qt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nl">DecorationRole&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">QIcon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">country&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">flagIcon&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="n">CountryRoles&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nl">CapitalRole&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">country&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">capital&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">default&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">QVariant&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, &lt;code>data()&lt;/code> takes 2 parameters:&lt;/p>
&lt;ul>
&lt;li>&lt;code>index&lt;/code> to know the index of the requested data — the country index in our case.&lt;/li>
&lt;li>&lt;code>role&lt;/code> to indicate the type of the data it wants to retrieve.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>data()&lt;/code> &lt;em>is not called to retrieve a whole row&lt;/em> (i.e. a full &lt;code>Country&lt;/code> object). It&amp;rsquo;s &lt;strong>&lt;em>called for each piece of data it wants to display&lt;/em>&lt;/strong>. How does it know which piece of data it wants to display? That&amp;rsquo;s the point of the &lt;code>role&lt;/code> parameter! There are &lt;a href="http://doc.qt.io/qt-5/qt.html#ItemDataRole-enum">predefined roles&lt;/a> that can be requested by the View. In a &lt;code>QListView&lt;/code>, the content of a displayed row will be &lt;code>Qt::DisplayRole&lt;/code>.&lt;/p>
&lt;p>First, we retrieve the &lt;code>Country&lt;/code> for a given &lt;code>index.row()&lt;/code>. Then, the &lt;code>switch(role) { … }&lt;/code> block comes into play. if the View want&amp;rsquo;s to see the &lt;code>Qt::DisplayRole&lt;/code>, we return the &lt;code>country.name&lt;/code>. If the View wants to see the &lt;code>Qt::DecorationRole&lt;/code> (the icon just at the left of the row label), we return a &lt;code>QIcon(contry.flagIcon)&lt;/code>. Every return value must be able to be converted to a &lt;code>QVariant&lt;/code>, this lets &lt;code>data()&lt;/code> to have a generic signature.&lt;/p>
&lt;p>Alright, the principle seems sound. But what if we want to display custom data? Here, a &lt;code>Country&lt;/code> also owns a &lt;code>capital&lt;/code>, which could — and will — be used by the View. That&amp;rsquo;s where the forgotten &lt;code>enum CountryRoles&lt;/code> strikes back. Let me avoid you some scrolling:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="nc">CountryRoles&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CapitalRole&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Qt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UserRole&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, we defined a custom role, &lt;code>CapitalRole&lt;/code> that takes the &lt;code>Qt::UserRole&lt;/code> value. &lt;code>Qt::UserRole&lt;/code> is simply the starting value (&lt;code>0x0100&lt;/code>) for user defined roles. If you have more data to provide in your Model, just continue to add custom roles in your &lt;code>enum&lt;/code>.&lt;/p>
&lt;p>A last important detail —&lt;code>data()&lt;/code> is &lt;code>const&lt;/code>. In other words, it cannot change&lt;code>CountryModel&lt;/code>, and can only call&lt;code>const&lt;/code> functions. If you want to generate thumbnails on the fly for a country&amp;rsquo;s flag, you&amp;rsquo;re in for a world of pain. By design, the &lt;code>data()&lt;/code> functions expects to retrieve const-data and returns a &lt;code>QVariant&lt;/code> to be copied by value. There are possible workarounds with a &lt;a href="http://doc.qt.io/qt-5/qidentityproxymodel.html">QIdentityProxyModel&lt;/a>. If you&amp;rsquo;re going with QML, the Qt folks were thoughtful with &lt;a href="http://doc.qt.io/qt-5/qquickimageprovider.html">QQuickImageProvider&lt;/a>.&lt;/p>
&lt;h2 id="the-view">The View&lt;/h2>
&lt;p>The View is straightforward, in a usual &lt;code>MainWindow&lt;/code> class, The form looks like this:&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/qt-modelview-read/model-view-read-ui-form.png"
onclick="openLightbox('\/img\/qt-modelview-read\/model-view-read-ui-form.png')"
style="cursor: pointer;" />
&lt;figcaption class="center" >UI Form of the MainWindow&lt;/figcaption>
&lt;/figure>
&lt;ul>
&lt;li>On the left, the most important widget is &lt;code>countryListView&lt;/code>, a &lt;code>QListView&lt;/code> that will display the data coming from &lt;code>CountryModel&lt;/code>.&lt;/li>
&lt;li>On the right, &lt;code>countryCapitalLabel&lt;/code> and &lt;code>countryNameLabel&lt;/code> will be updated with the currently selected &lt;code>Country&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The source is short, starting with &lt;code>MainWindow.h&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifndef MAINWINDOW_H
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#define MAINWINDOW_H
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;QMainWindow&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;QItemSelection&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;CountryModel.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">namespace&lt;/span> &lt;span class="n">Ui&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">    &lt;span class="k">class&lt;/span> &lt;span class="nc">MainWindow&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MainWindow&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">QMainWindow&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="n">Q_OBJECT&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="k">explicit&lt;/span> &lt;span class="n">MainWindow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QWidget&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">parent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="o">~&lt;/span>&lt;span class="n">MainWindow&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="nl">slots&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="kt">void&lt;/span> &lt;span class="n">onSelectionChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QItemSelection&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">selected&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">QItemSelection&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">deselected&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="n">Ui&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MainWindow&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ui&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">     &lt;span class="n">CountryModel&lt;/span> &lt;span class="n">countryModel&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif &lt;/span>&lt;span class="c1">// MAINWINDOW_H
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>onSelectionChanged()&lt;/code> will be called each time a selection changes on &lt;code>countryListView&lt;/code>.&lt;/li>
&lt;li>&lt;code>countryModel&lt;/code> is the &lt;code>CountryModel&lt;/code> member variable that will feed the data to &lt;code>countryListView&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The last piece of the puzzle hides in &lt;code>MainWindow.cpp&lt;/code>, starting with the constructor:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;MainWindow.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;ui_MainWindow.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;QItemSelectionModel&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MainWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MainWindow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">QWidget&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QMainWindow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">parent&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ui&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">Ui&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MainWindow&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">countryModel&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ui&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">setupUi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">setWindowTitle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Model / View sample - countries&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ui&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">countryListView&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">setModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">countryModel&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QItemSelectionModel&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">selectionModel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ui&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">countryListView&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">selectionModel&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">connect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">selectionModel&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">QItemSelectionModel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">selectionChanged&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">MainWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">onSelectionChanged&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The whole magic happens when &lt;code>countryListView-&amp;gt;setModel(&amp;amp;countryModel)&lt;/code> is called. Because &lt;code>CountryModel&lt;/code> extends &lt;code>QAbstractListModel&lt;/code>, this call is generic from &lt;code>countryListView&lt;/code>&amp;rsquo;s perspective. If we decide to update &lt;code>CountryModel&lt;/code> to store the countries in a database, &lt;code>countryListView&lt;/code> couldn&amp;rsquo;t care less.&lt;/p>
&lt;p>The next lines are here to update our custom country name / capital labels. The selection of a &lt;code>QListView&lt;/code> is also handled by a Model, this time it&amp;rsquo;s a &lt;a href="http://doc.qt.io/qt-5/qitemselectionmodel.html">QItemSelectionModel&lt;/a>. The implementation of &lt;code>onSelectionChanged()&lt;/code> looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">MainWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">onSelectionChanged&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">QItemSelection&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">selected&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">QItemSelection&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">deselected&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Q_UNUSED&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">deselected&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QModelIndexList&lt;/span> &lt;span class="n">list&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">selected&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">indexes&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">QModelIndex&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QString&lt;/span> &lt;span class="n">countryName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Qt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">DisplayRole&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ui&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">countryNameLabel&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">countryName&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">QString&lt;/span> &lt;span class="n">countryCapital&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">index&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">CountryModel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CapitalRole&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ui&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">countryCapitalLabel&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">setText&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">countryCapital&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The parameters &lt;code>selected&lt;/code> &amp;amp; &lt;code>deselected&lt;/code> encapsulate the current state of the &lt;code>countryListView&lt;/code>. From here, we retrieve the currently selected indexes (&lt;code>QListView&lt;/code> can support multiple selections). We take the first index, and start working with our now-old-friend, &lt;code>QModelIndex&lt;/code>.&lt;/p>
&lt;p>To update &lt;code>countryNameLabel&lt;/code>, we get the data from the Model, using the &lt;code>index.data()&lt;/code> function. Remember &lt;code>CountryModel::data()&lt;/code> with the &lt;code>role&lt;/code> parameter? Here it is, in all its glory! The returned &lt;code>QVariant&lt;/code> is converted to a &lt;code>QString&lt;/code> and then passed to &lt;code>countryNameLabel&lt;/code>. The same pattern is used for &lt;code>countryCapitalLabel&lt;/code>, although we rely on our custom role — &lt;code>CountryModel::CapitalRole&lt;/code> — to target the right piece of data.&lt;/p>
&lt;div style="text-align: center; margin: 2em 0; clear: both;">
&lt;span style="font-size: 1.5em; color: #666;">• • •&lt;/span>
&lt;/div>
&lt;p>As you can see, there&amp;rsquo;s still no trace of &lt;code>CountryModel&lt;/code> around, even though it&amp;rsquo;s working behind the scene. It took &lt;strong>less than 150 lines of C++ code to scaffold a generic data source that can be plugged in a standard &lt;code>QListView&lt;/code>&lt;/strong>. In my humble opinion, it&amp;rsquo;s quite a treat to achieve this much with so little code.&lt;/p>
&lt;p>Again, everything is available on &lt;a href="https://github.com/synapticvoid/sample-qt-model-view">GitHub&lt;/a>, go and take a look.&lt;/p>
&lt;p>The first time I really understood Model / View like so, I wondered. Is the documentation size inversely proportional to the simplicity of the concept it tries to explain? Is it a just a smokescreen, some kind of conspiration? &lt;em>Is life like the Model / View documentation, so simple and yet so hard to explain?&lt;/em>&lt;/p>
&lt;h2 id="going-deeper">Going deeper&lt;/h2>
&lt;p>If you want to dig deeper in Qt, I co-wrote a whole book about the Qt framework, &lt;a href="https://www.packtpub.com/web-development/mastering-qt-5-second-edition">Mastering Qt 5&lt;/a>. The Model / View is covered inside out, with the full data CRUD explanation, QML implementation to connect to a Model — it takes 3 chapters to cover the material.&lt;/p>
&lt;figure class="left" >
&lt;img src="https://gryt.io/img/qt-modelview-read/mastering-qt5-cover.png"
onclick="openLightbox('\/img\/qt-modelview-read\/mastering-qt5-cover.png')"
style="cursor: pointer;" />
&lt;figcaption class="center" >Mastering Qt 5 cover&lt;/figcaption>
&lt;/figure>
&lt;p>The book aims to teach you how to be a well-rounded Qt developer. We took our years of experience, frustrations, discoveries and joys about Qt and poured them into these 500 page. We explore the inner working of the Qt signal / slot system, build advanced multi-threaded applications, explain how Qt can work with C++11/14 and much more.&lt;/p></content></item></channel></rss>