Non closable QDialog

This is quite beginner post but I believe it’s good to share what we learn through experience with the people that are starting. In this entry I’ll show you how to have a UI non closable QDialog but that it can be close from the code.

In GitQlient I’m using this dialog to inform the user that the repository is being loaded. There is some data I’m loading in the cache/background (branches, tags, etc) that can take a bit if the repository is massive. In my case, I’m working with some repos that are quite big (~10 Gb only text files) and old. People usually forget to remove their branches and right know I can see around 2000 branches and close to 500 tags. In that case an async load is needed so the user has to wait.

How to do a non closable QDialog?

We want to prevent the user to close the dialog in all it’s forms:

  • Disabling the window close icon.
  • Disabling the Esc key that in QDialog closes/cancels the dialog.

Acting on the windows title

The way to do that is quite simple. In the first place we hide the close button:

setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint);

This will make the close button gone and keep the resize behaviour and dialog title. If you want a dialog only for the text you can do:

setWindowFlags(Qt::FramelessWindowHint);

That will remove the title bar and all it’s components.

Method 1: Acting on the Esc key event

QDialog process the Esc key as close/cancel. Since we don’t want that the user closes our dialog, we need to take care of that behaviour. To do that we need to capture the key press event and avoid processing it:

void WaitingDlg::keyPressEvent(QKeyEvent *e)
{
   if (e->key() == Qt::Key_Escape)
      return;

   QDialog::keyPressEvent(e);
}

The code is quite simple, we check the pressed key and if it’s the one we’re expecting, we don’t process the event.

Additionally, it’s also useful to have full control of another event: the closeEvent. That method plus a custom close method will give us the full behaviour:

void WaitingDlg::closeEvent(QCloseEvent *e)
{
   if (!mPrepareToClose)
      e->ignore();
   else
      QDialog::closeEvent(e);
}

void WaitingDlg::close()
{
   mPrepareToClose = true;

   QDialog::close();
}

In this code I’m using a class member to store when I do want to close the dialog, preventing the dialog to close if it wasn’t me how said it. The result is that this dialog can only be close by calling the close() method.

Method 2: Override reject method

Suggested in the comments by NickD2039 another possibility is to override the reject method, that would prevent the end user from closing the dialog. The code posted has a closeNow method that acts in a similar way to the method 1, it activates a control variable that finall calls accept.

I kind of like a bit more this code, since it’s a bit more elegant and requires you to write less code:

void closeNow()
{
   mPrepareToClose = true;
   accept();
   mPrepareToClose = false;
}

void accept()
{
   if (mPrepareToClose)
      QDialog::accept();
}

void reject()
{
   if (mPrepareToClose)
      QDialog::reject();
}

Where to find the source coude?

You can find the source code for the non closable dialog in my GitHub repository for Qt tips. Please noticed that I name it WaitingDlg since I did the code for GitQlient and that was its purpose.

Non closable QDialog first appeared on My C++ & Qt blog - Cesc M..

July ’20 – monthly digest

Blogs

My file menu is not full of eels

by James Turner

Last week, a user reported that the ‘File’ menu of the application was not appearing on macOS.

Some investigation showed this didn’t happen when using the default translation (i.e English), but a bit more investigation showed that it only happened when the language in use was Dutch.

Read the blog…

Fixing a common antipattern when loading translations in Qt

by Miłosz Kosobucki

“I’m a Polish guy working with computers, mostly on Windows. However, the lingua franca of the IT industry is English, so every time I see a tutorial for some dev tool, it’s in that language. To lessen the burden of decoding which menu entry in the tutorial corresponds to which menu entry on my PC I decided to run the system with an English display language.”

Read the blog…

 

White paper

Containers – Cloud tech comes to embedded

by Till Adam and Sven Knebel

This white paper provides a general overview of containers in embedded from our own knowledge, research, and experience, as well as that from our partner Toradex – also an expert in embedded containers.

Hopefully, it will help you answer if now is the right time to add this technology to your embedded tool chest.

Read the white paper…

News

GammaRay online tutorials

We’re delighted to announce the launch of our brand new series about GammaRay™, the introspection tool for Qt applications developed by KDAB. Let one of our presenters, Giuseppe D’Angelo explain what you’ll learn:

 

Online CMake training – August 25th

CMake has strong support for building Qt applications and will replace qmake as the build system for Qt in Qt6, so it’s more than a useful alternative if you hit limitations in qmake.

A one-day online training course together with James Turner, KDAB Senior Software Engineer who has been developing Qt since 2002. James will teach you all the basics you need to start with CMake but will leave out labs and some advanced topics that are covered in the two-day classroom training.

Find out more and register…

 

Events

Upcoming events

  • SIGGRAPH – Aug 17 – 22 What’s not to like about this mega online virtual event for transformative advancements in computer graphics and interactive techniques, with Marco Tempest as Keynote speaker.
  • KDE Akademy – Sept 4 – 11 KDAB is very proud to be Platinum sponsor at this community-based event, and will be offering half-day training online in QML, UI/UX design, Debugging for Linux, and Multithreading in Qt, free to KDE members.
  • Qt Desktop Days – Sept 7 – 11 KDAB is hosting this desktop-focused event during the second half of KDE Akademy, with live online talks and Q&A from Qt partners and the Qt community. Look out for the official launch and think about a 45 minute talk you’d like to present. CfP opens soon.
  • CppCon – Sept 13 – 18 KDAB is proud to be Bronze sponsor at the biggest global annual event for the C++ community, now gone virtual. Find out more and register…
  • Meeting Cpp – Nov 12 – 14 A mix of live in Berlin and online, with Keynotes from Jonathan Boccara and Teresa Johnson. Read the latest from organizer Jens Weller and join the waiting list.

Visit our page Upcoming events for more updates…

The post July ’20 – monthly digest appeared first on KDAB.

My file menu is not full of eels

This is the story of a bug in an open-source project I maintain; as the maintainer I review and sometimes fix bug reports from the community. Last week, a user reported that the ‘File’ menu of the application was not appearing on macOS. Some investigation showed this didn’t happen when using the default translation (i.e English), but a bit more investigation showed that it only happened when the language in use was Dutch.

At this point I’d like to make it clear that I like the Dutch and especially gevulde koeken, a type of almond cookie you can only get in the Netherlands. When passing through Amsterdam Schiphol, I take care to stock up at the supermarket on the main concourse. If you’re passing through Schiphol and wonder why they’ve been cleaned out of cookies, it was me.

Anyway, it was weird that the menu code I had written seemed to dislike the Dutch. Actually, as part of investigating the defect, I needed to switch my system language to Dutch. So I just did that for a week, and got to learn most of the macOS UI in Dutch. Lekker!

It turns out, the File menu (or actually, the ‘Bestand’ menu) was missing for a very sensible reason: it contained zero items. In this particular application, the menu is quite simple and contains:

  • load configuration file…
  • save configuration file…
  • quit

macOS File menu screenshot

If you’ve used Qt on macOS, you’ll know the ‘Quit’ item is moved to the application menu (the one with name of the software) automatically. What you may not know is that Qt will, by default, move some other items:

  • About
  • Help
  • Preferences

All of these items have special locations on macOS. The default way to detect if a menu item should be moved this way, is based on a heuristic. A heuristic is a rule which works some of the time and goes wrong when your customer / manager is using the software. The default rule is to do this based on a string search of the (translated) menu item name. In this case, the Dutch translation of ‘Preferences’ is … ‘Configuration’. Therefore, both the first and second items in my file menu get treated as the application preferences item. Hence, the file menu ends up empty and is therefore made invisible.

Fortunately, the fix is simple: rather than accepting the default TextHeuristic MenuRole on your QActions, just set a role explicitly, such as Qt::NoRole. This disables the text heuristic and Dutch users can once again see the Bestand menu in all its glory.


QMenuBar* mb = new QMenuBar();

QAction* openAction = new QAction(tr("Open saved configuration..."));
openAction->setMenuRole(QAction::NoRole); // change from Qt::TextHeuristicRole

QAction* saveAction = new QAction(tr("Save configuration as..."));
saveAction->setMenuRole(QAction::NoRole); // change from Qt::TextHeuristicRole

QMenu* fileMenu = mb->addMenu(tr("File"));
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);

Now I just need to get some more of those cookies.

If you’re confused about the title of this post, kindly Google the ‘Hungarian phrasebook’ sketch by Monty Python — only maybe not on a work computer.

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post My file menu is not full of eels appeared first on KDAB.

'wasm memory too small' Qt for WebAssembly

Sometimes when I am building a larger project with Qt for WebAssembly, I get this type of message:

wasm-ld: error: initial memory too small, 17553264 bytes needed

and the build fails.

This means that you need to tell Emscripten compiler to allocate more than the standard 1GB initial memory.
Qt allows you to specify to add more initial memory by using QMAKE_TOTAL_MEMORY in your pro file.

So it makes sense to add something like this:

QMAKE_TOTAL_MEMORY=17553264

BUT the result is:

shared:ERROR: For wasm, TOTAL_MEMORY must be a multiple of 64KB, was 17553264

grrrr...  ok, what if we find a multiple of 64?

17553264 / 64 = 274,269.95

We need a whole number, so lets round up to the next whole number.

274270 * 64 = 17553280

But no. That doesn't work either:

shared:ERROR: For wasm, TOTAL_MEMORY must be a multiple of 64KB, was 17553280

WTH?!?

The answer is that a KB is 1024 bytes, so 64 * 1024 = 65536 bytes

So let's find the closest whole number multiple of 64 KB (65536)

17553264 / 65536 = 267.841...

so let's try 268

268 * 65536 = 17563648

QMAKE_TOTAL_MEMORY=17563648

and this now builds, and hopefully runs!


You can also read more about Mobile and Embedded development and Qt for WebAssembly in the book Hands-On Mobile and Embedded Development with Qt 5

CMake-Based Qt Creator Snapshots

About a year ago we started porting the build system that we use for building Qt Creator itself from qmake to CMake. Nowadays we are in the state that many Qt Creator developers use CMake for building Qt Creator for their daily work, but the official packages are still based on the qmake build.

Qt Design Studio - Sketch Bridge Tutorial Part 2

Welcome back to this Sketch Bridge for Qt Design Studio Tutorial, in the last part we created our first button and got it working with it's different states in Qt Design Studio (referred to as qds for the rest of the tutorial). In this part we're going to go back in and create a menu component from instances of this button and look at overriding the icons and text for each instance. If you missed the first part of the tutorial or would like to go back to check something you can find that here - https://www.qt.io/blog/qt-design-studio-sketch-bridge-tutorial-part-1

Part 2.1 - Building a menu component using instances of our buttons.


So let’s open up our Sketch file and start by creating our menu background, we can make it large enough to fit four of our buttons on and let’s keep it fairly simple, just a rectangle for now.

Again we want to create the rectangle, convert it into a symbol and then delete this instance and work on the master symbol on our symbol page.

dsblog_addMenuBackground.mp4


Here we can adjust the size and colors of our rectangle until we are happy with it and in this case we can leave the single settings set to merge as we just want a simple single asset for this to begin with.

We'll also give our background a good qml id, in this case myMenu_Symbol_background

background_menu

Qt & Android: Job Scheduler

Description

This tutorial describes how to schedule tasks in a Qt app for Android using Android's JobScheduler API. If your Android app needs to perform a repetitive task whether the app is active or not, you should consider creating an Android app with a JobService to perform the work.  You can add one or more job services, where each one defines a different task that is performed in the future based upon specified conditions.  The tasks can even survive application restarts and device reboots.


Source code

The following highlights the steps needed to create an Android app using Qt and Android JobScheduling API.  The complete sample app is found in Github at https://github.com/pgulotta/JobServiceExample.

Getting Started 

Create a new Qt Quick empty project naming it JobServiceExample.  The app will link against the Qt Android Extras module, so add this line to the project file:  

    android:QT += androidextras

 

This sample app displays a message box, so you need to add entry to support the QMessageBox class. 

    QT += widgets 


Create Android Package Templates files. This can be easily done with QtCreator. If you are creating the Templates manually and you need assistance, refer to the Qt for Android  documentation. 


Verify the app was generated correctly by building all Android ABI's and deploying your new app onto an  Android device or emulator.  Upon successfully running your new Android app, it's time to make the changes needed to create a job scheduling service. 

 

Update AndroidManifest.xml

In the AndroidManifest.xml, name the package following the normal java package naming conventions. The JobServiceExample package name is com.example.jobserviceexample. The AndroidManifest.xml file should contain an entry similar to this.

    <manifest package="com.example.jobserviceexample"  

        xmlns:android="http://schemas.android.com/apk/res/android" 

        android:versionName="100" android:versionCode="100" android:installLocation="auto">


There are several other changes to make to the AndroidManifest.xml and now is as good a time as any.  The job scheduling api requires a minimum android version.   Specify the supported Android sdk, with an entry such as this.

    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29"/>


In the AndroidManifest.xml file , the JobServiceExample must be declared as a service with the BIND_JOB_SERVICE permission.  The name JobServiceExample is the name of the Java class you will be creating.

    <service android:name=".JobServiceExample" android:label="Example job service"

        android:permission="android.permission.BIND_JOB_SERVICE"/>


Because Android will run the job, per the job's defined schedule, after the system has finished booting, the RECEIVE_BOOT_COMPLETED permission is needed.

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>


The sample app scheduled task is to append a line of text composed of a time stamp to a file periodically.  Thus, the permission WRITE_EXTERNAL_STORAGE is required.

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


Please refer to the sample app, if you are unsure as to what the AndroidManifest.xml entries are.


JobScheduler Java API

An Android service is a component that runs in background and has no user interface. The service will continue to run even if the application exits.  The work or task you are scheduling is known as a 'job' and is defined in a Java class which enables Android to perform the work, even when the app is not active. Refer to Android documentation for details. 


Java files belong to the package specified in the AndroidManifest.xml file.  Java files called by Qt application must be placed in a directory which conforms to the path hierarchy defined by the package name.  Create a java class called JobServiceExample.class which extends android.app.job.JobService in the directory ...JobServiceExample/android/src/com/example/jobserviceexample. This Java class is an Android Service that extends the Android JobService class.   Since JobServiceExample class extends the JobService class, a couple of methods must be implemented: onStartJob(), which is called by the system when the job has begun executing, and onStopJob(), which is called by the system if the job is cancelled before finishing. Note, JobServiceExample class runs on the main thread so the task should be run on a  worker thread. 


    @Override

    public boolean onStartJob( JobParameters jobParameters )

   {

        Log.i( TAG, "JobServiceExample.onStartJob : jobParameters.getJobId() = " +

            jobParameters.getJobId() );

        try {

            Thread thread = new Thread( new ExampleWork( this ) );

            thread.start();

            thread.join();

        } catch ( Exception e ) {

             e.printStackTrace();

        }

        return false;  // Returns false from when job has finished. onStopJob will not be invoked

    }


    @Override

    public boolean onStopJob( JobParameters jobParameters )

    {

         // This method is typically not invoked

         Log.i( TAG, "JobServiceExample.onStopJob : jobParameters.getJobId() = " +

            jobParameters.getJobId() );

         return false;  // Returns false to end the job entirely

    }



The class ExampleWork specified above implements Runnable.  For this example, the task is to append a line of text composed of a timestamp to a file.


    @Override

     public void run()

     {

        ...

       doWork( mContext );

        ...

     }

 

public static void doWork( Context context )

 {

   try {

       SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

       String textToAppend = dateFormat.format(new Date());

       File path = Environment.getExternalStoragePublicDirectory( 

           Environment.DIRECTORY_DOWNLOADS );

       String filename =  path.getAbsolutePath() + File.separatorChar + 

       "JobServiceExampleLog.txt";

       Log.i( TAG, "ExampleWork.doWork path =" + filename + " appending text " + textToAppend);

       BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true));

       writer.newLine();

       writer.write(textToAppend);

       writer.close();

   } catch ( IOException e ) {

       e.printStackTrace();

   }

 }


Schedule a job by using Android's JobScheduler java class. Using this class, Android can efficiently batch your job with other jobs that need network access. JobScheduler can request retries and when needed Android will rescheduled the work; the work is guaranteed.  In the sample project JobServiceExample, the class ExampleJobScheduler illustrates scheduling.  A unit of work is encapsulated by a java JobInfo class specifying the scheduling criteria. The JobInfo.Builder class is used to configure how the scheduled task should run.


    public static void scheduleJob (Context context, int intervalinMS )

   {

        handleJob(context, intervalinMS );

   }

    private static void handleJob (Context context, long intervalinMS)

    {

         ...

         ComponentName serviceComponent = 

         new ComponentName( context, JobServiceExample.class );

         ...

         JobScheduler jobScheduler = context.getSystemService( JobScheduler.class );

         ...

         JobInfo.Builder builder = new JobInfo.Builder( JOB_ID, serviceComponent );

         ...

         builder.setPeriodic( intervalinMS );  // job runs within the intervalinMS; API 21

         builder.setPersisted( true ); // persist this job across device reboots; API 21

         builder.setRequiredNetworkType( JobInfo.NETWORK_TYPE_ANY ); //  API 21

         builder.setRequiresDeviceIdle(false); //  API 21

         int result = jobScheduler.schedule( builder.build() );

         String resultText = ( JobScheduler.RESULT_SUCCESS == result ) ? 

        "successfully" : "failed";

        Log.i ( TAG, "ExampleJobScheduler.handleJob scheduled for intervalinMS of " + 

        intervalinMS + " is "  + resultText );

      ...

  }



QML

In this example app, the QML UI, main.qml,  allows the user to schedule how frequently the task is executed.

  ...

  Button {

    text: qsTr("Apply")

    anchors.horizontalCenter: parent.horizontalCenter

    onClicked: Controller.scheduleJobService(scheduleModelId.

        get(scheduleSelectorId.currentIndex).intervalMS)

    }

    ...



Qt & C++

The FrontController C++ class  exposes the job  scheduling function to the QML interface.


Q_INVOKABLE void scheduleJobService(int intervalinMS);

 ...

    void FrontController::scheduleJobService( int intervalinMS )

    {

         QAndroidJniObject::callStaticMethod<void>

         ( "com/example/jobserviceexample/JobServiceExample","scheduleJobService",

             "(Landroid/content/Context;I)V",

             QtAndroid::androidActivity().object(), intervalinMS);

    }


The Permissions C++ class is called when the application starts for check for and request of needed permissions.

    void Permissions::requestExternalStoragePermission()

    {

        ...

        QtAndroid::PermissionResult request = QtAndroid::checkPermission( 

            "android.permission.WRITE_EXTERNAL_STORAGE" );

        if ( request == QtAndroid::PermissionResult::Denied ) {

            QtAndroid::requestPermissionsSync( QStringList() <<  

            "android.permission.WRITE_EXTERNAL_STORAGE" );

             request = QtAndroid::checkPermission( 

             "android.permission.WRITE_EXTERNAL_STORAGE" );

             if ( request == QtAndroid::PermissionResult::Denied ) {

                 mPermissionGranted = false;

                 if ( QtAndroid::shouldShowRequestPermissionRationale( 

                 "android.permission.READ_EXTERNAL_STORAGE" ) ) {

                      QAndroidJniObject 

                      ( "com/example/jobserviceexample/ShowPermissionRationale",

                      "(Landroid/app/Activity;)V",

                      QtAndroid::androidActivity().object<jobject>());

                      QAndroidJniEnvironment env;

                      if ( env->ExceptionCheck() ) {

                          env->ExceptionClear();

              }

        }

    } else {

      mPermissionGranted = true;

    }

  } else {

    mPermissionGranted = true;

  }

  ...

}


The communication between the C++ Qt/QML and Java needs to be specified  in main.cpp.

    #include <QQmlContext>

    #include "frontcontroller.h"

    #include "permissions.hpp"


    int main(int argc, char *argv[])

    {

     ...

     QQmlApplicationEngine engine;

     FrontController frontController{app.parent()};

     engine.rootContext()->setContextProperty( "Controller", &frontController );

     const QUrl url(QStringLiteral("qrc:/main.qml"));

     QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,

       &app, [url](QObject *obj, const QUrl &objUrl) {

           if (!obj && url == objUrl)

               QCoreApplication::exit(-1);

        }, Qt::QueuedConnection); engine.load(url);


      Permissions permissions;

      permissions.requestExternalStoragePermission();

      if ( permissions.getPermissionResult() ) {

          qInfo( "Successfully obtained required permissions, app starting" );

          return app.exec();

      } else {

          qWarning( "Failed to obtain required permissions, app terminating" );

      }

  }


Test it Out

Upon successfully building the app, run the JobServiceExample app and schedule a job by selecting the "Recording interval" and pressing "Apply".  Quit the app.  On Android, the file /storage/emulated/0/Download/JobServiceExampleLog.txt will be updated at the specified time interval.  Upon rebooting the Android, you can observe, the file /storage/emulated/0/Download/JobServiceExampleLog.txt will continue to be updated at the specified time interval.  The sample app, JobServiceExample, logs often to help you follow along.

        



Conclusion

To code a job scheduler in your QT Android application, there are many small steps, but this is true with all Android app development.  Android job scheduling  is a powerful, performant, robust feature that enables the Android OS to shoulder the burden of executing tasks based upon specific conditions. It's a nice tool to have in your toolbox.






Fixing a common antipattern when loading translations in Qt

TL;DR: If you choose a default translation in your Qt application based on a locale name like this:

QTranslator myappTranslator;
myappTranslator.load("myapp_" + QLocale::system().name());

then please change it to:

QTranslator myappTranslator;
myappTranslator.load(QLocale(), "myapp", "_");

Some of your users (me!) will be thankful that your application appears in the same language as the rest of their system.

Background

I’m a Polish guy working with computers, mostly on Windows. However, the lingua franca of the IT industry is English, so every time I see a tutorial for some dev tool, it’s in that language. To lessen the burden of decoding which menu entry in the tutorial corresponds to which menu entry on my PC I decided to run the system with an English display language. I still want the rest of the i18n-related stuff (date format, keyboard, currency etc.) to be in Polish however.

That’s why my system has Polish locale and English display language. This leads to a problem:

As you can see, Thunderbird and Windows Settings show up in English but Qt Linguist is encrypted with some overengineered Slavic cipher (aka Polish language). What I further noticed, is that this incorrect language selection is particularly prevalent in Qt-based applications. Subsequent digging revealed that this antipattern is widespread in Qt world, see the relevant GitHub search (requires login).

The Root Cause

My investigation made me think: Why do Qt users keep doing this wrong?

It turns out that it’s what the documentation tells them to do! See, the first time a Qt user wants to do i18n they are led to the Internationalization landing page which contained the following section in the middle:

Which in case of my system is unfortunately incorrect. QLocale::system().name() will return "pl_PL" which is not what I want. You should use the other overload of QTranslator::load() that consults the list of user’s preferred display languages (yes, it’s a list) and chooses the correct one.

To be fair to Qt’s docs, they actually tell you to use it but you need to navigate to the QTranslator docs and scroll to the bottom of the section about the first QTranslator::load() overload:

Not very discoverable

I personally believe that we shouldn’t blame the developers if they use the first thing they see prominently featured on the subsystem landing page. They don’t want to delve into intricacies of Qt’s i18n support, they have other problems to solve with their application (also, developers are lazy, at least, I am).

That’s why it’s so important to make sure that users are taught best practice the first time they are dealing with a concept. Or at least they are explicitly warned that they should do things differently in a real-world code (with examples).

The Fix

The obvious fix is to change the offending line as I show in the beginning of the post. This won’t however prevent new users from making this mistake again. The real fix is thus to fix the Qt documentation. And I did just that:

https://codereview.qt-project.org/c/qt/qtdoc/+/297560

Oh, and I fixed some applications too:

https://codereview.qt-project.org/c/qt/qttools/+/299827

The Call

If you are a maintainer of Qt-based application, please take a look in your codebase and see whether you use this antipattern. If you won’t fix it, and I happen to use your application, be prepared to deal with a change request at some point. 🙂

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post Fixing a common antipattern when loading translations in Qt appeared first on KDAB.

New GitQlient v1.1.1 (+ Mac)

There is available a patch for the version 1.1 of GitQlient.

The new patch version solves a problem with new repositories. The problem prevented users to work with repositories that have no commits. That applies to both new repositories (via init new repo) and already created repos that don’t have any commits.

I took advantage of this issue to release also a release for OSX. It’s as functional as I would expect to have it in the next 1.2.0. However, I’d like if people can test it before going into the next release.

The binaries

You can find the binaries for GitQlient v1.1.1 on the release section on the GitHub repo:

New GitQlient v1.1.1 (+ Mac) first appeared on My C++ & Qt blog - Cesc M..

Qt for MCUs 1.3 released

Qt for MCUs 1.3 is now available in the Qt installer. Download it to get the latest improvements and create stunning GUIs with the newly available timeline animation system.

June ’20 – monthly digest

Blogs

Why is my screen dark

by Robert Brock

A digestible blog on common render issues and what their cause could be.

This is part 3 of a blog series explaining common render issues and what their cause could be. Robert Brock sheds some light on possible scenarios.

Read the blog…

QStringView Diaries: Zero-Allocation String Splitting

by Marc Mutz

“After four months of intensive development work, I am happy to announce that the first QStringTokenizer commits have landed in what will eventually become Qt 6.0. The docs should show up, soon.”

Read the blog…

How to declare a qHash overload

by Giuseppe D’Angelo

This blog post is about something that should be simple but apparently causes trouble: how to declare a qHash overload for a custom datatype. This is necessary when we want to use custom data types as keys in a QHash.

Read the blog…


News

June edition of KDAB News

KDAB News June edition content:

  • Catharina Maracke, Lawyer and Founder of the Software Compliance Academy will discuss the benefits and downsides of Open Source Software
  • New versions of Squish Test Center
  • Event updates: CppCon, Meeting C++, QtWS
  • And more

Watch the video…

Online training – Introduction to CMake

Date: August 25th

Time: 10.00-16:00 CEST

A one-day training course together with James Turner, KDAB Senior Software Engineer who has been developing Qt since 2002.

Extract from course content

  • Build system overview; targets and dependencies
  • Building executables and libraries
  • CMake language and debugging
  • Platform-independence
  • Using and writing package finders
  • Code generators
  • Cross compilation

Read more and sign up…

‘Module 6 – Presenting data’ released

In this module, we are going to start exploring model/view programming with Qt Quick. Model/view is a cornerstone of Qt development: every non-trivial Qt application uses it, sometimes extensively. The end goal for us is going to be to have the data managed by the C++ side of the application, and the view created in QML that shows that data.

Subscribe to our KDABTV channel and get the latest Qt News and updates.

Kuesa 3D 1.2

We released version 1.2 of the 3D integration workflow Kuesa 3D, built on top of Qt 3D.

Kuesa™ 3D is a complete design-to-code workflow solution for 3D in real-time applications, centered around the open glTF™ 2 format, supported by Blender, Maya, and 3ds Max.

In this blog post, we will highlight some of the new features we have introduced.

Read more…

Kuesa 3D Studio 1.2 – Press Release

A Complete Design-to-Code Workflow Solution for 3D Assets in Real-time Applications, Kuesa 3D Studio 1.2; Press Release

  • version 1.2 released
  • makes 3D design and development workflows easy, fast and reliable
  • offers support for Maya, Autodesk 3ds Max, and Blender as well as any other glTF-compatible digital content creation tools
  • New: Iro materials library can simulate common surface properties with less designer and GPU overhead

Read more…


Events

Italian C++

KDAB senior software engineer and KDE Community core developer Ivan Čukić presented his talk ‘Move-only types can save the API’ at the virtual Italian C++.

Watch the talk here…

Upcoming events

We are happy to name a few interesting events coming up this July, August, and September. The majority of the events have had some changes made during the last months but we will try to keep you updated at all times.

C++ On Sea – The international C++ Conference will be accessible as a virtual event. Don’t miss out on the workshops running the following week, from the 20th. Date: July 15th – 17th

SIGGRAPH – The international event with the latest news about transformative advancements in computer graphics and interactive techniques has gone virtual. Date: August 17th

KDE Akademy – The conference will be hosting the event online this year. Hundreds of attendees from the global KDE Community are expected to participate to showcase, discuss, and plan for the future Community and its technology. Date: September 4th – 11th

CppCon – The one-week long conference for the entire C++ community that offers talks and panels, presentations by the C++ community, and lightning talks. KDAB will be sponsoring the event. Early bird registration will be available on or before July 17th. Date: September 13th – 18th

Visit our page Upcoming events for more updates…

The post June ’20 – monthly digest appeared first on KDAB.

PyQt5/PySIde2 Book Create GUI Applications with Python & Qt5 (4th Edition) now available

Hello! This morning I released a new update to my PyQt5 book Create GUI Applications, with Python & Qt5. This is an enormous update, expanding it from 258 to 665 pages and adding 211 complete code examples.

To celebrate the milestone the book is available this week with 20% off. Readers get access to all future updates for free, so it's a great time to snap it up!

render_pyqt5_small.png

This is the 4th Edition of the book and adds a number of entirely new chapters including MVC-like model views, SQL database-views, plotting with matplotlib & PyQtGraph, custom widgets & bitmap graphics, concurrent programming with threads and processes and theming Qt applications.

Existing chapters have been expanded with more examples and step-by-step guides to using them. All source code shown in the book is available as standalone, runnable, examples to try out and experiment with.

book-pages.png

Chapters have been updated and expanded with new examples and more detailed explanations and diagrams. Cross-platform screenshots show how your application will look on different systems. Starting from the basic principles of GUI development in Python and building up to more complex topics.

modelviews.png

Learn how to use Qt's MVC-like model views architecture to sync data with widgets, including querying and editing SQL databases from within your applications.

themes.png

Tired of the default application look? Use styles, palettes and Qt Style Sheets to completely customize the look and feel of your applications.

Examples of customised bars.

Discover how to create your own widgets to use in your Qt applications. Starting from the basics of bitmap graphics and building up to a fully-customizable widgets.

concurrent.png

Use threads an processes to perform long-running calculations or communicate with remote services, without locking up your applications. Receive data back from threads and processes and display progress bars. Stop and pause running jobs from your UI.

plotting.png

Visualize data inside your applications, using matplotlib or PyQtGraph. Sync plots with external threads to create live updating dashboards to visualize data in real-time.

There is also a PySide2 edition of the book available, which features largely the same content, with examples converted to "Qt for Python". Purchasing either book gets you access to both editions, including any future updates of either.

Feedback, as always is very welcome!

Get the PyQt5 book
Get the PySide2 book

Why is my screen dark

Part 3

If you are here from Part 1, you missed Part 2, somehow.

Black Screen

It’s a night scene

You have tried everything from the first two parts that seemed applicable, and your screen is still a window to the void? No problem: we’ve gathered another five reasons this could be happening and how to go about fixing them.

Issue 11: Is it a bird, is it a plane?

Your 3D environment (aka: scene) normally has several elements and those elements each have their own properties. One element of particular importance is ‘you’, the viewer of the scene. If you aren’t in a room, you can’t be expected to see what is in that room. With 3D scenes, the viewer is usually referred to as the camera. (Unlike in 2D where it’s often called a window or view)

Perspective View Frustrum

Wikibooks: Vertex Transformations

Part of the camera’s properties are the near and far clipping planes, which specify the closest point to the camera which is visible, and the furthest away point. Anything closer than the near plane, or further away than the far plane, will be clipped and hence invisible.

Of course, you can get something in between. If your cube is 200 units across, sitting at 900 units from the camera, and the far plane is at 1000 units … you will see half of it.

The solution here is to set the near and far plane distances appropriately to the scene you’re working in: sometimes this is easy, everything is a similar scale and stays a consistent distance to the camera. Other times, it’s a huge topic which requires redesigning your renderer to avoid  artefacts : especially when you have large distances or tiny objects. For more on this, and why selecting good near/far value is hard, read up ‘depth buffer precision’.


Issue 12: I just want to be normal.

When transforming surface normals, it’s important to use a correctly derived normal matrix (from your model and view transformations). If this normal matrix is incorrect, your normals will be incorrectly scaled or rotated, and this can break all the the corresponding lighting calculations.

(There’s many ways incorrectly loaded, transformed or generated normals can break lighting, more to come in a future part)

Technically you need to compute the transpose of the inverse of the upper 3×3 of your combined model-view matrix. Which is some slightly nasty mathematical juggling. Fortunately, QMatrix4x4 has a helper to compute the correct matrix for you. Just make sure to compute the normal matrix for any of your transformation matrices, and pass this into your shaders as an additional uniform value.


Issue 13: All the world’s a stage…

Ready, steady,… and? You have a beautifully crafted startup animation. There are fades, there’s camera movement, there’s a reflection map swooshing over the shiny surface of your model. Just remember to actually start the animation: in Qt, animations are not played on load (unlike some authoring tools), so maybe you just need to press ‘play’.


Issue 14: Triangels. Vertexes. Phong shading.

If you’re writing shaders by hand, and you have a misnaming of the attribute in your shader code, compared to the C++ or QML code which binds uniforms or attributes to those names, then most rendering languages will treat the unbound data as 0,0,0,0 in the shader. If it’s a colour, you’ll get black (if it’s a normal or vertex position, it’s likely also not what you want). The good news is the shading language doesn’t care about your spelling, it just cares that the names you use match. So you can happily use Triangels, so long as you call them that everywhere. (But it will break if someone helpfully fixes your code in one place and not the other…)

If you’re lucky, your graphics driver has a debug mode, or some developer tooling, to warn you when you set a name which is not used in the shader. However, there are various techniques which rely on unbound uniforms or attributes efficiently returning zeroes, so the default production driver is unlikely to warn you about this.


Issue 15: Primitive thoughts.

GPUs draw triangles. Lots of triangles, lovely triangles everywhere. But occasionally some old-timer with an SGI Indigo under their desk will mention some other stuff – fans and strips? Or quads? Or maybe you’re using tessellation shaders (they are great). All of these things are different primitive types, which tell the GPU what kind of thing we’re drawing. Even if you’re not using tessellation shaders (where you draw patches), drawing lines can be very useful in industrial and scientific models, and drawing points can be one way to draw many lights (think flying over a city at night) or clouds of particles.

But if you’re sending drawing commands yourself, you need to specify the primitive type: even if you’ve carefully arranged your geometry into buffers and arrays and indexes of beautiful triangles. And if the type is incorrect, you won’t see triangles, but maybe just points, which by default are single-fragment dots. Which can be really hard to see, or even invisible (depending on your lighting model and fragment shader)


There are many ways to mess up 3D rendering; new technologies, new languages, new engines are coming out everyday. If we helped you with your issue, or even more than one, great! However, if you are still having issues, we have more help on the way. Why not comment your issue below to possibly have it feature in one of the following parts?

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post Why is my screen dark appeared first on KDAB.

How to declare a qHash overload

Today’s blog post is about something that should be simple and apparently it causes trouble: how to declare a qHash overload for a custom datatype. This is necessary when we want to use custom datatypes as keys in a QHash. From the documentation:

A QHash’s key type has additional requirements other than being an assignable data type: it must provide operator==(), and there must also be a qHash() function in the type’s namespace that returns a hash value for an argument of the key’s type.

In other words, given a class like:

namespace NS {

class EXPORT MyClass
{
    A m_a;
    B m_b;
    C m_c; 
    D m_d;
    ~~~
};

}

How do we implement its qHash() overload? Let’s find out! (Most of this applies to std::hash too.)

Look at its operator==

The first thing to look at is the implementation of operator== (or operator<=>, coming C++20).

Assuming the implementation is sane, then the code will tell us which members are relevant for equality and which ones are not (caches, tags, whatever). The ones not relevant shall not be hashed (otherwise, you might end up with objects that compare equal and have different hashes).

If the type in question has a botched operator==, then either fix it if possible, otherwise kill the corresponding qHash() overload via = delete. This will avoid having people re-adding it in client code.

If the type does not have an operator==, or cannot have a meaningful one, do not provide qHash() at all.

What’s a “sane” implementation? Generally one that recursively uses plain operator== on the member subobjects. Fuzzy comparisons are a bad idea, making the type not hashable efficiently.

Prepare for Qt 6 (optional)

Optional step: prepare for some source-incompatible changes in Qt 6 (the impact is still unknown; possibly just warnings). Qt 6 changed the return type of qHash() from uint to size_t, so you can guard yourself with something like:

#if QT_VERSION < QT_VERSION(6, 0, 0)
using qhash_result_t = uint;
#else
using qhash_result_t = size_t;
#endif

Declaring your qHash overload

The idiomatic approach is:

namespace NS {

// 1. add a forward declaration for the class
class MyClass;

// 2. consume the forward declaration for the class, by
// forward declaring its qHash overload
EXPORT qhash_result_t qHash(const MyClass &c, qhash_result_t seed = 0) noexcept;

class EXPORT MyClass
{
    ~~~ // same as before
    
    // 3. add:
    friend EXPORT qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept;
};

}

The reason for going all the trouble with 1+2 is because friend declarations cannot have default parameters unless you’re also defining the function (cf. [dcl.fct.default]). Although QHash will never call your qHash overload without a seed, you may want to unit test it, call it from your own containers that aren’t seeded, etc.; so it’s just “polite” to have the second argument defaulted.)

In case your implementation is entirely inline, you can skip 1+2 and go just with 3, adding the default argument there, and defining (not just declaring) qHash in the body of the class. This would also make the qHash a hidden friend, which would be generally a good thing (see here, here and here for more information about hidden friends).

Using just 3 is not doable in general (e.g. if MyClass is pimpled), in which case your qHash must be out of line, so you have 1+2+3.

Whatever way you go, this will put your qHash overload in the surrounding namespace of your class, which is what you want (qHash is called via argument-dependent lookup in Qt).

Implementing your qHash overload

Then, implement it (inline or outline depending on MyClass). The idiomatic way to do so is to look at your operator==:

bool operator==(const MyClass &lhs, const MyClass &rhs) noexcept
{
    return 
        lhs.a == rhs.a &&
        lhs.b == rhs.b && ~~~;
}

Build your qHash exactly like it: on the same fields, in the same order. This explains why you want qHash to be a friend, just like operator==, to spare going through accessors:

qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept
{
    QtPrivate::QHashCombine hash;
   
    seed = hash(seed, c.a);
    seed = hash(seed, c.b);
    seed = hash(seed, c.c);
    ~~~
    return seed;
}

QtPrivate::QHashCombine is private API, but does not require any special buildsystem magic; it’s in <qhashfunctions.h>, a public header.

The same result can be obtained with the convenience that I have added in Qt 6:

qhash_result_t qHash(const MyClass &c, qhash_result_t seed) noexcept
{
    return qHashMulti(seed, c.a, c.b, ~~~);
}

Reusing the same fields as operator==, in the same order, ensures the casual reader of the code that your hashing is correctly implemented.

If two objects are to be considered identical even if they have their data in a different order, then use QHashCombineCommutative / qHashMultiCommutative. For instance, you must use the commutative versions if you have a class that represents a set of elements, and two sets are equal if they have the same elements, even if stored in a different order. This again can be detected by looking at operator== and spotting a call to std::is_permutation or similar.

However, if your class simply uses a set, then in your operator== you will compare those sets via their own operator==, which means that hashing for your class will still use the non-commutative qHashMulti.

Things not to do in your qHash implementation

Summing

inline size_t qHash(const QQuickShapeGradientCacheKey &v, uint seed = 0)
{
    uint h = seed + v.spread;
    for (int i = 0; i < 3 && i < v.stops.count(); ++i)
        h += v.stops[i].second.rgba();
    return h;
}

A simple sum usually doesn’t spread different values apart enough for the hashing to be effective.

XORing

inline size_t qHash(const QV4Debugger::BreakPoint &b, uint seed = 0) Q_DECL_NOTHROW
{
    return qHash(b.fileName, seed) ^ b.lineNumber;
}

This cancels information out (the seed, the fields themselves).

Getting creative with magic numbers

inline size_t qHash(const QQuickPixmapKey &key)
{
    return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.frame*23) ^
            (key.region->x()*29) ^ (key.region->y()*31) ^ (key.options.autoTransform() * 0x5c5c5c5c);
}

Completely “magic” prime sequence, raising many questions on the values used.

Being out of sync with your operator==

inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs)
{
    return *lhs.region == *rhs.region && *lhs.size == *rhs.size && *lhs.url == *rhs.url &&
            lhs.options == rhs.options && lhs.frame == rhs.frame;
}

This is the same datatype as the previous example. How can you know, from a quick glance, if the qHash overload is correct? (Not good, just correct; equal objects yield equal hashes.)

Creating lots of unnecessary copies/temporaries

size_t qHash(const QQuickTextNodeEngine::BinaryTreeNodeKey &key)
{
    return qHash(qMakePair(key.fontEngine, qMakePair(key.clipNode,
                                                     qMakePair(key.color, key.selectionState))));
}

This is because qMakePair copies. Not necessarily a big deal in case of small, trivially copiable types; but qHashMulti would just lead to cleaner code, and possibly spread the hashing more equally.

Hashing Qt datatypes for which Qt does not provide a qHash overload

All the above discussion refers to the case where the datatype is a user-defined one. Sometimes we may need to build a QHash using a Qt datatype as a key, and we find out that the data type does not have a qHash overload. What do we do in this case?

  1. Report a bug. Any Qt datatype that provides an operator== should also provide a qHash overload.
  2. Roll your own qHash, but protected by a Qt version check that blocks the usage of a higher version of Qt. That code must be revisited when Qt itself gets upgraded — if a new version introduces a qHash overload, your code may break in interesting ways (e.g. ODR violation).
  3. (“Worst case scenario”) Ditch QHash for std::unordered_map and a custom hasher (following the same reasoning of 1+2, you don’t want to specialize std::hash for Qt datatypes), or use a wrapper type.

Hashing std:: datatypes via qHash

Unfortunately, you really shouldn’t do it. First, it should be Qt’s job at providing hashers for datatypes defined by the Standard, not yours, so the previous point applies.

Second, it’s impossible to do it reliably, because a qHash overload for such datatypes should be declared in the std namespace, and that’s not allowed.

An overload in any other namespace will not work correctly. Given your rolled out qHash for an arbitrary std::foo datatype:

// not in namespace std
qhash_result_t qHash(const std::foo &foo, qhash_result_t seed = 0)
{
    return ~~~;
}

Then merely using a QHash will lull us into a false sense of security, as this code will work:

QHash<std::foo, bar> h; 
h.insert(~~~, ~~~);

But many other usages will be, unfortunately, broken. Suppose you just use the datatype as a data member of a class:

struct MyStruct 
{
    std::foo m_a;
    int m_b;
    ~~~
};

And then you want to define its qHash overload just like we’ve seen before:

qhash_result_t qHash(const MyStruct &s, qhash_result_t seed = 0) 
{
    // (very likely) ERROR: qHashMulti does not find qHash(std::foo)
    return qHashMulti(seed, c.m_a, c.m_b); 
}

How is this possible? This is due to how ADL works, combined with 2-phase name lookup. Suppose that you have this little piece of code:

// calls f() on a T object
template <typename T> void call_f() { T t; f(t); }

struct S {};
void f(S);

void use() { call_f<S>(); }

This code works, even if S and f(S) were declared after call_f(). This is what gives us the power of using qHashMulti (AKA call_f) that can use qHash overloads on arbitrary types (AKA f(S)), even if these overloads got introduced after qHashMulti‘s own definition.

If we change the code just slightly, however, we have a problem:

// same as before
template <typename T> void call_f() { T t; f(t); }

#include <stdfoo>
void f(std::foo);

void use() { call_f<std::foo>(); }

This code now does not compile (surprise!). The reason is that the call to f() inside call_f() will only look in some scopes:

  1. it will check if a f(std::foo) was declared before call_f() (it wasn’t)
  2. it will check for a f(std::foo) in std::foo‘s associated namespaces. Which is namespace std, not the one we’re in!

To make the example work, we would need to do something like this:

// same as before
template <typename T> void call_f() { T t; f(t); }

#include <stdfoo>
namespace std {
void f(foo);
}

void use() { call_f<std::foo>(); }

… which would be OK for just about any other namespace. Not for namespace std, though: as explained before, we are not allowed to declare an arbitrary new function in that namespace.

Moving the definition of f(std::foo) before call_f would work (this would mean defining our qHash(std::foo) before including the definition of qHashMulti). But if we do so, we lose the convenience of using qHashMulti (as well as qHash for any other fundamental type or Qt type!).

You can play with this code here on Godbolt.

Long story short: due to qHash design, it’s not possible to conveniently and reliably hash datatypes defined by the Standard. You can use workarounds, e.g. define wrapper types and use such types as keys in your QHash objects.

Bonus points

As in, nice things to have.

  • qHash overloads should really really be noexcept. Throwing from a hashing function sounds wrong on multiple levels.
  • qHash overloads are good candidates for being marked as pure functions, that is, functions that only read memory, never modify it (or have any visible side effects). Such markers should help the compiler optimizing the code further.Qt defines a couple of convenience macros for this. One is Q_DECL_PURE_FUNCTION, that means that the function only reads global memory. There’s also Q_DECL_CONST_FUNCTION, as a even more strict version: the function only reads the values of the parameters, not any memory at all. Note that passing a reference/pointer and reading through it makes a function PURE, not CONST (something like strlen is PURE). Use CONST with a qHash overload where you’d pass an argument by value because it’s a small trivially copiable type like QPoint, QVector3D or similar, otherwise use PURE.

Why do I bother with all this?

Because declaring “ordinary” hashing functions should be straightforward, unless you have profiling data to believe that you can do better than the idiomatic way. I keep seeing code that makes it look dark magic. It’s not!

Ideally, in some not-too-far future, we may reach a level where the compiler will autogenerate the hash function for us — just like in C++20 it may autogenerate the implementation of operator== for us!

Thanks for reading!

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post How to declare a qHash overload appeared first on KDAB.

Released GitQlientPlugin 1.2.0 for QtCreator

I’m happy to announce the release of GitQlientPlugin 1.2.0

A month ago I released the version 1.1.0 of GitQlient. With that I shipped some improvements, a few fixes and a big new functionality I was missing: the Merge view.

I still have to add some features and add OSX support in GitQlient, but for now the code is fully compatible with the three big ones (Linux, Mac and Windows).

What was missing is some fixes at plugin level, even that I’ve tried to be on track with it, I prefer to focus on GitQlient as app. However, after a month I think I have a stable version of the plugin that can be ship or at least published as version.

I’ve generated two binaries: one for Linux, compiled with GCC 5.3.1 and another with MSVC2017 32bits. The main reason is because those are the compilers that QtCreator uses and then I make sure that the dependencies, that are shipped as dynamic libraries work well. As you can see, Mac is still the pending task.

The binaries

You can find the binaries for GitQlientPlugin 1.2.0 on the release section on the GitHub repo:

Further work

Since GitQlientPlugin is a wrap of GitQlient, you can see the release planning and all the features that every release will contain in the Release Plan for GitQlient.

As always, if you’d like some feature or you’re missing something in GitQlient or in GitQlientPlugin, check that it’s not yet in the backlog and open an issue on GitHub!

Released GitQlientPlugin 1.2.0 for QtCreator first appeared on My C++ & Qt blog - Cesc M..

QStringView Diaries: Zero-Allocation String Splitting

After four months of intensive development work, I am happy to announce that the first QStringTokenizer commits have landed in what will eventually become Qt 6.0. The docs should show up, soon.

While the version in Qt will be Qt 6-only, KDAB will release this tool for Qt 5 as part of its KDToolBox productivity suite. Yes, that means the code doesn’t require C++17 and works perfectly fine in pure C++11.

This is a good time to recapitulate what QStringTokenizer is all about.

QStringTokenizer: The Zero-Allocation String Splitter

Three years ago, when QStringView was first merged for Qt 5.10, I already wrote that we wouldn’t want to have a method like QString::split() on QStringView. QStringView is all about zero memory allocations, and split() returns an owning container of parts, say QVector, allocating memory.

So how do you return the result of string splitting, if not in a container? You take a cue from C++20’s std::ranges and implement a Lazy Sequence. A Lazy Sequence is like a container, except that it’s elements aren’t stored in memory, but calculated on the fly. That, in C++20 coroutine terms, is called a Generator.

So, QStringTokenizer is a Generator of tokens, and, apart from its inputs, holds only constant memory.

Here’s the example from 2017, now in executable form:

const QString s = ~~~;
for (QStringView line : QStringTokenizer{s, u'\n'})
    use(line);

Except, we beefed it up some:

const std::u16string s = ~~~;
for (QStringView line : QStringTokenizer{s, u'\n'})
    use(line);

Oh, and this also works now:

const QLatin1String s = ~~~;
for (QLatin1String line : QStringTokenizer{s, u'\n'})
    use(line);

QStringTokenizer: The Universal String Splitter

When I initially conceived QStringTokenizer in 2017, I thought it would just work on QStringView and that’d be it. But the last example clearly shows that it also supports splitting QLatin1String. How is that possible?

This is where C++17 comes in, on which Qt 6.0 will depend. C++17 brought us Class Template Argument Deduction (CTAD):

std::mutex m;
std::unique_lock lock(m); // not "std::unique_lock<std::mutex> lock(m);"

And that’s what we used in the examples above. In reality, QStringTokenizer is a template, but the template arguments are deduced for you.

So, this is how QStringTokenizer splits QStrings as well as QLatin1Strings: in the first case, it’s QStringTokenizer<QStringView, QChar>, in the second, QStringTokenizer<QLatin1String, QChar>. But be warned: you should never, ever, explicitly specify the template arguments yourself, as you will likely get it wrong, because they’re subtle and non-intuitive. Just let the compiler do its job. Or, if you can’t rely on C++17, yet, you can use the factory function qTokenize():

const QLatin1String s = ~~~;
for (QLatin1String line : qTokenize(s, u'\n'))
    use(line);

QStringTokenizer: The Safe String Splitter

One thing I definitely wanted to avoid is dangling references a la QStringBuilder:

auto expr = QString::number(42) % " is the answer"; // decltype(expr) is QStringBuilder<~~~, ~~~>
QString s = expr; // oops, accessing the temporary return value of QString::number(), since deleted

The following must work:

for (QStringView line : QStringTokenizer{widget->text(), u'\n'})
    use(line);

But since the ranged for loop there is equivalent to

{
   auto&& __range = QStringTokenizer{widget->text(), u'\n'};
   auto __b = __range.begin(); // I know, this is not the full truth
   auto __e = __range.end();   // it's what happens for QStringTokenizer, though!
   for ( ; __b != __e; ++__b) {
     QStringView line = *__b;
     use(line);
   }
}

if QStringTokenizer simply operated on QStringView or QLatin1String, the following would happen: The __range variable keeps the QStringTokenizer object alive throughout the for loop (ok!), but the temporary returned from widget->text() would have been destroyed in line 3, even before we enter the for loop (oops).

This is not desirable, but what can we do against it? The solution is as simple as it is complex: detect temporaries and store them inside the tokenizer.

Yes, you heard that right: if you pass a temporary (“rvalue”) owning container to QStringTokenizer, the object will contain a copy (moved from the argument if possible) to extend the string’s lifetime to that of the QStringTokenizer itself.

Future

Now that we have developed the technique, we very strongly expect it to be used in Qt 6.0 for QStringBuilder, too.

By Qt 6.0, we expect QStringTokenizer to also handle the then-available QUtf8StringView as haystack and needle, as well as QRegularExpression and std::boyer_moore_searcher and std::boyer_moore_horspool_searcher as needles. We might also re-implement it as a C++20 coroutine on compilers that support them, depending on how much more performance we’ll get out of it.

Conclusion

QStringTokenizer splits strings, with zero memory allocations, universally, and safely. Get it for free right now from KDToolBox, and you can future-proof your code with an eye towards Qt 6.

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post QStringView Diaries: Zero-Allocation String Splitting appeared first on KDAB.

Kuesa 3D Studio 1.2 – Press Release

KUESA™ 3D Studio 1.2 released

A Complete Design-to-Code Workflow Solution for 3D Assets in Real-time Applications

  • version 1.2 released
  • makes 3D design and development workflows easy, fast and reliable
  • offers support for Maya, Autodesk 3ds Max, and Blender as well as any other glTF-compatible digital content creation tools
  • New: Iro materials library can simulate common surface properties with less designer and GPU overhead
  • free live webinar June 4th, 6pm CEST

Berlin, June 2nd 2020

Building software that is dependent on real-time 3D models – like for example an automotive dashboard, MRI machine, factory control system or furniture design tool – requires not only 3D designers and 3D programmers. It also demands the ability to bring these very different skill sets together into a smoothly operating workflow.

In conventional workflows, a massive problem is that the assets created by 3D artists can rarely be used by programmers directly, forcing the development team to hand-tweak models and hand-code animations to make things work. It also forces designers to move their designs to target hardware to see how they’ll actually appear. This manual work extends product release timelines, and the artificial wall it creates between 3D artists and software engineers is a bottleneck that prohibits rapid iteration and the fine-tuning required to develop fast and high-quality results.

To eliminate this problem KDAB created KUESA 3D Studio. It makes the collaboration of designers and developers much more manageable by offering a seamless, integrated workflow. Designers can realize their vision using their favorite tools, developers can focus on their strengths without having to deal with design issues, and management can rely on getting better products to market faster.

How does KUESA 3D Studio work?

KUESA 3D Studio is comprised of three main components.

The KUESA 3D Design Plugins augment a designer’s preferred digital content creation tools, allowing them to create and export fully accessible 3D scenes and to display content that visually matches the run-time environment.

Many additional utilities are integrated in KUESA 3D Tools for fine-tuning, conditioning, investigating, debugging, and optimizing 3D assets, usable by either designers or developers.

Libraries built on top of Qt and Qt 3D allow developers to integrate 3D scenes directly into applications. This module is called KUESA 3D Runtime.

What’s new in version 1.2?

One of the most significant improvements is the Iro Materials Library. Physically-based rendering (PBR) can generate amazing images, but not all embedded systems have enough processing power to handle it. Also, without knowing the actual physics, PBR isn’t easily tweakable to simulate materials such as a pre-defined bitmap that needs to appear glossy. For these cases, or any other where PBR may be overkill, KUESA 3D Studio supports the Iro Material Library. This library provides a catalog of materials that can simulate common surface properties (such as reflections, clear-coated paint, and transparency) and gives great looking results with less designer and GPU overhead.

By default, glTF™ only allows you to animate transformation properties like translation, rotation, scale. In lots of cases, it would be useful to animate other properties like material properties, camera properties etc. This has now been added through the use of a custom glTF extension.

An additional benefit is brought in by Qt 3D, which KUESA 3D Studio is based on and which is maintained by KDAB. With Qt 3D on Qt 5.15.0 very potent profiling capabilities have been added.

More features:

  • Support for Maya, Autodesk 3ds Max, and Blender, as well as any other glTF-compatible digital content creation tools
  • Ability to build 3D scenes with PBR, non-PBR, and node-based materials
  • Real-time performance on desktop and embedded systems
  • Integration of both 3D and 2D assets
  • Compressed textures, meshes, and images for faster load times, reduced application size, and optimal use of GPU resources
  • Full programmatic access to scene items with APIs in C++ and QML
  • Photo-realistic results consistent across design tools and applications
  • Special effects like bloom and depth-of-field
  • Up to date with Qt 3D performance improvements in Qt 5.15
  • Ability to incorporate tools into a continuous integration system resulting in consistent 3D asset verification and conditioning
  • Availability of AGPL 3 license for KUESA 3D Runtime

———————————————————————————————————————————

More information on www.kuesa.com

Live demo webinars are scheduled for:

June 4th 2020… (6pm Central European Time)

June 18th 2020... (8am Central European Time)

Video footage

About the KDAB Group

The KDAB Group is the world’s leading software consultancy for architecture, development and design of Qt, C++ and OpenGL applications across desktop, embedded and mobile platforms and is one of the biggest independent contributors to Qt. Our experts build run-times, mix native and web technologies, and solve hardware stack performance issues and porting problems for hundreds of customers, many among the Fortune 500. KDAB’s tools and extensive experience in creating, debugging, profiling and porting complex applications help developers worldwide to deliver successful projects. KDAB’s trainers, all full-time developers, provide market leading, hands-on, training for Qt, OpenGL and modern C++ in multiple languages. Founded in 1999, KDAB has offices throughout North America and Europe.

More Information on www.kdab.com Press Contact: press@kdab.com

Download this report

The post Kuesa 3D Studio 1.2 – Press Release appeared first on KDAB.

Kuesa 3D 1.2 release!

Today, KDAB is releasing version 1.2 of the 3D integration workflow Kuesa 3D, built on top of Qt 3D.

Kuesa™ 3D is a complete design-to-code workflow solution for 3D in real-time applications, centered around the open glTF™ 2 format, supported by Blender, Maya and 3ds Max.

Read the Press Release…

In short, Kuesa provides a workflow that simplifies work for both designers and developers. It is centered around the glTF 2 format. The idea behind Kuesa 3D is that changes made on 3D models shouldn’t require much, if any, work on the developer’s side. As a consequence, you can iterate more frequently, get feedback more often and release on time.

In this blog post, we will highlight some of the new features we have introduced. You can get the full details here.

What’s new since 1.1?

The Iro Material library

The glTF 2 format currently only supports a Metallic Roughness physically-based material. As a result, it looks great but can be very expensive to render and requires lots of assets. For many use cases, simpler materials can be used instead: this is what the Iro Material library offers.

The library provides several materials that simulate reflections, clear coats of paint or simple transparent surfaces. The benefits of Iro materials are twofold:

  • they significantly reduce your GPU usage (compared with PBR materials), making them ideal for embedded or mobile applications;
  • they offer a real WYSIWYG integration with your 3D authoring tool: Kuesa is going to render your 3D models exactly like they appear in your artists’ editing suite.

This video by my colleague Timo Buske shows Iro Materials in action:

Improved Blender Support

Despite a steep learning curve, Blender is a fantastic tool and the latest version brings lots of interesting features to the table. For that reason, we have added support for Blender 2.8.

We can therefore now rely on the official glTF 2.0 export bundled with Blender. Furthermore, to make the best use of it, we’ve contributed patches that allow extending the exporter through custom extensions.

Then, we’ve added an extension to allow exporting of Iro Materials to glTF 2.0 files. In addition, we’ve also updated the Kuesa Blender addon to show a real time preview of the Iro Materials in Blender. What you see in Blender is what you’ll get with Kuesa 3D.

Improved animation support

Currently, glTF 2.0 only supports animating transformation properties (translation, scale, rotation) of objects. Incidentally, there is a draft of an extension EXT_property_animation to add support to animate any property.

Since that was a really important feature for us, we’ve decided to implement it for Kuesa 3D. In that sense, we’ve added a custom EXT_property_animation to the exporter. Next, we’ve updated the glTF 2 importer in the Kuesa 3D Runtime library to parse it properly.

Hence, we can now animate material, lights or camera properties in Blender, export the scene as a glTF file (with the extensions) and load it with Kuesa 3D Runtime.

An updated offering

With this new release, we changed the offering of the product to satisfy different use-cases.

Kuesa 3D Studio

Kuesa 3D Studio is the complete solution a team can use in production, with everything needed to satisfy both designers and developers:

  • Kuesa 3D Design Plugins:
    • Blender addons supporting:
      • Kuesa 3D extensions for Layers and Iro materials
      • The EXT_property_animation extension
      • Real time preview of the Iro materials in the Eevee viewport
  • Kuesa 3D Tools:
    • glTF editor application allowing preview and introspection of glTF 2 scenes
    • collection of command line tools to check and optimize assets
  • Kuesa 3D Runtime: Qt module library on top of Qt 3D
    •  glTF 2.0 fully compliant loader
    • support of Kuesa 3D and EXT_property_animation extensions
    • retrieve the various resources held in the scene
    • playback animations
    • add post processing effects such as Bloom, Depth of Field …
    • use a premade Qt 3D FrameGraph

The Kuesa 3D Tools and Runtime also support any glTF 2.0 files coming from other application like Maya, 3DS Max or others, as long as they can export to glTF 2.0.

You can find out more about Kuesa 3D Studio here.

Kuesa 3D Runtime

Kuesa 3D Runtime is also available as a separate product, full support from us. The product is available on the Qt marketplace or directly from us. This is perfect if you want to try out Kuesa and see what you can do with it.

Like previous releases, it is freely available under the AGPL 3 license.

Since it is built on top of Qt 3D, you can use the full Qt 3D API to further customize your application. For the most part, you can leverage things like Picking, Camera handling and a lot more for free.

As for actual Qt requirements, Kuesa 3D Runtime requires either the latest Qt 5.12 or the new Qt 5.15 release.

Find out more about Kuesa 3D Runtime.

Additional Contributions

Lastly, Kuesa 3D is also a way for us to justify contributions to open source projects. In that sense, we’ve made a few contributions targeting:

  • Support for registering and creating user defined extension in Blender’s glTF exporter
  • Fixes for animations on the official Blender glTF exporter
  • Qt 3D bug fixes and performance improvements

Try it out!

You can download Kuesa 3D Runtime from our GitHub repository.

Download the latest Kuesa 3D Studio brochure.

Join our live webinars and ask questions:

Watch our Realtime Music Box short video where I explain all this running on one of our demos:

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post Kuesa 3D 1.2 release! appeared first on KDAB.

Debugging Qt for WebAssembly


Debugging is often difficult in the best of times. Debugging Qt webassembly apps in the web browser is not something I want to do on a nice summer day, and certainly makes a person more appreciative of mature, gui based debugging. But sometimes you have to do what you have to do.

If you have a crash on your Qt for WebAssembly app, you would see a not very human readable backtrace in the console output. You can start by recompiling your app with CONFIG+=debug, which allows nice symbol names in the backtrace that appears in the dev console of the browser when an exception or crash happens. Many times this can lead you to find your bug fairly quickly.

NOTE: The Qt libraries do not need to be compiled in debug mode for you to just see symbols while debugging. Although, if you want to step through C++ source code using sourcemaps, you will need to compile Qt and your application in debug mode.

The easy way is to add qDebug output, run it and see what happens. I do this quite often. But just as often, it is not enough to find a particular bug.

One issue with that is linking emscripten based wasm files takes a long time (because that is where all the magic happen), so this method is rather time consuming. Thankfully, with Qt 5.15, we use Emscripten 1.39.8 which is based on upstream clang, transpiles directly to wasm binaries and skips the internediate step of outputting javascript, so link time is greatly reduced.


What if you need to step through the source code like a desktop debugger? QtCreator does not at this time, have support for debugging Qt WebAssembly apps. I suppose it could be implemented using the browsers remote debugging interface API 

Emscripten can be used to generate source maps for the browser debugger to utilize for debugging. In Qt, we use qmake to add the necessary linker lines to allow emscripten to generate these source maps. Qt should do this by default when you use CONFIG+=debug

There is a bug that was recently fixed regarding this. If you do not find any source maps files (.map), the workaround is to add QMAKE_LFLAGS_DEBUG += -g4 in your pro file.

By default, the source base in Qt is set to use http://localhost:8000/

You can change this by adding into your .pro file, something like:
QMAKE_WASM_SOURCE_MAP_BASE = http://myip:6931 /

Emrun by default uses port 6931.

If you are running a server of some sort, you can use the ip for that. 

Sources need to be in the directory tree seen by the server, so an in-source build is the easiest. In my experience, Chrome browser seems to be able to debug with sourcemaps better. If you are using Mac or Linux, you can simply symlink the source file path into the web server base.

This is what happens when you do not have this set up correctly:

Error while fetching an original source: request failed with status 404
Source URL: http://192.168.0.21:6931/src/wasm-windows/main.cpp

Or pehaps you see a message in the sources tab of the browser console such as:
Could not load content for http://localhost:8000/depot/qt/qt5/qtbase/examples/widgets/richtext/textedit/textedit.cpp (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)

It means it would not find the specific source file. You can symlink the sources directory.
I am using chrome, as I had better luck with it utilizing the sourcemaps. The actual procedure for firefox might be a bit different. It isn't working for me at this time.
  • emrun --browser chrome --port 8000 --hostname localhost --serve_after_close textedit.html 
    • (or wasmsourceserver.py as below)
  • open the web console
  • [chrome] Go into the debugger by clicking on 'sources'
  • [firefox] Uses the 'Debugger' tab
You should now see some entries to source files, including our main.cpp


You can now step in, step out, step over and even set breakpoints, just like a desktop gui debugger like QtCreator.



Note that these file paths are relative, and will create an error when you try debugging it, when it tries to step into a source file not local. This defeats the purpose of steping into code for the most part.

Morten has a work around on the QTBUG https://bugreports.qt.io/browse/QTBUG-72002 with a python web server named:
wasmsourceserver.py

By running this custom server, it will build a file map of all the files in the current directly reursively.


Another way to add a type of breakpoint, if you want to stop execution on a certain line and popup the browser debugger programmatically, you can add
 emscripten_debugger(); 
on the line you want it to stop and pop up. This is in the header emscripten.h, and recompile.




You can also read more about Mobile and Embedded development and Qt for WebAssembly in the book Hands-On Mobile and Embedded Development with Qt 5


Using Visual Studio Code for Qt Applications – Part Two

In the last blog post we saw an essential, C++ oriented, Visual Studio Code setup. That was enough to get going right away, but we can still definitely do more and better. Here I’ll show you how to get a complete setup for your qmake and CMake projects, all this while also wearing a Qt hat (on top of my C++ hat) and having a deeper look at the Qt side.

Build qmake Qt projects

Qmake is not integrated with Visual Studio Code the way CMake is, so setting up a qmake project for build is slightly more convoluted than doing the same with CMake. This means we’ll have to define our own build tasks. We’re going to do this in two stages: build steps definition and build steps combination, leveraging the fact that Visual Studio Code implements task dependencies and ordered sequential execution of dependencies.

Create build steps

As far as build steps are concerned, the following are, in a nutshell, the ones that will cover most cases:

  • Create the build directory (in a way that doesn’t fail if the directory already exists)
    {
      "label": "Create build dir",
      "type": "shell",
      "command": "mkdir -Force path/to/build/dir"
    }
    

    Here, -Force is a powershell parameter that will prevent the command to fail if the directory already exists. On Unix based systems, you can use mkdir -p instead.

  • Run qmake
    {
      "label": "Run qmake",
      "type": "shell",
      "command": "qmake",
      "arg": [ ... add your qmake arguments here ... ],
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
  • Run make/nmake/jom, depending on the platform
    {
      "label": "Run make",
      "type": "shell",
      "command": "jom",
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
  • Clear build folder This can mean different things depending on how the build file is configured. It could be a simple make clean, or a more thorough removal of the whole content of the build folder.
    {
      "label": "Clear build folder",
      "type": "shell",
      "command": "jom clean",
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
Combine build steps

Now that our steps are defined, we can go on and define the actual build tasks. We’ll prepare two for this example, one for running a build, and one for running a clean build. Let’s see the task for a regular build:

{
  "label": "Build",
  "dependsOn": [
    "Create build dir",
    "Run qmake",
    "Run make"
  ],
  "dependsOrder": "sequence"
}

There are two new properties here: "dependsOn" is a list of task labels, and it means that those tasks need to be executed before the current task is built, while "dependsOrder", when set to "sequence", will tell Visual Studio Code to run all dependent tasks sequentially and in the given order.

The task for a clean build is very similar and will only have an extra step where the project is cleaned before being built again:

{
  "label": "Clean build",
  "dependsOn": [
    "Clear build folder",
    "Create build dir",
    "Run qmake",
    "Run make"
  ],
  "dependsOrder": "sequence"
}

And that’s it, now it’s just a matter to open the command palette (Ctrl+Shift+P), select “Task: Run Task” and then “Build”.

Use a default task

As an alternative (or better, an addition) to selecting manually the build task from a list every time, Visual Studio Code also allows to run a default task with a key combination (Ctrl+Shift+B). To mark a task as default, you need to add a few extra lines to the task configuration:

{
  // ... task configuration
  "group": {
    "kind": "build",
    "isDefault": true
  }
}
Use your own Qt

If Qt is not configured at a system level, or you want to use a Qt version other than the default one installed and configured in your system, you need to explicitly configure the environment so that every task is run with the right Qt version in the path. Visual Studio Code allows you to do this every time a terminal is launched for running a task, so our environment customizations are set before running the task command.

This is done in the settings file (or in the workspace settings if you’re working with a workspace), and the property name for this setting is system dependent: either "terminal.integrated.env.windows", "terminal.integrated.env.linux", or "terminal.integrated.env.osx". The property requires an object, where each property is the name of an environment variable, and the associated value is the value for the variable. Below is an example configuration for Windows:

{
  // All other settings...
  "terminal.integrated.env.windows": {
    "PATH": "C:/Qt/5.12.4/msvc2017_64/bin;${env:PATH}"
  }
}

Build CMake Qt projects

Setting up a CMake based project using the CMake extension doesn’t require any settings manipulation if Qt is already configured on your system. What you will need is to select a CMake kit (the CMake extension finds them automatically), a build variant, and launch the build with F7.

Short video showing how to launch a CMake build with Visual Studio Code

However, you may want to use extra arguments in the configuration step or specify your build directory so for instance it doesn’t end up being inside the source directory. You can customize CMake configuration arguments by setting the property "cmake.configureSettings" in your settings file. This property expects a list of string arguments that will be passed to CMake during the configuration step:

"cmake.configureSettings": {
  "CMAKE_PREFIX_PATH": "my/prefix/path",
  "ENABLE_FEATURE": "1",
  "ENABLE_OTHER_FEATURE": "0"
}

To customize the build directory, just set "cmake.buildDirectory" to the desired path. This value may contain variables, so it can be configured, for instance, to point a path relative to the project folder:

"cmake.buildDirectory": "${workspaceFolder}/../build-cmake-project"

If you want to use a custom Qt version, or Qt is not configured system-wide (as is the case on Windows) it’s enough to set CMAKE_PREFIX_PATH properly in the "cmake.configureSettings" property in the settings file. For example:

"cmake.configureSettings": {
  "CMAKE_PREFIX_PATH": "otherprefixpath;C:/Qt/5.12.4/msvc2017_64"
  // ... other args
]

You can find a complete documentation for the CMake Tools extension here, featuring a guide on how to use CMake Tools from the UI, and a documentation for all available settings.

Running and debugging our Qt application

Now that your application has been built, let’s see how we can launch it and, most importantly, debug it.

Running qmake projects

For projects built with qmake, we don’t have any help from extensions and the only option we have is to bake our own launch configurations in the way we’ve seen in the last blog post. This is done in the launch configurations file (launch.json) or in the workspace file, and this is how a launch configuration looks:

{
  "name": "My application",
  "type": "cppvsdbg",
  "request": "launch",
  "program": "path/to/application",
  "stopAtEntry": false,
  "cwd": "${workspaceFolder}",
  "environment": [],
  "externalConsole": false
}

You can run launch configurations both with or without debugger, using “Debug: Start Debugging” (F5) or “Run: Start Without Debugging” (Ctrl+F5) respectively. If Qt is not configured at a system level, or you want to use a custom Qt version, the corresponding launch configuration will need to be explicitly configured to include Qt in its path.

You can do this by updating the "environment" property in the launch configuration. Below is an example for Windows, setting only the "PATH" environment variable. Configurations for other systems need to be adjusted but are essentially similar.

"environment": [
  {
    "name": "PATH",
    "value": "C:/Qt/5.12.4/msvc2017_64/bin;${env:PATH}"
  }
]

Side note: here ${env:PATH} means whaterever value the environment variable PATH has before the launch configuration is run. In general, the syntax ${env:VARNAME} can be used to get an environment variable in a task or a launch configuration.

Running CMake projects

Working with CMake is easier in principle, as the CMake extension provides the commands “CMake: Run Without Debugging” and “CMake: Debug”, allowing you to respectively launch and debug CMake targets.

However, this approach has a number of shortcomings:

  • It’s not possible to specify per-target run arguments for debug runs.
  • It’s not possible at all to specify run arguments for non-debug runs.
  • Some debugging options such as source mapping or custom views with natvis are not configurable using cmake settings.

So in conclusion using the CMake extension for running targets is not really convenient if you want a comprehensive debugging experience, and the best way to go is still to create your own launch configurations.

The CMake extension provides a few convenient variables for launch configurations:

  • ${command:cmake.launchTargetPath}: resolves to the full path of the executable for the target selected from the launch target menu.
  • ${command:cmake.launchTargetDirectory}: resolves to the directory containing the executable for the target selected from the launch target menu.

Qt aware debugging

What we’ve seen until now will let you build and run your Qt applications, using either your system provided Qt or your own. Debugging will work out of the box already, as long as the application code is involved. But wouldn’t it be great to also be able to peek inside Qt’s source code while debugging? Or if we had a better visualization for Qt specific types?

Turns out we can do both with little manipulation on launch configurations. Let’s see how.

Configure debug symbols

Usually Qt debug symbols are distributed alongside libraries, so there’s no real need to explicitly configure debug symbols paths. If that’s not the case, you can configure the debug symbols path by setting the "symbolSearchPath" property on a launch configuration. This property is a string and contains a list of paths separated by a semicolon.

"symbolSearchPath": "otherSearchPath;C:/Qt/5.12.4/msvc2017_64/bin"

This of course can be useful for adding debug symbols for other libraries too.

Source mapping

If the source directory for your Qt differs from the actual source directory (or directories) used while building it, you can configure the debugger to resolve those paths correctly. This happens for instance with binary Qt releases on Windows. You can enable source mapping in launch configurations by adding the "sourceFileMap" property. This property requires an object where each key is the source folder as it’s provided by the debug symbols, and the corresponding value is the path where the source code is in your system. This is how it can be configured for a binary Qt release on Windows:

"sourceFileMap": {
    "C:/work/build/qt5_workdir/w/s": "C:/Qt/5.12.4/Src",
    "Q:/qt5_workdir/w/s": "C:/Qt/5.12.4/Src",
    "C:/Users/qt/work/install": "C:/Qt/5.12.4/Src",
    "C:/Users/qt/work/qt": "C:/Qt/5.12.4/Src"
}
Using Natvis for Qt aware objects visualization

Natvis is a Visual Studio framework that allows you to customize how native C++ objects are visualized in the debugger. Natvis visualization rules are specified through xml files with a specific schema. A natvis file lists visualization rules for each C++ type, and every visualization rule consists in a series of properties. Such properties are meant to be user friendly and will be displayed on the debug window when visualizing objects of the corresponding type.

To name a few examples, a QString is visualized as the string it contains and has a size property and a number of items corresponding to its characters, and QRect will have a width and a height property instead of just the bare (and less intuitive) internal representation of the top left and bottom right points (x1, y1, x2, y2).

If you want to enable natvis in a debug run, just set the "visualizerFile" property in your launch configuration so that it points to the natvis file.

"visualizerFile": "path/to/qt5.natvis"

Debug pane before and after configuring natvis

You can find a ready to use natvis file for Qt 5 at this link.

Updating the code model

In order to be able to navigate Qt headers and enable IntelliSense for the Qt API, it’s enough to adjust the C++ settings for our project (c_cpp_properties.json) by adding the Qt include folder (and all its subfolders):

{
  // ...
  "includePath": [
    // ...
    "C:/Qt/5.12.4/msvc2017_64/include/**"
  ]
}

If you’re working on a CMake project, it’s also possible to use the CMake plugin as a configuration provider. Doing so, include paths and defines will be bound to the currently configured CMake build, and won’t need to be specified manually. This simplifies the C++ properties file considerably, as it’s shown in the example below:

{
  "configurations": [
    {
      "name": "Win32",
      "intelliSenseMode": "msvc-x64",
      "configurationProvider": "vector-of-bool.cmake-tools"
    }
  ],
  "version": 4
}

A note about using Visual Studio compilers on Windows

Visual Studio provides batch files that automate the environment setup necessary to use their C++ compiler and linker. In the last post we saw how it’s possible to configure a task so that it sets up the environment through the vcvars.bat script before running a command.

However, if you need to configure the environment with vcvars.bat for most of your build steps, it is also possible to configure Visual Studio Code so that it runs the batch file for every task. To do so, you need to tweak the configured shell (which is powershell by default on windows) and pass a few args. The setting name for doing this is “terminal.integrated.shellArgs.windows” and it’s set as follows:

"terminal.integrated.shellArgs.windows": [
  "Invoke-BatchFile 'C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Auxiliary/Build/vcvars64.bat' amd64",
  "; powershell"
]

What’s going on here is this: Visual Studio Code will launch by default every shell task by calling this command:

powershell <terminal.integrated.shellargs.windows> -Command <task command> <task argument list>

So, if you set “terminal.integrated.shellArgs.windows” this way, the final command will look like this:

powershell Invoke-BatchFile 'path/to/vcvars' ; powershell -Command <task command> <task argument list>

As a result, task commands will be effectively run in a powershell with the right environment set.

And that’s it for now. Many new things on the table, and some advanced features too. Hopefully this will help you with your workflow.

But there is still more to say, so make sure you don’t miss the next post!

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

The post Using Visual Studio Code for Qt Applications – Part Two appeared first on KDAB.