diff --git a/QGCCommon.pri b/QGCCommon.pri index bf0a81b612e9bdea6cbef887b3e3b5a53be9432e..345aa8e076c27a5c739565b98bfb8e021b987e15 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -30,12 +30,18 @@ linux { CONFIG += LinuxBuild DEFINES += __STDC_LIMIT_MACROS __rasp_pi2__ } else : android-g++ { - message("Android build") CONFIG += AndroidBuild MobileBuild DEFINES += __android__ DEFINES += __STDC_LIMIT_MACROS DEFINES += QGC_ENABLE_BLUETOOTH target.path = $$DESTDIR + equals(ANDROID_TARGET_ARCH, x86) { + CONFIG += Androidx86Build + DEFINES += __androidx86__ + message("Android x86 build") + } else { + message("Android Arm build") + } } else { error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported") } @@ -54,11 +60,11 @@ linux { DEFINES += __macos__ CONFIG += x86_64 CONFIG -= x86 -equals(QT_MAJOR_VERSION, 5) | greaterThan(QT_MINOR_VERSION, 5) { - QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 -} else { - QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.6 -} + equals(QT_MAJOR_VERSION, 5) | greaterThan(QT_MINOR_VERSION, 5) { + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 + } else { + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.6 + } QMAKE_MAC_SDK = macosx10.11 QMAKE_CXXFLAGS += -fvisibility=hidden } else { diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 320cc7595404cac7e22f70c152c3e09f47c7f884..3d84270e147018eba19d923005ef78c6e70289b8 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -46,6 +46,15 @@ contains (DEFINES, QGC_DISABLE_BLUETOOTH) { DEFINES += QGC_ENABLE_BLUETOOTH } +# USB Camera and UVC Video Sources +contains (DEFINES, QGC_DISABLE_UVC) { + message("Skipping support for UVC devices (manual override from command line)") + DEFINES -= QGC_DISABLE_UVC +} else:exists(user_config.pri):infile(user_config.pri, DEFINES, QGC_DISABLE_UVC) { + message("Skipping support for UVC devices (manual override from user_config.pri)") + DEFINES -= QGC_DISABLE_UVC +} + LinuxBuild { CONFIG += link_pkgconfig } diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 8f9bbf04e8d72dcd9f3c908c02a2dd0dfe60c393..79e71888df79f1dc375f2eadbef137b2e5f25d37 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -15,6 +15,8 @@ src/ViewWidgets/LogDownload.qml src/VehicleSetup/FirmwareUpgrade.qml src/FlightDisplay/FlightDisplayView.qml + src/FlightDisplay/FlightDisplayViewUVC.qml + src/FlightDisplay/FlightDisplayViewDummy.qml src/AutoPilotPlugins/PX4/PX4FlightModes.qml src/AutoPilotPlugins/PX4/PX4AdvancedFlightModes.qml src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index b8be023456dbf4a733b55186cf4f3818082a3c11..d66428385e64a468db0ed0e7a899ce27c85b61e2 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -184,34 +184,17 @@ QGCView { } } ] - //-- UDP Video Streaming + //-- Video Streaming FlightDisplayViewVideo { anchors.fill: parent visible: QGroundControl.videoManager.isGStreamer } //-- UVC Video (USB Camera or Video Device) - Rectangle { - id: noVideo + Loader { + id: cameraLoader anchors.fill: parent - color: Qt.rgba(0,0,0,0.75) visible: !QGroundControl.videoManager.isGStreamer - Camera { - id: camera - deviceId: QGroundControl.videoManager.videoSourceID - captureMode: Camera.CaptureViewfinder - } - VideoOutput { - id: viewFinder - source: camera - anchors.fill: parent - visible: !QGroundControl.videoManager.isGStreamer - } - onVisibleChanged: { - if(visible) - camera.start() - else - camera.stop() - } + source: QGroundControl.videoManager.uvcEnabled ? "qrc:/qml/FlightDisplayViewUVC.qml" : "qrc:/qml/FlightDisplayViewDummy.qml" } } diff --git a/src/FlightDisplay/FlightDisplayViewDummy.qml b/src/FlightDisplay/FlightDisplayViewDummy.qml new file mode 100644 index 0000000000000000000000000000000000000000..286c321432ca8995364e01c30fb4ebff1b8f1cb6 --- /dev/null +++ b/src/FlightDisplay/FlightDisplayViewDummy.qml @@ -0,0 +1,15 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.5 + +Rectangle { + anchors.fill: parent + color: Qt.rgba(0,0,0,0.75) +} diff --git a/src/FlightDisplay/FlightDisplayViewUVC.qml b/src/FlightDisplay/FlightDisplayViewUVC.qml new file mode 100644 index 0000000000000000000000000000000000000000..f9608320557348951e12a495a9afae4d7f507d1e --- /dev/null +++ b/src/FlightDisplay/FlightDisplayViewUVC.qml @@ -0,0 +1,35 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtMultimedia 5.5 + +import QGroundControl 1.0 + +Rectangle { + anchors.fill: parent + color: Qt.rgba(0,0,0,0.75) + Camera { + id: camera + deviceId: QGroundControl.videoManager.videoSourceID + captureMode: Camera.CaptureViewfinder + } + VideoOutput { + source: camera + anchors.fill: parent + fillMode: VideoOutput.PreserveAspectCrop + visible: !QGroundControl.videoManager.isGStreamer + } + onVisibleChanged: { + if(visible) + camera.start() + else + camera.stop() + } +} diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 653f8ab2fb4faa83bf720cc478bc4bff5499f2ab..2b82520948f95229ad601ed5dd3d44af33c716e5 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -41,7 +41,6 @@ Item { display: QGroundControl.videoManager.videoSurface receiver: QGroundControl.videoManager.videoReceiver visible: QGroundControl.videoManager.videoRunning - runVideo: true /* TODO: Come up with a way to make this an option QGCAttitudeHUD { id: attitudeHUD diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 9739d4f08ca7d92334143ac15abe561fd066a290..39963bafc9aa3a5b4c9c2b69a8078557d0178c9e 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -19,51 +19,33 @@ #include "VideoManager.h" static const char* kVideoSourceKey = "VideoSource"; -static const char* kGStreamerSource = "UDP Video Stream"; +static const char* kVideoUDPPortKey = "VideoUDPPort"; +static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; +static const char* kUDPStream = "UDP Video Stream"; +static const char* kRTSPStream = "RTSP Video Stream"; QGC_LOGGING_CATEGORY(VideoManagerLog, "VideoManagerLog") //----------------------------------------------------------------------------- VideoManager::VideoManager(QGCApplication* app) : QGCTool(app) + , _videoSurface(NULL) + , _videoReceiver(NULL) , _videoRunning(false) + , _udpPort(5600) //-- Defalut Port 5600 == Solo UDP Port + , _init(false) { - /* - * This is the receiving end of an UDP RTP stream. The sender can be setup with this command: - * - * gst-launch-1.0 uvch264src initial-bitrate=1000000 average-bitrate=1000000 iframe-period=1000 name=src auto-start=true src.vidsrc ! \ - * video/x-h264,width=1280,height=720,framerate=24/1 ! h264parse ! rtph264pay ! udpsink host=192.168.1.9 port=5600 - * - * Where the main parameters are: - * - * uvch264src: Your h264 video source (the example above uses a Logitech C920 on an Raspberry PI 2+ or Odroid C1 - * host=192.168.1.9 This is the IP address of QGC. You can use Avahi/Zeroconf to find QGC using the "_qgroundcontrol._udp" service. - * - * Advanced settings (you should probably read the gstreamer documentation before changing these): - * - * initial-bitrate=1000000 average-bitrate=1000000 - * The bit rate to use. The greater, the better quality at the cost of higher bandwidth. - * - * width=1280,height=720,framerate=24/1 - * The video resolution and frame rate. This depends on the camera used. - * - * iframe-period=1000 - * Interval between iFrames. The greater the interval the lesser bandwidth at the cost of a longer time to recover from lost packets. - * - * Do not change anything else unless you know what you are doing. Any other change will require a matching change on the receiving end. - * - */ - _videoSurface = new VideoSurface; - _videoReceiver = new VideoReceiver(this); - _videoReceiver->setUri(QLatin1Literal("udp://0.0.0.0:5600")); // Port 5600=Solo UDP port, if you change it, you will break Solo video support + //-- Get saved settings + QSettings settings; + setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); + setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); + setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL + _init = true; #if defined(QGC_GST_STREAMING) - _videoReceiver->setVideoSink(_videoSurface->videoSink()); + _updateVideo(); connect(&_frameTimer, &QTimer::timeout, this, &VideoManager::_updateTimer); _frameTimer.start(1000); #endif - //-- Get saved video source - QSettings settings; - setVideoSource(settings.value(kVideoSourceKey, kGStreamerSource).toString()); } //----------------------------------------------------------------------------- @@ -96,12 +78,21 @@ bool VideoManager::isGStreamer() { #if defined(QGC_GST_STREAMING) - return _videoSource == kGStreamerSource; + return _videoSource == kUDPStream || _videoSource == kRTSPStream; #else return false; #endif } +//----------------------------------------------------------------------------- +#ifndef QGC_DISABLE_UVC +bool +VideoManager::uvcEnabled() +{ + return QCameraInfo::availableCameras().count() > 0; +} +#endif + //----------------------------------------------------------------------------- void VideoManager::setVideoSource(QString vSource) @@ -110,6 +101,7 @@ VideoManager::setVideoSource(QString vSource) QSettings settings; settings.setValue(kVideoSourceKey, vSource); emit videoSourceChanged(); +#ifndef QGC_DISABLE_UVC QList cameras = QCameraInfo::availableCameras(); foreach (const QCameraInfo &cameraInfo, cameras) { if(cameraInfo.description() == vSource) { @@ -119,8 +111,51 @@ VideoManager::setVideoSource(QString vSource) break; } } +#endif emit isGStreamerChanged(); qCDebug(VideoManagerLog) << "New Video Source:" << vSource; + /* + * Not working. Requires restart for now + if(isGStreamer()) + _updateVideo(); + */ + if(_videoReceiver) { + if(isGStreamer()) { + _videoReceiver->start(); + } else { + _videoReceiver->stop(); + } + } +} + +//----------------------------------------------------------------------------- +void +VideoManager::setUdpPort(quint16 port) +{ + _udpPort = port; + QSettings settings; + settings.setValue(kVideoUDPPortKey, port); + emit udpPortChanged(); + /* + * Not working. Requires restart for now + if(_videoSource == kUDPStream) + _updateVideo(); + */ +} + +//----------------------------------------------------------------------------- +void +VideoManager::setRtspURL(QString url) +{ + _rtspURL = url; + QSettings settings; + settings.setValue(kVideoRTSPUrlKey, url); + emit rtspURLChanged(); + /* + * Not working. Requires restart for now + if(_videoSource == kRTSPStream) + _updateVideo(); + */ } //----------------------------------------------------------------------------- @@ -129,20 +164,23 @@ VideoManager::videoSourceList() { _videoSourceList.clear(); #if defined(QGC_GST_STREAMING) - _videoSourceList.append(kGStreamerSource); + _videoSourceList.append(kUDPStream); + _videoSourceList.append(kRTSPStream); #endif +#ifndef QGC_DISABLE_UVC QList cameras = QCameraInfo::availableCameras(); foreach (const QCameraInfo &cameraInfo, cameras) { qCDebug(VideoManagerLog) << "UVC Video source ID:" << cameraInfo.deviceName() << " Name:" << cameraInfo.description(); _videoSourceList.append(cameraInfo.description()); } +#endif return _videoSourceList; } //----------------------------------------------------------------------------- -#if defined(QGC_GST_STREAMING) -void VideoManager::_updateTimer(void) +void VideoManager::_updateTimer() { +#if defined(QGC_GST_STREAMING) if(_videoRunning) { time_t elapsed = 0; @@ -150,7 +188,7 @@ void VideoManager::_updateTimer(void) { elapsed = time(0) - _videoSurface->lastFrame(); } - if(elapsed > 2) + if(elapsed > 2 && _videoSurface) { _videoRunning = false; _videoSurface->setLastFrame(0); @@ -167,5 +205,26 @@ void VideoManager::_updateTimer(void) } } } -} #endif +} + +//----------------------------------------------------------------------------- +void VideoManager::_updateVideo() +{ + if(_init) { + if(_videoReceiver) + delete _videoReceiver; + if(_videoSurface) + delete _videoSurface; + _videoSurface = new VideoSurface; + _videoReceiver = new VideoReceiver(this); + _videoReceiver->setVideoSink(_videoSurface->videoSink()); + #if defined(QGC_GST_STREAMING) + if(_videoSource == kUDPStream) + _videoReceiver->setUri(QStringLiteral("udp://0.0.0.0:%1").arg(_udpPort)); + else + _videoReceiver->setUri(_rtspURL); + #endif + _videoReceiver->start(); + } +} diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index 2a249f0e9c679ff2015f4346e9a00105e6130d99..2ff5cfac6e476c9eea7b5c16c2c5fa9408bc2406 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -35,6 +35,9 @@ public: Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) + Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) + Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) + Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) @@ -44,7 +47,18 @@ public: QString videoSourceID () { return _videoSourceID; } QString videoSource () { return _videoSource; } QStringList videoSourceList (); + quint16 udpPort () { return _udpPort; } + QString rtspURL () { return _rtspURL; } + +#if defined(QGC_DISABLE_UVC) + bool uvcEnabled () { return false; } +#else + bool uvcEnabled (); +#endif + void setVideoSource (QString vSource); + void setUdpPort (quint16 port); + void setRtspURL (QString url); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); @@ -56,9 +70,12 @@ signals: void videoSourceListChanged (); void isGStreamerChanged (); void videoSourceIDChanged (); + void udpPortChanged (); + void rtspURLChanged (); private: - void _updateTimer(void); + void _updateTimer (); + void _updateVideo (); private: VideoSurface* _videoSurface; @@ -70,6 +87,9 @@ private: QString _videoSource; QString _videoSourceID; QStringList _videoSourceList; + quint16 _udpPort; + QString _rtspURL; + bool _init; }; #endif diff --git a/src/FlightMap/QGCVideoBackground.qml b/src/FlightMap/QGCVideoBackground.qml index 842bc9f7dcd4111c3bf3dd5e5e8cd5e3bf2a022e..4a99d6da368f6c7ba0d680cd36e8291eb8bea5e4 100644 --- a/src/FlightMap/QGCVideoBackground.qml +++ b/src/FlightMap/QGCVideoBackground.qml @@ -22,20 +22,5 @@ VideoItem { id: videoBackground property var display property var receiver - property var runVideo: false surface: display - onRunVideoChanged: { - if(videoBackground.receiver && videoBackground.display) { - if(videoBackground.runVideo) { - videoBackground.receiver.start(); - } else { - videoBackground.receiver.stop(); - } - } - } - Component.onCompleted: { - if(videoBackground.runVideo && videoBackground.receiver) { - videoBackground.receiver.start(); - } - } } diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index b637700c23a83cdb93cfb77859757a9c9ab2ce30..9613124dfc26384b152a32b78e96b80bfc25900e 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -49,6 +49,21 @@ void VideoReceiver::setVideoSink(GstElement* sink) } #endif +static void newPadCB(GstElement * element, GstPad* pad, gpointer data) +{ + gchar *name; + name = gst_pad_get_name(pad); + g_print("A new pad %s was created\n", name); + GstCaps * p_caps = gst_pad_get_pad_template_caps (pad); + gchar * description = gst_caps_to_string(p_caps); + qDebug() << p_caps << ", " << description; + g_free(description); + GstElement * p_rtph264depay = GST_ELEMENT(data); + if(gst_element_link_pads(element, name, p_rtph264depay, "sink") == false) + qCritical() << "newPadCB : failed to link elements\n"; + g_free(name); +} + void VideoReceiver::start() { #if defined(QGC_GST_STREAMING) @@ -56,7 +71,6 @@ void VideoReceiver::start() qCritical() << "VideoReceiver::start() failed because URI is not specified"; return; } - if (_videoSink == NULL) { qCritical() << "VideoReceiver::start() failed because video sink is not set"; return; @@ -72,29 +86,44 @@ void VideoReceiver::start() GstElement* parser = NULL; GstElement* decoder = NULL; + bool isUdp = _uri.contains("udp://"); + do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_pipeline_new()"; break; } - if ((dataSource = gst_element_factory_make("udpsrc", "udp-source")) == NULL) { - qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('udpsrc')"; - break; + if(isUdp) { + dataSource = gst_element_factory_make("udpsrc", "udp-source"); + } else { + dataSource = gst_element_factory_make("rtspsrc", "rtsp-source"); } - if ((caps = gst_caps_from_string("application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264")) == NULL) { - qCritical() << "VideoReceiver::start() failed. Error with gst_caps_from_string()"; + if (!dataSource) { + qCritical() << "VideoReceiver::start() failed. Error with data source for gst_element_factory_make()"; break; } - g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); + if(isUdp) { + if ((caps = gst_caps_from_string("application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264")) == NULL) { + qCritical() << "VideoReceiver::start() failed. Error with gst_caps_from_string()"; + break; + } + g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); + } else { + g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, NULL); + } if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('rtph264depay')"; break; } + if(!isUdp) { + g_signal_connect(dataSource, "pad-added", G_CALLBACK(newPadCB), demux); + } + if ((parser = gst_element_factory_make("h264parse", "h264-parser")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('h264parse')"; break; @@ -107,7 +136,15 @@ void VideoReceiver::start() gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, decoder, _videoSink, NULL); - if (gst_element_link_many(dataSource, demux, parser, decoder, _videoSink, NULL) != (gboolean)TRUE) { + gboolean res = FALSE; + + if(isUdp) { + res = gst_element_link_many(dataSource, demux, parser, decoder, _videoSink, NULL); + } else { + res = gst_element_link_many(demux, parser, decoder, _videoSink, NULL); + } + + if (!res) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_link_many()"; break; } diff --git a/src/VideoStreaming/VideoStreaming.pri b/src/VideoStreaming/VideoStreaming.pri index 1b967c8f55f86715c5839988b904a1f4b3eb5aa6..03a3bc1bb0add0b350a230d152ada34936dcc370 100644 --- a/src/VideoStreaming/VideoStreaming.pri +++ b/src/VideoStreaming/VideoStreaming.pri @@ -73,8 +73,12 @@ LinuxBuild { QMAKE_POST_LINK += $$escape_expand(\\n) xcopy \"$$GST_ROOT_WIN\\lib\\gstreamer-1.0\\validate\\*.dll\" \"$$DESTDIR_WIN\\gstreamer-plugins\\validate\\\" /Y $$escape_expand(\\n) } } else:AndroidBuild { - #- gstreamer assumed to be installed in $$PWD/../../android/gstreamer-1.0-android-armv7-1.5.2 - GST_ROOT = $$PWD/../../gstreamer-1.0-android-armv7-1.5.2 + #- gstreamer assumed to be installed in $$PWD/../../android/gstreamer-1.0-android-armv7-1.5.2 (or x86) + Androidx86Build { + GST_ROOT = $$PWD/../../gstreamer-1.0-android-x86-1.5.2 + } else { + GST_ROOT = $$PWD/../../gstreamer-1.0-android-armv7-1.5.2 + } exists($$GST_ROOT) { QMAKE_CXXFLAGS += -pthread CONFIG += VideoEnabled diff --git a/src/VideoStreaming/VideoSurface.cc b/src/VideoStreaming/VideoSurface.cc index 7c153f5facc4bc6f3abc4b4cf1ff1e0e33fc02bd..77fb96ab988b51a1c5db77aeba67f535217df8c0 100644 --- a/src/VideoStreaming/VideoSurface.cc +++ b/src/VideoStreaming/VideoSurface.cc @@ -27,6 +27,7 @@ VideoSurface::VideoSurface(QObject *parent) #if defined(QGC_GST_STREAMING) , _data(new VideoSurfacePrivate) , _lastFrame(0) + , _refed(false) #endif { } @@ -34,7 +35,7 @@ VideoSurface::VideoSurface(QObject *parent) VideoSurface::~VideoSurface() { #if defined(QGC_GST_STREAMING) - if (_data->videoSink != NULL) { + if (!_refed && _data->videoSink != NULL) { gst_element_set_state(_data->videoSink, GST_STATE_NULL); } delete _data; @@ -42,7 +43,7 @@ VideoSurface::~VideoSurface() } #if defined(QGC_GST_STREAMING) -GstElement* VideoSurface::videoSink() const +GstElement* VideoSurface::videoSink() { if (_data->videoSink == NULL) { if ((_data->videoSink = gst_element_factory_make("qtquick2videosink", NULL)) == NULL) { @@ -50,6 +51,7 @@ GstElement* VideoSurface::videoSink() const return NULL; } g_signal_connect(_data->videoSink, "update", G_CALLBACK(onUpdateThunk), (void* )this); + _refed = true; } return _data->videoSink; } diff --git a/src/VideoStreaming/VideoSurface.h b/src/VideoStreaming/VideoSurface.h index 964f7f497e9f1fc96a8f1c5a7f3a93586101061f..1f84e7966442ad53c0a63ccda8ee7c56b6b65949 100644 --- a/src/VideoStreaming/VideoSurface.h +++ b/src/VideoStreaming/VideoSurface.h @@ -40,7 +40,7 @@ public: * is called. The surface will always keep a reference to this element. */ #if defined(QGC_GST_STREAMING) - GstElement* videoSink() const; + GstElement* videoSink(); time_t lastFrame() { return _lastFrame; } void setLastFrame(time_t t) { _lastFrame = t; } #endif @@ -55,7 +55,8 @@ private: friend class VideoItem; #if defined(QGC_GST_STREAMING) VideoSurfacePrivate * const _data; - time_t _lastFrame; + time_t _lastFrame; + bool _refed; #endif }; diff --git a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp index 77bb1e90b04ee97c23b49d403180e37f40f93e9b..132f60764980d8474b634d58f22042cf52ae3c11 100644 --- a/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp +++ b/src/VideoStreaming/gstqtvideosink/gstqtvideosinkplugin.cpp @@ -27,7 +27,7 @@ #include "gstqwidgetvideosink.h" #define PACKAGE "mini-qt-gstreamer" -#define PACKAGE_NAME "QgcQtGStreamer" +#define PACKAGE_NAME "QgcQtGStreamer" #define PACKAGE_ORIGIN "http://gstreamer.freedesktop.org/" #define PACKAGE_VERSION "1.2.0" @@ -44,7 +44,7 @@ static gboolean plugin_init(GstPlugin *plugin) G_STRINGIFY(QGC_VIDEOSINK_PLUGIN), 0, "Debug category for GstQtVideoSink"); - if(!gst_element_register(plugin, G_STRINGIFY(QGC_VIDEOSINK_PLUGIN), + if(!gst_element_register(plugin, G_STRINGIFY(QGC_VIDEOSINK_PLUGIN), GST_RANK_NONE, GST_TYPE_QT_VIDEO_SINK)) { GST_ERROR("Failed to register " G_STRINGIFY(QGC_VIDEOSINK_PLUGIN)); return FALSE; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 1044e9155c1be915b87bcbeff11436400f13be19..1a119646a07f8fefdfb5c4edd695162073ecd377 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -31,493 +31,546 @@ QGCView { anchors.margins: ScreenTools.defaultFontPixelWidth property Fact _percentRemainingAnnounce: QGroundControl.batteryPercentRemainingAnnounce - property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 20 + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 + property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 QGCPalette { id: qgcPal } QGCViewPanel { id: panel anchors.fill: parent - QGCFlickable { clip: true anchors.fill: parent contentHeight: settingsColumn.height contentWidth: settingsColumn.width - Column { id: settingsColumn + width: qgcView.width + spacing: ScreenTools.defaultFontPixelHeight * 0.5 anchors.margins: ScreenTools.defaultFontPixelWidth - spacing: ScreenTools.defaultFontPixelHeight / 2 - //----------------------------------------------------------------- - //-- Base UI Font Point Size - Row { - spacing: ScreenTools.defaultFontPixelWidth - + //-- Units + Item { + width: qgcView.width * 0.8 + height: unitLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCLabel { - id: baseFontLabel - text: qsTr("Base UI font size:") - anchors.verticalCenter: parent.verticalCenter + id: unitLabel + text: qsTr("Units (Requires Restart)") + font.family: ScreenTools.demiboldFontFamily } - - Row { - id: baseFontRow - spacing: ScreenTools.defaultFontPixelWidth / 2 - anchors.verticalCenter: parent.verticalCenter - - QGCButton { - id: decrementButton - width: height - height: baseFontEdit.height - text: "-" - - onClicked: { - if(ScreenTools.defaultFontPointSize > 6) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize - 1 - } + } + Rectangle { + height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: unitsCol + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: distanceUnitsCombo.baseline + text: qsTr("Distance:") + } + FactComboBox { + id: distanceUnitsCombo + width: _editFieldWidth + fact: QGroundControl.distanceUnits + indexModel: false } } - - QGCTextField { - id: baseFontEdit - width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) - text: QGroundControl.baseFontPointSize - showUnits: true - unitsLabel: "pt" - maximumLength: 6 - validator: DoubleValidator {bottom: 6.0; top: 48.0; decimals: 2;} - - onEditingFinished: { - var point = parseFloat(text) - if(point >= 6.0 && point <= 48.0) - QGroundControl.baseFontPointSize = point; + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: areaUnitsCombo.baseline + text: qsTr("Area:") + } + FactComboBox { + id: areaUnitsCombo + width: _editFieldWidth + fact: QGroundControl.areaUnits + indexModel: false } } - - QGCButton { - width: height - height: baseFontEdit.height - text: "+" - - onClicked: { - if(ScreenTools.defaultFontPointSize < 49) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize + 1 - } + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: speedUnitsCombo.baseline + text: qsTr("Speed:") + } + FactComboBox { + id: speedUnitsCombo + width: _editFieldWidth + fact: QGroundControl.speedUnits + indexModel: false } } } - - QGCLabel { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("(requires app restart)") - } } - //----------------------------------------------------------------- - //-- Units - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - width: baseFontLabel.width - anchors.baseline: distanceUnitsCombo.baseline - text: qsTr("Distance units:") - } - - FactComboBox { - id: distanceUnitsCombo - width: _editFieldWidth - fact: QGroundControl.distanceUnits - indexModel: false - } - - QGCLabel { - anchors.baseline: distanceUnitsCombo.baseline - text: qsTr("(requires app restart)") - } - - } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - width: baseFontLabel.width - anchors.baseline: areaUnitsCombo.baseline - text: qsTr("Area units:") - } - - FactComboBox { - id: areaUnitsCombo - width: _editFieldWidth - fact: QGroundControl.areaUnits - indexModel: false - } - - QGCLabel { - anchors.baseline: areaUnitsCombo.baseline - text: qsTr("(requires app restart)") - } - - } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - width: baseFontLabel.width - anchors.baseline: speedUnitsCombo.baseline - text: qsTr("Speed units:") - } - - FactComboBox { - id: speedUnitsCombo - width: _editFieldWidth - fact: QGroundControl.speedUnits - indexModel: false - } - - QGCLabel { - anchors.baseline: speedUnitsCombo.baseline - text: qsTr("(requires app restart)") - } - } - + //-- Video Source Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - - //----------------------------------------------------------------- - //-- Audio preferences - QGCCheckBox { - text: qsTr("Mute all audio output") - checked: QGroundControl.isAudioMuted - onClicked: { - QGroundControl.isAudioMuted = checked - } - } - //----------------------------------------------------------------- - //-- Prompt Save Log - QGCCheckBox { - id: promptSaveLog - text: qsTr("Prompt to save Flight Data Log after each flight") - checked: QGroundControl.isSaveLogPrompt - visible: !ScreenTools.isMobile - onClicked: { - QGroundControl.isSaveLogPrompt = checked - } - } - //----------------------------------------------------------------- - //-- Prompt Save even if not armed - QGCCheckBox { - text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - checked: QGroundControl.isSaveLogPromptNotArmed - visible: !ScreenTools.isMobile - enabled: promptSaveLog.checked - onClicked: { - QGroundControl.isSaveLogPromptNotArmed = checked + width: qgcView.width * 0.8 + height: videoLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: videoLabel + text: qsTr("Video (Requires Restart)") + font.family: ScreenTools.demiboldFontFamily } } - //----------------------------------------------------------------- - //-- Clear settings - QGCCheckBox { - id: clearCheck - text: qsTr("Clear all settings on next start") - checked: false - onClicked: { - checked ? clearDialog.visible = true : QGroundControl.clearDeleteAllSettingsNextBoot() - } - MessageDialog { - id: clearDialog - visible: false - icon: StandardIcon.Warning - standardButtons: StandardButton.Yes | StandardButton.No - title: qsTr("Clear Settings") - text: qsTr("All saved settings will be reset the next time you start QGroundControl. Is this really what you want?") - onYes: { - QGroundControl.deleteAllSettingsNextBoot() - clearDialog.visible = false + Rectangle { + height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: videoCol + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + anchors.baseline: videoSource.baseline + text: qsTr("Video Source:") + width: _labelWidth + } + QGCComboBox { + id: videoSource + width: _editFieldWidth + model: QGroundControl.videoManager.videoSourceList + Component.onCompleted: { + var index = videoSource.find(QGroundControl.videoManager.videoSource) + if (index >= 0) { + videoSource.currentIndex = index + } + } + onActivated: { + if (index != -1) { + currentIndex = index + QGroundControl.videoManager.videoSource = model[index] + } + } + } } - onNo: { - clearCheck.checked = false - clearDialog.visible = false + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 0 + QGCLabel { + anchors.baseline: udpField.baseline + text: qsTr("UDP Port:") + width: _labelWidth + } + QGCTextField { + id: udpField + width: _editFieldWidth + text: QGroundControl.videoManager.udpPort + validator: IntValidator {bottom: 1024; top: 65535;} + inputMethodHints: Qt.ImhDigitsOnly + onEditingFinished: { + QGroundControl.videoManager.udpPort = parseInt(text) + } + } } - } - } - //----------------------------------------------------------------- - //-- Battery talker - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCCheckBox { - id: announcePercentCheckbox - anchors.baseline: announcePercent.baseline - text: qsTr("Announce battery lower than:") - checked: _percentRemainingAnnounce.value != 0 - - onClicked: { - if (checked) { - _percentRemainingAnnounce.value = _percentRemainingAnnounce.defaultValueString - } else { - _percentRemainingAnnounce.value = 0 + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 1 + QGCLabel { + anchors.baseline: rtspField.baseline + text: qsTr("RTSP URL:") + width: _labelWidth + } + QGCTextField { + id: rtspField + width: _editFieldWidth + text: QGroundControl.videoManager.rtspURL + onEditingFinished: { + QGroundControl.videoManager.rtspURL = text + } } } } - - FactTextField { - id: announcePercent - fact: _percentRemainingAnnounce - enabled: announcePercentCheckbox.checked - } - } - - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width } - //----------------------------------------------------------------- - //-- Video Source - Row { - spacing: ScreenTools.defaultFontPixelWidth + //-- Offline mission editing + Item { + width: qgcView.width * 0.8 + height: offlineLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCLabel { - anchors.baseline: videoSource.baseline - text: qsTr("Video Source:") + id: offlineLabel + text: qsTr("Offline Mission Editing") + font.family: ScreenTools.demiboldFontFamily } - QGCComboBox { - id: videoSource - width: _editFieldWidth - model: QGroundControl.videoManager.videoSourceList - Component.onCompleted: { - var index = videoSource.find(QGroundControl.videoManager.videoSource) - if (index >= 0) { - videoSource.currentIndex = index + } + Rectangle { + height: offlineCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: offlineCol + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: qsTr("Firmware:") + width: _labelWidth + anchors.baseline: offlineTypeCombo.baseline + } + FactComboBox { + id: offlineTypeCombo + width: _editFieldWidth + fact: QGroundControl.offlineEditingFirmwareType + indexModel: false } } - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.videoManager.videoSource = model[index] + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: qsTr("Vehicle:") + width: _labelWidth + anchors.baseline: offlineVehicleCombo.baseline + } + FactComboBox { + id: offlineVehicleCombo + width: _editFieldWidth + fact: QGroundControl.offlineEditingVehicleType + indexModel: false } } - } - } - //----------------------------------------------------------------- - //-- Map Providers - Row { - - /* - TODO: Map settings should come from QGroundControl.mapEngineManager. What is currently in - QGroundControl.flightMapSettings should be moved there so all map related funtions are in - one place. - */ - - spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.flightMapSettings.googleMapEnabled - - QGCLabel { - id: mapProvidersLabel - anchors.baseline: mapProviders.baseline - text: qsTr("Map Provider:") - } - - QGCComboBox { - id: mapProviders - width: _editFieldWidth - model: QGroundControl.flightMapSettings.mapProviders - Component.onCompleted: { - var index = mapProviders.find(QGroundControl.flightMapSettings.mapProvider) - if (index < 0) { - console.warn(qsTr("Active map provider not in combobox"), QGroundControl.flightMapSettings.mapProvider) - } else { - mapProviders.currentIndex = index + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: offlineVehicleCombo.currentText != "Multicopter" + QGCLabel { + text: qsTr("Cruise speed:") + width: _labelWidth + anchors.baseline: cruiseSpeedField.baseline + } + FactTextField { + id: cruiseSpeedField + width: _editFieldWidth + fact: QGroundControl.offlineEditingCruiseSpeed + enabled: true } } - onActivated: { - if (index != -1) { - currentIndex = index - console.log(qsTr("New map provider: ") + model[index]) - QGroundControl.flightMapSettings.mapProvider = model[index] + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: offlineVehicleCombo.currentText != "Fixedwing" + QGCLabel { + id: hoverSpeedLabel + text: qsTr("Hover speed:") + width: _labelWidth + anchors.baseline: hoverSpeedField.baseline + } + FactTextField { + id: hoverSpeedField + width: _editFieldWidth + fact: QGroundControl.offlineEditingHoverSpeed + enabled: true } } } } //----------------------------------------------------------------- - //-- Palette Styles - Row { - spacing: ScreenTools.defaultFontPixelWidth - + //-- Miscelanous + Item { + width: qgcView.width * 0.8 + height: miscLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCLabel { - width: mapProvidersLabel.width - anchors.baseline: paletteCombo.baseline - text: qsTr("Style:") + id: miscLabel + text: qsTr("Miscelaneous") + font.family: ScreenTools.demiboldFontFamily } - - QGCComboBox { - id: paletteCombo - width: _editFieldWidth - model: [ qsTr("Indoor"), qsTr("Outdoor") ] - currentIndex: QGroundControl.isDarkStyle ? 0 : 1 - - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.isDarkStyle = index === 0 ? true : false + } + Rectangle { + height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: miscCol + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + //----------------------------------------------------------------- + //-- Base UI Font Point Size + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + id: baseFontLabel + text: qsTr("Base UI font size:") + anchors.verticalCenter: parent.verticalCenter + } + Row { + id: baseFontRow + spacing: ScreenTools.defaultFontPixelWidth / 2 + anchors.verticalCenter: parent.verticalCenter + QGCButton { + id: decrementButton + width: height + height: baseFontEdit.height + text: "-" + onClicked: { + if(ScreenTools.defaultFontPointSize > 6) { + QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize - 1 + } + } + } + QGCTextField { + id: baseFontEdit + width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) + text: QGroundControl.baseFontPointSize + showUnits: true + unitsLabel: "pt" + maximumLength: 6 + validator: DoubleValidator {bottom: 6.0; top: 48.0; decimals: 2;} + onEditingFinished: { + var point = parseFloat(text) + if(point >= 6.0 && point <= 48.0) + QGroundControl.baseFontPointSize = point; + } + } + QGCButton { + width: height + height: baseFontEdit.height + text: "+" + onClicked: { + if(ScreenTools.defaultFontPointSize < 49) { + QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize + 1 + } + } + } + } + QGCLabel { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("(Requires Restart)") + } + } + //----------------------------------------------------------------- + //-- Audio preferences + QGCCheckBox { + text: qsTr("Mute all audio output") + checked: QGroundControl.isAudioMuted + onClicked: { + QGroundControl.isAudioMuted = checked + } + } + //----------------------------------------------------------------- + //-- Prompt Save Log + QGCCheckBox { + id: promptSaveLog + text: qsTr("Prompt to save Flight Data Log after each flight") + checked: QGroundControl.isSaveLogPrompt + visible: !ScreenTools.isMobile + onClicked: { + QGroundControl.isSaveLogPrompt = checked + } + } + //----------------------------------------------------------------- + //-- Prompt Save even if not armed + QGCCheckBox { + text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") + checked: QGroundControl.isSaveLogPromptNotArmed + visible: !ScreenTools.isMobile + enabled: promptSaveLog.checked + onClicked: { + QGroundControl.isSaveLogPromptNotArmed = checked + } + } + //----------------------------------------------------------------- + //-- Clear settings + QGCCheckBox { + id: clearCheck + text: qsTr("Clear all settings on next start") + checked: false + onClicked: { + checked ? clearDialog.visible = true : QGroundControl.clearDeleteAllSettingsNextBoot() + } + MessageDialog { + id: clearDialog + visible: false + icon: StandardIcon.Warning + standardButtons: StandardButton.Yes | StandardButton.No + title: qsTr("Clear Settings") + text: qsTr("All saved settings will be reset the next time you start QGroundControl. Is this really what you want?") + onYes: { + QGroundControl.deleteAllSettingsNextBoot() + clearDialog.visible = false + } + onNo: { + clearCheck.checked = false + clearDialog.visible = false + } + } + } + //----------------------------------------------------------------- + //-- Battery talker + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCCheckBox { + id: announcePercentCheckbox + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Announce battery lower than:") + checked: _percentRemainingAnnounce.value != 0 + onClicked: { + if (checked) { + _percentRemainingAnnounce.value = _percentRemainingAnnounce.defaultValueString + } else { + _percentRemainingAnnounce.value = 0 + } + } + } + FactTextField { + id: announcePercent + fact: _percentRemainingAnnounce + enabled: announcePercentCheckbox.checked + anchors.verticalCenter: parent.verticalCenter + } + } + //----------------------------------------------------------------- + //-- Virtual joystick settings + QGCCheckBox { + text: qsTr("Virtual Joystick") + checked: QGroundControl.virtualTabletJoystick + onClicked: QGroundControl.virtualTabletJoystick = checked + } + //----------------------------------------------------------------- + //-- Map Providers + Row { + /* + TODO: Map settings should come from QGroundControl.mapEngineManager. What is currently in + QGroundControl.flightMapSettings should be moved there so all map related funtions are in + one place. + */ + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.flightMapSettings.googleMapEnabled + QGCLabel { + id: mapProvidersLabel + anchors.baseline: mapProviders.baseline + text: qsTr("Map Provider:") + width: _labelWidth + } + QGCComboBox { + id: mapProviders + width: _editFieldWidth + model: QGroundControl.flightMapSettings.mapProviders + Component.onCompleted: { + var index = mapProviders.find(QGroundControl.flightMapSettings.mapProvider) + if (index < 0) { + console.warn(qsTr("Active map provider not in combobox"), QGroundControl.flightMapSettings.mapProvider) + } else { + mapProviders.currentIndex = index + } + } + onActivated: { + if (index != -1) { + currentIndex = index + console.log(qsTr("New map provider: ") + model[index]) + QGroundControl.flightMapSettings.mapProvider = model[index] + } + } + } + } + //----------------------------------------------------------------- + //-- Palette Styles + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + anchors.baseline: paletteCombo.baseline + text: qsTr("Style:") + width: _labelWidth + } + QGCComboBox { + id: paletteCombo + width: _editFieldWidth + model: [ qsTr("Indoor"), qsTr("Outdoor") ] + currentIndex: QGroundControl.isDarkStyle ? 0 : 1 + onActivated: { + if (index != -1) { + currentIndex = index + QGroundControl.isDarkStyle = index === 0 ? true : false + } + } } } } } - - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - //----------------------------------------------------------------- //-- Autoconnect settings - QGCLabel { text: "Autoconnect to the following devices:" } - - Row { - spacing: ScreenTools.defaultFontPixelWidth * 2 - - QGCCheckBox { - text: qsTr("Pixhawk") - visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnectPixhawk - onClicked: QGroundControl.linkManager.autoconnectPixhawk = checked - } - - QGCCheckBox { - text: qsTr("SiK Radio") - visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnect3DRRadio - onClicked: QGroundControl.linkManager.autoconnect3DRRadio = checked - } - - QGCCheckBox { - text: qsTr("PX4 Flow") - visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnectPX4Flow - onClicked: QGroundControl.linkManager.autoconnectPX4Flow = checked - } - - QGCCheckBox { - text: qsTr("UDP") - checked: QGroundControl.linkManager.autoconnectUDP - onClicked: QGroundControl.linkManager.autoconnectUDP = checked - } - - QGCCheckBox { - text: qsTr("RTK GPS") - checked: QGroundControl.linkManager.autoconnectRTKGPS - onClicked: QGroundControl.linkManager.autoconnectRTKGPS = checked - } - } - - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - - //----------------------------------------------------------------- - //-- Virtual joystick settings - QGCCheckBox { - text: qsTr("Virtual Joystick") - checked: QGroundControl.virtualTabletJoystick - onClicked: QGroundControl.virtualTabletJoystick = checked - } - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - - //----------------------------------------------------------------- - //-- Offline mission editing settings - - QGCLabel { text: "Offline mission editing" } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - text: qsTr("Firmware:") - width: hoverSpeedLabel.width - anchors.baseline: offlineTypeCombo.baseline - } - - FactComboBox { - id: offlineTypeCombo - width: ScreenTools.defaultFontPixelWidth * 18 - fact: QGroundControl.offlineEditingFirmwareType - indexModel: false - } - } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - + width: qgcView.width * 0.8 + height: autoConnectLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCLabel { - text: qsTr("Vehicle:") - width: hoverSpeedLabel.width - anchors.baseline: offlineVehicleCombo.baseline - } - - FactComboBox { - id: offlineVehicleCombo - width: offlineTypeCombo.width - fact: QGroundControl.offlineEditingVehicleType - indexModel: false + id: autoConnectLabel + text: qsTr("Autoconnect to the following devices:") + font.family: ScreenTools.demiboldFontFamily } } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - visible: offlineVehicleCombo.currentText != "Multicopter" - - QGCLabel { - text: qsTr("Cruise speed:") - width: hoverSpeedLabel.width - anchors.baseline: cruiseSpeedField.baseline - } - - - FactTextField { - id: cruiseSpeedField - width: offlineTypeCombo.width - fact: QGroundControl.offlineEditingCruiseSpeed - enabled: true - } - } - - Row { - spacing: ScreenTools.defaultFontPixelWidth - visible: offlineVehicleCombo.currentText != "Fixedwing" - - QGCLabel { - id: hoverSpeedLabel - text: qsTr("Hover speed:") - width: baseFontLabel.width - anchors.baseline: hoverSpeedField.baseline - } - - - FactTextField { - id: hoverSpeedField - width: offlineTypeCombo.width - fact: QGroundControl.offlineEditingHoverSpeed - enabled: true + Rectangle { + height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: autoConnectCol + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + //----------------------------------------------------------------- + //-- Autoconnect settings + Row { + spacing: ScreenTools.defaultFontPixelWidth * 2 + QGCCheckBox { + text: qsTr("Pixhawk") + visible: !ScreenTools.isiOS + checked: QGroundControl.linkManager.autoconnectPixhawk + onClicked: QGroundControl.linkManager.autoconnectPixhawk = checked + } + QGCCheckBox { + text: qsTr("SiK Radio") + visible: !ScreenTools.isiOS + checked: QGroundControl.linkManager.autoconnect3DRRadio + onClicked: QGroundControl.linkManager.autoconnect3DRRadio = checked + } + QGCCheckBox { + text: qsTr("PX4 Flow") + visible: !ScreenTools.isiOS + checked: QGroundControl.linkManager.autoconnectPX4Flow + onClicked: QGroundControl.linkManager.autoconnectPX4Flow = checked + } + QGCCheckBox { + text: qsTr("UDP") + checked: QGroundControl.linkManager.autoconnectUDP + onClicked: QGroundControl.linkManager.autoconnectUDP = checked + } + QGCCheckBox { + text: qsTr("RTK GPS") + checked: QGroundControl.linkManager.autoconnectRTKGPS + onClicked: QGroundControl.linkManager.autoconnectRTKGPS = checked + } + } } } - - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - } - } + } // settingsColumn + } // QGCFlickable } // QGCViewPanel } // QGCView