Skip to content

Wednesday, 19 January 2022

Plasma 5.24 Beta Review Day

When a new Plasma release enters Beta Phase, there are three weeks of intense testing, bugfixing and polishing.

During this time we need as many users and developers as possible to help with finding regressions, trying to reproduce incoming reports and generally being on top of as much as possible. The more users, workflows, use cases and hardware the tests are being run on greatly helps to cover a wide variety of the entire software stack.

In order to make this process more accessible, more systematic and hopefully more fun we have an official “Plasma Beta Review Day”

Who can take part?

Any user of plasma who is able and willing to install the latest beta or run a live ISO with our beta on it and wants to help.

When will it take place?

Thursday 20 January 2022 11:00UTC – 17:00UTC

Please see our Plasma Schedule in the Future releases section. Look for Beta Review Day.

Where will it be coordinated?

Join us in our Matrix chat room.

What will this consist of?

  • Introductions to Our Bugzilla Bugtracking System for people who want support for filing or triaging their first bugs
  • Being assigned a short list of bugs to validate or de-duplicate entries (for those more experienced)
  • Going through a defined list of all the new areas of Plasma to check for regressions
  • Developers being online: if Developers can get debug info for issues you have (we will help you with getting this info), they might be able identify and fix things in real time!

What should I prepare?

Ideally, get yourself set up with a beta of the latest Plasma. You can either use one of the Live-Images (without the need to install) or use packages provided by your distribution.

Disclaimer: As this is Beta Software, there is a (small) chance that you might encounter data loss if you’re installing the Beta version to your system. Make sure that you backup any data before installing. Users of the Live-Image are less likely to encounter data loss, but a backup of your data is still encouraged!

If you can’t use the Plasma beta on your own system, has provided us demo accounts, so you can try the beta inside your web browser. These accounts will be available in the shared notes in the web conference channel.

We hope to see you all soon!

We are happy to announce the release of Qt Creator 6.0.2!

Here at KDAB, we recently published a library called KDBindings, which aims to reimplement both Qt signals and slots and data binding in pure C++17.

To get an introduction to the KDBindings implementation of signals and slots, I recommend that you take a look at the KDBindings Getting Started Guide. It will give you an overview of what signals and slots are, as well as how our implementation of them is used. Alternatively, take a look at our introductory blog post.

On a basic level, signals model an event that might occur, like a command being issued or a variable being set. The signal will then “emit” the data associated with the event. This data is the signal’s “signature,” similar to a normal function signature. The signal can then be connected to any function with a matching signature. Such functions are referred to as “slots.” Whenever the signal is emitted, it will call all of these slots in an arbitrary order. They can then react to the event.

Because this mechanism is very well-suited to loosely couple different systems, we discovered that it is often useful to connect a signal to a slot, even if their signatures only partially match.

This is especially useful if the signature of the signal was not designed to explicitly work together with this particular slot, but rather to be generic. Signals might emit too much information, for example, when a slot is only interested in the occurrence of an event and not the data associated with it. On the other hand, a slot might require additional information that is not emitted by the signal but might be available when the slot is connected.

Commands and Titles

Let’s say we have a Signal<std::string, std::chrono::time_point<std::chrono::system_clock>> that is emitted every time the user issues a command. The std::string that is emitted contains the command issued and the time_point contains the timestamp of when the command was issued.

Now imagine that we want to update the title of our application so that it always displays the last issued command.

Ideally, we would want to be able to write something like this to make that happen:

class Window {
  void setTitle(const std::string& title) { ... }

// This Signal would be emitted every time the user issues a command
Signal<std::string, std::chrono::time_point<std::chrono::system_clock>> commandSignal; 

Window appWindow;
commandSignal.connect(&Window::setTitle, &appWindow);

Unfortunately, this won’t just work immediately. The connect function on the signal needs to do a lot of work here:

  • Window::setTitle is a member function, so it will need a this pointer to the object it should modify. In this case, this is &appWindow.
  • The function doesn’t have any use for the time_point emitted by the signal, only for the std::string. So, the signal must discard its second argument for this particular slot.

std::bind Function Pointer as a Workaround

The problem is that the default implementation of the connect function only takes a “std::function<void(std::string, std::chrono::time_point<std::chrono::system_clock>)>” as its argument.

A workaround for this could be the use of std::bind:

commandSignal.connect(std::bind(&Window::setTitle, &appWindow, std::placeholders::_1));

With this, we can provide our member function with the required pointer to our appWindow. Furthermore, std::bind returns an object that can be called with a basically unlimited number of arguments. Since we only used std::placeholders::_1, it will then discard all arguments except the first one. In our case, this would discard the time_point argument, which is exactly what we need.

In general, using std::bind would work. However, the previously shown API would be a lot nicer, as it hides the std::bind call and doesn’t require the use of any placeholders.

Let’s use template meta-programming to write a function that can generate the needed call to std::bind for us.

The bind_first Function

Our initial goal is to create a function that, similar to std::bind, can bind arguments to a function. However, for our use case, we only want to bind the first arguments and refrain from explicitly specifying the placeholders for every remaining argument.

For the general idea of how to solve this problem, I found a Github Gist that defines a function bind_first that almost does what we need. It can generate the needed placeholders for std::bind as well as forward the arguments to be bound.

The problem with this implementation is that it uses sizeof…(Args) to determine how many placeholders need to be generated. The Args variadic template argument is only available because it requires the function to be a non-const member function. So, this won’t work on just any function type. It won’t even work on const member functions.

However, if we assume there is a compile-time function get_arity that returns the arity (number of arguments) of a function, we can improve bind_first to accept any function type:

// We create a template struct that can be used instead of std::placeholders::_X. 
// Then we can use template-meta-programming to generate a sequence of placeholders. 
struct placeholder { 

// To be able to use our custom placeholder type, we can specialize std::is_placeholder. 
namespace std { 
    template<int N> 
    struct is_placeholder<placeholder<N>> 
        : integral_constant<int, N> { 
}; // namespace std
// This helper function can then bind a certain number of arguments "Args",
// and generate placeholders for the rest of the arguments, given an
// index_sequence of placeholder numbers.
// Note the +1 here, std::placeholders are 1-indexed and the 1 offset needs
// to be added to the 0-indexed index_sequence. 
template<typename Func, typename... Args, std::size_t... Is> 
auto bind_first_helper(std::index_sequence<Is...>, Func &&fun, Args... args) 
    return std::bind(
        placeholder<Is + 1>{}...
// The bind_first function then simply generates the index_sequence by
// subtracting the number of arguments the function "fun" takes, with
// the number of arguments Args, that are to be bound. 
    typename Func, 
    typename... Args, 
      Disallow any placeholder arguments, they would mess with the number
      and ordering of required and bound arguments, and are, for now, unsupported
    typename = std::enable_if_t<
auto bind_first(Func &&fun, Args &&...args) 
    return bind_first_helper(
        std::make_index_sequence<get_arity<Func>() - sizeof...(Args)>{},

// An example use:
QWindow window;
std::function<void(QString, std::chrono::time_point<std::chrono::system_clock>)> bound = 
    bind_first(&QWindow::setTitle, &window);

The get_arity Function

As noted earlier, the bind_first implementation assumes the existence of a get_arity function, which can determine the number of arguments of a function at compile time.

This function is, however, not part of standard C++. So, how does one go about implementing it?

To understand the basic idea behind how this can work, take a look at this Stackoverflow post:

In comparison to the StackOverflow answer, I chose to implement this using a constexpr function, as I find the interface clearer when called.

Without further ado, here’s the code:

// This is just a template struct necessary to overload the get_arity function by type. 
// C++ doesn't allow partial template function specialization, but we can overload it with our tagged type. 
template<typename T> 
struct TypeMarker { 
    constexpr TypeMarker() = default; 
// Base implementation of get_arity refers to specialized implementations for each 
// type of callable object by using the overload for it's specialized TypeMarker. 
template<typename T> 
constexpr size_t get_arity() 
    return get_arity(TypeMarker<std::decay_t<T>>{}); 

// The arity of a function pointer is simply it's number of arguments. 
template<typename Return, typename... Arguments> 
constexpr size_t get_arity(TypeMarker<Return (*)(Arguments...)>) 
    return sizeof...(Arguments);

template<typename Return, typename... Arguments> 
constexpr size_t get_arity(TypeMarker<Return (*)(Arguments...) noexcept>) 
    return sizeof...(Arguments); 

// The arity of a generic callable object is the arity of it's operator() - 1,
// as the "this" pointer is already known for such an object. 
// As lambdas are also just instances of an anonymous class, they must also implement 
// the operator() member function, so this also works for lambdas. 
template<typename T> 
constexpr size_t get_arity(TypeMarker<T>) 
    return get_arity(TypeMarker<decltype(&T::operator())>{}) - 1; 

// Syntactic sugar version of get_arity, allows passing any callable object
// to get_arity, instead of having to pass its decltype as a template argument.
template<typename T>
constexpr size_t get_arity(const T &)
    return get_arity<T>();

This code will work for free functions. It also works for arbitrary objects that implement the function call operator (operator()) by delegating to the get_arity function for that operator() member function. This also includes lambdas, as they must implement the function call operator as well.

Unfortunately, implementing get_arity for member functions is a bit more complicated.

An initial implementation might look like this:

template<typename Return, typename Class, typename... Arguments> 
constexpr size_t get_arity(TypeMarker<Return (Class::*)(Arguments...)>) 
    return sizeof...(Arguments) + 1; 

This works well for normal, plain member functions. What about const-correctness though?

We would need another overload for TypeMarker<Return (Class::*)(Arguments...) const>. Taking a look at the cppreference for function declarations reveals that we also need to take care of volatile, noexcept, as well as ref qualified (& and &&) member functions.

Unfortunately, I have not found any way to remove these specifiers from the function pointer types that are already available. But instantiating every combination isn’t too bad when we use a macro to do the heavy lifting for us:

// remember to add +1, the "this" pointer is an implicit argument 
#define KDBINDINGS_DEFINE_MEMBER_GET_ARITY(MODIFIERS)                                                   template<typename Return, typename Class, typename... Arguments> \
constexpr size_t get_arity(TypeMarker<Return (Class::*)(Arguments...) MODIFIERS>) \ 
{ \ 
    return sizeof...(Arguments) + 1; \ 




KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const noexcept) 
KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &noexcept) 
KDBINDINGS_DEFINE_MEMBER_GET_ARITY(volatile const &&noexcept)

So it’s a macro to generate template functions, which are pretty much macros themselves — that’s a lot of meta programming.

But in the end, we are successful. This code will work for (almost) any callable object. The current limits I can think of are overloaded functions as well as template functions. These have to be disambiguated, as it’s not clear which overload get_arity should refer to.

The definition of get_arity might especially come in handy in the future, if further metaprogramming with arbitrary callable objects is needed.

Putting It All Together

With our completed bind_first implementation, we can now define a new connect function for our signal:

// We use the enable_if_t here to disable this function if the argument is already 
// convertible to the correct std::function type 
template<typename Func, typename... FuncArgs> 
auto connect(Func &&slot, FuncArgs &&...args) const
    -> std::enable_if_t< 
              std::negation<std::is_convertible<Func, std::function<void(Args...)>>>, 
              /* Also enable this function if we want to bind at least one argument*/
              std::integral_constant<bool, sizeof...(FuncArgs)>>, 
    return connect(

And, finally, we have our desired API:

Signal<std::string, std::chrono::time_point<std::chrono::system_clock>> commandSignal; 

Window appWindow; 
commandSignal.connect(&Window::setTitle, &window);

As mentioned earlier, this also allows us to bind functions to a signal that need additional information.

Signal<std::string, std::chrono::time_point<std::chrono::system_clock>> commandSignal;

// Imagine we had a logging function that takes a logging level
// and a message to log.
void log(const LogLevel& level, const std::string& message);

// Connecting this function will now log the user command every time
// with the log level "Info".
commandSignal.connect(log, LogLevel::Info);

All of these features come together to make KDBindings signals very flexible in how they can connect to signals. That makes it very easy to couple together systems without having to make the signals and slots entirely line up first.


If you want to check out the complete code, take a look at the KDBindings source on Github.

The library also offers a lot more awesome features, like properties and data binding in pure C++17. It’s a header-only library that is easy to integrate and licensed with the very liberal MIT-License.

This, of course, also means that you’re free to use the code shown here however you like.

About KDAB

If you like this article and want to read similar material, 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 Loose Coupling with Signals & Slots appeared first on KDAB.

In my 2022 roadmap, I mentioned something called the “15-Minute Bug Initiative.” Today I’d like to flesh it out and request participation! This blog post is not only informational, but I really hope any developers reading along will get excited and decide to participate. 🙂

KDE software has historically been accused of being resource-intensive, ugly, and buggy. Over the years we’ve largely resolved the first two, but the issue of bugginess persists.

Have you ever had that experience where you’re introducing someone to a KDE Plasma system and to your horror, they run into multiple bugs within moments? These are the issues we need to fix first: those that can be easily encountered within 15 minutes of basic usage. They leave a bad taste in people’s mouths and provide the impression that the system is a house of cards. It’s time to remedy this final strategic weakness of KDE, starting with Plasma itself. So I’d like to present our initial list of bugs:

If you have any software development skills, working on these bugs is a super impactful way to make a difference with code!! Every fixed bug is a huge deal, and brings Plasma meaningfully closer to a position of true stability.

Likely-to-be-frequently-asked questions

1. What are the criteria for being a 15-minute bug?

It’s an inherently squishy thing, but I look for the following:

  1. Affects the default setup
  2. 100% reproducible
  3. Something basic doesn’t work (e.g. a button doesn’t do anything when clicked)
  4. Something basic looks visually broken
  5. Causes Plasma or the full session to crash
  6. Requires a reboot or terminal commands to fix
  7. The bug report has more than 5 duplicates

The more of those conditions apply, the more likely that any Plasma user will run into it quickly during normal usage, and the more I feel like it qualifies.

2. Who determines what gets to be a 15-minute bug?

KDE developers and bug triagers make the call.

3. I’m a developer or bug triager; how do I add a bug to this list?

Change its Priority to HI. If you don’t have permission to do this, ask sysadmins for “editbugs” permission over here:

4. I’m not a developer or a bug triager; how can I help?

You can go through the list and try to reproduce or confim the bugs, and do investigation into root causes and triggering factors for the ones where this isn’t already known. Those are important because a skilled developer can usually quickly fix a bug they can reproduce. But if they can’t, then they may never be able to. So if you can help developers reproduce bugs, that’s extremely valuable.

5. I’m experiencing this annoying issue that’s not on the list! Can you add it?

Maybe. Mention the 15-minute bug initiative in the bug report for it, and KDE’s bug triagers will see if it makes the cut.

6. Why are you only doing Plasma bugs right now?

Lack of resources. The list currently has almost 100 bugs, and I don’t anticipate that we’ll get it down to zero in a year. A lot of the issues there are quite challenging to fix. But if I’m wrong and we blaze through everything, then I’ll absolutely broaden the initiative to include first frameworks, and then apps! Stabilize all the things!

So that’s the 15-Minute Bug Initiative. Let’s get cracking and make Plasma rock solid in 2022!

New year, new revision of the digiKam Recipes book. It is a relatively modest update that features two new additions: how to upload photos to a remove machine via SSH directly from digiKam and how to access digiKam remotely via RDP. Oh, and there is a new colorful book cover. As always, all digiKam Recipes readers will receive the updated version of the book automatically and free of charge. The digiKam Recipes book is available from Google Play Store and Gumroad.

Tuesday, 18 January 2022

Krita 3 and later are compatible with G’MIC, an open-source digital image processing framework. This support is provided by G’MIC-Qt, a Qt-based frontend for G’MIC. Since its inception, G’MIC-Qt was shipped as a standalone, externally built executable that is an optional, runtime dependency of Krita.

Krita 5 changes the way G’MIC-Qt is consumed. In order to support CentOS and macOS, G’MIC-Qt has been converted into a dynamically loadable library that is a dependent of Krita.

This file reviews these changes, and how to package Krita accordingly.


We have chosen to ship G’MIC-Qt as a library because of two longstanding bugs.

The Krita host for G’MIC-Qt relies on QSharedMemory, i.e. a shared memory segment, on wich a pipe is instantiated to pass messages to and from the host app. Firstly, this approach made opening two simultaneous G’MIC-Qt instances (each paired to its own Krita instance) impossible 1. Secondly, it also forbade using G’MIC-Qt with Krita on CentOS, as well as macOS, because the former doesn’t support QSharedMemory 2, and the latter has a meager 4KB as the maximum shared segment size. While there’s no workaround (to our knowledge) in CentOS, the only workaround for macOS is to manipulate the maximum segment size via sysctl 3, which was already difficult pre-Mojave 4 and now, due to the significant security measures of recent macOS versions, is nothing short of a sysadmin task 5.

There were two approaches. One was to move to a mmap-d file, which is unpredictable to sync due to each canvas’s differing space requirements. The easiest, and the one we chose, was to move to a tighter coupled memory model– a dynamically loadable plugin, as shown in my proposal PR 6. This was rejected by the G’MIC developers because of the possibility of crashing the host app due to a G’MIC internal bug 78. This decision was later enacted as part of G’MIC contributing policies 9.

How did you fix it?

Due to the above, the only path forward was to fork G’MIC, which we did in Krita MR !581 10.

From a source code point of view, our fork is based on top of the latest version’s tarball. Each tarball’s contents are committed to the main branch of the amyspark/gmic GitHub repository 11. For every covered release, there is a branch that in turn overlays our own plugin implementation, along with additional fixes that ensure that G’MIC-Qt doesn’t attempt to overwrite the internal state of the host application; namely, QCoreApplication settings, widget styles, and the installed translators.

From a technical point of view, this library interfaces with Krita through a new, purpose specific library, kisqmicinterface. This library contains nothing more than the previous iteration of the communications system, but now exported through namesake APIs 12.

In short, we have reversed the dependency flow; while in Krita v4 and earlier G’MIC-Qt was a runtime dependency, in v5, it’s G’MIC-Qt that depends on Krita as a build and runtime dependency.

Getting the source code

The patched version’s tarballs are GPG signed and available at the Releases section of the GitHub repository 13. Alternatively, the tarballs (though not the signatures) are also mirrored at our dependencies stash at 14. The tarballs are signed with the GPG key which is available at my GitHub profile. Its fingerprint is 4894424D2412FEE5176732A3FC00108CFD9DBF1E.

Building Krita’s G’MIC-Qt library

After building Krita with your standard process, the CMake install process should have put in your lib folder:

[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/
[2022-01-09T16:21:32.589Z] -- Set runtime path of "/home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/" to "/home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu:/home/appimage/appimage-workspace/deps/usr/lib:/home/appimage/appimage-workspace/deps/usr/lib/x86_64-linux-gnu"
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/

It should also install these headers, as illustrated below:

  • kis_qmic_plugin_interface.h exports a G’MIC-alike launch entry point that the plugin will implement
  • kis_qmic_interface.h implements the G’MIC request-response APIs
  • kritaqmicinterface_export.h is the CMake auto-generated export decoration header
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kis_qmic_interface.h
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kis_qmic_plugin_interface.h
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kritaqmicinterface_export.h

The three headers, along with the libkritaqmicinterface.a archive library (if building for Windows under MinGW), comprise a krita-gmic-dev package that’ll be a build dependency of the new G’MIC-Qt plugin. Please note that is consumed by Krita and MUST NOT be placed inside this dev package.

Now, download the G’MIC-Qt tarball from one of the sources listed previously, and unpack it to an isolated directory. Then, you can build it with these lines (adjust them as described):

mkdir build
cmake -S ./gmic-$<the tarball's G'MIC version>-patched/gmic-qt \
      -B ./build \
      -DCMAKE_PREFIX_PATH=$<installation prefix of krita-gmic-dev> \
      -DCMAKE_INSTALL_PREFIX=$<installation prefix of krita itself> \ 
      -DENABLE_SYSTEM_GMIC=$<false if you don't want to use your system's G'MIC> \
cmake --build . --config $<your desired build type> --target install

The changes from a standard G’MIC build are:

  • the new GMIC_QT_HOST value, krita-plugin
  • the requirement for the krita-gmic-dev package to be available in CMAKE_PREFIX_PATH

This process is illustrated in any of our official build scripts for Windows 15 and for macOS/Linux 16. You can also check the 3rdparty_plugins section of our source tree 17 to see what other hardening we apply to the build.

Clipboard use on desktop platforms is ubiquitous. Most people use it without thinking. Copy, Paste, and Cut keyboard strokes are in-grained into muscle memory. 

We are very happy to announce that the Qt Developer Conference is back on track after having been postponed last September. Mark your calendars and save the date on 13th-15th June 2022. This will be our first in-person event since the pandemic started. Do not miss out on this wonderful experience!

We opened the ticket shop for the Qt DevCon again. Get your early-bird tickets for a temporarily reduced price here:

About the Qt Developer Conference

The Qt Developer Conference is a new addition to our catalogue of events, covering any aspect of software development with Qt: embedded, widgets, Qt Quick — you name it!  If you’re a developer building applications with Qt, this conference is for you. The event will take place at Radialsystem in Berlin, a beautiful, industrial event location at the banks of River Spree, not far away from Alexanderplatz and Berlin Ostbahnhof. On the agenda, there will be 1 day of training and 2 days of technical talks during the main event.

Here, you can find a full list of all the trainings that will be offered during the training day on 13th June:

For a list of talks and their speakers during the main event on 14th and 15th June, look here:

More information is available on the Qt Dev Con website, where you can also sign up to receive updates.

We are aware, that under the current circumstances even thinking to attend a live event seems a bit out of place. Nevertheless, we have chosen the date in June to minimize the odds of being affected by another Covid wave. Measures will be taken to make the event as safe as possible. Also, if we would have to cancel the event after all, tickets will be refunded.

We are looking forward to meeting you in Berlin! Stay safe!

The post Qt Developer Conference 2022, June 13th-15th, Berlin appeared first on KDAB.

KStars v3.5.7 is released on January 18th for Windows, MacOS, and Linux. This release includes a number of new features and bug fixes.

Mosaic Dragging

This feature is finally implemented in 3.5.7. Many assumed it existed before and were disappointed when they couldn't drag the mosaic panels around for finer adjustments.

Better MacOS support

Robert Lancaster worked vigilantly on improving KStars & INDI support for MacOS. This includes working on all the plumping required to get a daily build going in KDE Binary Farms using KDE's Craft technology so that MacOS users may finally have access to nightly builds.

Flat Darks

Capture Flat Darks with a click of a button. After adding your desired flat frames, simply click the Flat Dark generator icon to add Dark Flats to your sequence queue. When Ekos completes capture of the flat frames, it will use the same exposure time (per filter, if one exists) for the dark flats. 

Please note that for this to work, the flats must be captured in the same session. That is, you cannot partially capture flats, restart Ekos, and then proceed to capture dark flats.

Comets Visibility

Hy Murveit made comets more discoverable in KStars by displaying them on almost all zoom levels. You'll find a lot more comets to learn and discover about in this release.

Internal refactoring

Wolfgang Reissenberger introduced State Machine for Capture Control changes as part of Ekos Road map to separate state machines, command processors, and business logic into their own manageable silos. While this is not exactly a user-facing activity, it remains to be critical for Ekos stability and growth in the future.

Faster Dithering

Hy Murveit introduced a One Pulse Dither option in Ekos guide module with randomly generated pulse. This should reduce the time it takes to dither at the expense of verifying that the guide star moved by the specific number of pixels required. Experiment with this new option and let us know what you think!

More highlights

  • Wolfgang Reissenberger: Bugfix for capture guiding deviation recovery
  • Eric Dejouhanet: Mosaic Tool documentation update
  • Akarsh Simha: Fix bugs involving the reading and writing of user's notes (logs) on objects
  • Wolfgang Reissenberger: Deactivating meridian flip while PAA is running
  • Paweł Pleskaczyński: change guide graph style to line graph
  • Wolfgang Reissenberger: Display FITS viewer only when option is set
  • Jasem Mutlaq: Compressed FITS image with fpack are now properly loaded in KStars when they are sent as buffer
  • Antonio Escriban: Standardized dialog for guider 'Control Parameters' &'Other Settings'
  • Hy Murveit: Display comets at almost all zooms, default names now 1AU.
  • Jasem Mutlaq: Extend properties that can be saved in a sequence file beyond just numbers

Many of you have been raising the question: when will Qt provide a full framework to monetize my Qt-based cross-platform application, implementing an advertising campaign directly on my user interface?