Qt
Qt: custom-class and specialities for QVariant and QSettings
While implementing some custom-class (C++17; Qt5.12) some specialities became (painfully) noticeable.
The guide at the Qt-source is quite well written.
- ctor, copy-ctor and dtor are all required and have to be public
- add Q_DECLARE_METATYPE(CustomClass)
- QDebug-stream-operator in case of simple debugging-messages
- if the QVariant-version of the CustomClass shall be written to QSettings, then implement also the two streaming operators: << and >> (else crash with Assert “unable to save type”): howto
Three ways to find the location of a certain DLL at Windows
Common problem: just using the current working directory is not sufficient, because especially for unit-tests started from the Visual Studio-testrunner the opriginal, relative path is not fitting anymore. Fallback would be check an environment variable , which is set during installation or using the WinAPI (ugh).
versioned code can be found at: github/cppcCollection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
// +++ ATTENTION! +++ // This is just a snippet to show how to get the path for a certain DLL on the Windows-platform. // First absed on the "current working directory", then the environment variable "PATH", then via WinAPI-call. // Of course, this does not work for any other OS. Nor is this epxected to survive any Windows-makeover (or patch set :X) // +++ ATTENTION! +++ #if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) // for the plugin-path #include <windows.h> #include <libloaderapi.h> #endif bool properSdkPath(QString const path) { #if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) // Check for the existance of a very, very, very important DLL. return QFileInfo(path + "/SDK.dll").exists(); #endif #ifdef Q_OS_MAC // macOS has SDK-support, so using the current application-path is sufficient return true; #endif } // Return the path to the hardware-plugins. // First check is based on the current working directory. // If this fails, check the contents of the environment-variable 'PATH' (set at install time). // If this fails, then check via Win-API. QString getPluginPath() { QString pathToSDK; // Try first the current working directory pathToSDK = QCoreApplication::applicationDirPath(); #if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) // If this failed (has no SDK.dll), indagnate the PATH environment variable. // Which is set during installation. if(!properSdkPath(pathToSDK)) { // Get the directory from the PATH (is set during APPLICATION SDK installation). auto const path = QString::fromLocal8Bit(qgetenv("PATH")); auto const splitPath = path.split(";", QString::SkipEmptyParts); for(auto const& item : splitPath) { if(item.contains("APPLICATION SDK")) { pathToSDK = QFileInfo(item).absoluteFilePath(); break; } } // If even this failed, then fall back to GetModuleFileName (happens with VS-testrunner). if(!properSdkPath(pathToSDK)) { // get the handle of the DLL HMODULE handleModule = GetModuleHandleA("SDK.dll"); LPWSTR modulePath = nullptr; DWORD bufLen = 4096; // reserve enough memory if(!(modulePath = (TCHAR*)malloc(sizeof(TCHAR) * (size_t)bufLen))) { TRACE_INFO("Could not allocate 4 KB memory. Please consider an hardware upgrade.") } // retrieve the path DWORD pathSize = 4096; pathSize = GetModuleFileName(handleModule, modulePath, pathSize); if(pathSize) { QString outputPath = QString::fromWCharArray(modulePath, static_cast<int>(pathSize)); pathToSDK = outputPath.replace("SDK.dll", ""); } else { TRACE_INFO("GetModuleFileName failed:" << GetLastError()); } } } #endif if(!properSdkPath(pathToSDK)) { TRACE_INFO("Could not determine proper plugin-path."); pathToSDK.clear(); // reset } // convert to proper separators and append the suffix for the plugin-location QString const hardwarePluginPath = QFileInfo(pathToSDK).filePath() + "/Plugins"; return hardwarePluginPath; } |
QTest: increase the timeout duration for a test-case
Set via environment variable QTEST_FUNCTION_TIMEOUT a higher timeout duration.
qputenv sets them temporary for the run of the single test case (put this at the beginning of the test case).
1 2 3 4 5 6 7 |
std::string duration("1200000"); // 1200 seconds -> 20 min QByteArray timeoutDuration(duration.c_str(), static_cast<int>(duration.length())); qputenv("QTEST_FUNCTION_TIMEOUT", timeoutDuration); { // just for checking .. auto result = qgetenv("QTEST_FUNCTION_TIMEOUT"); qDebug() << "timeout set to:" << result << "ms"; } |
nota bene: This block has to be put before qExec is ran, so put it into the constructor of the test-suite!
Qt: libpng warning (exception) about wrong color profile (sRGB)
Problem: libpng warning: iCCP: known incorrect sRGB profile
When debugging Qt applications which load some PNG as icons at startup, it could be annoying to continue with the debugger for each thrown exception.
So, let us fix them.
Invoking $ mogrify *.png in the icon directory will fix them.
How to get mogrify? Install imagemagick.
Either manually or on macOS via homebrew: $ brew install imagemagick
libPNG in version 1.6 has to be at least installed: $ convert -list format | grep PNG
Qt: Error: Not a signal or slot declaration
This error is too ‘good’ to be just put into “Today I learned”.
While adapting some unit-testing-code for one of the classes, I received from minGW a error stating:
1 2 3 |
Output ------ C:/Repos/repoName/filename.h:23: Error: Not a signal or slot declaration |
What is the problem? The member-declaration looked totally valid.
Until I realized I had removed the (now commented) “private”-statement and therefore the pointers were seen by the MOC as signals/slots. Which they weren’t!
1 2 3 4 5 6 7 8 9 |
.. private Q_SLOTS: void initTestCase( void ); void cleanupTestCase( void ); //private: classA* _serialPort = nullptr; classB* _flickerSensor = nullptr; .. |
Shame on me :’)
Qt: clean includes
(I’ve decided to follow a more agile workflow: instead of creation a “one post covers the whole topic”-post, to post also updates for each step. Article will be therefore edited while “doing”)
Last week I’ve cleaned Qt-includes for a larger project. Like #include
to #include
how it should be. With proper indirection, so that the Qt-library handles how and where the class is implemented.
A colleauge raised the question, why not use #include
to be even more precise and to see the used module directly (needed for the CMakeLists or qmake).
First step: identify all used Qt-includes
1 2 3 4 5 6 |
$ git grep "include <Q" | cut -d : -f 2 | sort -u # include <QApplication> # include <QAtomicInt> # include <QDebug> # include <QFileDialog> # include <QHBoxLayout> |
Greps all includes starting with an uppercase Q; then split the result at the “:”; then sort und make it unique
Second step: create a replacement-list and a (python?) script which does this for all .h/.cpp-files
tdb
Advanced debugging: find call which triggers Qt-errors
Currently the debugging version of the Qt-libraries is not available. But I receive a report "QIODevice::write (QIODevice): Called with maxSize < 0"
deep within them.
Our codebase is quite large, I am more familiar with other sections and let's say it: some is 'legacy'. So, where to start looking?!?
Yesterday I remembered a trick to get via call-stack to the last-recent position where our functions trigger that mistake.
Inserted in the main.cpp after show from the MainWindow was called a
1 |
qInstallMessageHandler(messageHandler); |
which stands for this
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg ) { Q_UNUSED(context); switch( type ) { case QtDebugMsg: qDebug() << msg.toLatin1().data(); break; case QtWarningMsg: qDebug() << msg.toLatin1().data(); break; case QtInfoMsg: qDebug() << msg.toLatin1().data(); break; case QtCriticalMsg: qDebug() << msg.toLatin1().data(); break; case QtFatalMsg: qDebug() << msg.toLatin1().data(); break; default: qDebug() << msg.toLatin1().data(); break; } } |
Add a breakpoint in the messageHandler and debug the app. Maybe disable the breakpoint until you start to trigger the critical functionality.
When it breaks, you have a nice callstack and can backtrace where it came from :)
how to resume broken downloads
aka: fix the weird firewalling settings ..
aka: How to download the latest stable Qt SDK source code?
curl -L -O -C - http://download.qt.io/official_releases/qt/5.12/5.12.3/single/qt-everywhere-src-5.12.3.zip
Updating to the current package of Qt Charts (from the commercial version)
Qt (or Digia? or how was the company-owning-Qt called at that time?) released in 2014 the version 1.4 of their Charts add-on for Qt. It was available only for the commercial-license and had some distinc namespace-requirements. And was also quite bare-metal.
Further development lead to more opportunities regarding the emitted signals for the cursor-handling (pressed/released instead of just clicked, for instance) and it became part of the regular package for Qt.
## Advice for a CMake-based project ##
If you want to maintain and upgrade your legacy code, then:
- add “Charts” to your find_package:
1find_package(Qt5 COMPONENTS Widgets Charts REQUIRED) - change the namespace inside the CMakeLists from “former naming” to “Qt5::Charts”
- remove the dependency to the old package in the top-level CMakeLists.txt
- replace inside the h/cpp all occurences of “QtCommercialChart::” with “QtCharts::”
- replace inside the h/cpp all occurences of “QTCOMMERCIALCHART_USE_NAMESPACE” with “using namespace QtCharts;”
- update the installer-creator-script(s) to include the Qt5Charts.dll
Et voilà , it should build now.
Retrospective view at 2018
The first month of 2019 already passed. And we passed it with flying colors!
But let’s have a look at 2018 – a year full of challenges and success: I’ve worked full-time, organized and participated in advanced courses for Python and in Requirements Engineering (officially: IREB Requirements Engineering Foundation Level-approved) and pursued a new employment as software engineer.
And I wrote some software in my spare-time, as you can see in the graph for the public github-repositories. The gaps in the commits can be explained with the birth of my daughter and the time where I acquired the new job and moved nearly 900 km across the country. Yay! Nice personal projects were and are Cullendula and the Daily Coding Challenges, which I solve mostly with fully Unit-tested Python (3).
More new, hands-on knowledge was gained in the area of CMake and Qt-charts.
Well – 2018 was great. Let me make 2019 greater! 💪