<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>KDAB</title><description>Global software consultancy specialising in Qt, C++, Rust, Slint and Embedded Linux. Consulting, development, training and open-source tools since 1999.</description><link>https://www.kdab.com/</link><language>en-gb</language><item><title>Hotspot v1.6.0 released</title><link>https://www.kdab.com/hotspot-v1-6-0-released/</link><guid isPermaLink="true">https://www.kdab.com/hotspot-v1-6-0-released/</guid><description>&lt;p data-block-key=&quot;1sw01&quot;&gt;Hotspot is a standalone GUI designed to provide a user-friendly interface for analyzing performance data. It takes a perf.data file, parses and evaluates its contents, and presents the results in a visually appealing and easily understandable manner. Our goal with Hotspot is to offer a modern alternative to perf report, making performance analysis on Linux […]&lt;/p&gt;</description><pubDate>Fri, 13 Mar 2026 07:19:47 GMT</pubDate><content:encoded>&lt;h1&gt;Hotspot v1.6.0 released&lt;/h1&gt;
&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;3x6h0&quot;&gt;&lt;a href=&quot;https://www.kdab.com/hotspot-video/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hotspot&lt;/a&gt; is a standalone GUI designed to provide a user-friendly interface for analyzing performance data. It takes a &lt;code&gt;perf.data&lt;/code&gt; file, parses and evaluates its contents, and presents the results in a visually appealing and easily understandable manner. Hotspot’s goal is to offer a modern alternative to &lt;code&gt;perf report&lt;/code&gt;, making performance analysis on Linux systems more intuitive and efficient.&lt;/p&gt;&lt;p data-block-key=&quot;c275l&quot;&gt;Version 1.6.0 introduces powerful new features, improved workflows, and numerous bug fixes to make profiling even smoother.&lt;/p&gt;&lt;h2 id=&quot;changelog-for-hotspot-v1.6.0&quot; data-block-key=&quot;39oj1&quot;&gt;ChangeLog for Hotspot v1.6.0&lt;/h2&gt;&lt;p data-block-key=&quot;duuev&quot;&gt;&lt;a href=&quot;https://github.com/KDAB/hotspot/releases/tag/v1.6.0&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;This release&lt;/a&gt; focuses on usability improvements and extended analysis capabilities. The most notable additions are:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;8eokv&quot;&gt;Support for archived perf files (e.g. &lt;code&gt;perf.data.zip&lt;/code&gt;)&lt;/li&gt;&lt;li data-block-key=&quot;6s6o4&quot;&gt;Regex filtering in the flamegraph&lt;/li&gt;&lt;li data-block-key=&quot;6dmsd&quot;&gt;Tracepoint support&lt;/li&gt;&lt;li data-block-key=&quot;fkvnu&quot;&gt;Various bug fixes and stability improvements&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;open-archived-perf-files-directly&quot; data-block-key=&quot;dw1a8&quot;&gt;Open Archived perf Files Directly&lt;/h2&gt;&lt;p data-block-key=&quot;7v5j4&quot;&gt;Hotspot can now open perf recordings stored inside archives such as &lt;code&gt;perf.data.zip&lt;/code&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;23rsk&quot;&gt;This simplifies sharing profiling results, storing CI artifacts, and working with compressed recordings - no manual extraction required.&lt;/p&gt;&lt;h2 id=&quot;regex-in-the-flamegraph&quot; data-block-key=&quot;hupsd&quot;&gt;Regex in the Flamegraph&lt;/h2&gt;&lt;p data-block-key=&quot;81umj&quot;&gt;The Flamegraph view now supports &lt;b&gt;regular expression filtering&lt;/b&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;2el70&quot;&gt;This makes it much easier to:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;gn9m&quot;&gt;Match complex symbol patterns&lt;/li&gt;&lt;li data-block-key=&quot;5m0p9&quot;&gt;Focus on specific subsystems or namespaces&lt;/li&gt;&lt;li data-block-key=&quot;1qokc&quot;&gt;Quickly narrow down large profiling datasets&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;6s8g3&quot;&gt;Especially for large C++ codebases, regex search significantly speeds up navigation.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Hotspot_V1.6.0_Regex_Example&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Clipboard_-_March_3_2026_1_17_PM.original.png&quot; class=&quot;Hotspot_V1.6.0_Regex_Example&quot; alt=&quot;Hotspot_V1.6.0_Regex_Example&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;gcck3&quot;&gt;Regex in the flamegraph&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;tracepoint-support&quot; data-block-key=&quot;6ua5w&quot;&gt;Tracepoint Support&lt;/h2&gt;&lt;p data-block-key=&quot;243vm&quot;&gt;Hotspot 1.6.0 introduces support for &lt;b&gt;tracepoints&lt;/b&gt; captured via &lt;code&gt;perf&lt;/code&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;6ps2v&quot;&gt;This enables analysis of event-based data in addition to traditional sampling, giving deeper insight into system and runtime behavior.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Hotspot_V1.6.0_Tracepoints_Example&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Clipboard_-_March_3_2026_1_23_PM.original.png&quot; class=&quot;Hotspot_V1.6.0_Tracepoints_Example&quot; alt=&quot;Hotspot_V1.6.0_Tracepoints_Example&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;gcck3&quot;&gt;Tracepoints&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;bug-fixes-and-improvements&quot; data-block-key=&quot;2yvln&quot;&gt;Bug Fixes and Improvements&lt;/h2&gt;&lt;p data-block-key=&quot;eh5nf&quot;&gt;As usual, this release also includes numerous smaller fixes, UI refinements, and internal cleanups to improve overall stability and user experience.&lt;/p&gt;&lt;p data-block-key=&quot;a8i75&quot;&gt;For a complete overview of all changes, see the &lt;a href=&quot;https://github.com/KDAB/hotspot/releases/tag/v1.6.0&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;full changelog&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;p data-block-key=&quot;csck&quot;&gt;Happy profiling!&lt;/p&gt;&lt;h2 id=&quot;videos&quot; data-block-key=&quot;uvkg0&quot;&gt;Videos&lt;/h2&gt;&lt;p data-block-key=&quot;dmlud&quot;&gt;Hotspot - A GUI for perf report:&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/6ogEkQ-vKt4?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;Hotspot – A GUI for perf report&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;



&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;3x6h0&quot;&gt;Hotspot Demo from Embedded World 2022:&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/1_EOIsg89GA?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;Hotspot Demo&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/hotspot-v1-6-0-released/&quot;&gt;Hotspot v1.6.0 released&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Editor Team</dc:creator><category>c++</category><category>desktop</category><category>linux</category><category>open source</category><category>performance</category><category>tools</category><category>ux/ui</category></item><item><title>Automating Repetitive GUI Interactions in Embedded Development with Spix</title><link>https://www.kdab.com/automating-repetitive-gui-interactions-in-embedded-development-with-spix/</link><guid isPermaLink="true">https://www.kdab.com/automating-repetitive-gui-interactions-in-embedded-development-with-spix/</guid><description>&lt;p data-block-key=&quot;k9ick&quot;&gt;This blog post showcases Spix, a small but useful library for automating GUI-interaction in your Qt/QML-based applications. Just sending clicks or entering text to checking properties, Spix offers basic automated interaction. Combining the automated screenshot function with computer vision, you can even perform simple optical checks with Spix.&lt;/p&gt;</description><pubDate>Thu, 19 Feb 2026 08:18:12 GMT</pubDate><content:encoded>&lt;h1&gt;Automating Repetitive GUI Interactions in Embedded Development with Spix&lt;/h1&gt;
&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot;&gt;
        &lt;div class=&quot;image-variable-size-image&quot;&gt;
            &lt;img src=&quot;https://www.kdab.com/documents/2023-07-05-18-08-38-small_1.gif&quot; alt=&quot;2023-07-05-18-08-38-small_Blog_Christoph_Spix&quot;&gt;
        &lt;/div&gt;

        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;r7hie&quot;&gt;As Embedded Software Developers, we all know the pain: you make a code change, rebuild your project, restart the application - and then spend precious seconds repeating the same five clicks just to reach the screen you want to test. Add a login dialog on top of it, and suddenly those seconds turn into minutes. Multiply that by a hundred iterations per day, and it’s clear: this workflow is frustrating, error-prone, and a waste of valuable development time.&lt;/p&gt;&lt;p data-block-key=&quot;5kds4&quot;&gt;In this article, we’ll look at how to automate these repetitive steps using &lt;a href=&quot;https://github.com/faaxm/spix&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Spix&lt;/a&gt;, an open-source tool for GUI automation in Qt/QML applications. We’ll cover setup, usage scenarios, and how Spix can be integrated into your workflow to save hours of clicking, typing, and waiting.&lt;/p&gt;&lt;h2 id=&quot;the-problem:-click-fatigue-in-gui-testing&quot; data-block-key=&quot;ght5m&quot;&gt;The Problem: Click Fatigue in GUI Testing&lt;/h2&gt;&lt;p data-block-key=&quot;djqar&quot;&gt;Imagine this:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;1tiuj&quot;&gt;You start your application.&lt;/li&gt;&lt;li data-block-key=&quot;4ths2&quot;&gt;The login screen appears.&lt;/li&gt;&lt;li data-block-key=&quot;dii9m&quot;&gt;You enter your username and password.&lt;/li&gt;&lt;li data-block-key=&quot;d80kp&quot;&gt;You click &quot;Login&quot;.&lt;/li&gt;&lt;li data-block-key=&quot;78ntm&quot;&gt;Only then do you finally reach the UI where you can verify whether your code changes worked.&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;9hpm3&quot;&gt;This is fine the first few times - but if you’re doing it 100+ times a day, it becomes a serious bottleneck. While features like &lt;b&gt;hot reload&lt;/b&gt; can help in some cases, they aren’t always applicable - especially when structural changes are involved or when you must work with &quot;real&quot; production data.&lt;/p&gt;&lt;p data-block-key=&quot;1mabg&quot;&gt;So, what’s the alternative?&lt;/p&gt;&lt;h2 id=&quot;the-solution:-automating-gui-input-with-spix&quot; data-block-key=&quot;3ymr9&quot;&gt;The Solution: Automating GUI Input with Spix&lt;/h2&gt;&lt;p data-block-key=&quot;5e5pv&quot;&gt;&lt;a href=&quot;https://github.com/faaxm/spix&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Spix&lt;/a&gt; allows you to control your Qt/QML applications programmatically. Using scripts (typically Python), you can automatically:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;76u3q&quot;&gt;Insert text into input fields&lt;/li&gt;&lt;li data-block-key=&quot;eajkm&quot;&gt;Click buttons&lt;/li&gt;&lt;li data-block-key=&quot;699mb&quot;&gt;Wait for UI elements to appear&lt;/li&gt;&lt;li data-block-key=&quot;c7j6r&quot;&gt;Take and compare screenshots&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;e3mi6&quot;&gt;This means you can automate login steps, set up UI states consistently, and even extend your CI pipeline with &lt;b&gt;visual testing&lt;/b&gt;. Unlike manual hot reload tweaks or hardcoding start screens, Spix provides an &lt;b&gt;external, scriptable solution&lt;/b&gt; without altering your application logic.&lt;/p&gt;&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;setting-up-spix-in-your-project&quot; data-block-key=&quot;awzjs&quot;&gt;Setting up Spix in Your Project&lt;/h2&gt;&lt;p data-block-key=&quot;bu5nk&quot;&gt;Getting Spix integrated requires a few straightforward steps:&lt;/p&gt;&lt;h3 id=&quot;1.-add-spix-as-a-dependency&quot; data-block-key=&quot;pp4qw&quot;&gt;1. &lt;b&gt;Add Spix as a dependency&lt;/b&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li data-block-key=&quot;cs1gu&quot;&gt;Typically done via a Git submodule into your project’s third-party folder.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-bash  line-numbers &quot;&gt;git submodule add 3rdparty/spix git@github.com:faaxm/spix.git&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;



&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;2.-register-spix-in-cmake&quot; data-block-key=&quot;swc4s&quot;&gt;2.&lt;b&gt; Register Spix in CMake&lt;/b&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li data-block-key=&quot;78qpm&quot;&gt;Update your &lt;code&gt;CMakeLists.txt&lt;/code&gt; with a &lt;code&gt;find_package(Spix REQUIRED)&lt;/code&gt; call.&lt;/li&gt;&lt;li data-block-key=&quot;5ec9b&quot;&gt;Because of CMake quirks, you may also need to manually specify the path to Spix’s CMake modules.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cmake  line-numbers &quot;&gt;LIST(APPEND CMAKE_MODULE_PATH /home/christoph/KDAB/spix/cmake/modules)
find_package(Spix REQUIRED)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;



&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;3.-link-against-spix&quot; data-block-key=&quot;m3xad&quot;&gt;3.&lt;b&gt; Link against Spix&lt;/b&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li data-block-key=&quot;7cv8c&quot;&gt;Add &lt;code&gt;Spix&lt;/code&gt; to your &lt;code&gt;target_link_libraries&lt;/code&gt; call.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cmake  line-numbers &quot;&gt;target_link_libraries(myApp
  PRIVATE Qt6::Core
          Qt6::Quick 
          Qt6::SerialPort 
          Spix::Spix
)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;



&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;4.-initialize-spix-in-your-application&quot; data-block-key=&quot;odvmk&quot;&gt;4.&lt;b&gt; Initialize Spix in your application&lt;/b&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li data-block-key=&quot;1q365&quot;&gt;Include Spix headers in &lt;code&gt;main.cpp&lt;/code&gt;.&lt;/li&gt;&lt;li data-block-key=&quot;d95bc&quot;&gt;Add some lines of boilerplate code:&lt;ul&gt;&lt;li data-block-key=&quot;2ljlr&quot;&gt;Include the 2 Spix Headers (AnyRPCServer for Communication and QtQmlBot)&lt;/li&gt;&lt;li data-block-key=&quot;b2ora&quot;&gt;Start the Spix RPC server.&lt;/li&gt;&lt;li data-block-key=&quot;d3th0&quot;&gt;Create a &lt;code&gt;Spix::QtQmlBot&lt;/code&gt;.&lt;/li&gt;&lt;li data-block-key=&quot;7rom3&quot;&gt;Run the test server on a specified port (e.g. &lt;code&gt;9000&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;#include &amp;lt;Spix/AnyRpcServer.h&amp;gt;
#include &amp;lt;Spix/QtQmlBot.h&amp;gt;
[...]

//Start the actual Runner/Server
spix::AnyRpcServer server;
auto bot = new spix::QtQmlBot();
bot-&amp;gt;runTestServer(server);&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;idubt&quot;&gt;At this point, your application is &quot;Spix-enabled&quot;. You can verify this by checking for the open port (e.g. &lt;code&gt;localhost:9000&lt;/code&gt;).&lt;/p&gt;&lt;/div&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;or7ol&quot;&gt;Spix can be a Security Risk: Make sure to not expose Spix in any production environment, maybe only enable it for your Debug-builds.&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;where-spix-shines&quot; data-block-key=&quot;c0rsv&quot;&gt;Where Spix Shines&lt;/h2&gt;&lt;p data-block-key=&quot;5fkof&quot;&gt;Once the setup is done, Spix can be used to automate repetitive tasks. Let’s look at two particularly useful examples:&lt;/p&gt;&lt;h3 id=&quot;1.-automating-logins-with-a-python-script&quot; data-block-key=&quot;0n0mj&quot;&gt;1. Automating Logins with a Python Script&lt;/h3&gt;&lt;p data-block-key=&quot;38fq4&quot;&gt;Instead of typing your credentials and clicking &quot;Login&quot; manually, you can write a simple Python script that:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;fvros&quot;&gt;Connects to the Spix server on &lt;code&gt;localhost:9000&lt;/code&gt;&lt;/li&gt;&lt;li data-block-key=&quot;f286n&quot;&gt;Inputs text into the &lt;code&gt;userField&lt;/code&gt; and &lt;code&gt;passwordField&lt;/code&gt;&lt;/li&gt;&lt;li data-block-key=&quot;9ft5u&quot;&gt;Clicks the &quot;Login&quot; button (Items marked with &quot;Quotes&quot; are literal That-Specific-Text-Identifiers for Spix)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-python  line-numbers &quot;&gt;import xmlrpc.client

session = xmlrpc.client.ServerProxy(&amp;#x27;http://localhost:9000&amp;#x27;)

session.inputText(&amp;#x27;mainWindow/userField&amp;#x27;, &amp;#x27;christoph&amp;#x27;)
session.inputText(&amp;#x27;mainWindow/passwordField&amp;#x27;, &amp;#x27;secret&amp;#x27;) 
session.mouseClick(&amp;#x27;mainWindow/&amp;quot;Login&amp;quot;&amp;#x27;)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;6qw4v&quot;&gt;When executed, this script takes care of the entire login flow - no typing, no clicking, no wasted time. Better yet, you can check the script into your repository, so your whole team can reuse it.&lt;/p&gt;&lt;p data-block-key=&quot;f3qad&quot;&gt;For Development, Integration in Qt-Creator can be achieved with a Custom startup executable, that also starts this python script.&lt;/p&gt;&lt;/div&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;yoeyx&quot;&gt;In a CI environment, this approach is particularly powerful, since you can ensure every test run starts from a clean state without relying on manual navigation.&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;2.-screenshot-comparison&quot; data-block-key=&quot;fzpkt&quot;&gt;2. Screenshot Comparison&lt;/h3&gt;&lt;p data-block-key=&quot;bf9dk&quot;&gt;Beyond input automation, Spix also supports &lt;b&gt;taking screenshots&lt;/b&gt;. Combined with Python libraries like OpenCV or &lt;code&gt;scikit-image&lt;/code&gt;, this opens up interesting possibilities for testing.&lt;/p&gt;&lt;h4 id=&quot;example-1:-full-screen-comparison&quot; data-block-key=&quot;ovfo0&quot;&gt;Example 1: Full-screen comparison&lt;/h4&gt;&lt;p data-block-key=&quot;4n3v3&quot;&gt;Take a screenshot of the main window and store it first:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-python  line-numbers &quot;&gt;import xmlrpc.client

session = xmlrpc.client.ServerProxy(&amp;#x27;http://localhost:9000&amp;#x27;)

[...]
session.takeScreenshot(&amp;#x27;mainWindow&amp;#x27;, &amp;#x27;/tmp/screenshot.png&amp;#x27;)k&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;6qw4v&quot;&gt;Now we can compare it with a reference image:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-python  line-numbers &quot;&gt;from skimage import io
from skimage.metrics import structural_similarity as ssim

screenshot1 = io.imread(&amp;#x27;/tmp/reference.png&amp;#x27;, as_gray=True)
screenshot2 = io.imread(&amp;#x27;/tmp/screenshot.png&amp;#x27;, as_gray=True)

ssim_index = ssim(screenshot1, screenshot2, data_range=screenshot1.max() - screenshot1.min())

threshold = 0.95

if ssim_index == 1.0: 
    print(&amp;quot;The screenshots are a perfect match&amp;quot;)
elif ssim_index &amp;gt;= threshold:
    print(&amp;quot;The screenshots are similar, similarity: &amp;quot; + str(ssim_index * 100) + &amp;quot;%&amp;quot;)
else:
    print(&amp;quot;The screenshots are not similar at all, similarity: &amp;quot; + str(ssim_index * 100) + &amp;quot;%&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;6qw4v&quot;&gt;This is useful for catching unexpected regressions in visual layout.&lt;/p&gt;&lt;h4 id=&quot;example-2:-finding-differences-in-the-same-ui&quot; data-block-key=&quot;0zrg1&quot;&gt;Example 2: Finding differences in the same UI&lt;/h4&gt;&lt;p data-block-key=&quot;fsbra&quot;&gt;Use OpenCV to highlight pixel-level differences between two screenshots—for instance, missing or misaligned elements:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-python  line-numbers &quot;&gt;import cv2

image1 = cv2.imread(&amp;#x27;/tmp/reference.png&amp;#x27;)
image2 = cv2.imread(&amp;#x27;/tmp/screenshot.png&amp;#x27;)

diff = cv2.absdiff(image1, image2)

# Convert the difference image to grayscale
gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

# Threshold the grayscale image to get a binary image
_, thresh = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY)

contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image1, contours, -1, (0, 0, 255), 2)

cv2.imshow(&amp;#x27;Difference Image&amp;#x27;, image1)
cv2.waitKey(0)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;6qw4v&quot;&gt;This form of &lt;b&gt;visual regression testing&lt;/b&gt; can be integrated into your CI system. If the UI changes unintentionally, Spix can detect it and trigger an alert.&lt;/p&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;1024-637_Blog_Christoph_Spix&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/1024-637_1.original.png&quot; class=&quot;1024-637_Blog_Christoph_Spix&quot; alt=&quot;1024-637_Blog_Christoph_Spix&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;2759c&quot;&gt;Defective Image&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;1024-639_Blog_Christoph_Spix&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/1024-639_1.original.png&quot; class=&quot;1024-639_Blog_Christoph_Spix&quot; alt=&quot;1024-639_Blog_Christoph_Spix&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;2759c&quot;&gt;The script marked the defective parts of the image compared to the should-be image.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;recap&quot; data-block-key=&quot;j3bi4&quot;&gt;Recap&lt;/h2&gt;&lt;p data-block-key=&quot;398cv&quot;&gt;&lt;a href=&quot;https://github.com/faaxm/spix&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Spix&lt;/a&gt; is not a full-blown GUI testing framework like Squish, but it fills a useful niche for embedded developers who want to:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;30u8&quot;&gt;Save time on repetitive input (like logins).&lt;/li&gt;&lt;li data-block-key=&quot;2l6p7&quot;&gt;Share reproducible setup scripts with colleagues.&lt;/li&gt;&lt;li data-block-key=&quot;ba5ar&quot;&gt;Perform lightweight visual regression testing in CI.&lt;/li&gt;&lt;li data-block-key=&quot;3s1sr&quot;&gt;Interact with their applications on embedded devices remotely.&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;9as1q&quot;&gt;While there are limitations (e.g. manual wait times, lack of deep synchronization with UI states), Spix provides a powerful and flexible way to automate everyday development tasks - without having to alter your application logic.&lt;/p&gt;&lt;p data-block-key=&quot;8pqgh&quot;&gt;If you’re tired of clicking the same buttons all day, give Spix a try. It might just save you hours of time and frustration in your embedded development workflow.&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/WTYl4wHL0NI?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;GUI Automation and Testing with Spix&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/automating-repetitive-gui-interactions-in-embedded-development-with-spix/&quot;&gt;Automating Repetitive GUI Interactions in Embedded Development with Spix&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>python</category><category>qt</category><category>tools</category></item><item><title>Model/View Drag and Drop in Qt - Part 3</title><link>https://www.kdab.com/modelview-drag-and-drop-in-qt-part-3/</link><guid isPermaLink="true">https://www.kdab.com/modelview-drag-and-drop-in-qt-part-3/</guid><description>&lt;p data-block-key=&quot;ar0am&quot;&gt;This post explores implementing drag-and-drop onto existing items in Qt, such as moving emails between folders. It covers both &lt;i&gt;Model/View&lt;/i&gt; and &lt;i&gt;Item Widget&lt;/i&gt; approaches, detailing key setup steps and improvements made to Qt along the way.&lt;/p&gt;</description><pubDate>Thu, 03 Apr 2025 08:43:00 GMT</pubDate><content:encoded>&lt;h1&gt;Model/View Drag and Drop in Qt - Part 3&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;1z9vu&quot;&gt;In this third blog post of the Model/View Drag and Drop series (&lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-part-1/&quot;&gt;part 1&lt;/a&gt; and &lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-in-qt-part-2/&quot;&gt;part 2&lt;/a&gt;), the idea is to implement dropping onto items, rather than in between items. QListWidget and QTableWidget have out of the box support for replacing the value of existing items when doing that, but there aren&apos;t many use cases for that. What is much more common is to associate a custom semantic to such a drop. For instance, the examples detailed below show email folders and their contents, and dropping an email onto another folder will move (or copy) the email into that folder.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step1&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part3-treeview-step1.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step1&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step1&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;44fbh&quot;&gt;Step 1&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;Initial state, the email is in the inbox&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step2&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part3-treeview-step2.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step2&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step2&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;44fbh&quot;&gt;Step 2&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;Dragging the email onto the Customers folder&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step3&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part3-treeview-step3.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step3&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step3&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;44fbh&quot;&gt;Step 3&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;Dropping the email&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step4&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part3-treeview-step4.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step4&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part3-treeview-step4&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;44fbh&quot;&gt;Step 4&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;The email is now in the customers folder&lt;/p&gt;&lt;h2 id=&quot;with-modelview-separation&quot; anchor=&quot;with-modelview-separation&quot; data-block-key=&quot;ksk9j&quot;&gt;With Model/View separation&lt;/h2&gt;&lt;p data-block-key=&quot;a95g8&quot;&gt;Example code can be found &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/model-view/drop-onto-items-with-model-view.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for flat models and &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/treemodel/drop-onto-items-with-treemodel.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for tree models.&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-view-on-the-drag-side&quot; anchor=&quot;setting-up-the-view-on-the-drag-side&quot; data-block-key=&quot;mfiqr&quot;&gt;Setting up the view on the drag side&lt;/h3&gt;&lt;p data-block-key=&quot;baki1&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropMode(QAbstractItemView::DragOnly)&lt;/code&gt;&lt;br/&gt; unless of course the same view should also support drops. In our example, only emails can be dragged, and only folders allow drops, so the drag and drop sides are distinct.&lt;/p&gt;&lt;p data-block-key=&quot;6qoh6&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropOverwriteMode(...)&lt;/code&gt;&lt;br/&gt; &lt;code&gt;true&lt;/code&gt; if moving should clear cells, &lt;code&gt;false&lt;/code&gt; if moving should remove rows.&lt;br/&gt; Note that the default is &lt;code&gt;true&lt;/code&gt; for &lt;code&gt;QTableView&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; for &lt;code&gt;QListView&lt;/code&gt; and &lt;code&gt;QTreeView&lt;/code&gt;. In our example, we want to remove emails that have been moved elsewhere, so &lt;code&gt;false&lt;/code&gt; is correct.&lt;/p&gt;&lt;p data-block-key=&quot;53bqd&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDefaultDropAction(Qt::MoveAction)&lt;/code&gt; so that the drag defaults to a move and not a copy, adjust as needed&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-model-on-the-drag-side&quot; anchor=&quot;setting-up-the-model-on-the-drag-side&quot; data-block-key=&quot;762wa&quot;&gt;Setting up the model on the drag side&lt;/h3&gt;&lt;p data-block-key=&quot;f1jgr&quot;&gt;To implement dragging items out of a model, you need to implement the following -- this is very similar to the section of the same name in the previous blog post, obviously:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class EmailsModel : public QAbstractTableModel
{
    ~~~
    Qt::ItemFlags flags(const QModelIndex &amp;amp;index) const override
    {
        if (!index.isValid())
            return {};
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
    }

    // the default is &amp;quot;copy only&amp;quot;, change it
    Qt::DropActions supportedDragActions() const override { return Qt::MoveAction | Qt::CopyAction; }

    QMimeData *mimeData(const QModelIndexList &amp;amp;indexes) const override;

    bool removeRows(int position, int rows, const QModelIndex &amp;amp;parent) override;&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;☑ Reimplement &lt;code&gt;flags()&lt;/code&gt; to add &lt;code&gt;Qt::ItemIsDragEnabled&lt;/code&gt; in the case of a valid index&lt;/p&gt;&lt;p data-block-key=&quot;btaih&quot;&gt;☑ Reimplement &lt;code&gt;supportedDragActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction | Qt::CopyAction&lt;/code&gt; or whichever you want to support (the default is CopyAction only).&lt;/p&gt;&lt;p data-block-key=&quot;4k7os&quot;&gt;☑ Reimplement &lt;code&gt;mimeData()&lt;/code&gt; to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only node pointers (if you have that) and application PID (to refuse dropping onto another process). See the previous part of this blog series for more details.&lt;/p&gt;&lt;p data-block-key=&quot;7p1a9&quot;&gt;☑ Reimplement &lt;code&gt;removeRows()&lt;/code&gt;, it will be called after a successful drop with &lt;code&gt;MoveAction&lt;/code&gt;. An example implementation looks like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;bool EmailsModel::removeRows(int position, int rows, const QModelIndex &amp;amp;parent)
{
    beginRemoveRows(parent, position, position + rows - 1);
    for (int row = 0; row &amp;lt; rows; ++row) {
        m_emailFolder-&amp;gt;emails.removeAt(position);
    }
    endRemoveRows();
    return true;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;setting-up-the-view-on-the-drop-side&quot; anchor=&quot;setting-up-the-view-on-the-drop-side&quot; data-block-key=&quot;4tcsf&quot;&gt;Setting up the view on the drop side&lt;/h3&gt;&lt;p data-block-key=&quot;170pv&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropMode(QAbstractItemView::DropOnly)&lt;/code&gt; unless of course it supports dragging too. In our example, we can drop onto email folders but we cannot reorganize the folders, so &lt;code&gt;DropOnly&lt;/code&gt; is correct.&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-model-on-the-drop-side&quot; anchor=&quot;setting-up-the-model-on-the-drop-side&quot; data-block-key=&quot;f2j2p&quot;&gt;Setting up the model on the drop side&lt;/h3&gt;&lt;p data-block-key=&quot;dui9h&quot;&gt;To implement dropping items into a model&apos;s existing items, you need to do the following:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class FoldersModel : public QAbstractTableModel
{
    ~~~    
    Qt::ItemFlags flags(const QModelIndex &amp;amp;index) const override
    {
        CHECK_flags(index);
        if (!index.isValid())
            return {}; // do not allow dropping between items
        if (index.column() &amp;gt; 0)
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // don&amp;#x27;t drop on other columns
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
    }

    // the default is &amp;quot;copy only&amp;quot;, change it
    Qt::DropActions supportedDropActions() const override { return Qt::MoveAction | Qt::CopyAction; }
  
    QStringList mimeTypes() const override { return {QString::fromLatin1(s_emailsMimeType)}; }
  
    bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &amp;amp;parent) override;
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chbha&quot;&gt;☑ Reimplement &lt;code&gt;flags()&lt;/code&gt;&lt;br/&gt; For a valid index (and only in that case), add &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt;. As you can see, you can also restrict drops to column 0, which can be more sensible when using &lt;code&gt;QTreeView&lt;/code&gt; (the user should drop onto the folder name, not onto the folder size).&lt;/p&gt;&lt;p data-block-key=&quot;ep99i&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction | Qt::CopyAction&lt;/code&gt; or whichever you want to support (the default is CopyAction only).&lt;/p&gt;&lt;p data-block-key=&quot;dnh6m&quot;&gt;☑ Reimplement &lt;code&gt;mimeTypes()&lt;/code&gt; - the list should include the MIME type used by the drag model.&lt;/p&gt;&lt;p data-block-key=&quot;2g8a&quot;&gt;☑ Reimplement &lt;code&gt;dropMimeData()&lt;/code&gt;&lt;br/&gt; to deserialize the data and handle the drop.&lt;br/&gt; This could mean calling &lt;code&gt;setData()&lt;/code&gt; to replace item contents, or anything else that should happen on a drop: in the email example, this is where we copy or move the email into the destination folder. Once you&apos;re done, return true, so that the drag side then deletes the dragged rows by calling &lt;code&gt;removeRows()&lt;/code&gt; on its model.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;bool FoldersModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &amp;amp;parent)
{
    ~~~  // safety checks, see full example code

    EmailFolder *destFolder = folderForIndex(parent);

    const QByteArray encodedData = mimeData-&amp;gt;data(s_emailsMimeType);
    QDataStream stream(encodedData);
    ~~~ // code to detect and reject dropping onto the folder currently holding those emails

    while (!stream.atEnd()) {
        QString email;
        stream &amp;gt;&amp;gt; email;
        destFolder-&amp;gt;emails.append(email);
    }
    emit dataChanged(parent, parent); // update count

    return true; // let the view handle deletion on the source side by calling removeRows there
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;using-item-widgets&quot; anchor=&quot;using-item-widgets&quot; data-block-key=&quot;o5jxg&quot;&gt;Using item widgets&lt;/h2&gt;&lt;p data-block-key=&quot;f13cl&quot;&gt;Example code:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;6i1ck&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qlistwidget/drop-onto-qlistwidgetitems.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QListWidget&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;902bi&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qtablewidget/drop-onto-qtablewidgetitems.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTableWidget&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;3rqfn&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qtreewidget/drop-onto-qtreewidgetitems.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTreeWidget&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;on-the-drag-side&quot; anchor=&quot;on-the-drag-side&quot; data-block-key=&quot;ohfhl&quot;&gt;On the &quot;drag&quot; side&lt;/h3&gt;&lt;p data-block-key=&quot;18t03&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropMode(QAbstractItemView::DragOnly)&lt;/code&gt; or &lt;code&gt;DragDrop&lt;/code&gt; if it should support both&lt;/p&gt;&lt;p data-block-key=&quot;ca2ba&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDefaultDropAction(Qt::MoveAction)&lt;/code&gt; so that the drag defaults to a move and not a copy, adjust as needed&lt;/p&gt;&lt;p data-block-key=&quot;evi3q&quot;&gt;☑ Reimplement &lt;code&gt;Widget::mimeData()&lt;/code&gt; to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only item pointers and application PID (to refuse dropping onto another process). In our email folders example we also serialize the pointer to the source folder (where the emails come from) so that we can detect dropping onto the same folder (which should do nothing).&lt;/p&gt;&lt;p data-block-key=&quot;9ggql&quot;&gt;To serialize pointers in QDataStream, cast them to quintptr, see the &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/blob/main/ItemViews-DragAndDrop/part3-dropping-onto-items/qlistwidget/drop-onto-qlistwidgetitems.cpp#L58&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;example code&lt;/a&gt; for details.&lt;/p&gt;&lt;h3 id=&quot;on-the-drop-side&quot; anchor=&quot;on-the-drop-side&quot; data-block-key=&quot;fypts&quot;&gt;On the &quot;drop&quot; side&lt;/h3&gt;&lt;p data-block-key=&quot;csd0h&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropMode(QAbstractItemView::DropOnly)&lt;/code&gt; or &lt;code&gt;DragDrop&lt;/code&gt; if it should support both&lt;/p&gt;&lt;p data-block-key=&quot;8bf8n&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropOverwriteMode(true)&lt;/code&gt; for a minor improvement: no forbidden cursor when moving the drag between folders. Instead Qt only computes drop positions which are onto items, as we want here.&lt;/p&gt;&lt;p data-block-key=&quot;ahkov&quot;&gt;☑ Reimplement &lt;code&gt;Widget::mimeTypes()&lt;/code&gt; and return the same name as the one used on the drag side&apos;s &lt;code&gt;mimeData&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;2e3uc&quot;&gt;☑ Reimplement &lt;code&gt;Widget::dropMimeData()&lt;/code&gt; (note that the signature is different between &lt;code&gt;QListWidget&lt;/code&gt;, &lt;code&gt;QTableWidget&lt;/code&gt; and &lt;code&gt;QTreeWidget&lt;/code&gt;) This is where you deserialize the data and handle the drop. In the email example, this is where we copy or move the email into the destination folder.&lt;/p&gt;&lt;p data-block-key=&quot;dklo6&quot;&gt;Make sure to do all of the following:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;f8g44&quot;&gt;any necessary behind the scenes work (in our case, moving the actual email)&lt;/li&gt;&lt;li data-block-key=&quot;9afgt&quot;&gt;updating the UI (creating or deleting items as needed)&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;8m0jd&quot;&gt;This is a case where proper model/view separation is actually much simpler.&lt;/p&gt;&lt;h2 id=&quot;improvements-to-qt&quot; anchor=&quot;improvements-to-qt&quot; data-block-key=&quot;1cp4j&quot;&gt;Improvements to Qt&lt;/h2&gt;&lt;p data-block-key=&quot;ffn49&quot;&gt;While writing and testing these code examples, I improved the following things in Qt, in addition to those listed in the previous blog posts:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;7nmug&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-2553&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-2553&lt;/a&gt; QTreeView with setAutoExpandDelay() collapses items while dragging over it, fixed in Qt 6.8.1&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;conclusion&quot; anchor=&quot;conclusion&quot; data-block-key=&quot;6231l&quot;&gt;Conclusion&lt;/h2&gt;&lt;p data-block-key=&quot;al71j&quot;&gt;I hope you enjoyed this blog post series and learned a few things.&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/35_OrMik0ok?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;Introduction to Qt Widgets (Part 67A) - Dropping Files onto Widgets&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-in-qt-part-3/&quot;&gt;Model/View Drag and Drop in Qt - Part 3&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>David Faure</dc:creator><category>c++</category><category>qt</category></item><item><title>Model/View Drag and Drop in Qt - Part 2</title><link>https://www.kdab.com/modelview-drag-and-drop-in-qt-part-2/</link><guid isPermaLink="true">https://www.kdab.com/modelview-drag-and-drop-in-qt-part-2/</guid><description>&lt;p data-block-key=&quot;1j2m5&quot;&gt;In this follow-up to our drag-and-drop guide, we explore how to move items between views in Qt’s model/view framework. Learn how to set up both drag and drop sides, handle MIME data, and implement key model methods. Whether you&apos;re working with model/view separation or with item widgets, this post provides practical examples to help you customize UI interactions effectively.&lt;/p&gt;</description><pubDate>Mon, 10 Mar 2025 08:19:00 GMT</pubDate><content:encoded>&lt;h1&gt;Model/View Drag and Drop in Qt - Part 2&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chaks&quot;&gt;In the &lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-part-1/&quot;&gt;previous blog&lt;/a&gt;, you learned all about moving items within a single view, to reorder them.&lt;/p&gt;&lt;p data-block-key=&quot;etoh6&quot;&gt;In part 2, we are still talking about moving items, and still about inserting them between existing items (never overwriting items) but this time the user can move items from one view to another. A typical use case is a list of available items on the left, and a list of selected items on the right (one concrete example would be to let the user customize which buttons should appear in a toolbar). This also often includes reordering items in the right-side list, the good news being that this comes for free (no extra code needed).&lt;/p&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step1&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part2-step1.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step1&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step1&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ebe0l&quot;&gt;Moving a row between treeviews, step 1&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step2&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part2-step2.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step2&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step2&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ebe0l&quot;&gt;Moving a row between treeviews, step 2&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step3&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Blog_DragDrop_Qt_part2-step3.original.png&quot; class=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step3&quot; alt=&quot;Blog_Drag&amp;amp;Drop_Qt_part2-step3&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ebe0l&quot;&gt;Moving a row between treeviews, step 3&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;with-modelview-separation&quot; anchor=&quot;with-modelview-separation&quot; data-block-key=&quot;2bvtm&quot;&gt;With Model/View separation&lt;/h2&gt;&lt;p data-block-key=&quot;d6ka5&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part2-moving-items-between-views/model-view&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Example code&lt;/a&gt; for flat models and &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part2-moving-items-between-views/treemodel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;example code&lt;/a&gt; for tree models.&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-view-on-the-drag-side&quot; anchor=&quot;setting-up-the-view-on-the-drag-side&quot; data-block-key=&quot;riiwb&quot;&gt;Setting up the view on the drag side&lt;/h3&gt;&lt;p data-block-key=&quot;6p241&quot;&gt;To allow dragging items out of the view, make sure to do the following:&lt;/p&gt;&lt;p data-block-key=&quot;fcvf1&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropMode(QAbstractItemView::DragOnly)&lt;/code&gt; (or &lt;code&gt;DragDrop&lt;/code&gt; if it should support both).&lt;/p&gt;&lt;p data-block-key=&quot;1bk19&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropOverwriteMode(false)&lt;/code&gt; so that &lt;code&gt;QTableView&lt;/code&gt; calls removeRows when moving rows, rather than just clearing their cells&lt;/p&gt;&lt;p data-block-key=&quot;68gvv&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDefaultDropAction(Qt::MoveAction)&lt;/code&gt; so it&apos;s a move and not a copy&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-model-on-the-drag-side&quot; anchor=&quot;setting-up-the-model-on-the-drag-side&quot; data-block-key=&quot;3io1k&quot;&gt;Setting up the model on the drag side&lt;/h3&gt;&lt;p data-block-key=&quot;bs2j1&quot;&gt;To implement dragging items out of a model, you need to implement the following:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class CountryModel : public QAbstractTableModel
{
    ~~~
    Qt::ItemFlags flags(const QModelIndex &amp;amp;index) const override
    {
        if (!index.isValid())
            return {}; // depending on whether you want drops as well (next section)
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
    }

    // the default is &amp;quot;return supportedDropActions()&amp;quot;, let&amp;#x27;s be explicit
    Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }

    QMimeData *mimeData(const QModelIndexList &amp;amp;indexes) const override; // see below

    bool removeRows(int position, int rows, const QModelIndex &amp;amp;parent) override; // see below
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chaks&quot;&gt;More precisely, the check-list is the following:&lt;/p&gt;&lt;p data-block-key=&quot;2aq8&quot;&gt;☑ Reimplement &lt;code&gt;flags()&lt;/code&gt; to add &lt;code&gt;Qt::ItemIsDragEnabled&lt;/code&gt; in the case of a valid index&lt;/p&gt;&lt;p data-block-key=&quot;6ilmi&quot;&gt;☑ Reimplement &lt;code&gt;supportedDragActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;3n8ic&quot;&gt;☑ Reimplement &lt;code&gt;mimeData()&lt;/code&gt; to serialize the complete data for the dragged items. If the views are always in the same process, you can get away with serializing only node pointers (if you have that, e.g. for tree models) and application PID (to refuse dropping onto another process). Otherwise you can encode the actual data, like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;QMimeData *CountryModel::mimeData(const QModelIndexList &amp;amp;indexes) const
{
    QByteArray encodedData;
    QDataStream stream(&amp;amp;encodedData, QIODevice::WriteOnly);
    for (const QModelIndex &amp;amp;index : indexes) {
        // This calls operator&amp;lt;&amp;lt;(QDataStream &amp;amp;stream, const CountryData &amp;amp;countryData), which you must implement
        stream &amp;lt;&amp;lt; m_data.at(index.row());
    }

    QMimeData *mimeData = new QMimeData;
    mimeData-&amp;gt;setData(s_mimeType, encodedData);
    return mimeData;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chaks&quot;&gt;&lt;code&gt;s_mimeType&lt;/code&gt; is the name of the type of data (make up a name, it usually starts with &lt;code&gt;application/x-&lt;/code&gt;)&lt;/p&gt;&lt;p data-block-key=&quot;466rc&quot;&gt;☑ Reimplement &lt;code&gt;removeRows()&lt;/code&gt;, it will be called after a successful drop. For instance, if your data is in a vector called &lt;code&gt;m_data&lt;/code&gt;, the implementation would look like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;bool CountryModel::removeRows(int position, int rows, const QModelIndex &amp;amp;parent)
{
    beginRemoveRows(parent, position, position + rows - 1);
    for (int row = 0; row &amp;lt; rows; ++row)
        m_data.removeAt(position);
    endRemoveRows();
    return true;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;setting-up-the-view-on-the-drop-side&quot; anchor=&quot;setting-up-the-view-on-the-drop-side&quot; data-block-key=&quot;fdoo8&quot;&gt;Setting up the view on the drop side&lt;/h3&gt;&lt;p data-block-key=&quot;1nlju&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropMode(QAbstractItemView::DragDrop)&lt;/code&gt; (already done if both views should support dragging and dropping)&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-model-on-the-drop-side&quot; anchor=&quot;setting-up-the-model-on-the-drop-side&quot; data-block-key=&quot;3macl&quot;&gt;Setting up the model on the drop side&lt;/h3&gt;&lt;p data-block-key=&quot;bpo2m&quot;&gt;To implement dropping items into a model (between existing items), you need to implement the following:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class DropModel : public QAbstractTableModel
{
    ~~~
    Qt::ItemFlags flags(const QModelIndex &amp;amp;index) const override
    {
        if (!index.isValid())
            return Qt::ItemIsDropEnabled;
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // and optionally Qt::ItemIsDragEnabled (previous section)
    }

    // the default is &amp;quot;copy only&amp;quot;, change it
    Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }

    QStringList mimeTypes() const override { return {QString::fromLatin1(s_mimeType)}; }

    bool dropMimeData(const QMimeData *mimeData, Qt::DropAction action, 
                      int row, int column, const QModelIndex &amp;amp;parent) override; // see below
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;chaks&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;1hb2o&quot;&gt;☑ Reimplement &lt;code&gt;flags()&lt;/code&gt;&lt;br/&gt; For a valid index, make sure &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt; is NOT set (except for tree models where we need to drop onto items in order to insert a first child).&lt;br/&gt; For the invalid index, add &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt;, to allow dropping between items.&lt;/p&gt;&lt;p data-block-key=&quot;c6b0b&quot;&gt;☑ Reimplement &lt;code&gt;mimeTypes()&lt;/code&gt; and return the name of the MIME type used by the &lt;code&gt;mimeData()&lt;/code&gt; function on the drag side.&lt;/p&gt;&lt;p data-block-key=&quot;7otep&quot;&gt;☑ Reimplement &lt;code&gt;dropMimeData()&lt;/code&gt;&lt;br/&gt; to deserialize the data and insert new rows.&lt;br/&gt; In the special case of in-process tree models, clone the dragged nodes.&lt;br/&gt; In both cases, once you&apos;re done, return true, so that the drag side then deletes the dragged rows by calling &lt;code&gt;removeRows()&lt;/code&gt; on its model.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;bool DropModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int column, const QModelIndex &amp;amp;parent)
{
    ~~~  // safety checks, see full example code

    if (row == -1) // drop into empty area = append
        row = rowCount(parent);

    // decode data
    const QByteArray encodedData = mimeData-&amp;gt;data(s_mimeType);
    QDataStream stream(encodedData);
    QVector&amp;lt;CountryData&amp;gt; newCountries;
    while (!stream.atEnd()) {
        CountryData countryData;
        stream &amp;gt;&amp;gt; countryData;
        newCountries.append(countryData);
    }

    // insert new countries
    beginInsertRows(parent, row, row + newCountries.count() - 1);
    for (const CountryData &amp;amp;countryData : newCountries)
        m_data.insert(row++, countryData);
    endInsertRows();

    return true; // let the view handle deletion on the source side by calling removeRows there
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;using-item-widgets&quot; anchor=&quot;using-item-widgets&quot; data-block-key=&quot;u2nre&quot;&gt;Using item widgets&lt;/h2&gt;&lt;p data-block-key=&quot;3u2av&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part2-moving-items-between-views/itemwidgets&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Example code&lt;/a&gt; can be found following this link.&lt;/p&gt;&lt;h3 id=&quot;for-all-kinds-of-widgets&quot; anchor=&quot;for-all-kinds-of-widgets&quot; data-block-key=&quot;4ciwc&quot;&gt;For all kinds of widgets&lt;/h3&gt;&lt;p data-block-key=&quot;3bv9q&quot;&gt;On the &quot;drag&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;7aunc&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropMode(QAbstractItemView::DragOnly)&lt;/code&gt; or &lt;code&gt;DragDrop&lt;/code&gt; if it should support both&lt;/p&gt;&lt;p data-block-key=&quot;8vajf&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDefaultDropAction(Qt::MoveAction)&lt;/code&gt; so the drag starts as a move right away&lt;/p&gt;&lt;p data-block-key=&quot;1dkq5&quot;&gt;On the &quot;drop&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;7etkj&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropMode(QAbstractItemView::DropOnly)&lt;/code&gt; or &lt;code&gt;DragDrop&lt;/code&gt; if it should support both&lt;/p&gt;&lt;p data-block-key=&quot;47ha8&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; to return only &lt;code&gt;Qt::MoveAction&lt;/code&gt;&lt;/p&gt;&lt;h3 id=&quot;additional-requirements-for-qtablewidget&quot; anchor=&quot;additional-requirements-for-qtablewidget&quot; data-block-key=&quot;fswhx&quot;&gt;Additional requirements for QTableWidget&lt;/h3&gt;&lt;p data-block-key=&quot;b0jo2&quot;&gt;When using &lt;code&gt;QTableWidget&lt;/code&gt;, in addition to the common steps above you need to:&lt;/p&gt;&lt;p data-block-key=&quot;3prk2&quot;&gt;On the &quot;drag&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;33p4d&quot;&gt;☑ Call &lt;code&gt;item-&amp;gt;setFlags(item-&amp;gt;flags() &amp;amp; ~Qt::ItemIsDropEnabled);&lt;/code&gt; for each item, to disable dropping onto items.&lt;/p&gt;&lt;p data-block-key=&quot;5l9fm&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropOverwriteMode(false)&lt;/code&gt; so that after a move the rows are removed rather than cleared&lt;/p&gt;&lt;p data-block-key=&quot;37qks&quot;&gt;On the &quot;drop&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;aukud&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDragDropOverwriteMode(false)&lt;/code&gt; so that it inserts rows instead of replacing cells (the default is &lt;code&gt;false&lt;/code&gt; for the other views anyway)&lt;/p&gt;&lt;p data-block-key=&quot;54bnl&quot;&gt;☑ Another problem is that the items created by a drop will automatically get the &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt; flag, which you don&apos;t want. To solve this, use &lt;code&gt;widget-&amp;gt;setItemPrototype()&lt;/code&gt; with an item that has the right flags (see the example).&lt;/p&gt;&lt;h3 id=&quot;additional-requirements-for-qtreewidget&quot; anchor=&quot;additional-requirements-for-qtreewidget&quot; data-block-key=&quot;xy0ix&quot;&gt;Additional requirements for QTreeWidget&lt;/h3&gt;&lt;p data-block-key=&quot;3bgd2&quot;&gt;When using &lt;code&gt;QTreeWidget&lt;/code&gt;, you cannot disable dropping onto items (which creates a child of the item).&lt;/p&gt;&lt;p data-block-key=&quot;4159k&quot;&gt;You could call &lt;code&gt;item-&amp;gt;setFlags(item-&amp;gt;flags() &amp;amp; ~Qt::ItemIsDropEnabled);&lt;/code&gt; on your own items, but when &lt;code&gt;QTreeWidget&lt;/code&gt; creates new items upon a drop, you cannot prevent them from having the flag &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt; set. The prototype solution used above for &lt;code&gt;QTableWidget&lt;/code&gt; doesn&apos;t exist for &lt;code&gt;QTreeWidget&lt;/code&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;bn3qv&quot;&gt;This means, if you want to let the user build and reorganize an actual tree, you can use &lt;code&gt;QTreeWidget&lt;/code&gt;. But if you just want a flat multi-column list, then you should use &lt;code&gt;QTreeView&lt;/code&gt; (see previous section on model/view separation).&lt;/p&gt;&lt;h2 id=&quot;addendum-movecopy-items-between-views&quot; anchor=&quot;addendum-movecopy-items-between-views&quot; data-block-key=&quot;ye2yn&quot;&gt;Addendum: Move/copy items between views&lt;/h2&gt;&lt;p data-block-key=&quot;f0bg3&quot;&gt;If the user should be able to choose between copying and moving items, follow the previous section and make the following changes.&lt;/p&gt;&lt;h3 id=&quot;with-modelview-separation&quot; anchor=&quot;with-modelview-separation&quot; data-block-key=&quot;gt2wm&quot;&gt;With Model/View separation&lt;/h3&gt;&lt;p data-block-key=&quot;5g8oh&quot;&gt;On the &quot;drag&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;1ou38&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDefaultDropAction(...)&lt;/code&gt; to choose whether the default should be move or copy. The user can press Shift to force a move, and Ctrl to force a copy.&lt;/p&gt;&lt;p data-block-key=&quot;akbil&quot;&gt;☑ Reimplement &lt;code&gt;supportedDragActions()&lt;/code&gt; in the model to return &lt;code&gt;Qt::MoveAction | Qt::CopyAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;9m4l2&quot;&gt;On the &quot;drop&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;79ftu&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; in the model to return &lt;code&gt;Qt::MoveAction | Qt::CopyAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;20u4f&quot;&gt;The good news is that there&apos;s nothing else to do.&lt;/p&gt;&lt;h3 id=&quot;using-item-widgets&quot; anchor=&quot;using-item-widgets&quot; data-block-key=&quot;eo36z&quot;&gt;Using item widgets&lt;/h3&gt;&lt;p data-block-key=&quot;civc1&quot;&gt;On the &quot;drag&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;cht14&quot;&gt;☑ Call &lt;code&gt;widget-&amp;gt;setDefaultDropAction(...)&lt;/code&gt; to choose whether the default should be move or copy. The user can press Shift to force a move, and Ctrl to force a copy.&lt;/p&gt;&lt;p data-block-key=&quot;dfojn&quot;&gt;Until Qt 6.10 there was no &lt;code&gt;setSupportedDragActions()&lt;/code&gt; method in the item widget classes (that was &lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-87465&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-87465&lt;/a&gt;, I implemented it for 6.10). Fortunately the default behavior is to use what &lt;code&gt;supportedDropActions()&lt;/code&gt; returns so if you just want move and copy in both, reimplementing &lt;code&gt;supportedDropActions()&lt;/code&gt; is enough.&lt;/p&gt;&lt;p data-block-key=&quot;f1135&quot;&gt;On the &quot;drop&quot; side:&lt;/p&gt;&lt;p data-block-key=&quot;d2u4p&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; in the item widget class to return &lt;code&gt;Qt::MoveAction | Qt::CopyAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;6f810&quot;&gt;The good news is that there&apos;s nothing else to do.&lt;/p&gt;&lt;h3 id=&quot;improvements-to-qt&quot; anchor=&quot;improvements-to-qt&quot; data-block-key=&quot;nxuuc&quot;&gt;Improvements to Qt&lt;/h3&gt;&lt;p data-block-key=&quot;18jv6&quot;&gt;While writing and testing these code examples, I improved the following things in Qt:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;dkpn9&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-1387&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-1387&lt;/a&gt; &quot;Drag and drop multiple columns with item views. Dragging a row and dropping it in a column &amp;gt; 0 creates multiple rows.&quot;, fixed in 6.8.1&lt;/li&gt;&lt;li data-block-key=&quot;1naij&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-36831&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-36831&lt;/a&gt; &quot;Drop indicator painted as single pixel when not shown&quot; fixed in 6.8.1&lt;/li&gt;&lt;li data-block-key=&quot;8jnjc&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-87465&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-87465&lt;/a&gt; ItemWidgets: add supportedDragActions()/setSupportedDragActions(), implemented in 6.10&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;conclusion&quot; anchor=&quot;conclusion&quot; data-block-key=&quot;n7g21&quot;&gt;Conclusion&lt;/h2&gt;&lt;p data-block-key=&quot;3rn34&quot;&gt;In the next blog post of this series, you will learn how to move (or copy) onto existing items, rather than between them.&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/35_OrMik0ok?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;Introduction to Qt Widgets (Part 67A) - Dropping Files onto Widgets&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-in-qt-part-2/&quot;&gt;Model/View Drag and Drop in Qt - Part 2&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>David Faure</dc:creator><category>c++</category><category>qt</category></item><item><title>Model/View Drag and Drop in Qt - Part 1</title><link>https://www.kdab.com/modelview-drag-and-drop-part-1/</link><guid isPermaLink="true">https://www.kdab.com/modelview-drag-and-drop-part-1/</guid><description>&lt;p data-block-key=&quot;228lt&quot;&gt;This blog post is the first in a series on implementing drag-and-drop in the Qt model/view framework. It covers how to reorder items within a single view, whether you&apos;re using QListView, QTableView, or QTreeView with a custom model, or QListWidget, QTableWidget, or QTreeWidget with items. The post includes code examples, checklists, and insights into improvements made in Qt 6.8 for smoother drag-and-drop functionality.&lt;/p&gt;</description><pubDate>Thu, 06 Feb 2025 08:07:00 GMT</pubDate><content:encoded>&lt;h1&gt;Model/View Drag and Drop in Qt - Part 1&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;This blog series is all about implementing drag-and-drop in the Qt model/view framework. In addition to complete code examples, you&apos;ll find checklists that you can go through to make sure that you did not forget anything in your own implementation, when something isn&apos;t working as expected.&lt;/p&gt;&lt;p data-block-key=&quot;8b84o&quot;&gt;At first, we are going to look at Drag and Drop within a single view, to change the order of the items. The view can be a list, a table or a tree, there are very little differences in what you have to do.&lt;/p&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-table-step1&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-table-step1.original.png&quot; class=&quot;part1-table-step1&quot; alt=&quot;part1-table-step1&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Moving a row in a tableview, step 1&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-table-step2&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-table-step2.original.png&quot; class=&quot;part1-table-step2&quot; alt=&quot;part1-table-step2&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Moving a row in a tableview, step 2&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-table-step3&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-table-step3.original.png&quot; class=&quot;part1-table-step3&quot; alt=&quot;part1-table-step3&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Moving a row in a tableview, step 3&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;The main question, however, is whether you are using QListView/QTableView/QTreeView on top of a custom item model, or QListWidget/QTableWidget/QTreeWidget with items in them. Let&apos;s explore each one in turn.&lt;/p&gt;&lt;h2 id=&quot;with-modelview-separation&quot; anchor=&quot;with-modelview-separation&quot; data-block-key=&quot;zmu6n&quot;&gt;With Model/View separation&lt;/h2&gt;&lt;p data-block-key=&quot;30k34&quot;&gt;The code being discussed here is extracted from &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part1-reordering-elements/model-view&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;the example&lt;/a&gt;. That example features a flat model, while &lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part1-reordering-elements/treemodel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this example&lt;/a&gt; features a tree model. The checklist is the same for these two cases.&lt;/p&gt;&lt;h3 id=&quot;setting-up-the-view&quot; anchor=&quot;setting-up-the-view&quot; data-block-key=&quot;5rsnb&quot;&gt;Setting up the view&lt;/h3&gt;&lt;p data-block-key=&quot;6g39s&quot;&gt;☑ Call &lt;code&gt;view-&amp;gt;setDragDropMode(QAbstractItemView::InternalMove)&lt;/code&gt; to enable the mode where only moving within the same view is allowed&lt;/p&gt;&lt;p data-block-key=&quot;5an2c&quot;&gt;☑ When using &lt;code&gt;QTableView&lt;/code&gt;, call &lt;code&gt;view-&amp;gt;setDragDropOverwriteMode(false)&lt;/code&gt; so that it inserts rows instead of replacing cells (the default is &lt;code&gt;false&lt;/code&gt; for the other views anyway)&lt;/p&gt;&lt;h3 id=&quot;adding-drag-n-drop-support-to-the-model&quot; anchor=&quot;adding-drag-n-drop-support-to-the-model&quot; data-block-key=&quot;rv5to&quot;&gt;Adding drag-n-drop support to the model&lt;/h3&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-list&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-list.original.png&quot; class=&quot;part1-list&quot; alt=&quot;part1-list&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable ListView&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-table&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-table.original.png&quot; class=&quot;part1-table&quot; alt=&quot;part1-table&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable TableView&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;For a model being used in QListView or QTableView, all you need is something like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class CountryModel : public QAbstractTableModel
{
    ~~~
    Qt::ItemFlags flags(const QModelIndex &amp;amp;index) const override
    {
        if (!index.isValid())
            return Qt::ItemIsDropEnabled; // allow dropping between items
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
    }

    // the default is &amp;quot;copy only&amp;quot;, change it
    Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }

    // the default is &amp;quot;return supportedDropActions()&amp;quot;, let&amp;#x27;s be explicit
    Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }

    QStringList mimeTypes() const override { return {QString::fromLatin1(s_mimeType)}; }

    bool moveRows(const QModelIndex &amp;amp;sourceParent, int sourceRow, int count, const QModelIndex &amp;amp;destinationParent, int destinationChild) override; // see below
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;The checklist for the changes you need to make in your model is therefore the following:&lt;/p&gt;&lt;p data-block-key=&quot;3gmg7&quot;&gt;☑ Reimplement &lt;code&gt;flags()&lt;/code&gt;&lt;br/&gt; For a valid index, add &lt;code&gt;Qt::ItemIsDragEnabled&lt;/code&gt; and make sure &lt;code&gt;Qt::ItemIsDropEnabled&lt;/code&gt; is NOT set (except for tree models where we need to drop onto items in order to insert a first child). \&lt;/p&gt;&lt;p data-block-key=&quot;2laoa&quot;&gt;☑ Reimplement &lt;code&gt;mimeTypes()&lt;/code&gt; and make up a name for the mimetype (usually starting with &lt;code&gt;application/x-&lt;/code&gt;)&lt;/p&gt;&lt;p data-block-key=&quot;9po6f&quot;&gt;☑ Reimplement &lt;code&gt;supportedDragActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;dd3l0&quot;&gt;☑ Reimplement &lt;code&gt;supportedDropActions()&lt;/code&gt; to return &lt;code&gt;Qt::MoveAction&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;jues&quot;&gt;☑ Reimplement &lt;code&gt;moveRows()&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;7gh1v&quot;&gt;Note that this approach is only valid when using &lt;code&gt;QListView&lt;/code&gt; or, assuming Qt &amp;gt;= 6.8.0, &lt;code&gt;QTableView&lt;/code&gt; - see the following sections for details.&lt;/p&gt;&lt;p data-block-key=&quot;aqmhr&quot;&gt;In a model that encapsulates a &lt;code&gt;QVector&lt;/code&gt; called &lt;code&gt;m_data&lt;/code&gt;, the implementation of &lt;code&gt;moveRows&lt;/code&gt; can look like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;bool CountryModel::moveRows(const QModelIndex &amp;amp;sourceParent, int sourceRow, int count, const QModelIndex &amp;amp;destinationParent, int destinationChild)
{
    if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild))
        return false; // invalid move, e.g. no-op (move row 2 to row 2 or to row 3)

    for (int i = 0; i &amp;lt; count; ++i) {
        m_data.move(sourceRow + i, destinationChild + (sourceRow &amp;gt; destinationChild ? 0 : -1));
    }

    endMoveRows();
    return true;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;qtreeview-does-not-call-moverows&quot; anchor=&quot;qtreeview-does-not-call-moverows&quot; data-block-key=&quot;7rzqf&quot;&gt;QTreeView does not call moveRows&lt;/h3&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-tree&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-tree.original.png&quot; class=&quot;part1-tree&quot; alt=&quot;part1-tree&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable treeview&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-treemodel&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-treemodel.original.png&quot; class=&quot;part1-treemodel&quot; alt=&quot;part1-treemodel&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable treeview with a tree model&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;QTreeView does not (yet?) call &lt;code&gt;moveRows&lt;/code&gt; in the model, so you need to:&lt;/p&gt;&lt;p data-block-key=&quot;2n85v&quot;&gt;☑ Reimplement &lt;code&gt;mimeData()&lt;/code&gt; to encode row numbers for flat models, and node pointers for tree models&lt;/p&gt;&lt;p data-block-key=&quot;3iusc&quot;&gt;☑ Reimplement &lt;code&gt;dropMimeData()&lt;/code&gt; to implement the move and return false (meaning: all done)&lt;/p&gt;&lt;p data-block-key=&quot;fcc85&quot;&gt;Note that this means a move is in fact an insertion and a deletion, so the selection isn&apos;t automatically updated to point to the moved row(s).&lt;/p&gt;&lt;h3 id=&quot;qtableview-in-qt-680&quot; anchor=&quot;qtableview-in-qt-680&quot; data-block-key=&quot;c7rbr&quot;&gt;QTableView in Qt &amp;lt; 6.8.0&lt;/h3&gt;&lt;p data-block-key=&quot;24crl&quot;&gt;I implemented moving of rows in &lt;code&gt;QTableView&lt;/code&gt; itself for Qt 6.8.0, so that moving rows in a table view is simpler to implement (one method instead of two), more efficient, and so that selection is updated. If you&apos;re not yet using Qt &amp;gt;= 6.8.0 then you&apos;ll have to reimplement &lt;code&gt;mimeData()&lt;/code&gt; and &lt;code&gt;dropMimeData()&lt;/code&gt; in your model, as per the previous section.&lt;/p&gt;&lt;p data-block-key=&quot;cf0jh&quot;&gt;This concludes the section on how to implement a reorderable view using a separate model class.&lt;/p&gt;&lt;h2 id=&quot;using-item-widgets&quot; anchor=&quot;using-item-widgets&quot; data-block-key=&quot;s05h5&quot;&gt;Using item widgets&lt;/h2&gt;&lt;p data-block-key=&quot;6u93&quot;&gt;The alternative to model/view separation is the use of the item widgets (&lt;code&gt;QListWidget&lt;/code&gt;, &lt;code&gt;QTableWidget&lt;/code&gt; or &lt;code&gt;QTreeWidget&lt;/code&gt;) which you populate directly by creating items.&lt;/p&gt;&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-listwidget&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-listwidget.original.png&quot; class=&quot;part1-listwidget&quot; alt=&quot;part1-listwidget&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable QListWidget&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-tablewidget&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-tablewidget.original.png&quot; class=&quot;part1-tablewidget&quot; alt=&quot;part1-tablewidget&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable QTableWidget&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;part1-treewidget&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/part1-treewidget.original.png&quot; class=&quot;part1-treewidget&quot; alt=&quot;part1-treewidget&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pnkqi&quot;&gt;Reorderable QTreeWidget&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ame85&quot;&gt;Here&apos;s what you need to do to allow users to reorder those items.&lt;/p&gt;&lt;p data-block-key=&quot;avskc&quot;&gt;&lt;a href=&quot;https://github.com/KDABLabs/blogs-qt/tree/main/ItemViews-DragAndDrop/part1-reordering-elements/itemwidgets&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Example code&lt;/a&gt; can be found following this link.&lt;/p&gt;&lt;h3 id=&quot;reorderable-qlistwidget&quot; anchor=&quot;reorderable-qlistwidget&quot; data-block-key=&quot;pyoci&quot;&gt;Reorderable QListWidget&lt;/h3&gt;&lt;p data-block-key=&quot;6dnej&quot;&gt;☑ Call &lt;code&gt;listWidget-&amp;gt;setDragDropMode(QAbstractItemView::InternalMove)&lt;/code&gt; to enable the mode where only moving within the same view is allowed&lt;/p&gt;&lt;p data-block-key=&quot;6c1ku&quot;&gt;For a &lt;code&gt;QListWidget&lt;/code&gt;, this is all you need. That was easy!&lt;/p&gt;&lt;h3 id=&quot;reorderable-qtablewidget&quot; anchor=&quot;reorderable-qtablewidget&quot; data-block-key=&quot;e6x07&quot;&gt;Reorderable QTableWidget&lt;/h3&gt;&lt;p data-block-key=&quot;8sglk&quot;&gt;When using &lt;code&gt;QTableWidget&lt;/code&gt;:&lt;/p&gt;&lt;p data-block-key=&quot;fo6ir&quot;&gt;☑ Call &lt;code&gt;tableWidget-&amp;gt;setDragDropMode(QAbstractItemView::InternalMove)&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;8cfv5&quot;&gt;☑ Call &lt;code&gt;tableWidget-&amp;gt;setDragDropOverwriteMode(false)&lt;/code&gt; so that it inserts rows instead of replacing cells&lt;/p&gt;&lt;p data-block-key=&quot;22rpq&quot;&gt;☑ Call &lt;code&gt;item-&amp;gt;setFlags(item-&amp;gt;flags() &amp;amp; ~Qt::ItemIsDropEnabled);&lt;/code&gt; on each item, to disable dropping onto items&lt;/p&gt;&lt;p data-block-key=&quot;cpaqu&quot;&gt;Note: Before Qt 6.8.0, &lt;code&gt;QTableWidget&lt;/code&gt; did not really support moving rows. It would instead move data into cells (like Excel). The example code shows a workaround, but since it calls code that inserts a row and deletes the old one, header data is lost in the process. My changes in Qt 6.8.0 implement support for moving rows in &lt;code&gt;QTableWidget&lt;/code&gt;&apos;s internal model, so it&apos;s all fixed there. If you really need this feature in older versions of Qt, consider switching to &lt;code&gt;QTableView&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;reorderable-qtreewidget&quot; anchor=&quot;reorderable-qtreewidget&quot; data-block-key=&quot;o5ljf&quot;&gt;Reorderable QTreeWidget&lt;/h3&gt;&lt;p data-block-key=&quot;76fbk&quot;&gt;When using &lt;code&gt;QTreeWidget&lt;/code&gt;:&lt;/p&gt;&lt;p data-block-key=&quot;cjm4i&quot;&gt;☑ Call &lt;code&gt;tableWidget-&amp;gt;setDragDropMode(QAbstractItemView::InternalMove)&lt;/code&gt;&lt;/p&gt;&lt;p data-block-key=&quot;8ubr8&quot;&gt;☑ Call &lt;code&gt;item-&amp;gt;setFlags(item-&amp;gt;flags() &amp;amp; ~Qt::ItemIsDropEnabled);&lt;/code&gt; on each item, to disable dropping onto items&lt;/p&gt;&lt;h3 id=&quot;conclusion-about-reorderable-item-widgets&quot; anchor=&quot;conclusion-about-reorderable-item-widgets&quot; data-block-key=&quot;fafa0&quot;&gt;Conclusion about reorderable item widgets&lt;/h3&gt;&lt;p data-block-key=&quot;ckv9l&quot;&gt;Of course, you&apos;ll also need to iterate over the items at the end to grab the new order, like the example code does. As usual, item widgets lead to less code to write, but the runtime performance is worse than when using model/view separation. So, only use item widgets when the number of items is small (and you don&apos;t need proxy models).&lt;/p&gt;&lt;h2 id=&quot;improvements-to-qt&quot; anchor=&quot;improvements-to-qt&quot; data-block-key=&quot;os039&quot;&gt;Improvements to Qt&lt;/h2&gt;&lt;p data-block-key=&quot;6e6r8&quot;&gt;While writing and testing these code examples, I improved the following things in Qt 6.8:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;1ao5n&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-13873&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-13873&lt;/a&gt; / &lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-101475&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-101475&lt;/a&gt; - QTableView: implement moving rows by drag-n-drop&lt;/li&gt;&lt;li data-block-key=&quot;fokbn&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-69807&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-69807&lt;/a&gt; - Implement QTableModel::moveRows&lt;/li&gt;&lt;li data-block-key=&quot;5iu0p&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-130045&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-130045&lt;/a&gt; - QTableView: fix dropping between items when precisely on the cell border&lt;/li&gt;&lt;li data-block-key=&quot;cd23s&quot;&gt;&lt;a href=&quot;https://bugreports.qt.io/browse/QTBUG-1656&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QTBUG-1656&lt;/a&gt; - Implement full-row drop indicator when the selection behavior is SelectRows&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;conclusion&quot; anchor=&quot;conclusion&quot; data-block-key=&quot;doo2y&quot;&gt;Conclusion&lt;/h2&gt;&lt;p data-block-key=&quot;1rpb3&quot;&gt;I hope this checklist will be useful when you have to implement your own reordering of items in a model or an item-widget. Please post a comment if anything appears to be incorrect or missing.&lt;/p&gt;&lt;p data-block-key=&quot;1bnuu&quot;&gt;In the next blog post of this series, you will learn how to move (or even copy) items from one view to another.&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/35_OrMik0ok?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;Introduction to Qt Widgets (Part 67A) - Dropping Files onto Widgets&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/modelview-drag-and-drop-part-1/&quot;&gt;Model/View Drag and Drop in Qt - Part 1&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>David Faure</dc:creator><category>c++</category><category>qt</category></item><item><title>Improvements to Mozilla&apos;s Searchfox Code Browser</title><link>https://www.kdab.com/searchfox-improvements/</link><guid isPermaLink="true">https://www.kdab.com/searchfox-improvements/</guid><description>&lt;p data-block-key=&quot;ip6kr&quot;&gt;Mozilla is the maker of the famous Firefox web browser and the birthplace of the likes of Rust and Servo (read more about Embedding the Servo Web Engine in Qt). Firefox is a huge, multi-platform, multi-language project with 21 million lines of code back in 2020, according to their own blog post. Navigating in projects […]&lt;/p&gt;</description><pubDate>Tue, 17 Dec 2024 08:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Improvements to Mozilla&apos;s Searchfox Code Browser&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;jtulc&quot;&gt;Mozilla is the maker of the famous Firefox web browser and the birthplace of the likes of Rust and Servo (read more about &lt;a href=&quot;https://www.kdab.com/embedding-servo-in-qt/&quot;&gt;Embedding the Servo Web Engine in Qt&lt;/a&gt;).&lt;/p&gt;&lt;p data-block-key=&quot;ayucg&quot;&gt;Firefox is a huge, multi-platform, multi-language project with 21 million lines of code back in 2020, according to &lt;a href=&quot;https://hacks.mozilla.org/2020/04/code-quality-tools-at-mozilla/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;their own blog post&lt;/a&gt;. Navigating in projects like those is always a challenge, especially at the cross-language boundaries and in platform-specific code.&lt;/p&gt;&lt;p data-block-key=&quot;icrdw&quot;&gt;To improve working with the Firefox code-base, Mozilla hosts an online code browser tailored for Firefox called &lt;a href=&quot;https://searchfox.org&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Searchfox&lt;/a&gt;. Searchfox analyzes C++, JavaScript, various IDLs (interface definition languages), and Rust source code and makes them all browsable from a single interface with full-text search, semantic search, code navigation, test coverage report, and git blame support. It&amp;#x27;s the combination of a number of projects working together, both internal to Mozilla (like their Clang plugin for C++ analysis) and external (such as &lt;a href=&quot;https://rust-analyzer.github.io&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rust-analyzer&lt;/a&gt; maintained by &lt;a href=&quot;https://ferrous-systems.com&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ferrous Systems&lt;/a&gt;).&lt;/p&gt;&lt;p data-block-key=&quot;u9kpb&quot;&gt;It takes a whole repository in and separately indexes C++, Rust, JavaScript and now Java and Kotlin source code. All those analyses are then merged together across platforms, before running a cross-reference step and building the final index used by the web front-end available at searchfox.org.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;EverythingInOne_Mozilla_ShowGraph_V3.png&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/EverythingInOne_Mozilla_ShowGraph_V3.original.png&quot; class=&quot;EverythingInOne_Mozilla_ShowGraph_V3.png&quot; alt=&quot;EverythingInOne_Mozilla_ShowGraph_V3.png&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;3w9dr&quot;&gt;Mozilla asked KDAB to help them with adding Java and Kotlin support to Searchfox in prevision of the &lt;a href=&quot;https://github.com/mozilla-mobile/firefox-android/wiki#upcoming-migration-to-mozilla-central&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;merge of Firefox for Android into the main mozilla-central repository&lt;/a&gt; and enhance their C++ support with macro expansions. Let&amp;#x27;s dive into the details of those tasks.&lt;/p&gt;&lt;h2 id=&quot;javakotlin-support&quot; anchor=&quot;javakotlin-support&quot; data-block-key=&quot;qgpu9&quot;&gt;Java/Kotlin Support&lt;/h2&gt;&lt;p data-block-key=&quot;fl7zw&quot;&gt;Mozilla merged the Firefox for Android source code into the main mozilla-central repository that Searchfox indexes. To add support for that new Java and Kotlin code to Searchfox, we reused open-source tooling built by Sourcegraph around the SemanticDB and SCIP code indexing formats. (Many thanks to them!)&lt;/p&gt;&lt;p data-block-key=&quot;b92pg&quot;&gt;Sourcegraph&amp;#x27;s &lt;a href=&quot;https://github.com/sourcegraph/scip-java/tree/main/semanticdb-javac&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semanticdb-javac&lt;/a&gt; and &lt;a href=&quot;https://github.com/sourcegraph/scip-kotlin/tree/main/semanticdb-kotlinc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;semanticdb-kotlinc&lt;/a&gt; compiler plugins are integrated into Firefox&amp;#x27;s CI system to export SemanticDB artifacts. The Searchfox indexer fetches those SemanticDB files and turns them into a SCIP index, using &lt;a href=&quot;https://github.com/sourcegraph/scip-java/tree/main/scip-semanticdb&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;scip-semanticdb&lt;/a&gt;. That SCIP index is then consumed by the existing Searchfox-internal scip-indexer tool.&lt;/p&gt;&lt;p data-block-key=&quot;npc7g&quot;&gt;In the process, a couple of upstream contributions were made to rust-analyzer (which also emits SCIP data) and scip-semanticdb.&lt;/p&gt;&lt;p data-block-key=&quot;nnbd6&quot;&gt;A few examples of Searchfox at work:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;opb3g&quot;&gt;Searching for the Java class: &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol%3AS_jvm_org%2Fmozilla%2Fgeckoview%2FGeckoSession%23PromptDelegate%23AutocompleteRequest%23&amp;amp;path=&amp;amp;case=false&amp;amp;regexp=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;org.mozilla.geckoview.GeckoSession$PromptDelegate$AutocompleteRequest&lt;/a&gt; shows the definition, a superclass, some uses in Java source code and some uses in Kotlin tests.&lt;/li&gt;&lt;li data-block-key=&quot;ys603&quot;&gt;Searching for the Java interface method: &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol:S_jvm_org%2Fmozilla%2Fgeckoview%2FAutocomplete%23StorageDelegate%23onAddressFetch().&amp;amp;redirect=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;org.mozilla.geckoview.Autocomplete$StorageDelegate$onAddressFetch&lt;/a&gt; shows the definition, a couple of users, and a couple of implementers across Java and Kotlin code.&lt;/li&gt;&lt;li data-block-key=&quot;w5s79&quot;&gt;Querying the callers of a method with up to 2 indirections: &lt;a href=&quot;https://searchfox.org/mozilla-central/query/default?q=calls-to%3A%27org%3A%3Amozilla%3A%3Ageckoview%3A%3AAutofill%3A%3ASession%3A%3AgetDefaultDimensions%27+depth%3A2&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;calls-to:&amp;#x27;org::mozilla::geckoview::Autofill::Session::getDefaultDimensions&amp;#x27; depth:2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;xph8l&quot;&gt;If you want to dive into more details, see the feature request &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1490144&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on Bugzilla&lt;/a&gt;, the implementation and further discussion &lt;a href=&quot;https://github.com/mozsearch/mozsearch/pull/667&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt; and the release announcement &lt;a href=&quot;https://groups.google.com/a/mozilla.org/g/dev-platform/c/kerF6YSMFIY/m/BGcQB57fAAAJ&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on the mozilla dev-platform mailing list&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;javac-cross-language-support&quot; anchor=&quot;javac-cross-language-support&quot; data-block-key=&quot;p1aic&quot;&gt;Java/C++ Cross-language Support&lt;/h2&gt;&lt;p data-block-key=&quot;21ah3&quot;&gt;GeckoView is an Android wrapper around Gecko, the Firefox web engine. It extensively uses cross-language calls between Java and C++.&lt;/p&gt;&lt;p data-block-key=&quot;pgpde&quot;&gt;Searchfox already had support for cross-language interfaces, thanks to its IDL support. We built on top of that to support direct cross-language calls between Java and C++.&lt;/p&gt;&lt;p data-block-key=&quot;xxjce&quot;&gt;First, we identified the different ways the C++ and Java code interact and call each other. There are three ways Java methods marked with the &lt;code&gt;native&lt;/code&gt; keyword call into C++:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;vc0y0&quot;&gt;Case A1: By default, the JVM will search for a matching C function to call based on its name. For instance, calling &lt;code&gt;org.mozilla.gecko.mozglue.GeckoLoader.nativeRun&lt;/code&gt; from Java will call &lt;code&gt;Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun&lt;/code&gt; on the C++ side.&lt;/li&gt;&lt;li data-block-key=&quot;ic2ln&quot;&gt;Case A2: This behavior can be overridden at runtime by calling the &lt;code&gt;JNIEnv::RegisterNatives&lt;/code&gt; function on the C++ side to point at another function.&lt;/li&gt;&lt;li data-block-key=&quot;hnt2j&quot;&gt;Case A3: GeckoView has a code generator that looks for Java items decorated with the &lt;code&gt;@WrapForJNI&lt;/code&gt; and &lt;code&gt;native&lt;/code&gt; annotations and generates a C++ class template meant to be used through the &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/crtp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Curiously Recurring Template Pattern&lt;/a&gt;. This template provides an &lt;code&gt;Init&lt;/code&gt; static member function that does the right &lt;code&gt;JNIEnv::RegisterNatives&lt;/code&gt; calls to bind the Java methods to the implementing C++ class&amp;#x27;s member functions.&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;etpnt&quot;&gt;We also identified two ways the C++ code calls Java methods:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;xskm4&quot;&gt;Case B1: directly with &lt;code&gt;JNIEnv::Call…&lt;/code&gt; functions.&lt;/li&gt;&lt;li data-block-key=&quot;wd5mt&quot;&gt;Case B2: GeckoView&amp;#x27;s code generator also looks for Java methods marked with &lt;code&gt;@WrapForJNI&lt;/code&gt; (&lt;i&gt;without&lt;/i&gt; the &lt;code&gt;native&lt;/code&gt; keyword this time) and generates a C++ wrapper class and member functions with the right &lt;code&gt;JNIEnv::Call…&lt;/code&gt; calls.&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;25zvk&quot;&gt;Only the C++ side has the complete view of the bindings; so that&amp;#x27;s where we decided to extract the information from, by extending Mozilla&amp;#x27;s existing Clang plugin.&lt;/p&gt;&lt;p data-block-key=&quot;irkrk&quot;&gt;First, we defined custom C++ &lt;a href=&quot;https://clang.llvm.org/docs/AttributeReference.html#annotate-type&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;annotations&lt;/a&gt; &lt;code&gt;bound_as&lt;/code&gt; and &lt;code&gt;binding_to&lt;/code&gt; that the clang plugin transforms into the right format for the cross-reference analysis. This means we can manually set the binding information:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;class&amp;quot;, &amp;quot;S_jvm_sample/Jni#&amp;quot;))) CallingJavaFromCpp
{
    __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;method&amp;quot;, &amp;quot;S_jvm_sample/Jni#javaStaticMethod().&amp;quot;)))
    static void javaStaticMethod()
    {
        // Wrapper code
    }

    __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;method&amp;quot;, &amp;quot;S_jvm_sample/Jni#javaMethod().&amp;quot;)))
    void javaMethod()
    {
        // Wrapper code
    }

    __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;getter&amp;quot;, &amp;quot;S_jvm_sample/Jni#javaField.&amp;quot;)))
    int javaField()
    {
        // Wrapper code
        return 0;
    }

    __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;setter&amp;quot;, &amp;quot;S_jvm_sample/Jni#javaField.&amp;quot;)))
    void javaField(int)
    {
        // Wrapper code
    }

    __attribute__((annotate(&amp;quot;binding_to&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;const&amp;quot;, &amp;quot;S_jvm_sample/Jni#javaConst.&amp;quot;)))
    static constexpr int javaConst = 5;
};

class __attribute__((annotate(&amp;quot;bound_as&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;class&amp;quot;, &amp;quot;S_jvm_sample/Jni#&amp;quot;))) CallingCppFromJava
{
    __attribute__((annotate(&amp;quot;bound_as&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;method&amp;quot;, &amp;quot;S_jvm_sample/Jni#nativeStaticMethod().&amp;quot;)))
    static void nativeStaticMethod()
    {
        // Real code
    }

    __attribute__((annotate(&amp;quot;bound_as&amp;quot;, &amp;quot;jvm&amp;quot;, &amp;quot;method&amp;quot;, &amp;quot;S_jvm_sample/Jni#nativeMethod().&amp;quot;)))
    void nativeMethod()
    {
        // Real code
    }
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ljf5d&quot;&gt;(This example is, in fact, extracted from our test suite, &lt;a href=&quot;https://searchfox.org/mozsearch-tests/source/jni.cpp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;jni.cpp&lt;/a&gt; vs &lt;a href=&quot;https://searchfox.org/mozsearch-tests/source/src/main/java/sample/Jni.java&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jni.java&lt;/a&gt;.)&lt;/p&gt;&lt;p data-block-key=&quot;tddg6&quot;&gt;Then, we wrote some heuristics that try and identify cases A1 (C functions named &lt;code&gt;Java_…&lt;/code&gt;), A3 and B2 (C++ code generated from &lt;code&gt;@WrapForJNI&lt;/code&gt; decorators) and automatically generate these annotations. Cases A2 and B1 (manually calling &lt;code&gt;JNIEnv::RegisterNatives&lt;/code&gt; or &lt;code&gt;JNIEnv::Call…&lt;/code&gt; functions) are rare enough in the Firefox code base and impossible to reliably recognize; so it was decided not to cover them at the time. Developers who wish to declare such bindings could manually annotate them.&lt;/p&gt;&lt;p data-block-key=&quot;awwta&quot;&gt;After this point, we used Searchfox&amp;#x27;s existing analysis JSON format and mostly re-used what was already available from IDL support. When triggering the context menu for a binding wrapper or bound function, the definitions in both languages are made available, with “Go to” actions that jump over the generally irrelevant binding internals.&lt;/p&gt;&lt;p data-block-key=&quot;6kkis&quot;&gt;The search results also display both sides of the bridge, for instance:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;nd90a&quot;&gt;searching for the &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol:_ZN7mozilla6widget16GeckoViewSupport4OpenERKNS_3jni8LocalRefINS2_11TypedObjectIP7_jclassEEEERKNS2_3RefINS_4java12GeckoSession6WindowEP8_jobjectEERKNSB_INS2_6ObjectESG_EESN_SN_SN_SN_RKNS2_11StringParamESQ_b&amp;amp;redirect=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;mozilla::widget::GeckoViewSupport::Open&lt;/a&gt; C++ member function links to its Java binding &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol:S_jvm_org%2Fmozilla%2Fgeckoview%2FGeckoSession%23Window%23open().&amp;amp;redirect=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;org.mozilla.geckoview.GeckoSession$Window.open&lt;/a&gt;.&lt;/li&gt;&lt;li data-block-key=&quot;g0yn0&quot;&gt;searching for the &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol:S_jvm_org%2Fmozilla%2Fgeckoview%2FGeckoSession%23getCompositorFromNative().&amp;amp;redirect=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;org.mozilla.geckoview.GeckoSession.getCompositorFromNative&lt;/a&gt; Java method links to its generated C++ binding &lt;a href=&quot;https://searchfox.org/mozilla-central/search?q=symbol:_ZNK7mozilla4java12GeckoSession13GetCompositorEv&amp;amp;redirect=false&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;mozilla::java::GeckoSession::GetCompositor&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;0rugk&quot;&gt;If you want to dive into more details, see the feature request and detailed problem analysis &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1865048&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on Bugzilla&lt;/a&gt;, the implementation and further discussion &lt;a href=&quot;https://github.com/mozsearch/mozsearch/pull/673&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt;, and the release announcement &lt;a href=&quot;https://groups.google.com/a/mozilla.org/g/dev-platform/c/-6q2kEgH8LQ&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on the Mozilla dev-platform mailing list&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;displaying-interactive-macro-expansions&quot; anchor=&quot;displaying-interactive-macro-expansions&quot; data-block-key=&quot;om5he&quot;&gt;Displaying Interactive Macro Expansions&lt;/h2&gt;&lt;p data-block-key=&quot;wn5lx&quot;&gt;Aside from this Java/Kotlin-related work, we also added support for displaying and interacting with macro expansions. This was inspired by KDAB&amp;#x27;s own &lt;a href=&quot;https://codebrowser.dev&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;codebrowser.dev&lt;/a&gt;, but improves it to:&lt;/p&gt;&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;ul&gt;&lt;li data-block-key=&quot;378sp&quot;&gt;Display all expansion variants, if they differ across platforms or by definition:&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Per-platform-expansions.png&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Per-platform-expansions.original.png&quot; class=&quot;Per-platform-expansions.png&quot; alt=&quot;Per-platform-expansions.png&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;c275z&quot;&gt;Per-platform expansions&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Many-defines-single-expansion-point-use-case.png&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Many-defines-single-expansion-point-use-case.original.png&quot; class=&quot;Many-defines-single-expansion-point-use-case.png&quot; alt=&quot;Many-defines-single-expansion-point-use-case.png&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;gpgzj&quot;&gt;Per-definition expansions&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;ul&gt;&lt;li data-block-key=&quot;ek11u&quot;&gt;Make macros fully indexed and interactive:&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;Full-expansion-with-inner-semantic-analysis-and-context-menu.png&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/Full-expansion-with-inner-semantic-analysis-and.original.png&quot; class=&quot;Full-expansion-with-inner-semantic-analysis-and-context-menu.png&quot; alt=&quot;Full-expansion-with-inner-semantic-analysis-and-context-menu.png&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;pztbw&quot;&gt;In-macro context menu&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;kj6zp&quot;&gt;This work mainly happened in the Mozsearch Clang plugin to extract macro expansions during the pre-processing stage and index them with the rest of the top-level code.&lt;/p&gt;&lt;/div&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;jxzg2&quot;&gt;Again, if you want more details, the feature request is available &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1583635&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on Bugzilla&lt;/a&gt; and the implementation and further technical discussion is &lt;a href=&quot;https://github.com/mozsearch/mozsearch/pull/679&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;summary&quot; anchor=&quot;summary&quot; data-block-key=&quot;fo11z&quot;&gt;Summary&lt;/h2&gt;&lt;p data-block-key=&quot;mwd54&quot;&gt;Because of the many technologies it makes use of, from compiler plugins and code analyzers written in many languages, to a web front-end written using the usual HTML/CSS/JS, by way of custom tooling and scripts in Rust, Python and Bash, Searchfox is a small but complex and really interesting project to work on. KDAB successfully added Java/Kotlin code indexing, including analyzing their C++ bindings, and are starting to improve Searchfox&amp;#x27;s C++ support itself, first with fully-indexed macro expansions and next with improved templates support.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/searchfox-improvements/&quot;&gt;Improvements to Mozilla&apos;s Searchfox Code Browser&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Nicolas Guichard</dc:creator><category>c++</category><category>tools</category></item><item><title>Setting C++ Defines with CMake</title><link>https://www.kdab.com/setting-defines-with-cmake/</link><guid isPermaLink="true">https://www.kdab.com/setting-defines-with-cmake/</guid><description>&lt;p data-block-key=&quot;y1bmk&quot;&gt;The goal When building C++ code with CMake, it is very common to want to set some pre-processor defines in the CMake code. For instance, we might want to set the project&amp;#x27;s version number in a single place, in CMake code like this: This sets the CMake variable PROJECT_VERSION to 1.5, which we can then […]&lt;/p&gt;</description><pubDate>Wed, 13 Nov 2024 09:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Setting C++ Defines with CMake&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;the-goal&quot; anchor=&quot;the-goal&quot; data-block-key=&quot;6hynz&quot;&gt;The goal&lt;/h2&gt;&lt;p data-block-key=&quot;5ewsm&quot;&gt;When building C++ code with &lt;a href=&quot;https://www.kdab.com/cmake-and-qt/&quot;&gt;CMake&lt;/a&gt;, it is very common to want to set some pre-processor defines in the CMake code.&lt;/p&gt;&lt;p data-block-key=&quot;0qa91&quot;&gt;For instance, we might want to set the project&amp;#x27;s version number in a single place, in CMake code like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;project(MyApp VERSION 1.5)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;5jv5c&quot;&gt;This sets the CMake variable &lt;code&gt;PROJECT_VERSION&lt;/code&gt; to 1.5, which we can then use to pass &lt;code&gt;-DMYAPP_VERSION_STRING=1.5&lt;/code&gt; to the C++ compiler. The about dialog of the application can then use this to show the application version number, like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;const QString aboutString = QStringLiteral(&amp;quot;My App version: %1&amp;quot;).arg(MYAPP_VERSION_STRING);
  QMessageBox::information(this, &amp;quot;My App&amp;quot;, aboutString);&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;a55t4&quot;&gt;Similarly, we might have a boolean CMake option like &lt;code&gt;START_MAXIMIZED&lt;/code&gt;, which the user compiling the software can set to ON or OFF:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;option(START_MAXIMIZED &amp;quot;Show the mainwindow maximized&amp;quot; OFF)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;z44zn&quot;&gt;If it&amp;#x27;s ON, you would pass &lt;code&gt;-DSTART_MAXIMIZED&lt;/code&gt;, otherwise nothing. The C++ code will then use &lt;code&gt;#ifdef&lt;/code&gt;. (We&amp;#x27;ll see that there&amp;#x27;s a better way.)&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;#ifdef START_MAXIMIZED
      w.showMaximized();
  #else
      w.show();
  #endif&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;the-common-but-suboptimal-solution&quot; anchor=&quot;the-common-but-suboptimal-solution&quot; data-block-key=&quot;80sfg&quot;&gt;The common (but suboptimal) solution&lt;/h2&gt;&lt;p data-block-key=&quot;84dpw&quot;&gt;A solution that many people use for this is the CMake function &lt;code&gt;add_definitions&lt;/code&gt;. It would look like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;add_definitions(-DMYAPP_VERSION_STRING=&amp;quot;${PROJECT_VERSION}&amp;quot;)
  if (START_MAXIMIZED)
     add_definitions(-DSTART_MAXIMIZED)
  endif()&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;q3o9i&quot;&gt;Technically, this works but there are a number of issues.&lt;/p&gt;&lt;p data-block-key=&quot;5jmsq&quot;&gt;First, &lt;code&gt;add_definitions&lt;/code&gt; is deprecated since CMake 3.12 and &lt;code&gt;add_compile_definitions&lt;/code&gt; should be used instead, which allows to remove the leading &lt;code&gt;-D&lt;/code&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;p4j8f&quot;&gt;More importantly, there&amp;#x27;s a major downside to this approach: changing the project version or the value of the boolean option will force CMake to rebuild every single .cpp file used in targets defined below these lines (including in subdirectories). This is because &lt;code&gt;add_definitions&lt;/code&gt; and &lt;code&gt;add_compile_definitions&lt;/code&gt; ask to pass &lt;code&gt;-D&lt;/code&gt; to all cpp files, instead of only those that need it. CMake doesn&amp;#x27;t know which ones need it, so it has to rebuild everything. On large real-world projects, this could take something like one hour, which is a major waste of time.&lt;/p&gt;&lt;p data-block-key=&quot;otoj6&quot;&gt;A first improvement we can do is to at least set the defines to all files in a single target (executable or library) instead of &amp;quot;all targets defined from now on&amp;quot;. This can be done like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;target_compile_definitions(myapp PRIVATE MYAPP_VERSION_STRING=&amp;quot;${PROJECT_VERSION}&amp;quot;)
  if(START_MAXIMIZED)
     target_compile_definitions(myapp PRIVATE START_MAXIMIZED)
  endif()&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;mkyy3&quot;&gt;We have narrowed the rebuilding effect a little bit, but are still rebuilding all cpp files in myapp, which could still take a long time.&lt;/p&gt;&lt;h2 id=&quot;the-recommended-solution&quot; anchor=&quot;the-recommended-solution&quot; data-block-key=&quot;0qo6o&quot;&gt;The recommended solution&lt;/h2&gt;&lt;p data-block-key=&quot;uzbwk&quot;&gt;There is a proper way to do this, such that only the files that use these defines will be rebuilt; we simply have to ask CMake to generate a header with &lt;code&gt;#define&lt;/code&gt; in it and include that header in the few cpp files that need it. Then, only those will be rebuilt when the generated header changes. This is very easy to do:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;configure_file(myapp_config.h.in myapp_config.h)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ckb6j&quot;&gt;We have to write the input file, &lt;code&gt;myapp_config.h.in&lt;/code&gt;, and CMake will generate the output file, &lt;code&gt;myapp_config.h&lt;/code&gt;, after expanding the values of CMake variables. Our input file would look like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;#define MYAPP_VERSION_STRING &amp;quot;${PROJECT_VERSION}&amp;quot;
  #cmakedefine01 START_MAXIMIZED&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;53aaw&quot;&gt;A good thing about generated headers is that you can read them if you want to make sure they contain the right settings. For instance, &lt;code&gt;myapp_config.h&lt;/code&gt; in your build directory might look like this:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;#define MYAPP_VERSION_STRING &amp;quot;1.5&amp;quot;
  #define START_MAXIMIZED 1&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;hosvb&quot;&gt;For larger use cases, we can even make this more modular by moving the version number to another input file, say &lt;code&gt;myapp_version.h.in&lt;/code&gt;, so that upgrading the version doesn&amp;#x27;t rebuild the file with the &lt;code&gt;showMaximized()&lt;/code&gt; code and changing the boolean option doesn&amp;#x27;t rebuild the about dialog.&lt;/p&gt;&lt;p data-block-key=&quot;gczz5&quot;&gt;If you try this and you hit a &amp;quot;file not found&amp;quot; error about the generated header, that&amp;#x27;s because the build directory (where headers get generated) is missing in the include path. You can solve this by adding &lt;code&gt;set(CMAKE_INCLUDE_CURRENT_DIR TRUE)&lt;/code&gt; near the top of your &lt;code&gt;CMakeLists.txt&lt;/code&gt; file. This is part of the CMake settings that I recommend should always be set; you can make it part of your new project template and never have to think about it again.&lt;/p&gt;&lt;p data-block-key=&quot;02t61&quot;&gt;There&amp;#x27;s just one thing left to explain: what&amp;#x27;s this &lt;code&gt;#cmakedefine01&lt;/code&gt; thing?&lt;/p&gt;&lt;p data-block-key=&quot;kngfi&quot;&gt;If your C++ code uses &lt;code&gt;#ifdef&lt;/code&gt;, you want to use &lt;code&gt;#cmakedefine&lt;/code&gt;, which either sets or doesn&amp;#x27;t set the define. But there&amp;#x27;s a major downside of doing that -- if you forget to include &lt;code&gt;myapp_config.h&lt;/code&gt;, you won&amp;#x27;t get a compile error; it will just always go to the &lt;code&gt;#else&lt;/code&gt; code path.&lt;/p&gt;&lt;p data-block-key=&quot;twsyt&quot;&gt;We want a solution that gives an error if the &lt;code&gt;#include&lt;/code&gt; is missing. The generated header should set the define to either 0 or 1 (but always set it), and the C++ code should use &lt;code&gt;#if&lt;/code&gt;. Then, you get a warning if the define hasn&amp;#x27;t been set and, because people tend to ignore warnings, I recommend that you upgrade it to an error by adding the compiler flag &lt;code&gt;-Werror=undef&lt;/code&gt;, with gcc or clang.  Let me know if you are aware of an equivalent flag for MSVC.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-plain  line-numbers &quot;&gt;if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES &amp;quot;Clang&amp;quot;)
    target_compile_options(myapp PRIVATE -Werror=undef)
  endif()&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;imahs&quot;&gt;And these are all the pieces we need. Never use &lt;code&gt;add_definitions&lt;/code&gt; or &lt;code&gt;add_compile_definitions&lt;/code&gt; again for things that are only used by a handful of files. Use &lt;code&gt;configure_file&lt;/code&gt; instead, and include the generated header. You&amp;#x27;ll save a lot of time compared to recompiling files unnecessarily.&lt;/p&gt;&lt;p data-block-key=&quot;uf2ec&quot;&gt;I hope this tip was useful.&lt;/p&gt;&lt;p data-block-key=&quot;sx8s1&quot;&gt;For more content on CMake, we curated &lt;a href=&quot;https://www.youtube.com/playlist?list=PL6CJYn40gN6g1_yY2YkqSym7FWUid926M&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;a collection of resources about CMake with or without Qt&lt;/a&gt;. Check out the videos.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/setting-defines-with-cmake/&quot;&gt;Setting C++ Defines with CMake&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>David Faure</dc:creator><category>c++</category><category>performance</category></item><item><title>KDAB Training Day - May 8th, 2025</title><link>https://www.kdab.com/kdab-training-day-may-8th-2025/</link><guid isPermaLink="true">https://www.kdab.com/kdab-training-day-may-8th-2025/</guid><description>&lt;p data-block-key=&quot;quovc&quot;&gt;Early-Bird tickets are on sale for the KDAB Training Day 2025 until 2025-03-31 23:59 The KDAB Training Day 2025 will take place in Munich on May 8th, right after the Qt World Summit on May 6th-7th. Choose to buy a combo ticket here (for access to QtWS and Training Day) or here (for access to […]&lt;/p&gt;</description><pubDate>Tue, 05 Nov 2024 12:56:00 GMT</pubDate><content:encoded>&lt;h1&gt;KDAB Training Day - May 8th, 2025&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;igfc3&quot;&gt;&lt;i&gt;Early-Bird tickets&lt;/i&gt; are on sale for the KDAB Training Day 2025 until 2025-03-31 23:59&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;image-variable-size-block&quot;&gt;
    &lt;div class=&quot;image-variable-positioning-block right-margin-auto left-margin-auto width-100 &quot; &gt;
            &lt;div class=&quot;image-variable-size-image&quot;&gt;
                
                
                
                &lt;img id=&quot;training-day-long-banner.png&quot; src=&quot;https://eu-central-1.linodeobjects.com/wagtail-production/images/training-day-long-banner.original.png&quot; class=&quot;training-day-long-banner.png&quot; alt=&quot;training-day-long-banner.png&quot;&gt;
                
                
        &lt;/div&gt;
        &lt;div class=&quot;image-variable-size-caption text-center&quot;&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ap7up&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;The KDAB Training Day 2025&lt;/a&gt; will take place in Munich on May 8th, right after the &lt;a href=&quot;https://www.qt.io/qt-world-summit-2025&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Qt World Summit&lt;/a&gt; on May 6th-7th. Choose to buy a combo ticket &lt;a href=&quot;https://www.qt.io/qt-world-summit-2025&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; (for access to QtWS and Training Day) or &lt;a href=&quot;https://pretix.kdab.com/KDAB/KTD25/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; (for access to Training Day only).&lt;/p&gt;&lt;p data-block-key=&quot;2tcbs&quot;&gt;Seats are limited, so don&apos;t wait too long if you want to participate in a specific course. Tickets include access to the selected training course, training material, lunch buffet, beverages, and coffee breaks. &lt;b&gt;Note:&lt;/b&gt; The Training Day is held at &lt;b&gt;Hotel NH Collection München Bavaria&lt;/b&gt;, located at the Munich Central Station &lt;i&gt;(not the same location as Qt World Summit)&lt;/i&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;d352j&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;p data-block-key=&quot;6ryli&quot;&gt;Why should you attend the KDAB Training Day?&lt;/p&gt;&lt;p data-block-key=&quot;l96hp&quot;&gt;With over 20 years of experience and a rich store of well-structured, constantly updated training material, KDAB offers hands-on, practical programming training in &lt;a href=&quot;https://training.kdab.com/portfolio/qtqml/&quot;&gt;Qt/QML&lt;/a&gt;, &lt;a href=&quot;https://training.kdab.com/portfolio/modern-cpp/&quot;&gt;Modern C++&lt;/a&gt;, &lt;a href=&quot;https://training.kdab.com/portfolio/3dopengl/&quot;&gt;3D/OpenGL&lt;/a&gt;, &lt;a href=&quot;https://training.kdab.com/portfolio/debugging-and-profiling/&quot;&gt;Debugging &amp;amp; Profiling&lt;/a&gt;, and lately &lt;a href=&quot;https://training.kdab.com/portfolio/rust/&quot;&gt;Rust&lt;/a&gt; - both for beginners as well as experienced developers.&lt;/p&gt;&lt;p data-block-key=&quot;xzslp&quot;&gt;All courses provided at the Training Day include central parts of the regular 3- to 4-day courses available as scheduled or customized on-site training. Choosing a compact, learning-rich one-day course, lets you experience the quality and effectiveness of KDAB’s usual training offerings.&lt;/p&gt;&lt;p data-block-key=&quot;fcobh&quot;&gt;Courses available at the KDAB Training Day 2025&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;x0mfk&quot;&gt;&lt;a href=&quot;#qml-application-architecture&quot;&gt;QML Application Architecture&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;9m8qm&quot;&gt;&lt;a href=&quot;#qmlc-integration&quot;&gt;QML/C++ integration&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;7p8o4&quot;&gt;&lt;a href=&quot;#modern-c-paradigms&quot;&gt;Modern C++ Paradigms&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;edx0o&quot;&gt;&lt;a href=&quot;#integrating-rust-into-qt-applications&quot;&gt;Integrating Rust into Qt applications&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;hl06k&quot;&gt;&lt;a href=&quot;#effective-modern-qml&quot;&gt;Effective Modern QML&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;s2cbo&quot;&gt;&lt;a href=&quot;#integrating-custom-3d-renderers-with-qt-applications&quot;&gt;Integrating Custom 3D Renderers with Qt Applications&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;qml-application-architecture&quot; anchor=&quot;qml-application-architecture&quot; data-block-key=&quot;qnmor&quot;&gt;QML Application Architecture&lt;/h2&gt;&lt;p data-block-key=&quot;geirr&quot;&gt;In this training, we do a step-by-step walkthrough of how to build a QML-based embedded application from the ground up and discuss some challenges that are typically met along the way.&lt;/p&gt;&lt;p data-block-key=&quot;hdzm7&quot;&gt;An important part of that journey is an investigation of where to put the boundaries between what you do in C++ and what you do in QML. We also look at some of the tools and building blocks we have available in QML that can help us achieve well-performing, well-structured, and well-maintainable applications.&lt;/p&gt;&lt;p data-block-key=&quot;wim67&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;m49v3&quot;&gt;(Qt) developers looking to improve their understanding of how to construct maintainable and efficient larger-scale QML applications.&lt;/p&gt;&lt;p data-block-key=&quot;zhs37&quot;&gt;Prerequisite&lt;/p&gt;&lt;p data-block-key=&quot;0bdn1&quot;&gt;Some real-world experience working on QML applications as well as a basic understanding of Qt and C++.&lt;/p&gt;&lt;p data-block-key=&quot;abz7m&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;qmlc-integration&quot; anchor=&quot;qmlc-integration&quot; data-block-key=&quot;keojs&quot;&gt;QML/C++ Integration&lt;/h2&gt;&lt;p data-block-key=&quot;3dqei&quot;&gt;In this training, we start with a recap of fundamentals:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;b4n6c&quot;&gt;How do we expose C++ API to QML?&lt;/li&gt;&lt;li data-block-key=&quot;j9u24&quot;&gt;How do we make data available to QML?&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;1cte8&quot;&gt;Afterward, we explore several more advanced techniques, often widely deployed within Qt&apos;s QML modules, such as Qt Quick.&lt;/p&gt;&lt;p data-block-key=&quot;mpkf2&quot;&gt;This will answer questions such as:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;jo5ny&quot;&gt;How would I do a Loader like component?&lt;/li&gt;&lt;li data-block-key=&quot;ittt3&quot;&gt;How would I do a Layout like component?&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;1qw3d&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;2982n&quot;&gt;Qt/QML developers who are familiar with the QML APIs of QtQuick and related modules and who have wondered how these are implemented and want to use similar techniques in their project-specific APIs.&lt;/p&gt;&lt;p data-block-key=&quot;jr59q&quot;&gt;Prerequisite&lt;/p&gt;&lt;p data-block-key=&quot;3jxrd&quot;&gt;Some real-world experience working on QML applications as well as a basic understanding of Qt and C++.&lt;/p&gt;&lt;p data-block-key=&quot;e0emd&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;modern-c-paradigms&quot; anchor=&quot;modern-c-paradigms&quot; data-block-key=&quot;llojl&quot;&gt;Modern C++ Paradigms&lt;/h2&gt;&lt;p data-block-key=&quot;dsf92&quot;&gt;Modern C++ emphasizes safer, more efficient, and maintainable code through higher-level abstractions that reduce error-prone manual work.&lt;/p&gt;&lt;p data-block-key=&quot;tz6cq&quot;&gt;This training will explore key paradigms shaping recent C++ evolution, starting with value semantics in class design, which enhances code safety, local reasoning, and thread safety. We will examine modern C++ tools for creating value-oriented types, including move semantics, smart pointers, and other library enablers.&lt;/p&gt;&lt;p data-block-key=&quot;8dpio&quot;&gt;Next, we will look at expressive, type and value-based error handling.&lt;/p&gt;&lt;p data-block-key=&quot;zigla&quot;&gt;Finally, we&apos;ll cover range-based programming, which enables clean, declarative code and unlocks new patterns through lazy, composable transformations.&lt;/p&gt;&lt;p data-block-key=&quot;q6ped&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;8isno&quot;&gt;C++ developers who wish to improve the quality of their code, in particular those who wish to write future-proof APIs.&lt;/p&gt;&lt;p data-block-key=&quot;8n6ph&quot;&gt;Prerequisites&lt;/p&gt;&lt;p data-block-key=&quot;c4hll&quot;&gt;Prior professional experience in C++. Experience with the latest C++ standards (C++20/23/26) is a plus. We will use several examples inspired by Qt APIs, so Qt knowledge is also a plus (but this is not going to be a Qt training).&lt;/p&gt;&lt;p data-block-key=&quot;2r8ap&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;integrating-rust-into-qt-applications&quot; anchor=&quot;integrating-rust-into-qt-applications&quot; data-block-key=&quot;yb023&quot;&gt;Integrating Rust into Qt Applications&lt;/h2&gt;&lt;p data-block-key=&quot;5h7mg&quot;&gt;In this step-by-step course, we start with a Qt/C++ application and add Rust code to it piece by piece. To achieve this, we will cover:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;gecw3&quot;&gt;Use of Cargo (Rusts build system) with CMake&lt;/li&gt;&lt;li data-block-key=&quot;e6rqy&quot;&gt;Accessing Rust code from C++ with CXX (and vice-versa)&lt;/li&gt;&lt;li data-block-key=&quot;sd983&quot;&gt;Defining your own QObject types in Rust with CXX-Qt&lt;/li&gt;&lt;/ul&gt;&lt;p data-block-key=&quot;lwnki&quot;&gt;We discuss when to use Rust compared to C++ to make the best of both languages and how to use them together effectively to make Qt applications safer and easier to maintain.&lt;/p&gt;&lt;p data-block-key=&quot;h8szb&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;k7841&quot;&gt;Qt/C++ Developers with an interest in Rust who want to learn how to use Rust in their existing applications.&lt;/p&gt;&lt;p data-block-key=&quot;qigmj&quot;&gt;Prerequisites&lt;/p&gt;&lt;p data-block-key=&quot;mw4l3&quot;&gt;Basic Qt/C++ knowledge, as well as basic Rust knowledge, is required. A working Qt installation with CMake and a working Rust installation is needed. We will provide material before the training day that participants should use to check their setup before the training.&lt;/p&gt;&lt;p data-block-key=&quot;y81t4&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;effective-modern-qml&quot; anchor=&quot;effective-modern-qml&quot; data-block-key=&quot;eli4s&quot;&gt;Effective Modern QML&lt;/h2&gt;&lt;p data-block-key=&quot;qthcx&quot;&gt;In this training, we look into all the new developments in QML over the last few years and how they lead to more expressive, performant, and maintainable code.&lt;/p&gt;&lt;p data-block-key=&quot;9dmn1&quot;&gt;This includes:&lt;br/&gt; - The qt_add_qml_module CMake API&lt;br/&gt; - Declarative type registration&lt;br/&gt; - The different QML compilers&lt;br/&gt; - New language and library features&lt;br/&gt; - New developments in Qt Quick Controls&lt;br/&gt; - Usage of tools like qmllint, QML Language Server, and qmlformat&lt;/p&gt;&lt;p data-block-key=&quot;lhn00&quot;&gt;The focus will be on gradually modernizing existing codebases with new tools and practices.&lt;/p&gt;&lt;p data-block-key=&quot;tsm66&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;licpc&quot;&gt;Developers who learned QML back in the days of Qt 5 and want to catch up with recent developments in QML and modernize their knowledge as well as codebases.&lt;/p&gt;&lt;p data-block-key=&quot;ov6qf&quot;&gt;Prerequities&lt;/p&gt;&lt;p data-block-key=&quot;fia6d&quot;&gt;Some real-world experience with QML and a desire to learn about modern best practices.&lt;/p&gt;&lt;p data-block-key=&quot;je3a8&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;integrating-custom-3d-renderers-with-qt-applications&quot; anchor=&quot;integrating-custom-3d-renderers-with-qt-applications&quot; data-block-key=&quot;z6hmr&quot;&gt;Integrating Custom 3D Renderers with Qt Applications&lt;/h2&gt;&lt;p data-block-key=&quot;r2mi4&quot;&gt;Qt has long offered ways of using low-level 3d libraries such as OpenGL to do custom rendering. Whether at the Window, the Widget, or Quick Item level, the underlying rendering system can be accessed in ways that make it safe to integrate such 3rd party renderers. This remains true in the Qt 6 timeline, although the underlying rendering system has changed and OpenGL has been replaced by RHI.&lt;/p&gt;&lt;p data-block-key=&quot;zc4jy&quot;&gt;In this course, we look at how the graphic stack is structured in Qt 6 and how third-party renderers can be integrated on the various platforms supported by Qt.&lt;/p&gt;&lt;p data-block-key=&quot;9jgtw&quot;&gt;We then focus on the specific case of integrating Vulkan-based renderers. Vulkan is the successor to OpenGL; it&apos;s much more powerful but harder to learn. To facilitate the initial use of Vulkan, we introduce KDGpu, a library that encapsulates Vulkan while preserving the underlying concepts of pipeline objects, buffer handling, synchronization, etc.&lt;/p&gt;&lt;p data-block-key=&quot;soj9b&quot;&gt;This course is for&lt;/p&gt;&lt;p data-block-key=&quot;sla8z&quot;&gt;This course targets developers wanting to understand the recent state of the graphics stack in Qt, discover the fundamental principles of modern graphics API, and integrate their custom renderers in their applications.&lt;/p&gt;&lt;p data-block-key=&quot;obnme&quot;&gt;Prerequisite&lt;/p&gt;&lt;p data-block-key=&quot;w7m3e&quot;&gt;Prior knowledge of Qt will be required. A basic understanding of 3d graphics would be beneficial.&lt;/p&gt;&lt;p data-block-key=&quot;2ebim&quot;&gt;&lt;a href=&quot;https://training.kdab.com/ktd25/&quot;&gt;Get your ticket&lt;/a&gt;&lt;/p&gt;&lt;p data-block-key=&quot;fc1c3&quot;&gt;&lt;i&gt;Video from KDAB Training Day 2023 held in Berlin&lt;/i&gt;&lt;/p&gt;&lt;/div&gt;







&lt;div class=&quot;cookieconsent-optin-marketing overlay-embed-block&quot;&gt;
    &lt;div style=&quot;padding-bottom: 56.49999999999999%;&quot; class=&quot;responsive-object&quot;&gt;
    &lt;iframe width=&quot;200&quot; height=&quot;113&quot; src=&quot;https://www.youtube.com/embed/Fv05J1V-uxo?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen title=&quot;KDAB Training Day 2023&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;/div&gt;




&lt;style&gt;
.overlay-embed-block .responsive-object {
    position: relative;
}

.overlay-embed-block .responsive-object iframe,
.overlay-embed-block .responsive-object object,
.overlay-embed-block .responsive-object embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
&lt;/style&gt;


&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/kdab-training-day-may-8th-2025/&quot;&gt;KDAB Training Day - May 8th, 2025&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Editor Team</dc:creator><category>news</category><category>qml</category><category>qt</category><category>training</category></item><item><title>CXX-Qt 0.7 Release</title><link>https://www.kdab.com/cxx-qt-0-7/</link><guid isPermaLink="true">https://www.kdab.com/cxx-qt-0-7/</guid><description>&lt;p data-block-key=&quot;h62a3&quot;&gt;We just released CXX-Qt version 0.7! CXX-Qt is a set of Rust crates for creating bidirectional Rust ⇄ C++ bindings with Qt. It supports integrating Rust into C++ applications using CMake or building Rust applications with Cargo. CXX-Qt provides tools for implementing QObject subclasses in Rust that can be used from C++, QML, and JavaScript. […]&lt;/p&gt;</description><pubDate>Thu, 31 Oct 2024 08:30:00 GMT</pubDate><content:encoded>&lt;h1&gt;CXX-Qt 0.7 Release&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;zkdyw&quot;&gt;We just released CXX-Qt version 0.7!&lt;/p&gt;&lt;p data-block-key=&quot;j48ei&quot;&gt;&lt;a href=&quot;https://www.kdab.com/cxx-qt/&quot;&gt;CXX-Qt&lt;/a&gt; is a set of Rust crates for creating bidirectional Rust ⇄ C++ bindings with Qt. It supports integrating Rust into C++ applications using CMake or building Rust applications with Cargo. CXX-Qt provides tools for implementing QObject subclasses in Rust that can be used from C++, QML, and JavaScript.&lt;/p&gt;&lt;p data-block-key=&quot;zpo9h&quot;&gt;For 0.7, we have stabilized the &lt;code&gt;cxx-qt&lt;/code&gt; bridge macro API and there have been many internal refactors to ensure that we have a consistent baseline to support going forward. We encourage developers to reach out if they find any unclear areas or missing features, to help us ensure a roadmap for them, as this may be the final time we can adapt the API. In the next releases, we&amp;#x27;re looking towards stabilizing the &lt;code&gt;cxx-qt-build&lt;/code&gt; and getting the &lt;code&gt;cxx-qt-lib&lt;/code&gt; APIs ready for 1.0.&lt;/p&gt;&lt;p data-block-key=&quot;go4jn&quot;&gt;Check out the new release through the usual channels:&lt;/p&gt;&lt;ul&gt;&lt;li data-block-key=&quot;qxlnz&quot;&gt;&lt;a href=&quot;https://kdab.github.io/cxx-qt/book/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The CXX-Qt book&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;23h47&quot;&gt;&lt;a href=&quot;https://docs.rs/cxx-qt/latest/cxx_qt/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The CXX-Qt documentation&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;szj0i&quot;&gt;&lt;a href=&quot;https://docs.rs/cxx-qt-lib/latest/cxx_qt_lib/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The CXX-Qt-lib documentation&lt;/a&gt;&lt;/li&gt;&lt;li data-block-key=&quot;h80ag&quot;&gt;&lt;a href=&quot;https://github.com/KDAB/cxx-qt&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Our Github repository&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;some-of-the-most-notable-developer-facing-changes&quot; anchor=&quot;some-of-the-most-notable-developer-facing-changes&quot; data-block-key=&quot;ap5hf&quot;&gt;Some of the most notable developer-facing changes:&lt;/h2&gt;&lt;h3 id=&quot;stabilized-cxx_qtbridge-macro&quot; anchor=&quot;stabilized-cxx_qtbridge-macro&quot; data-block-key=&quot;icwv5&quot;&gt;Stabilized #[cxx_qt::bridge] macro&lt;/h3&gt;&lt;p data-block-key=&quot;f7g4u&quot;&gt;CXX-Qt 0.7 reaches a major milestone by stabilizing the bridge macro that is at the heart of CXX-Qt.&lt;br/&gt; You can now depend on your CXX-Qt bridges to remain compatible with future CXX-Qt versions.&lt;br/&gt; As we&amp;#x27;re still pre-1.0, we may still introduce very minor breaking changes to fix critical bugs in the edge-cases of the API, but the vast majority of bridges should remain compatible with future versions.&lt;/p&gt;&lt;p data-block-key=&quot;bas2t&quot;&gt;This stabilization is also explicitly limited to the bridge API itself. Breaking changes may still occur in e.g. &lt;code&gt;cxx-qt-lib&lt;/code&gt;, &lt;code&gt;cxx-qt-build&lt;/code&gt;, and &lt;code&gt;cxx-qt-cmake&lt;/code&gt;. We plan to stabilize those crates in the next releases.&lt;/p&gt;&lt;h3 id=&quot;naming-changes&quot; anchor=&quot;naming-changes&quot; data-block-key=&quot;kv9iv&quot;&gt;Naming Changes&lt;/h3&gt;&lt;p data-block-key=&quot;8sgts&quot;&gt;The handling of names internally has been refactored to ensure consistency across all usages. During this process, implicit automatic case conversion has been removed, so &lt;code&gt;cxx_name&lt;/code&gt; and &lt;code&gt;rust_name&lt;/code&gt; are now used to specify differing Rust and C++ names. Since the automatic case conversion is useful, it can be explicitly enabled using per extern block attributes &lt;code&gt;auto_cxx_name&lt;/code&gt; and &lt;code&gt;auto_rust_name&lt;/code&gt;, while still complimenting CXX. For more details on how these attributes can be used, visit the attributes page in the &lt;a href=&quot;https://kdab.github.io/cxx-qt/book/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CXX-Qt book&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-rust  line-numbers &quot;&gt;// with 0.6 implicit automatic case conversion
#[cxx_qt::bridge]
mod ffi {
  unsafe extern &amp;quot;RustQt&amp;quot; {
    #[qobject]
    #[qproperty(i32, my_number) // myNumber in C++
    type MyObject = super::MyObjectRust;

    fn my_method(self: &amp;amp;MyObject); // myMethod in C++
  }
}

// with 0.7 cxx_name / rust_name
#[cxx_qt::bridge]
mod ffi {
  unsafe extern &amp;quot;RustQt&amp;quot; {
    #[qobject]
    #[qproperty(i32, my_number, cxx_name = &amp;quot;myNumber&amp;quot;)
    type MyObject = super::MyObjectRust;

    #[cxx_name = &amp;quot;myMethod&amp;quot;]
    fn my_method(self: &amp;amp;MyObject);
  }
}

// with 0.7 auto_cxx_name / auto_rust_name
#[cxx_qt::bridge]
mod ffi {
  #[auto_cxx_name] // &amp;lt;-- enables automatic cxx_name generation within the `extern &amp;quot;RustQt&amp;quot;` block
  unsafe extern &amp;quot;RustQt&amp;quot; {
    #[qobject]
    #[qproperty(i32, my_number) // myNumber in C++
    type MyObject = super::MyObjectRust;

    fn my_method(self: &amp;amp;MyObject); // myMethod in C++
  }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;cxx_file_stem-removal&quot; anchor=&quot;cxx_file_stem-removal&quot; data-block-key=&quot;oyr30&quot;&gt;cxx_file_stem Removal&lt;/h3&gt;&lt;p data-block-key=&quot;uofbv&quot;&gt;In previous releases, the output filename of generated C++ files used the &lt;code&gt;cxx_file_stem&lt;/code&gt; attribute of the CXX-Qt bridge. This has been changed to use the filename of the Rust source file including the directory structure.&lt;/p&gt;&lt;p data-block-key=&quot;gkr15&quot;&gt;Previously, the code below would generate a C++ header path of &lt;code&gt;my_file.cxxqt.h.&lt;/code&gt; After the changes, the &lt;code&gt;cxx_file_stem&lt;/code&gt; must be removed and the generated C++ header path changes to &lt;code&gt;crate-name/src/my_bridge.cxxqt.h.&lt;/code&gt; This follows a similar pattern to CXX.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-rust  line-numbers &quot;&gt;// crate-name/src/my_bridge.rs

// with 0.6 a file stem was specified
#[cxx_qt::bridge(cxx_file_stem = &amp;quot;my_file&amp;quot;)]
mod ffi {
...
}

// with 0.7 the file path is used
#[cxx_qt::bridge]
mod ffi {
...
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h3 id=&quot;build-system-changes&quot; anchor=&quot;build-system-changes&quot; data-block-key=&quot;kgtti&quot;&gt;Build System Changes&lt;/h3&gt;&lt;p data-block-key=&quot;6nofv&quot;&gt;The internals of the build system have changed so that dependencies are automatically detected and configured by &lt;code&gt;cxx-qt-build&lt;/code&gt;, libraries can pass build information to &lt;code&gt;cxx-qt-build&lt;/code&gt;, and a &lt;a href=&quot;https://github.com/KDAB/cxx-qt-cmake&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CXX-Qt CMake module&lt;/a&gt; is now available providing convenience wrappers around corrosion. This means that the &lt;code&gt;cxx-qt-lib-headers&lt;/code&gt; crate has been removed and only &lt;code&gt;cxx-qt-lib&lt;/code&gt; is required. With these changes, there is now no need for the &lt;code&gt;-header&lt;/code&gt; crates that existed before. Previously, some features were enabled by default in &lt;code&gt;cxx-qt-lib.&lt;/code&gt; Now these are all opt-in. We have provided &lt;code&gt;full&lt;/code&gt; and &lt;code&gt;qt_full&lt;/code&gt; as convenience to enable all features; however, we would recommend opting in to the specific features you need.&lt;/p&gt;&lt;p data-block-key=&quot;lj89q&quot;&gt;We hope to improve the API of &lt;code&gt;cxx-qt-build&lt;/code&gt; in the next cycle to match the internal changes and become more modular.&lt;/p&gt;&lt;h2 id=&quot;further-improvements&quot; anchor=&quot;further-improvements&quot; data-block-key=&quot;87md1&quot;&gt;Further Improvements&lt;/h2&gt;&lt;p data-block-key=&quot;2ykwa&quot;&gt;CXX-Qt can now be successfully built for WASM, with documented steps available in the book and CI builds for WASM to ensure continued support.&lt;/p&gt;&lt;p data-block-key=&quot;8e8c9&quot;&gt;Locking generation on the C++ side for all methods has been removed, which simplifies generation and improves performance. Using &lt;code&gt;queue&lt;/code&gt; from &lt;code&gt;cxx_qt::CxxQtThread&lt;/code&gt; is still safe, as it provides locking, but it is up to the developer to avoid incorrect multi-threading in C++ code (as in the &lt;code&gt;CXX&lt;/code&gt; crate). Note that Qt generally works well here, with the signal/slot mechanism working safely across threads.&lt;/p&gt;&lt;p data-block-key=&quot;k867t&quot;&gt;As with most releases, there are more Qt types wrapped in &lt;code&gt;cxx-qt-lib&lt;/code&gt; and various other changes are detailed in the &lt;a href=&quot;https://github.com/KDAB/cxx-qt/blob/main/CHANGELOG.md&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CHANGELOG&lt;/a&gt;.&lt;/p&gt;&lt;p data-block-key=&quot;iwkel&quot;&gt;Make sure to subscribe to the &lt;a href=&quot;https://www.youtube.com/@KDABtv&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;KDAB YouTube channel&lt;/a&gt;, where we&amp;#x27;ll post more videos on CXX-Qt in the coming weeks.&lt;/p&gt;&lt;h2 id=&quot;thanks-to-all-of-our-contributors-that-helped-us-with-this-release&quot; anchor=&quot;thanks-to-all-of-our-contributors-that-helped-us-with-this-release&quot; data-block-key=&quot;wywr9&quot;&gt;Thanks to all of our contributors that helped us with this release:&lt;/h2&gt;&lt;ul&gt;&lt;li data-block-key=&quot;jycgd&quot;&gt;Ben Ford&lt;/li&gt;&lt;li data-block-key=&quot;fe2xv&quot;&gt;Laurent Montel&lt;/li&gt;&lt;li data-block-key=&quot;qddhh&quot;&gt;Matt Aber&lt;/li&gt;&lt;li data-block-key=&quot;5onmi&quot;&gt;knox (aka @knoxfighter)&lt;/li&gt;&lt;li data-block-key=&quot;zmgod&quot;&gt;Be Wilson&lt;/li&gt;&lt;li data-block-key=&quot;12qv1&quot;&gt;Joshua Goins&lt;/li&gt;&lt;li data-block-key=&quot;j8z4o&quot;&gt;Alessandro Ambrosano&lt;/li&gt;&lt;li data-block-key=&quot;2hzzh&quot;&gt;Alexander Kiselev&lt;/li&gt;&lt;li data-block-key=&quot;5qa5c&quot;&gt;Alois Wohlschlager&lt;/li&gt;&lt;li data-block-key=&quot;atcuz&quot;&gt;Darshan Phaldesai&lt;/li&gt;&lt;li data-block-key=&quot;zoplg&quot;&gt;Jacob Alexander&lt;/li&gt;&lt;li data-block-key=&quot;epdnk&quot;&gt;Sander Vocke&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/cxx-qt-0-7/&quot;&gt;CXX-Qt 0.7 Release&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator/><category>news</category><category>qml</category><category>rust</category><category>tools</category></item><item><title>10 Tips to Make Your QML Code Faster and More Maintainable</title><link>https://www.kdab.com/10-tips-to-make-your-qml-code-faster-and-more-maintainable/</link><guid isPermaLink="true">https://www.kdab.com/10-tips-to-make-your-qml-code-faster-and-more-maintainable/</guid><description>&lt;p data-block-key=&quot;f1n73&quot;&gt;In recent years, a lot has been happening to improve performance, maintainability and tooling of QML. Some of those improvements can only take full effect when your code follows modern best practices. Here are 10 things you can do in order to modernize your QML code and take full advantage of QML&amp;#x27;s capabilities. 1. Use […]&lt;/p&gt;</description><pubDate>Wed, 23 Oct 2024 06:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;10 Tips to Make Your QML Code Faster and More Maintainable&lt;/h1&gt;&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;ei0bs&quot;&gt;In recent years, a lot has been happening to improve performance, maintainability and tooling of QML. Some of those improvements can only take full effect when your code follows modern best practices. Here are 10 things you can do in order to modernize your QML code and take full advantage of QML&amp;#x27;s capabilities.&lt;/p&gt;&lt;h2 id=&quot;1-use-qt_add_qml_module-cmake-api&quot; anchor=&quot;1-use-qt_add_qml_module-cmake-api&quot; data-block-key=&quot;gneio&quot;&gt;1. Use qt_add_qml_module CMake API&lt;/h2&gt;&lt;p data-block-key=&quot;9fwae&quot;&gt;Qt6 introduced a new CMake API to create QML modules. Not only is this more convenient than what previously had to be done manually, but it is also a prerequisite for being able to exploit most of the following tips.&lt;/p&gt;&lt;p data-block-key=&quot;7jxbx&quot;&gt;By using the qt_add_qml_module, your QML code is automatically processed by qmlcachegen, which not only creates QML byte code ahead of time, but also converts parts of your QML code to C++ code, improving performance. How much of your code can be compiled to C++ depends on the quality of the input code. The following tips are all about improving your code in that regard.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cmake  line-numbers &quot;&gt;add_executable(myapp main.cpp)

qt_add_qml_module(myapp
 URI &amp;quot;org.kde.myapp&amp;quot;
 QML_FILES Main.qml
)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;2-use-declarative-type-registration&quot; anchor=&quot;2-use-declarative-type-registration&quot; data-block-key=&quot;97m5g&quot;&gt;2. Use declarative type registration&lt;/h2&gt;&lt;p data-block-key=&quot;x30ev&quot;&gt;When creating custom types in C++ and registering them with qmlRegisterType and friends, they are not visible to the tooling at the compile time. qmlcachegen doesn&amp;#x27;t know which types exist and which properties they have. Hence, it cannot translate to C++ the code that&amp;#x27;s using them. Your experience with the QML Language Server will also suffer since it cannot autocomplete types and property names.&lt;/p&gt;&lt;p data-block-key=&quot;gjbvo&quot;&gt;To fix this, your types should be registered declaratively using the QML_ELEMENT (and its friends, QML_NAMED_ELEMENT, QML_SINGLETON, etc) macros.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;qmlRegisterType(&amp;quot;org.kde.myapp&amp;quot;, 1, 0, &amp;quot;MyThing&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;812ur&quot;&gt;becomes&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;class MyThing : public QObject
{
    Q_OBJECT
    QML_ELEMENT
};&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;su5pw&quot;&gt;The URL and version information are inferred from the qt_add_qml_module call.&lt;/p&gt;&lt;h2 id=&quot;3-declare-module-dependencies&quot; anchor=&quot;3-declare-module-dependencies&quot; data-block-key=&quot;4oox5&quot;&gt;3. Declare module dependencies&lt;/h2&gt;&lt;p data-block-key=&quot;1mqn0&quot;&gt;Sometimes your QML module depends on other modules. This can be due to importing it in the QML code, or more subtly by using types from another module in your QML-exposed C++ code. In the latter case, the dependency needs to be declared in the qt_add_qml_module call.&lt;/p&gt;&lt;p data-block-key=&quot;xoj1k&quot;&gt;For example, exposing a QAbstractItemModel subclass to QML adds a dependency to the QtCore (that&amp;#x27;s where QAbstractItemModel is registered) to your module. This does not only happen when subclassing a type but also when using it as a parameter type in properties or invokables.&lt;/p&gt;&lt;p data-block-key=&quot;c7dw4&quot;&gt;Another example is creating a custom QQuickItem-derived type in C++, which adds a dependency on the Qt Quick module.&lt;/p&gt;&lt;p data-block-key=&quot;godwx&quot;&gt;To fix this, add the DEPENDENCIES declaration to qt_add_qml_module:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cmake  line-numbers &quot;&gt;qt_add_qml_module(myapp
 URI &amp;quot;org.kde.myapp&amp;quot;
 QML_FILES Main.qml
 DEPENDENCIES QtCore
)&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;4-qualify-property-types-fully&quot; anchor=&quot;4-qualify-property-types-fully&quot; data-block-key=&quot;r18jr&quot;&gt;4. Qualify property types fully&lt;/h2&gt;&lt;p data-block-key=&quot;5d17y&quot;&gt;MOC needs types in C++ property definitions to be fully qualified, i.e. include the full namespace, even when inside that namespace. Not doing this will cause issues for the QML tooling.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-cpp  line-numbers &quot;&gt;namespace MyApp {

class MyHelper : public QObject {
    Q_OBJECT
};

class MyThing : public QObject {
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(MyHelper *helper READ helper CONSTANT) // bad
    Q_PROPERTY(MyApp::MyHelper *helper READ helper CONSTANT) // good
    ...
};
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;5-use-types&quot; anchor=&quot;5-use-types&quot; data-block-key=&quot;ggrsn&quot;&gt;5. Use types&lt;/h2&gt;&lt;p data-block-key=&quot;y8ao0&quot;&gt;In order for qmlcachegen to generate efficient code for your bindings, it needs to know the type for properties. Avoid using &amp;#x27;property var&amp;#x27; wherever possible and use concrete types. This may be built-in types like int, double, or string, or any declaratively-defined custom type. Sometimes you want to be able to use a type as a property type in QML but don&amp;#x27;t want the type to be creatable from QML directly. For this, you can register them using the QML_UNCREATABLE macro.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;property var size: 10 // bad
property int size: 10 // good

property var thing // bad
property MyThing thing // good&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;6-avoid-parent-and-other-generic-properties&quot; anchor=&quot;6-avoid-parent-and-other-generic-properties&quot; data-block-key=&quot;ddywh&quot;&gt;6. Avoid parent and other generic properties&lt;/h2&gt;&lt;p data-block-key=&quot;mggkt&quot;&gt;qmlcachegen can only work with the property types it knows at compile time. It cannot make any assumptions about which concrete subtype a property will hold at runtime. This means that, if a property is defined with type Item, it can only compile bindings using properties defined on Item, not any of its subtypes. This is particularly relevant for properties like &amp;#x27;parent&amp;#x27; or &amp;#x27;contentItem&amp;#x27;. For this reason, avoid using properties like these to look up items when not using properties defined on Item (properties like width, height, or visible are okay) and use look-ups via IDs instead.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;Item {
    id: thing

    property int size: 10

    Rectangle {
        width: parent.size // bad, Item has no &amp;#x27;size&amp;#x27; property
        height: thing.height // good, lookup via id

        color: parent.enabled ? &amp;quot;red&amp;quot; : &amp;quot;black&amp;quot; // good, Item has &amp;#x27;enabled&amp;#x27; property
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;7-annotate-function-parameters-with-types&quot; anchor=&quot;7-annotate-function-parameters-with-types&quot; data-block-key=&quot;ynv2d&quot;&gt;7. Annotate function parameters with types&lt;/h2&gt;&lt;p data-block-key=&quot;z0pp0&quot;&gt;In order for qmlcachegen to compile JavaScript functions, it needs to know the function&amp;#x27;s parameter and return type. For that, you need to add type annotations to the function:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;function calculateArea(width: double, height: double) : double {
    return width * height
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;2mkhh&quot;&gt;When using signal handlers with parameters, you should explicitly specify the signal parameters by supplying a JS function or an arrow expression:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;MouseArea {
    onClicked: event =&amp;gt; console.log(&amp;quot;clicked at&amp;quot;, event.x, event.y)
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;sad37&quot;&gt;Not only does this make qmlcachegen happy, it also makes your code far more readable.&lt;/p&gt;&lt;h2 id=&quot;8-use-qualified-property-lookup&quot; anchor=&quot;8-use-qualified-property-lookup&quot; data-block-key=&quot;xh09a&quot;&gt;8. Use qualified property lookup&lt;/h2&gt;&lt;p data-block-key=&quot;zv5is&quot;&gt;QML allows you to access properties from objects several times up in the parent hierarchy without explicitly specifying which object is being referenced. This is called an unqualified property look-up and generally considered bad practice since it leads to brittle and hard to reason about code. qmlcachegen also cannot properly reason about such code. So, it cannot properly compile it. You should only use qualified property lookups&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;Item {
    id: root
    property int size: 10

    Rectangle {
        width: size // bad, unqualified lookup
        height: root.size // good, qualified lookup
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;y50cv&quot;&gt;Another area that needs attention is accessing model roles in a delegate. Views like ListView inject their model data as properties into the context of the delegate where they can be accessed with expressions like &amp;#x27;foo&amp;#x27;, &amp;#x27;model.foo&amp;#x27;, or &amp;#x27;modelData.foo&amp;#x27;. This way, qmlcachegen has no information about the types of the roles and cannot do its job properly. To fix this, you should use required properties to fetch the model data:&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;ListView {
    model: MyModel

    delegate: ItemDelegate {
        text: name // bad, lookup from context
        icon.name: model.iconName // more readable, but still bad

        required property bool active // good, using required property
        checked: active
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;h2 id=&quot;9-use-pragma-componentbehavior-bound&quot; anchor=&quot;9-use-pragma-componentbehavior-bound&quot; data-block-key=&quot;8y3y4&quot;&gt;9. Use pragma ComponentBehavior: Bound&lt;/h2&gt;&lt;p data-block-key=&quot;vniq4&quot;&gt;When defining components, either explicitly via &lt;code&gt;Component {}&lt;/code&gt; or implicitly when using delegates, it is common to want to refer to IDs outside of that component, and this generally works. However, theoretically any component can be used outside of the context it is defined in and, when doing that, IDs might refer to another object entirely. For this reason, qmlcachegen cannot properly compile such code.&lt;/p&gt;&lt;p data-block-key=&quot;a9vwj&quot;&gt;To address this, we need to learn about &lt;code&gt;pragma ComponentBehavior&lt;/code&gt;. Pragmas are file-wide switches that influence the behavior of QML. By specifying &lt;code&gt;pragma ComponentBehavior: Bound&lt;/code&gt; at the top of the QML file, we can bind any components defined in this file to their surroundings. As a result, we cannot use the component in another place anymore but can now safely access IDs outside of it.&lt;/p&gt;&lt;/div&gt;


&lt;div class=&quot;formatted-code&quot;&gt;
    &lt;pre&gt;&lt;code class=&quot;language-qml  line-numbers &quot;&gt;pragma ComponentBehavior: Bound

import QtQuick

Item {
    id: root

    property int delegateHeight: 10

    ListView {
        model: MyModel

        delegate: Rectangle {
            height: root.delegateHeight // good with ComponentBehavior: Bound, bad otherwise
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
 &lt;/div&gt;


&lt;div class=&quot;rich-text&quot;&gt;&lt;p data-block-key=&quot;qayu6&quot;&gt;A side effect of this is that accessing model data now must happen using required properties, as described in the previous point. &lt;a href=&quot;https://doc.qt.io/qt-6/qtqml-documents-structure.html#componentbehavior&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Learn more about ComponentBehavior here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;10-know-your-tools&quot; anchor=&quot;10-know-your-tools&quot; data-block-key=&quot;qnr2n&quot;&gt;10. Know your tools&lt;/h2&gt;&lt;p data-block-key=&quot;agwo5&quot;&gt;A lot of these pitfalls are not obvious, even to seasoned QML programmers, especially when working with existing codebases. Fortunately, &lt;a href=&quot;https://doc.qt.io/qt-6/qtqml-tooling-qmllint.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;qmllint&lt;/a&gt; helps you find most of these issues and avoids introducing them. By using the &lt;a href=&quot;https://doc.qt.io/qt-6/qtqml-tooling-qmlls.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QML Language Server,&lt;/a&gt; you can incorporate qmllint directly into your preferred IDE/editor such as &lt;a href=&quot;https://kate-editor.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Kate&lt;/a&gt; or Visual Studio Code.&lt;/p&gt;&lt;p data-block-key=&quot;snprh&quot;&gt;While qmlcachegen can help boost your QML application&amp;#x27;s performance, there are performance problems it cannot help with, such as scenes that are too complex, slow C++ code, or inefficient rendering. To investigate such problems, tools like the &lt;a href=&quot;https://doc.qt.io/qtcreator/creator-qml-performance-monitor.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;QML profiler&lt;/a&gt;, &lt;a href=&quot;https://www.kdab.com/hotspot-video/&quot;&gt;Hotspot&lt;/a&gt; for CPU profiling, &lt;a href=&quot;https://invent.kde.org/sdk/heaptrack&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Heaptrack&lt;/a&gt; for memory profiling, and &lt;a href=&quot;https://github.com/KDAB/GammaRay/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GammaRay&lt;/a&gt; for analyzing QML scenes are very helpful.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;The post &lt;a href=&quot;https://www.kdab.com/10-tips-to-make-your-qml-code-faster-and-more-maintainable/&quot;&gt;10 Tips to Make Your QML Code Faster and More Maintainable&lt;/a&gt; appeared first on &lt;a href=&quot;https://www.kdab.com&quot;&gt;KDAB&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>c++</category><category>performance</category><category>qml</category><category>qt</category><category>tools</category></item></channel></rss>