Skip to content

Migrating to AMC2

This page is the single reference for porting a plugin from AMC1 (the Qt 5 app, deprecated) to AMC2 (the Qt 6 app). It applies regardless of which docs version you are reading.

Who this page is for

This page is only for developers who already have a plugin that worked on AMC1 and want to make it run on AMC2. If you are writing a new plugin, ignore this page — start from the rest of these docs, which already describe the AMC2 (Qt 6) format.

Why migrate

AMC2 runs on Qt 6. Qt 6 changed how QML imports, asset URLs, and signal-handler arguments work, so a v1 plugin will not load correctly under AMC2 without changes.

AMC reads the apiVersion field in plugin.json to decide compatibility. AMC2 requires apiVersion 2.0 or newer, comparing only the major component. A plugin with a missing, unparseable, or below-2 apiVersion is treated as incompatible: it is copied into the Plugins folder but not loaded, shows an "Incompatible" badge in Settings → Plugins, and triggers a one-time warning dialog linking back to this guide.

So an old v1 plugin (which has no apiVersion, or used the legacy version field) will not load on AMC2 until you add "apiVersion": "2.0" and make the Qt 6 code changes below.

You can compare a full before/after for every example plugin:

Migration checklist

1. Declare apiVersion in plugin.json

{
    "name": "MyPlugin",
    "type": "qml",
    "apiVersion": "2.0",
    ...
}

If your old manifest used a version field for this purpose, rename it to apiVersion.

2. Remove version numbers from QML imports

Qt 6 uses versionless imports.

- import QtQuick 2.15
- import QtQuick.Controls 2.15
- import QGroundControl.Controls 1.0
+ import QtQuick
+ import QtQuick.Controls
+ import QGroundControl.Controls

3. Replace Qt.labs.settings with QtCore

The Settings {} type moved to the QtCore module in Qt 6.

- import Qt.labs.settings 1.0
+ import QtCore

4. Drop modules removed or restructured in Qt 6

Some modules no longer exist (or are not used) under Qt 6 — for example QtQuick.Extras 1.4 and QtQuick.Controls.Styles 1.4. Remove them and replace any types they provided with current equivalents. See the converted MFD imports (MFDUpperFlyView.qml) for a worked example.

5. Wrap local asset URLs in Qt.resolvedUrl(...)

Relative paths to bundled assets must be resolved against the QML file's location.

- imageSource: "../img/hello.svg"
+ imageSource: Qt.resolvedUrl("../img/hello.svg")

This applies to any local asset property (imageSource, icon.source, source, …). Absolute qrc: or /images/... paths are not affected.

6. Name implicit signal-handler arguments

Qt 6 no longer injects implicit arguments (like index) into signal handlers. Name them explicitly with an arrow function.

- onActivated: {
-     const newVal = currentIndex
-     ...
- }
+ onActivated: (index) => {
+     const newVal = index
+     ...
+ }

Shadowing gotcha

The handler's parameter can shadow an outer variable of the same name. In the MFD settings list, the delegate already had an outer index, so naming the handler param index would shadow it — the fix was to reference the delegate's index under a distinct name (mfdIndex) inside the converted handler. Watch for this when a delegate and a handler both want index.

Before / after example

The smallest example is FlyViewTool's tool-strip button. Full files: v1 · v2.

v1 (AMC1 / Qt 5):

import QtQuick 2.15
import QtQuick.Controls 2.15
import QGroundControl.Controls   1.0
import QGroundControl.Palette    1.0
import QGroundControl.ScreenTools 1.0

QGCHoverButton {
    imageSource: "../img/hello.svg"
    // ...
}

v2 (AMC2 / Qt 6):

import QtQuick
import QtQuick.Controls
import QGroundControl.Controls
import QGroundControl.Palette
import QGroundControl.ScreenTools

QGCHoverButton {
    imageSource: Qt.resolvedUrl("../img/hello.svg")
    // ...
}

After migrating

Rebuild and deploy the plugin as usual (see Building & Deploying). Load it in AMC2 and confirm it appears and behaves correctly.