Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions custom/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
message(STATUS "QGC: Adding Custom Plugin")

if(ANDROID)
set(CUSTOM_ANDROID_DIR "${CMAKE_SOURCE_DIR}/custom/android")
if(EXISTS "${CUSTOM_ANDROID_DIR}")
file(GLOB CUSTOM_ANDROID_FILES "${CUSTOM_ANDROID_DIR}/*")
if(CUSTOM_ANDROID_FILES)
message(STATUS "QGC: Custom Android package template found. Overlaying custom files...")
set(DEFAULT_ANDROID_DIR "${CMAKE_SOURCE_DIR}/android")
set(FINAL_ANDROID_DIR "${CMAKE_BINARY_DIR}/custom/android")
file(COPY "${DEFAULT_ANDROID_DIR}/." DESTINATION "${FINAL_ANDROID_DIR}")
file(COPY "${CUSTOM_ANDROID_DIR}/." DESTINATION "${FINAL_ANDROID_DIR}")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
"${DEFAULT_ANDROID_DIR}/"
"${CUSTOM_ANDROID_DIR}/"
)
set(QGC_ANDROID_PACKAGE_SOURCE_DIR "${FINAL_ANDROID_DIR}" CACHE PATH "Path to a custom Android package template" FORCE)
message(STATUS "QGC: Android package template path will be set to: ${QGC_ANDROID_PACKAGE_SOURCE_DIR}")
else()
message(STATUS "QGC: Custom Android package template empty. Using default.")
endif()
else()
message(STATUS "QGC: No custom Android package template found. Using default.")
endif()
endif()

# Our own, custom resources
list(APPEND CUSTOM_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/custom.qrc
)
set(QGC_RESOURCES ${QGC_RESOURCES} ${CUSTOM_RESOURCES} CACHE STRING "Paths to .qrc Resources" FORCE)

set(QML_IMPORT_PATH ${QML_IMPORT_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/res" CACHE STRING "Extra qml import paths" FORCE)

qt_add_library(CustomModule STATIC)

set_source_files_properties(res/Custom/Widgets/CustomArtificialHorizon.qml PROPERTIES QT_RESOURCE_ALIAS CustomArtificialHorizon.qml)
set_source_files_properties(res/Custom/Widgets/CustomAttitudeWidget.qml PROPERTIES QT_RESOURCE_ALIAS CustomAttitudeWidget.qml)
set_source_files_properties(res/Custom/Widgets/CustomIconButton.qml PROPERTIES QT_RESOURCE_ALIAS CustomIconButton.qml)
set_source_files_properties(res/Custom/Widgets/CustomOnOffSwitch.qml PROPERTIES QT_RESOURCE_ALIAS CustomOnOffSwitch.qml)
set_source_files_properties(res/Custom/Widgets/CustomQuickButton.qml PROPERTIES QT_RESOURCE_ALIAS CustomQuickButton.qml)
set_source_files_properties(res/Custom/Widgets/CustomSignalStrength.qml PROPERTIES QT_RESOURCE_ALIAS CustomSignalStrength.qml)
set_source_files_properties(res/Custom/Widgets/CustomToolBarButton.qml PROPERTIES QT_RESOURCE_ALIAS CustomToolBarButton.qml)
set_source_files_properties(res/Custom/Widgets/CustomVehicleButton.qml PROPERTIES QT_RESOURCE_ALIAS CustomVehicleButton.qml)

qt_add_qml_module(CustomModule
URI Custom.Widgets
VERSION 1.0
RESOURCE_PREFIX /qml
QML_FILES
res/Custom/Widgets/CustomArtificialHorizon.qml
res/Custom/Widgets/CustomAttitudeWidget.qml
res/Custom/Widgets/CustomIconButton.qml
res/Custom/Widgets/CustomOnOffSwitch.qml
res/Custom/Widgets/CustomQuickButton.qml
res/Custom/Widgets/CustomSignalStrength.qml
res/Custom/Widgets/CustomToolBarButton.qml
res/Custom/Widgets/CustomVehicleButton.qml
NO_PLUGIN
)

set(CUSTOM_LIBRARIES
CustomModule # Explicitly link QML module, needed by Qt 6.6.3, TODO: Remove when support for this version is dropped
CACHE INTERNAL "" FORCE
)

set(CUSTOM_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}/src
CACHE INTERNAL "" FORCE
)
Binary file added custom/README.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# QGroundControl Ground Control Station

## Custom Build Example

To build this sample custom version:

* Clean you build directory of any previous build
* Rename the directory from `custom-example` to `custom`
* Change to the `custom` directory
* Build QGC

![Custom Build Screenshot](README.jpg)

More details on what a custom build is and how to create your own can be found in the [QGC Dev Guide](https://dev.qgroundcontrol.com/en/custom_build/custom_build.html).

The main features of this example:

* Assumes an "Off The Shelf" purchased commercial vehicle. This means most vehicle setup is hidden from the user since they should mostly never need to adjust those things. They would be set up correctly by the vehicle producing company prior to sale.
* The above assumption cause the QGC UI to adjust and not show various things. Providing an even simpler experience to the user.
* The full experience continues to be available in "Advanced Mode".
* Brands the build with various custom images and custom color palette which matches corporate branding of the theoretical commercial company this build is for.
* Customizes portions of the interface such as you can see in the above screenshot which shows a custom instrument widget replacing the standard QGC ui.
* It also overrides various QGC Application settings to hide some settings the users shouldn't modify as well as adjusting defaults for others.
* The source code is fully commented to explain what and why it is doing things.

## Make installer on Windows

- get source code
```
git clone --recursive -j8 git@github.com:IOES-Lab/qgroundcontrol.git
cd qgroundcontrol
git submodule update --recursive
```

- Install QT (https://www.qt.io/download-qt-installer-oss)
- Under Qt -> Qt 6.8.3 select:
Windows: MSVC 2022 <arch> - where <arch> is the architecture of your machine
Select all Additional Libraries
Deselect QT Design Studio

- Install Additional Packages with `qgroundcontrol/tools/setup/install-depedencies-windows.ps1`

- Install Visual Studio (for the libraries)
- https://visualstudio.microsoft.com/downloads/
- Select `Desktop Development for C++`

- Launch QT Creator and open `CMakeLists.txt`
- Takes long time to finish configuring
- On project settings, Add `Release` along side with `Debug`

- Build with QT Creator and do run test

- On Terminal on QT Creator, run below to make windows installer
```
cd qgroundcontrol\build\Desktop_Qt_6_8_3_MSVC2022_64bit-Release
"C:\Qt\Tools\CMake_64\bin\cmake.exe" --install . --config Release
```
109 changes: 109 additions & 0 deletions custom/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mavlink.qgroundcontrol"
android:installLocation="auto"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:versionName="-- %%INSERT_VERSION_NAME%% --">

<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />

<!-- %%INSERT_PERMISSIONS -->

<uses-permission android:name="android.permission.USB_PERMISSION" />
<!-- Need MulticastLock to receive broadcast UDP packets -->
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!-- Needed to keep working while 'asleep' -->
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!-- %%INSERT_FEATURES -->

<uses-feature android:name="android.hardware.audio.output" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<uses-feature android:name="android.hardware.wifi" android:required="false" />

<application
android:name="org.qtproject.qt.android.bindings.QtApplication"
android:hardwareAccelerated="true"
android:label="Custom QGroundControl"
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:fullBackupOnly="false"
android:icon="@drawable/icon"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config">
<!-- android:theme="@style/AppTheme" -->

<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/" /> -->

<activity
android:name="org.mavlink.qgroundcontrol.QGCActivity"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:label="Custom QGroundControl"
android:launchMode="singleTop"
android:screenOrientation="sensorLandscape"
android:exported="true">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<!-- TODO: CMake configure xml file -->
<!-- %%QGC_INSERT_ACTIVITY_INTENT_FILTER -->
</intent-filter>

<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"
android:resource="@xml/device_filter" />

<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.arguments"
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
<meta-data
android:name="android.app.splash_screen_drawable"
android:resource="@drawable/splashscreen" />
<!-- <meta-data android:name="android.app.splash_screen_drawable_portrait" -->
<!-- <meta-data android:name="android.app.splash_screen_drawable_landscape" -->
<meta-data
android:name="android.app.splash_screen_sticky"
android:value="false" />
<!-- <meta-data android:name="android.app.background_running" -->
<!-- <meta-data android:name="android.app.trace_location" -->
</activity>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.qtprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/qtprovider_paths" />
</provider>

</application>

</manifest>
Binary file added custom/android/res/drawable-hdpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom/android/res/drawable-ldpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom/android/res/drawable-mdpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom/android/res/drawable-xhdpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom/android/res/drawable-xxhdpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom/android/res/drawable-xxxhdpi/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions custom/cmake/CustomOverrides.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
set(QGC_APP_NAME "QGroundControl-MUTEC" CACHE STRING "App Name" FORCE)
set(QGC_APP_COPYRIGHT "Copyright (c) 2026 MUTEC KMOU (powered by IOES Lab.). All rights reserved." CACHE STRING "Copyright" FORCE)
set(QGC_APP_DESCRIPTION "Open Source Ground Control App Modified and powered by IOES Lab. KMOU" CACHE STRING "Description" FORCE)
set(QGC_ORG_NAME "MUTEC-KMOU" CACHE STRING "Org Name" FORCE)
set(QGC_ORG_DOMAIN "www.kmou.ac.kr/mutec" CACHE STRING "Domain" FORCE)

set(QGC_MACOS_ICON_PATH "${CMAKE_SOURCE_DIR}/custom/res" CACHE PATH "MacOS Icon Path" FORCE)
set(QGC_APPIMAGE_ICON_PATH "${CMAKE_SOURCE_DIR}/custom/res/icons/custom_qgroundcontrol.png" CACHE FILEPATH "AppImage Icon Path" FORCE)

if(EXISTS ${CMAKE_SOURCE_DIR}/custom/deploy/windows/installheader.bmp)
set(QGC_WINDOWS_INSTALL_HEADER_PATH "${CMAKE_SOURCE_DIR}/custom/deploy/windows/installheader.bmp" CACHE FILEPATH "Windows Install Header Path" FORCE)
endif()

if(EXISTS ${CMAKE_SOURCE_DIR}/custom/deploy/windows/WindowsQGC.ico)
set(QGC_WINDOWS_ICON_PATH "${CMAKE_SOURCE_DIR}/custom/deploy/windows/WindowsQGC.ico" CACHE FILEPATH "Windows Icon Path" FORCE)
endif()

# Build a single flight stack by disabling APM support
set(QGC_DISABLE_APM_MAVLINK ON CACHE BOOL "Disable APM Dialect" FORCE)
set(QGC_DISABLE_APM_PLUGIN ON CACHE BOOL "Disable APM Plugin" FORCE)
set(QGC_DISABLE_APM_PLUGIN_FACTORY ON CACHE BOOL "Disable APM Plugin Factory" FORCE)

# We implement our own PX4 plugin factory
set(QGC_DISABLE_PX4_PLUGIN_FACTORY ON CACHE BOOL "Disable PX4 Plugin Factory" FORCE)
25 changes: 25 additions & 0 deletions custom/custom.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<RCC>
<qresource prefix="/custom/img">
<file alias="altitude.svg">res/Images/altitude.svg</file>
<file alias="attitude_crosshair.svg">res/Images/attitude_crosshair.svg</file>
<file alias="attitude_dial.svg">res/Images/attitude_dial.svg</file>
<file alias="attitude_pointer.svg">res/Images/attitude_pointer.svg</file>
<file alias="chronometer.svg">res/Images/chronometer.svg</file>
<file alias="compass_needle.svg">res/Images/compass_needle.svg</file>
<file alias="compass_pointer.svg">res/Images/compass_pointer.svg</file>
<file alias="distance.svg">res/Images/distance.svg</file>
<file alias="horizontal_speed.svg">res/Images/horizontal_speed.svg</file>
<file alias="microSD.svg">res/Images/microSD.svg</file>
<file alias="odometer.svg">res/Images/odometer.svg</file>
<file alias="vertical_speed.svg">res/Images/vertical_speed.svg</file>
<file alias="dronecode-black.svg">res/Images/dronecode-black.svg</file>
<file alias="dronecode-white.svg">res/Images/dronecode-white.svg</file>
</qresource>
<qresource prefix="/Custom/qml">
<file alias="QGroundControl/FlightDisplay/FlyViewCustomLayer.qml">src/FlyViewCustomLayer.qml</file>
</qresource>
<qresource prefix="/Custom/res">
<file alias="QGCLogoFull.svg">res/QGCLogoFull.svg</file>
<file alias="qgroundcontrol.ico">res/icons/custom_qgroundcontrol.ico</file>
</qresource>
</RCC>
Binary file added custom/deploy/windows/WindowsQGC.ico
Binary file not shown.
Binary file added custom/deploy/windows/installheader.bmp
Binary file not shown.
68 changes: 68 additions & 0 deletions custom/res/Custom/Widgets/CustomArtificialHorizon.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/****************************************************************************
*
* (c) 2009-2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
* @file
* @author Gus Grubba <gus@auterion.com>
*/

import QtQuick

Item {
id: root
property real rollAngle : 0
property real pitchAngle: 0
property color skyColor1: Qt.hsla(0.6, 1.0, 0.25)
property color skyColor2: Qt.hsla(0.6, 0.5, 0.55)
property color groundColor1: Qt.hsla(0.25, 0.5, 0.45)
property color groundColor2: Qt.hsla(0.25, 0.75, 0.25)

clip: true
anchors.fill: parent

property real angularScale: pitchAngle * root.height / 45

Item {
id: artificialHorizon
width: root.width * 4
height: root.height * 8
anchors.centerIn: parent
Rectangle {
id: sky
anchors.fill: parent
smooth: true
antialiasing: true
gradient: Gradient {
GradientStop { position: 0.25; color: root.skyColor1 }
GradientStop { position: 0.5; color: root.skyColor2 }
}
}
Rectangle {
id: ground
height: sky.height / 2
anchors {
left: sky.left;
right: sky.right;
bottom: sky.bottom
}
smooth: true
antialiasing: true
gradient: Gradient {
GradientStop { position: 0.0; color: root.groundColor1 }
GradientStop { position: 0.25; color: root.groundColor2 }
}
}
transform: [
Translate {
y: angularScale
},
Rotation {
origin.x: artificialHorizon.width / 2
origin.y: artificialHorizon.height / 2
angle: -rollAngle
}]
}
}
Loading
Loading