From 1fe997d708a796893179cadbcc99e6cadb6370f8 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Sun, 7 Feb 2021 21:56:35 +0200 Subject: [PATCH 001/125] tree: enable Android build Signed-off-by: Adrian Suciu --- CMakeLists.txt | 77 ++++++-- android/AndroidManifest.xml | 85 +++++++++ android/build.gradle | 77 ++++++++ android/gradle.properties | 11 ++ android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + android/gradlew | 172 ++++++++++++++++++ android/gradlew.bat | 84 +++++++++ android/res/drawable-hdpi/icon.png | Bin 0 -> 1500 bytes android/res/drawable-hdpi/logo.png | Bin 0 -> 6535 bytes android/res/drawable-hdpi/logo_land.png | Bin 0 -> 6535 bytes android/res/drawable-hdpi/logo_port.png | Bin 0 -> 6535 bytes android/res/drawable-ldpi/icon.png | Bin 0 -> 701 bytes android/res/drawable-ldpi/logo.png | Bin 0 -> 2306 bytes android/res/drawable-ldpi/logo_land.png | Bin 0 -> 2306 bytes android/res/drawable-ldpi/logo_port.png | Bin 0 -> 2306 bytes android/res/drawable-mdpi/icon.png | Bin 0 -> 1032 bytes android/res/drawable-mdpi/logo.png | Bin 0 -> 3509 bytes android/res/drawable-mdpi/logo_land.png | Bin 0 -> 3509 bytes android/res/drawable-mdpi/logo_port.png | Bin 0 -> 3509 bytes android/res/drawable-xhdpi/icon.png | Bin 0 -> 2068 bytes android/res/drawable-xhdpi/logo.png | Bin 0 -> 10023 bytes android/res/drawable-xhdpi/logo_land.png | Bin 0 -> 10023 bytes android/res/drawable-xhdpi/logo_port.png | Bin 0 -> 10023 bytes android/res/drawable-xxhdpi/icon.png | Bin 0 -> 3224 bytes android/res/drawable-xxhdpi/logo.png | Bin 0 -> 17907 bytes android/res/drawable-xxhdpi/logo_land.png | Bin 0 -> 17907 bytes android/res/drawable-xxhdpi/logo_port.png | Bin 0 -> 17907 bytes android/res/drawable-xxxhdpi/icon.png | Bin 0 -> 4306 bytes android/res/drawable-xxxhdpi/logo.png | Bin 0 -> 27511 bytes android/res/drawable-xxxhdpi/logo_land.png | Bin 0 -> 27511 bytes android/res/drawable-xxxhdpi/logo_port.png | Bin 0 -> 27511 bytes android/res/drawable/splashscreen.xml | 11 ++ android/res/drawable/splashscreen_land.xml | 11 ++ android/res/drawable/splashscreen_port.xml | 11 ++ android/res/values-land/splashscreentheme.xml | 6 + android/res/values-port/splashscreentheme.xml | 6 + android/res/values/libs.xml | 22 +++ android/res/values/splashscreentheme.xml | 6 + android/res/xml/device_filter.xml | 3 + .../example/jnimessenger/JniMessenger.java | 113 ++++++++++++ resources/icon_big.png | Bin 0 -> 27511 bytes src/tool_launcher.cpp | 117 ++++++++---- src/tool_launcher.hpp | 7 + 44 files changed, 770 insertions(+), 54 deletions(-) create mode 100644 android/AndroidManifest.xml create mode 100644 android/build.gradle create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/res/drawable-hdpi/icon.png create mode 100644 android/res/drawable-hdpi/logo.png create mode 100644 android/res/drawable-hdpi/logo_land.png create mode 100644 android/res/drawable-hdpi/logo_port.png create mode 100644 android/res/drawable-ldpi/icon.png create mode 100644 android/res/drawable-ldpi/logo.png create mode 100644 android/res/drawable-ldpi/logo_land.png create mode 100644 android/res/drawable-ldpi/logo_port.png create mode 100644 android/res/drawable-mdpi/icon.png create mode 100644 android/res/drawable-mdpi/logo.png create mode 100644 android/res/drawable-mdpi/logo_land.png create mode 100644 android/res/drawable-mdpi/logo_port.png create mode 100644 android/res/drawable-xhdpi/icon.png create mode 100644 android/res/drawable-xhdpi/logo.png create mode 100644 android/res/drawable-xhdpi/logo_land.png create mode 100644 android/res/drawable-xhdpi/logo_port.png create mode 100644 android/res/drawable-xxhdpi/icon.png create mode 100644 android/res/drawable-xxhdpi/logo.png create mode 100644 android/res/drawable-xxhdpi/logo_land.png create mode 100644 android/res/drawable-xxhdpi/logo_port.png create mode 100644 android/res/drawable-xxxhdpi/icon.png create mode 100644 android/res/drawable-xxxhdpi/logo.png create mode 100644 android/res/drawable-xxxhdpi/logo_land.png create mode 100644 android/res/drawable-xxxhdpi/logo_port.png create mode 100644 android/res/drawable/splashscreen.xml create mode 100644 android/res/drawable/splashscreen_land.xml create mode 100644 android/res/drawable/splashscreen_port.xml create mode 100644 android/res/values-land/splashscreentheme.xml create mode 100644 android/res/values-port/splashscreentheme.xml create mode 100644 android/res/values/libs.xml create mode 100644 android/res/values/splashscreentheme.xml create mode 100644 android/res/xml/device_filter.xml create mode 100644 android/src/org/qtproject/example/jnimessenger/JniMessenger.java create mode 100644 resources/icon_big.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 854d8309a2..c2148055c0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,10 +80,22 @@ if (MSVC) add_definitions(-DQWT_DLL) endif (MSVC) +if(ANDROID) + set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + if (ANDROID_ABI STREQUAL "armeabi-v7a") + set(ANDROID_EXTRA_LIBS + ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so + ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so) + endif() +endif() + find_package(Qt5Widgets REQUIRED) find_package(Qt5 COMPONENTS LinguistTools REQUIRED) find_package(Qt5Concurrent REQUIRED) find_package(Qt5Network REQUIRED) +if(ANDROID) + find_package(Qt5AndroidExtras REQUIRED) +endif() FILE(GLOB TS_FILES ${CMAKE_SOURCE_DIR}/resources/translations/*.ts) set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}) @@ -147,7 +159,6 @@ else() endif() -find_package(Qwt REQUIRED) find_package(Qt5Qml REQUIRED) find_package(Qt5Svg REQUIRED) find_package(Qt5UiTools REQUIRED) @@ -156,7 +167,6 @@ find_package(Qt5Xml REQUIRED) set(CMAKE_VERBOSE_MAKEFILE TRUE) find_package(Gnuradio "3.8" REQUIRED COMPONENTS runtime analog blocks fft filter volk pmt ) -set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};/usr/local/lib/cmake) find_package(gnuradio-iio REQUIRED PATH_SUFFIXES iio) find_package(gnuradio-scopy REQUIRED PATH_SUFFIXES scopy) find_package(gnuradio-m2k REQUIRED PATH_SUFFIXES m2k) @@ -165,6 +175,7 @@ add_definitions(-DBOOST_ALL_DYN_LINK) find_package(Boost COMPONENTS system filesystem thread chrono REQUIRED) find_package(libm2k REQUIRED) +find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb) if (ENABLE_MATIO) message("-- Building with MATLAB support for SignalGenerator") @@ -172,32 +183,43 @@ if (ENABLE_MATIO) add_definitions(-DMATLAB_SUPPORT_SIGGEN) endif() -find_library(QWTPOLAR_LIBRARIES qwtpolar) -find_path(QWTPOLAR_INCLUDE_DIRS qwt_polar_plot.h PATH_SUFFIXES qwt) -if (QWTPOLAR_LIBRARIES) - message(STATUS "QwtPolar libraries found - ${QWTPOLAR_LIBRARIES}") -else() - message(STATUS "QwtPolar libraries not found - assuming they are built in Qwt") - set(QWTPOLAR_LIBRARIES "") -endif() -if (NOT QWTPOLAR_INCLUDE_DIRS) - message(SEND_ERROR "QwtPolar includes not found") + + + +find_library(QWT_LIBRARIES REQUIRED NAMES qwt_${ANDROID_ABI}) +set(CMAKE_FIND_DEBUG_MODE OFF) +if(ANDROID) + find_library(QWT_LIBRARIES REQUIRED NAMES qwt_${ANDROID_ABI}) else() - message(STATUS "Found QwtPolar include files - ${QWTPOLAR_INCLUDE_DIRS}/qwt_polar_plot.h") + find_library(QWTPOLAR_LIBRARIES qwtpolar) + find_path(QWTPOLAR_INCLUDE_DIRS qwt_polar_plot.h PATH_SUFFIXES qwt) + if (QWTPOLAR_LIBRARIES) + message(STATUS "QwtPolar libraries found - ${QWTPOLAR_LIBRARIES}") + else() + message(STATUS "QwtPolar libraries not found - assuming they are built in Qwt") + set(QWTPOLAR_LIBRARIES "") + endif() + if (NOT QWTPOLAR_INCLUDE_DIRS) + message(SEND_ERROR "QwtPolar includes not found") + else() + message(STATUS "Found QwtPolar include files - ${QWTPOLAR_INCLUDE_DIRS}/qwt_polar_plot.h") + endif() endif() find_package(PkgConfig) +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) pkg_check_modules(GLIB REQUIRED glib-2.0) pkg_check_modules(GLIBMM REQUIRED glibmm-2.4) pkg_check_modules(SIGCPP REQUIRED sigc++-2.0) pkg_check_modules(LIBSIGROK_DECODE REQUIRED libsigrokdecode) + pkg_get_variable(LIBSIGROK_DECODERS_DIR libsigrokdecode decodersdir) -message(CMAKE_PATH ${CMAKE_PREFIX_PATH}) -message(NETWORK ${Qt5Network_INCLUDE_DIRS} ${Qt5Network_LIBRARIES}) + include_directories( ${Boost_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} + ${Qt5AndroidExtras_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ${Qt5UiTools_INCLUDE_DIRS} ${QWT_INCLUDE_DIRS} @@ -311,7 +333,7 @@ if($ENV{APPVEYOR}) set(APPVEYOR_INFO ${APPVEYOR_INFO}job_id:\ $ENV{APPVEYOR_JOB_ID}\n) set(APPVEYOR_INFO ${APPVEYOR_INFO}job_name:\ $ENV{APPVEYOR_JOB_NAME}\n) set(APPVEYOR_INFO ${APPVEYOR_INFO}job_nr:\ $ENV{APPVEYOR_JOB_NUMBER}\n) - set(APPVEYOR_INFO ${APPVEYOR_INFO}job_link:\ $ENV{APPVEYOR_URL}/project/$ENV{APPVEYOR_ACCOUNT_NAME}/$ENV{APPVEYOR_PROJECT_NAME}/builds/$ENV{APPVEYOR_BUILD_ID}/job/$ENV{APPVEYOR_JOB_ID}\n) + set(APPVEYOR_INFO ${APPVEYOR_INFO}job_link:\ $ENV{APPVEYOR_URL}/project/$ENV{APPVEYOR_ACCOUNT_NAME}/$ENV{APPVEYOR_PROJECT_NAME}/builds/$ENV{APPVEYOR_BUILD_ID}/job/$ENV{APPVEYOR_JOB_ID}\n) if(EXISTS ${CMAKE_SOURCE_DIR}/scopy-mingw-build-status) message(scopy-mingw-build-status found .. populating) FILE(READ ${CMAKE_SOURCE_DIR}/scopy-mingw-build-status SCOPY_MINGW_BUILD_STATUS_INFO) @@ -349,10 +371,13 @@ if (CLONE_IIO_EMU) if(NOT GIT_SUBMOD_RESULT EQUAL "0") message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") endif() - add_subdirectory(iio-emu) + if(NOT ANDROID) + add_subdirectory(iio-emu) + endif() endif() -add_executable(${PROJECT_NAME} WIN32 ${OSX_BUNDLE} +if(ANDROID) + add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${RESOURCES} ${m2kscope_RESOURCES} @@ -360,7 +385,19 @@ add_executable(${PROJECT_NAME} WIN32 ${OSX_BUNDLE} ${EXTRA_BUNDLE_FILES} ${TRANSLATION_RESOURCES} ${ABOUT_PAGE_RESOURCES} -) + ) + else() + add_executable(${PROJECT_NAME} WIN32 ${OSX_BUNDLE} + ${SRC_LIST} + ${RESOURCES} + ${m2kscope_RESOURCES} + ${m2kscope_FORMS_HEADERS} + ${EXTRA_BUNDLE_FILES} + ${TRANSLATION_RESOURCES} + ${ABOUT_PAGE_RESOURCES} + ) +endif() + if (WIN32 OR ENABLE_APPLICATION_BUNDLE) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME Scopy) @@ -374,6 +411,8 @@ target_link_libraries(${PROJECT_NAME} LINK_PRIVATE ${Qt5Qml_LIBRARIES} ${Qt5UiTools_LIBRARIES} ${Qt5Network_LIBRARIES} + ${Qt5AndroidExtras_LIBRARIES} + ${LIBUSB_LIBRARIES} gnuradio::gnuradio-runtime gnuradio::gnuradio-analog gnuradio::gnuradio-blocks diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000000..3f2d718808 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000000..443a800244 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,77 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.6.0' + } +} + +repositories { + google() + jcenter() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion '28.0.3' + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['resources'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + // Do not compress Qt binary resources file + aaptOptions { + noCompress 'rcc' + } + + defaultConfig { + resConfig "en" + minSdkVersion = qtMinSdkVersion + targetSdkVersion = qtTargetSdkVersion + } +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000000..fded106b17 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,11 @@ +# Project-wide Gradle settings. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m + +# Gradle caching allows reusing the build artifacts from a previous +# build with the same inputs. However, over time, the cache size will +# grow. Uncomment the following line to enable it. +#org.gradle.caching=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..6623300beb --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000000..cccdd3d517 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000000..f9553162f1 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae98caf6be38e714c877b32cf6323a203c7b640 GIT binary patch literal 1500 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 U7VW=oKP!M|T6&t58n)s81qHdG+W-In literal 0 HcmV?d00001 diff --git a/android/res/drawable-hdpi/logo_land.png b/android/res/drawable-hdpi/logo_land.png new file mode 100644 index 0000000000000000000000000000000000000000..119fc5b33d5f19afdbf7ac4024466d22663298d0 GIT binary patch literal 6535 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 U7VW=oKP!M|T6&t58n)s81qHdG+W-In literal 0 HcmV?d00001 diff --git a/android/res/drawable-hdpi/logo_port.png b/android/res/drawable-hdpi/logo_port.png new file mode 100644 index 0000000000000000000000000000000000000000..119fc5b33d5f19afdbf7ac4024466d22663298d0 GIT binary patch literal 6535 zcmb7}XE+;xx5gv(*4m>eYSyYF$*-|`#s2# z=4zYAf;>*ELzgi?qZ9Oo&`Ysn%J05yKtf0|2>P*ZV zLvI*b5Tf!@&Vo!`{sXtIk9wlS6aOIPn3c=(VM0MW3*m5Z4&{fR8qX>;e^4ZkOic|l zN1T?Y#Bg(L7}!-BJP~^IR4CslRy;1e+cb+?uf6uCm z!8Hc@oE+mopXUb{BaA>k4T10K<$1`VZ48j_=(vHNh)+R)QCJExCufrVKt2(9;vN|bdhx+0E9qH7?2@H zXRX!zcKRe@E}Sm%F9UPIt3jKjt29-(-L54IvrxxIc;4JV?Br`WLw-U7wVA(~7 zfK2}hg<1gHkf=S;{ik$LOG`k}3xZ2p0@Xd)H2sV*waiTD-3QR6MBtp?Yms7WH#2BB zN9zj-{Tok2Rdj-?z_s4Y04$s~_3xq=uY{~JI5)XpKW&t>xs1AXv7R`+Mn?fvc@H}7 zu&acBm`ww+)(Q*atjHwPr$P7Ldkj2cFA&d7Y$Ax@T z{e736^J`^|GL(rkQ!9K8!fHx}Rn;CN+h}9*%ZLT>)*5F_afk#*)GP$1TnoA1E@ApWn zwW)jJdA;=CQ=j)0FuoG8F0JH3v`fSR80tyma6jgw@1Q5vsVB#Omiz#!xfue zx&h7`C>X1LR8(|Y!w8((hx8wQ+8@?^NodL#$zR0^GJ$%4*v>6sA<479ZKserQ(dSb zIh}ohVM@@_rCg{M!8KmM+JsP9zM z$>l~9+2|0GA zu~hRS@Q*p}i#J}i9TXJP;Ztz=uJ{w}we{Bu@%@^ltrt^2GCF*o3OcU7J8UVNnT|Rw zHPyO2Hy~Fe#-=3qJ259R{}d(1dYPgx{z$saZw?|FCN+|CNu!_TW|Y`7ZLy7l!dl8( z1J>QpL04*_kD7lmrnQQyOW6G-Uh%vJj^N%ndJYukJIT1HHo7|cn*SRIoNyYWaGH0L z8;DpY(-xZE0^9I4UKhWOicV+fCvZ*S?uzlE7gkngibK2$5xo>Pp|l)$De%&8)Ls^w z-5l`GGaW}b(j~s*)p6Sp0eS&dcw>Iou`+|V7gFcxu?YXV>ww8hR8Z2yP z7IC~PRkYDXM^S6LsPP z3iZikiHvx(x_8bozKDqInTD@-T{GOz7|}Zl+@-46sLdO;;Fwz0-cR<3ZVrJ=VyyjJmgd`c>e1d0!)(j1yG)~Z+KOb&0@ zcMefc0gW8&V|aU^^JcK5PBLA+0|t8t}?i29%UVUh4Mc!7jvwg;w2w^WzBzQtpXl3(pP4-z_As2_zZ~ zFr_!4kPsfaNVspI;x0G?uQr@i*%-M@sOB+hV1RKG^=1UJDiU? zes53JPLh;`C_pe9?$AJ&FTR)`cMe1mb*oU4hFx*(t0-n7bYdn=|qa( zwXAEe_v(kk7c(1*?hN>%7SwFAFD2v82!T-Yh|~dMsCfbG9Xrw9ab;1YMo%Mv+O@7J zg&uB`i0a+>+(MJAH}~7c$83!;P!p&a>P987wo@O^W5PP%Ab=tuO=FE!c$Ng(P-tVMyFGTP9 zet^spQhoB>-r+B|gl2xN4tw2f25DsikFR(C{@!f^aaP*LN5`w$6L*`?thKj@UA9wt z?JPt;6Rg_Gu~w%?-HSG-9%DvE&d&skHH z_o38J@!!YvX_xhjzHSYt3VzFe8n<)9p{G5MrI}-gLIosSPo7y49k6`E zi=K$6z4n2ToW#!ei)-&zQ%Pa{V7{Fq;Px@-6Y~R32hsHed-$t8ZaId%Z6YOH;l)!H zfn6vdjWMt5H{qQC6$~$&d2%|lGepLujlhcXd}sXdA>=W4HFBgZ&NqdQ*56q)X?Cv( z)!F#{VSDorK$X(A)7B(+E=j%pmdecn!gt_1?46V)iOtcot~CRJvK{<863CWV>m+K2zx<^pHK*U>HtWce2JNNUh5lkbubI`~S_< z8UnihCIjhmq8Ol9>B9eUMRPYQeJy~yCcB0=Ho)ziBmgsnjwp%wbH80cF+MYTm;Fsv zp}$$<-#=n1mlWxlqzrg>8`ek}GTEm@M5>ff=ms)Z=Y@ELS%mV|KQ z_$$*s8bfFFi9o4>%QymdS%MeV_L=2F231TFj~-0R{LFI3L7S)ota!_d!)UoJ|;oylj1I1P#1)A^P%q{X5%plKcD#wH!2`fKCU%X9}pD%3ZR!N zw4>H`mjrZ*xnC5h+h9?De>ZE-A2g-n2+OMRE0fMs5`vdFt@(0=g*s6B)Y%S zFE&W}*JY01&{2toG6`dn?!T_=Ep#`+AMyoI=^$F^a@H%fsi6o z3?b5C?bg?qa0St^Z;1KLL0(wwArpr@*#Z;8M=k+|A)yJ_BAZ}@B3iX_snzkMQdoY?iX zS)g#7aM)YE!beg7$pNPidz%B~O!^qJ>q^&ta#LZRqJ~WM`EUm( zC#H=TAQNCEr`laisOYC4l4MOKqIL~z^br*RUHFXlAI`W%=??#+MvP5mrS;N!Xw9Ke z!Lazg;eLCD1&X}-CC0q|eFl_1;GeMk*1u~O@Ors-t+Wf-AWJC+*Za`W2gbJ=0cG@t zPE%Y3h^I?L^U{+Lh-M1+>IWs78(KWWTMW7txbR}61!AY4-#kmnIh#k@5SMr=j4XGm zWistpB)rN%oMh{L)Gm>90$`WcFUH<4B2`*XYAVT-W@Vu4RmA_S$>%4Yc5y6<* zoBWlnTKCE1$k}^e6D^bc&l>xGkm3LD$|#n>g^SviklcO7MuZH*_xYj$T6m(C;Hs8k zIJ-6U#j=qqnS)lkJ?eaOK8)=34eaM?#CoF4b$k|%8Co$1W86N@-TH3)zC_^HU2MRq<;nrpNTV7-Y6{XCWntOE`M<6nru#a8NJDTPL~&R+RZS@y5A&tMsEE zH`&fOYUPdJMG`aEjf##29(G{tEF6YIB1Jk`)NNz`F-c19u<(ncKLQ6KT``8^=1p`Z z))UuW6ke}Sg^l~+nA~4G45FE}2hZ#6TY>ei$cv2khRNNFu6}hF3fnjzBk~I$!m;2M zvkv-c6%v+tZH4zEe12>(*|*tFaVTwxU^R!lz1)i}qB(3BK$}3M7o+!z15PqOX^8)+ z-VO^4yl{{Ra&T|AKzOg39KNG;5SRPn%xBjMG>*qaEBZEK^A`I(X=&`WgDqgQnL#E5 z$xUClw ze4*1&d03TRz;TKd#Po}=?ID`8co;Bc2$U7W?%v0iRwfFgPfr57}YRSonoIN~3E6R*86^OZ~GUCqz^Z<@3C zD=cZ$SCseA+!c0KW^ki4Z68wG84vC{Q3pGTVOL&4Kymzt0rU04g#${7kgtJ1%Qf8k zx!0>Yv*F#ssV&|RSr2=oIWMuYLM6y(z5_YrjH`DD<4iPX2`vZ%icFg4dK6rgGqQfl zzsZntu1I@Q)|IYRimA`EFe~ZRH+;Qq5Y)GIKa4;%1Yxv}8;|3)ho&u7d#o2P3|%nC zL1yifsQjwEljiQSI4}Q2=CZ09DD15X;c7R3*Qi%7Ag$Uz*Z-Y9ZfuN@XLd08mk2p- zKf2y%zw$9NC9OqnihDSyLi{Pf-wz3CP{0R;zykNLMv%NZKQ;l9Po|^Rc61)hff;TZ z;k~bC9%~~V&>E7eyE4Z|4S$Fg28}^6YgD^R<%k13U}=x49N7Dttx4R&n;vY&4gsO~ zY|q)h^*N{B(l@b*I66(V`z2QwjFbFInFTW-Tm?T<2u|e68MkTAk6Z0nAU}(ReNU)& zB+jb43jodEA*qH>SIi|S*ZC@9SH(l8PIDkc^W^_(lw0G{1l|4$(v)64&Wdg zHC5JIG5xZQp6w~w&hrfw#=6ZZIpD(Mw;P_JPw<$&R!cwnPn34-D!|G2#`-Dww&Cq# zzydkR=tOMywv~$t>NxIfxqw6VnU0RhQueKr#?F{c!kdD_!2Sd8c?<~v=xqIo)i^yM zY1l@ux%->)ab3Ybc2Aw2rWZW==;P&TykGcx&Fx*wL-JAt;@P(i`_zvnP`##i#m~$# zv>Uy8O}_S%_&%U~!Fg*~sJ9PRk9O!AW23cMa~o;2$g_5E12HPW%=*Er3%&Pdf|9pU9v=H^d6~~% zvk~wdE366_44cU~+{G5!PUfeMXpal34U6=9ZZG+-SFGY{&OOTlF6|qOq18zx22f?v z`eH2t=hWLn5b52#h{}pI{&A&8cR*3?$BUgN!f(+V!nDfV=r?STD91R5rw1Mhd|AIH z;An&CHr*L%v$!k&>0(2U03Jnix9Qkba`ynB+-ev>^6-z= zZvc7R+Ud)U+(gTQKqlarIX5YuhAy%UBsh47lNiv)^q!59&N@#*4&9>GK?9z)GbihE zp^5R}qcBJp0WITx*`3xBEpvSM8Q!BUKNY7N66+yHH14a(6AlJ)oiK<; U7VW=oKP!M|T6&t58n)s81qHdG+W-In literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5bf398f9bd408011169c0db94b87e8a7ad6b2efd GIT binary patch literal 701 zcmV;u0z&NQT8PWQNj>f43ZXM=ulLf z7NLX*ZbS-O1W6y{wkSbp<3w4sD5D}Vrok0$_%%2XkeTEHd1&OAba0d!(g)8Z9AQBr zcTxdt$j`R(WK;wOQ^78-5H?D=_M|ENVqPSeC@C%LwP*n=McEk?$ zv>mQo2h*G#x!VON%OS;K*``!rQ7W9Tg3Xzh?dK@?yWs0EI2S{1Hmpq7zCCV`5-v8x zCeP1>>N2p|^m%L?+B(82kd_J;PUyY>CW;98UJ{a?j)s5lJ81(xle4;t=7C*PX~ zl_gp%z`z$cR|iAigB4h@49-?U<~ob~>TP(`78%Sip#Bypz!Ma4s1T$8Z`0J_Oih8( zgRpNm2%&9#gK+v1{0Qi?c-X2`!FF(_X@jYtz7Zb3h@KJXfTt=TZ<}Qs`Ud`P7#)LE z>5!9c#Rcg30B0^k;8$22rK}+|45+&StsOIyhhVos^)c9)6Z`J-S9)M4e*cq5NWjO> zn*8({_S2aE*Z zZ7=i=%=|8555yY@{}v<#Bn8Oezgj||z>q+c6%iM7N=#}_r9fj=JY jP+%zVD-%zPSNQ$_!9l;ajd76`00000NkvXXu0mjf>039i literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/logo.png b/android/res/drawable-ldpi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2cd30e9ef3b9cedaad2672fdd0b846a00bd6d4 GIT binary patch literal 2306 zcmV+d3H|noP)A znmuNZH?J_C|A=#0-pf@vdtHVGPFQ#25_C|j9iMS zKywD9@r9uP*%_iUaj7E(5R4Q+FqymoY7^mRAQpE{r{G!O@E_LW`f#jc*;znh9y^WN zgnhb-0&H4gJXW_ISh7mnzq}1lz0~Mu?=QgAm)LPSk@hv9d_McQTS1`4=Y<--p%HjF z=^lEI(GN9#^+&)f$$a#=-d}oYDS%>mL*5vjx4BIQfa#_7Vehyl+`N49mHoNegz?8qXidzg_kdAF$-#LoxV$9wm%4-<b)4TX0gMxUvMM$%pL z8y<+J7uA~Jei9CXTQ4D}A8?(no7eCo@Y8;ypV5QZVOv^&j0~V^3)_Fy7UKTlXJGF> z?Rt!uY)Od#qwWU^9&-10-(A3+w^@(tYe{}C zJ8UdY1kTT8`>)zU-0!T{1ke|VoPNev6n5!Q0rX)nZ{z%W!-+@*7&HKw%;>$iVSdkJ zzPM3EeBE7EkIe3!k~uAwQE9i-Evy$J6CkXYcC=)B;nZp1s`lD`=Z<8%yprwT+>44o z2Q(jbPv>AVoZjGm?`5G2Fl$oyPoeE9*nBPPK$*1vWh*7S(l5}O?cIhCPpCeet zXLTCR{bM~p@kQ<7k%!Q_p`ETKnTf*q0MO+c?|v&6Y3>#8ho2XK2tnU90l?xHfQ`%f z;|Zk(ggr*U;`47cx6?gf-$9^fcWu9Bt9_iggMoD)YWr`O0#8gc`f+Mn&R;M-m}U4V z&Vgz`*R1G`zxH)L|C8sCt(Sb|jWOZghsr!SW`Fzbbtz%$5N1u>+NXr2kJyRC|+v@#=brR+H3F~RS&;)Q(I;{9xT7cn& z;m02)#p8@Kt(^!gfc}GqF%yBm&jpFi9oAraIzjXI+(KaY9(VtNx}giE8K;s*ZG4&z zkeX3AUTB^QXL>U}5I}(11aNb2;!bsGOh*y)HIvTF_JXe_h$dE4;%^ivEYhJlO3c&d zmHNpCcMZ^Av`FLAJOJWq!h5dsj&c= z$^7QJ;Bo2=pI8H||0sBz6pH`@`U5j`Pnzjo>h0LH!uWaX@Ivww?UjlJKvrjM|IuRL z#HsM|M^TO*Q`Ih=tS5$zH-47WT5-BLH~mhovD>xwenw9)PR0!zMO8I3M|}emjn|%1 zRHdDToR}pTIV33j&WAVf!%p8P}9UM}Q-L0{w5Y=8t7WRz4K~BL+HVH*x}fA>*gCqAX9<7WzMCL75w2_ptVynRb^DEeQ;7zrrgQWJ&AqS!D9$79c5R)*nU_oU zN@O=-R-3$Im*)0*qtwq+atc$kOmiPS<}GeBwVxXIx7CyXk8xx79$?vf{?bfoFKDi5 zsWC4iczL?5O#qpfY5RFI`12Nv14~z1k1tu}T-#%D@;#Ve0(~LF1M6Rpc*1!Nv$7gk zQ)dkmR^Oh+`N%WC$W$x8h*W?Rr`RR0ekVEpvP1XIjH;3_ zW$#+g?K?=$wKn4ea&D+_E~yu>>y(7d@^ c>B>6yAIyc^Wt~E?8~^|S07*qoM6N<$f|Pkv^Z)<= literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/logo_land.png b/android/res/drawable-ldpi/logo_land.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2cd30e9ef3b9cedaad2672fdd0b846a00bd6d4 GIT binary patch literal 2306 zcmV+d3H|noP)A znmuNZH?J_C|A=#0-pf@vdtHVGPFQ#25_C|j9iMS zKywD9@r9uP*%_iUaj7E(5R4Q+FqymoY7^mRAQpE{r{G!O@E_LW`f#jc*;znh9y^WN zgnhb-0&H4gJXW_ISh7mnzq}1lz0~Mu?=QgAm)LPSk@hv9d_McQTS1`4=Y<--p%HjF z=^lEI(GN9#^+&)f$$a#=-d}oYDS%>mL*5vjx4BIQfa#_7Vehyl+`N49mHoNegz?8qXidzg_kdAF$-#LoxV$9wm%4-<b)4TX0gMxUvMM$%pL z8y<+J7uA~Jei9CXTQ4D}A8?(no7eCo@Y8;ypV5QZVOv^&j0~V^3)_Fy7UKTlXJGF> z?Rt!uY)Od#qwWU^9&-10-(A3+w^@(tYe{}C zJ8UdY1kTT8`>)zU-0!T{1ke|VoPNev6n5!Q0rX)nZ{z%W!-+@*7&HKw%;>$iVSdkJ zzPM3EeBE7EkIe3!k~uAwQE9i-Evy$J6CkXYcC=)B;nZp1s`lD`=Z<8%yprwT+>44o z2Q(jbPv>AVoZjGm?`5G2Fl$oyPoeE9*nBPPK$*1vWh*7S(l5}O?cIhCPpCeet zXLTCR{bM~p@kQ<7k%!Q_p`ETKnTf*q0MO+c?|v&6Y3>#8ho2XK2tnU90l?xHfQ`%f z;|Zk(ggr*U;`47cx6?gf-$9^fcWu9Bt9_iggMoD)YWr`O0#8gc`f+Mn&R;M-m}U4V z&Vgz`*R1G`zxH)L|C8sCt(Sb|jWOZghsr!SW`Fzbbtz%$5N1u>+NXr2kJyRC|+v@#=brR+H3F~RS&;)Q(I;{9xT7cn& z;m02)#p8@Kt(^!gfc}GqF%yBm&jpFi9oAraIzjXI+(KaY9(VtNx}giE8K;s*ZG4&z zkeX3AUTB^QXL>U}5I}(11aNb2;!bsGOh*y)HIvTF_JXe_h$dE4;%^ivEYhJlO3c&d zmHNpCcMZ^Av`FLAJOJWq!h5dsj&c= z$^7QJ;Bo2=pI8H||0sBz6pH`@`U5j`Pnzjo>h0LH!uWaX@Ivww?UjlJKvrjM|IuRL z#HsM|M^TO*Q`Ih=tS5$zH-47WT5-BLH~mhovD>xwenw9)PR0!zMO8I3M|}emjn|%1 zRHdDToR}pTIV33j&WAVf!%p8P}9UM}Q-L0{w5Y=8t7WRz4K~BL+HVH*x}fA>*gCqAX9<7WzMCL75w2_ptVynRb^DEeQ;7zrrgQWJ&AqS!D9$79c5R)*nU_oU zN@O=-R-3$Im*)0*qtwq+atc$kOmiPS<}GeBwVxXIx7CyXk8xx79$?vf{?bfoFKDi5 zsWC4iczL?5O#qpfY5RFI`12Nv14~z1k1tu}T-#%D@;#Ve0(~LF1M6Rpc*1!Nv$7gk zQ)dkmR^Oh+`N%WC$W$x8h*W?Rr`RR0ekVEpvP1XIjH;3_ zW$#+g?K?=$wKn4ea&D+_E~yu>>y(7d@^ c>B>6yAIyc^Wt~E?8~^|S07*qoM6N<$f|Pkv^Z)<= literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/logo_port.png b/android/res/drawable-ldpi/logo_port.png new file mode 100644 index 0000000000000000000000000000000000000000..4a2cd30e9ef3b9cedaad2672fdd0b846a00bd6d4 GIT binary patch literal 2306 zcmV+d3H|noP)A znmuNZH?J_C|A=#0-pf@vdtHVGPFQ#25_C|j9iMS zKywD9@r9uP*%_iUaj7E(5R4Q+FqymoY7^mRAQpE{r{G!O@E_LW`f#jc*;znh9y^WN zgnhb-0&H4gJXW_ISh7mnzq}1lz0~Mu?=QgAm)LPSk@hv9d_McQTS1`4=Y<--p%HjF z=^lEI(GN9#^+&)f$$a#=-d}oYDS%>mL*5vjx4BIQfa#_7Vehyl+`N49mHoNegz?8qXidzg_kdAF$-#LoxV$9wm%4-<b)4TX0gMxUvMM$%pL z8y<+J7uA~Jei9CXTQ4D}A8?(no7eCo@Y8;ypV5QZVOv^&j0~V^3)_Fy7UKTlXJGF> z?Rt!uY)Od#qwWU^9&-10-(A3+w^@(tYe{}C zJ8UdY1kTT8`>)zU-0!T{1ke|VoPNev6n5!Q0rX)nZ{z%W!-+@*7&HKw%;>$iVSdkJ zzPM3EeBE7EkIe3!k~uAwQE9i-Evy$J6CkXYcC=)B;nZp1s`lD`=Z<8%yprwT+>44o z2Q(jbPv>AVoZjGm?`5G2Fl$oyPoeE9*nBPPK$*1vWh*7S(l5}O?cIhCPpCeet zXLTCR{bM~p@kQ<7k%!Q_p`ETKnTf*q0MO+c?|v&6Y3>#8ho2XK2tnU90l?xHfQ`%f z;|Zk(ggr*U;`47cx6?gf-$9^fcWu9Bt9_iggMoD)YWr`O0#8gc`f+Mn&R;M-m}U4V z&Vgz`*R1G`zxH)L|C8sCt(Sb|jWOZghsr!SW`Fzbbtz%$5N1u>+NXr2kJyRC|+v@#=brR+H3F~RS&;)Q(I;{9xT7cn& z;m02)#p8@Kt(^!gfc}GqF%yBm&jpFi9oAraIzjXI+(KaY9(VtNx}giE8K;s*ZG4&z zkeX3AUTB^QXL>U}5I}(11aNb2;!bsGOh*y)HIvTF_JXe_h$dE4;%^ivEYhJlO3c&d zmHNpCcMZ^Av`FLAJOJWq!h5dsj&c= z$^7QJ;Bo2=pI8H||0sBz6pH`@`U5j`Pnzjo>h0LH!uWaX@Ivww?UjlJKvrjM|IuRL z#HsM|M^TO*Q`Ih=tS5$zH-47WT5-BLH~mhovD>xwenw9)PR0!zMO8I3M|}emjn|%1 zRHdDToR}pTIV33j&WAVf!%p8P}9UM}Q-L0{w5Y=8t7WRz4K~BL+HVH*x}fA>*gCqAX9<7WzMCL75w2_ptVynRb^DEeQ;7zrrgQWJ&AqS!D9$79c5R)*nU_oU zN@O=-R-3$Im*)0*qtwq+atc$kOmiPS<}GeBwVxXIx7CyXk8xx79$?vf{?bfoFKDi5 zsWC4iczL?5O#qpfY5RFI`12Nv14~z1k1tu}T-#%D@;#Ve0(~LF1M6Rpc*1!Nv$7gk zQ)dkmR^Oh+`N%WC$W$x8h*W?Rr`RR0ekVEpvP1XIjH;3_ zW$#+g?K?=$wKn4ea&D+_E~yu>>y(7d@^ c>B>6yAIyc^Wt~E?8~^|S07*qoM6N<$f|Pkv^Z)<= literal 0 HcmV?d00001 diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d36b946a415f227902dcfe33195bb2b89205f62 GIT binary patch literal 1032 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 JU!ms|`9JVI!zBO! literal 0 HcmV?d00001 diff --git a/android/res/drawable-mdpi/logo_land.png b/android/res/drawable-mdpi/logo_land.png new file mode 100644 index 0000000000000000000000000000000000000000..bc60acaa50b27db14175d1be44b0590347759c64 GIT binary patch literal 3509 zcmaJ^Sv=H_*Z$5J>tKkGpDjs?^+!aQkqDJUVeD%X(;(~EnnYpjB)du|#*%#<)7aM` zMT0SxB#gZ@7oO5+P&pFR|V(;8G}D(=eTe?;v}8`O^xG3%9(w*02XG;n{(Z9yS01_k zll(~ju>-@=T?^`%n!V0gRs}h9r@5uHxwLrI*3io8$R?77bmsdFBavAdJW;Zjm_Nz2 z=O!gH6`h9HR`FY0=iyn##q`&L)zsl06HyDAvrDR9HE zWJTq}ZOw5`UBL)t59_2~YF_Ug0jYNyaU>Wo`_%mVYDsV}RtZDlmwDz>A10_F3;a6? zlX-|OhVns@HaRc^8CguEiIHGL6~DbMksr*Vp$C8rIi!s3#{X$#fS2YD>>zeopAeF% zQ>Td8=PU8w(ySn1JLNmA`j7F4FmX1v#Nq==p721yj}n7a?8@V2$+hWX6Kvht;f|hb zQnuIy8x5EiJtSh;<|N#y!YI|vo>9BGm8t?(c1V68;STlgA`ZhCF$nS(epcGh5R58xz#9&&kI6+)>GOW81rnrWZ$AX-1E&2V*?>uySa$k^PGuS zV1DZG{{tmoKALtJ4c z_)PEFw%q7{z6m4m-uyE#s0I^S)8Ffw7>}@w#C->>F{SMmjWEPmfq)z{v;eu(b%Bt{ zj%-h1&IKXdwJMd9P_(_6^`m9k>bjhvLX)VHl9Wf5`QiI`*Jnn&4>{vem4yvXf%f;^ z6_s_m-ChZ3V}xewlb+On3G?3gZjHr?JJtd=Y>r+!AICC1Qg0V0l*<+(Q)FIS!Cv3h zf^S`5teDrRe=sP-Z$!6Gvc>1Ukw9EwXAEtP`>s)7@hy_be{;m9crF@Fo3NpqE@yY} z5`-Ex2g25gqu(#cI31;%UX9)Pva3tERC82+#p&M1aIxE)2GBbKiwK0zbzvtL^q|v~ zzzsj4^{I{J^)_;mFdV=;XZn18tH8XL&G}YQ{Hc2RJnrsIb6ae?16O=8p{75vUWj)M zHZ3BHQ*<{gewG(+mhZsLJu+R8)TFkn+0`>Hl8y&^I^3n!6=GZ`12LL~P@UWpv=z*H zkFBu)_7&7v!u}=%NH#2%Hyz?y0;~4qlz6`k-ROh!ovbXp%b#$;B%q#i#gtXT}4zg`7Cr`p;}QihB$aucPVjr>@eOp zJ0s)OKU~-jM9Se74DdVmA)@d_DOEPH|2ST)%5?OM;fgUZTs zUF}|EK2V);>Jk9S=hF@F!>6=vQIF83VZQg;Zq@hOk(wvgI=seWherlfPSqON+6g>y7u&A;i|{HBXbQxdzdH% zCDyxZ1sv!~)s~O*xGY^I5lzwk)X`3lOLEUWZ<;X^~G8zNGGr6E_@_B{D;QdhG@)<)h+9 zxFVRhoW|O}E<{0{_T+cUzTzywO09G7_02{19fgSmNCf-_te`ZVR&qIaYd+_l-iT2q zP|*vltBAPFK0L2?(}p8>xXS5sSuOK<{rJWt54Tb+tbh+BlJi#oQ?LmxxnKGY zx7B%#!^(Uvslt*OrtwkOY%kfN$UVUl+W|EmGCG@|39CgL?>=<*S=+n?d|lkPDonFK zD811q&JJk*)vj7;`u^jEu#GQmhi{W}VWL2rAM5BfO(c}oC9eg9jej|thP;l?BiaMn zDvM|HwBBcQ@QOjuqB+yd&S#@5jXA4>k1jIkV~36m;3M}qqJZ9j?h86`kw5n!G$I=2 z;|X$4v~rA{tYa5}_rY5wXRUE34E)(JO91fqKqvXpj(Y0dMn&k8D!n>)3etnSTfd7LL|I$7xrUgRE@IX@YX+BQ4V0`@HfyNeZov+_F0Og%zI-6{B6(x&4wf@%H2&#AWbZc@!Aa)i{NP3zzjAUI!cM zpzOsP05!9^P00_phDhwaNBazDHLc8WzBx*@CAl`8^y+@9uo6_#X9O&gaHYue)0k1` zH9Y;Q?onBrX%E*;kjT<@1N{yHEhy7H?Ws<7*aKJ5ml+GKn_>G)4s1y*yOJ@8nc!p+ zrFvQ>57{UtSj^64UfwXjoI8K){#=0{mooLw5B`OR7uJh(wEsBs?E{kOC zGiVh#@xN^Tf2sa|78^z5z7X2_N#jQV>lnRb1dp@c`qS`}wz4*pkXcfglI3;yl^l7x zbB0fj+zw7JS8q|uzeOm2Ppm%wYq@J5W_$2sb~_4_MW_y@Vq~Z} zj!Ex)iE8u*Ih>3z`4T=y6LD4r&vk`9?GBP>MtOnUetl2jQPtKD@C(Vb*MfX_AC# zj^)1Vxw^pQt8OoGl(7IkY6XM*Xx%*hx*gPM!Tr$JBt2}3U?R6#KqU@;V?OxNJoILV zpYf?uWEooEeqi}pvNkM#(jD%q}QlodsN9)XaZ~)_(k^`o1A^$)*h~0qf4#Dmk-4@V)i2G?4sC%)mi>(t~xov9JYwwiL-;kES42ro+_b_KkZfE|C|Fb3v zbokASmKi6(`CjtAdz*n@-D|`YckZ}^$5gh2aOJ6+rf|{U%f;WE|8CVge7*dDA2evo zHjT+yA=K-ut%)ktPSihq*F}-AIoX&&Nf(_)&_YP7)2ihQ&6wPpFwIXv4NsNY3K8f@ zh{iHxH#nYl)<>nrgW2Dk%t>GVjQq>JxzFz3e*BT>Bf9OXNw zD74nH;|3&8YvgeEYfX{ynN$}1hdP)l%7rdVl^a0WaqDkpHjtHM)tQ*i58AwhSuwvO zy5L%uI-GZRve;A_@o^zXNY|DD6d(>qmx#8j6!L0N%R%iwPy!!z>V;z9pgarO`RBkH z%}@KAZsx32N4j*q#P3K%r*HpE$O*&cg>h%L1m0pzZD*N_vy8V;J{j15smJk3arP1O zzkPyyh=W`*nnT5Qq}{*jdW|jcxzQOZ4(Ym+=7uV4$yF+S7=D>pM@Qrd)yPjS_N*9d z1vh^_2*7F3U$L&PoSu05$4F$lUUz+f*)n|b&N=L{<*2ydc6k-T3fI*eZ7wpmYD;6C zL+zc%UXpuqb%9ihOQLMNFznqq?aX~7&)IsNEpXh@NPLaNlxzaiW`@gjn8!X0FgCcY JU!ms|`9JVI!zBO! literal 0 HcmV?d00001 diff --git a/android/res/drawable-mdpi/logo_port.png b/android/res/drawable-mdpi/logo_port.png new file mode 100644 index 0000000000000000000000000000000000000000..bc60acaa50b27db14175d1be44b0590347759c64 GIT binary patch literal 3509 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 JU!ms|`9JVI!zBO! literal 0 HcmV?d00001 diff --git a/android/res/drawable-xhdpi/icon.png b/android/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e64f3bad1a9f31bd53ced725139f073596e9dde8 GIT binary patch literal 2068 zcmbVN>pv3=1KnI_Zp)=-2$5SZiKJ;R%Vl#NhIn{{bP;1)LuxMDh@wKIp|LVOiCAH7 zF(bDyc~h=wD4rzdL1^TyKjD2poO3>$-}(K{r*m$4p;5A6RWJYmkacl(#Qf6G-vCMc z;#t}i*e{X7JO4=p0OVf%2JoJdoEiWi+3n(Z$~WP`H#!OL`%azAA)auX%8@KaC}>aw zH_FRTN<0_O4Un1053GA=5l_RM#9TO;)CcMqv4f_84dmE}cbF)>a-X+#uq4DNkNGdl zNdwL3Kzt6i8CDFHXm#!wJcmoUYMqXw7r_0 z=Nmuxf=;P;$$XN+qfUMl*y)H-^cZN@MDD?N^?r|QkTPBU~5H(JqISKjFkLIRMXRoqy;Y zR!SBzDO1yNj>yq+t#)A{Jm4K&XvLjZhZu;)F{^OOI-&-E+ix^jmuH>E7%T2_1$h4| zCqp3ebf!LO>qh&hy0fm`FVfp`L4wRb#28jh$F-djwfDL}DHI;ZV1ZYgQD!&#Y6rek zZN(Z*KBPjIpui$C5sPrKh;GoETl*L)hY-h*AZ0 z_S`wv3gqP|t#_ZEq^Dv2v}~=NykZZf#dD-d69+ zMp9&zzbfTNZ2#LUDY3mmqVou|0d=1vzGvIbtY>if2d zOgJ|4rH3xAc@}bX4L!y>@Gr`dbbSm82U}K=bBlhg^y5vdLgX6mT$#2s1j-Lm{M_bB zBj|UF%g)4YcW&GX>jWD>Rf0)9E(tT%TivOKzd?kzOve6l~kKJCqyFaQ;#(-94nm zpRtNm)rdIu+%#iP+>`TUR^eu{xD25)JZolpXne)^jri9PYD4xUF8qFpM#8dx&b+r1 zOK~lc8qup^*$H|wjeP6;$wJ}FuP%9JY*tBCefgsD4kg0_-ys9^>=uS zf*040Ffo3xtgBe6__S3JDWj9M09m>w%6ln4Q}T1DHti|Ob(w0)qwX8$^)zheTx&>= zuKwY;apW*+p+ZeB_j}c$;bAGyXo}*+2l5vCyFXP%L;i->VC;!T4{U0{9XvFm-77nA zC|k3v4drvrRVsy~$X@pHc^sUUKU`j61Ps{qfnAN~6BW5kLZs8|x>FvAnWn1g+d6Ok z1*OZ_1y}tnYz`{Vdt@IVq@9uC9(*YX^1~ui!-4k$N9u@dt!BBo!88#+sBHGxwmbLS zOax2uFb#*FL>+!BG9j|kuo9<-JlC1`(QoC(If4L+t)LdL$Ew5K$oZ@VhU?M3iKWY# za3{ZSL=Gd9?KnJ};r|m*FF8wW1&VJ?hLMiHBpkI>*DIQ|6L-_}HY!R`_$U1XX#{=2 z8h0J3^d?C;QOLOV#}<5cG|U{|{Y8yUKU0$Il*0gJCDAk8Qo8Pu+*f1x5NMc#xCwku1m-|g8~tG%M_WjMaB&D zrh$Z6TtWU;Z1!1}wyv!alo{xHHnDq@uXOm5v_u6vveK~@f_-np;HE>c)z1_L_am}O zZ)1x7$}wg#tGkAf|2z|w8@WN2@i{W1w>{Ob|A&;Ux%)OnbsyRGI;{pX(ht#{r{}S{d4P)(+OgU`l=@#BsLjs+ literal 0 HcmV?d00001 diff --git a/android/res/drawable-xhdpi/logo.png b/android/res/drawable-xhdpi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d35d63db6b76e08a05ce7a28464331c0fb60f4 GIT binary patch literal 10023 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 literal 0 HcmV?d00001 diff --git a/android/res/drawable-xhdpi/logo_land.png b/android/res/drawable-xhdpi/logo_land.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d35d63db6b76e08a05ce7a28464331c0fb60f4 GIT binary patch literal 10023 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 literal 0 HcmV?d00001 diff --git a/android/res/drawable-xhdpi/logo_port.png b/android/res/drawable-xhdpi/logo_port.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d35d63db6b76e08a05ce7a28464331c0fb60f4 GIT binary patch literal 10023 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 literal 0 HcmV?d00001 diff --git a/android/res/drawable-xxhdpi/icon.png b/android/res/drawable-xxhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1a83ad1c54c3dedf125bdab2a280a954f8bfa10c GIT binary patch literal 3224 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 wM~3KM}YSY5)KL literal 0 HcmV?d00001 diff --git a/android/res/drawable-xxhdpi/logo.png b/android/res/drawable-xxhdpi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e3f8b02a585046469045e59ed49905d43a1ef2cc GIT binary patch literal 17907 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 literal 0 HcmV?d00001 diff --git a/android/res/drawable-xxxhdpi/logo.png b/android/res/drawable-xxxhdpi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3545c6ce893c5b6b2c1a43a4b868aaa81e07843b GIT binary patch literal 27511 zcmdSBWmuHo7dASy(v3*@Lqa80x>XPXk?vLj>CS-}kP=W3q-#K=JBJ#E5D<`%?vT!* zhnRRD|L1x?pX+=)A7JJhp1o)7wbx#8uX_`%{YI6F{4O~N1fo)VrK}4A5dv=sLAOYO zmoxu~YvAR!`zsSK5QzHczaN5R0cti7=pIN-`I)|d*8ZYT7RMwOe>}aAyxqI_MM~!_ zEl=n3ThE?{{Ix7EcoV8y|0d#sLP$4PJGZG-gZ|Yg+W!bW{6_ekPH)z}VR@B&?GF)8 zr#G=da);*jzPc$}Bs^Tk=dP%UjZ#Yns(ddeJ;J z!Ik8`#~lJOb-yxYWo7z}u9tlqMEt$rxNdOOLSK;kf;XIEj1$x}ydBH>>hr0&)MBXN zj!XQfPp|yVjW;@;Qp=s}61~>27yX(7vfl4;Ejy7L_F4@x4=l{5;^yXd*f=U6ieFDY z-PWLyDbs~sGs+xu!}U}+r={Kh(@q=kiv9HIeK9>d=YZE$_aKc_te($U5;5qVuwScD z(?ZDLtdixU;fLJZiM9hC{&Jf~^G1hWn-Rga+C(8WEUhZG)bJH|p_XG{mP?((j^&d_ zP3Hw<*#|b+JU>7kJ}<3${iipo)b|lr}Ogte1E69Sg@a}j%suTfNDAgSZGe&K9u9=R%C`xR&BU5owYy-zR=@P%h(yiw;(O!$f>RgK9H9N;5vq(`eqdkH*`sP? z+1wGR1FUmBylYvu_}4$<;4Ayo`3LfdG>{YocDfl++6Y!rR<7;L2--%E5e?7nN}TwA z_sd35+-z@(0JCtICN;|uyEf2VO_GB0Mc&DoPHF)%wMu2V|K-iH#PS*30oR!6-+h-u zRPoU*{S%;WXIlt~yk+3%EF@RNaF$4W(>?Zo>oh6z3HY2@cKw$C4C*yM^sOqkmRJER z-UaDHy`h9c3LOnyIz3NHSa`n#wTiaq{h^9#|-_UeKvovQf$T8=FlmG!( zlD0aZi72iJF5$l|?+_IKT9ffU1@zp{6tF=(9end`d7A)`Y`#>m_ie7I?GQb%1=Yz( zecMNy!Na=)T0!|_xpMKRqeE7}WYdhj1JZ7It$h|yQyj%sQHu(&E%XZfy(BkAWEebP z45?2*sX03iDgV|tP_UIYxMOv-Jkvh1xASj&szDN}bs9wlGAFwOE4%<6dPUwuy(SPv z@D|w2BDT>d6&3#k(3T!`oaph6KoErNJ$ZuA{P&r@`&-`;I4JiyK?pF6@n + + + + + + + + + + + + + + diff --git a/android/res/values/splashscreentheme.xml b/android/res/values/splashscreentheme.xml new file mode 100644 index 0000000000..53b3673d83 --- /dev/null +++ b/android/res/values/splashscreentheme.xml @@ -0,0 +1,6 @@ + + + + diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml new file mode 100644 index 0000000000..e30fe0e84b --- /dev/null +++ b/android/res/xml/device_filter.xml @@ -0,0 +1,3 @@ + + + diff --git a/android/src/org/qtproject/example/jnimessenger/JniMessenger.java b/android/src/org/qtproject/example/jnimessenger/JniMessenger.java new file mode 100644 index 0000000000..750a643568 --- /dev/null +++ b/android/src/org/qtproject/example/jnimessenger/JniMessenger.java @@ -0,0 +1,113 @@ +package org.qtproject.example.jnimessenger; +import android.content.Context; +import android.hardware.usb.UsbManager; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.content.IntentFilter; +import android.app.PendingIntent; +import android.content.pm.PackageManager; +import android.content.Intent; +import android.content.BroadcastReceiver; +import android.os.ParcelFileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileDescriptor; +import android.util.Log; +import java.util.HashMap; +import java.util.Iterator; + + +public class JniMessenger +{ + + +private static final String ACTION_USB_PERMISSION = + "com.android.example.USB_PERMISSION"; +private static final String TAG = "MyActivity"; + +private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { + + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ACTION_USB_PERMISSION.equals(action)) { + synchronized (this) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + + if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + if(device != null){ + //call method to set up device communication + } + } + else { + Log.d(TAG, "permission denied for device " + device); + } + } + } + if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (device != null) { + // call your method that cleans up and closes communication with the device + } + } + } +}; + + private static native void callFromJava(String message); + + public JniMessenger() {} + + public static void printFromJava(String message) + { + System.out.println("This is printed from JAVA, message is: " + message); + callFromJava("Hello from JAVA!"); + } + + + UsbDeviceConnection connection; + UsbDevice device; + + public int getUsbFd(Context ctx) { + + UsbManager manager = (UsbManager) ctx.getSystemService(Context.USB_SERVICE); + + PendingIntent permissionIntent = PendingIntent.getBroadcast(ctx, 0, new Intent(ACTION_USB_PERMISSION), 0); + IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); + ctx.registerReceiver(usbReceiver, filter); + + HashMap deviceList = manager.getDeviceList(); + Iterator deviceIterator = deviceList.values().iterator(); + System.out.println("Devices found " + Integer.toString(deviceList.size())); + while(deviceIterator.hasNext()){ + device = deviceIterator.next(); + System.out.println("Device " + device.getDeviceName()); + manager.requestPermission(device, permissionIntent); + + // if(ContextCompat.checkSelfPermission(ACTION_USB_PERMISSION) == PackageManager.PERMISSION_GRANTED) { + try { + connection = manager.openDevice(device); + + + String fd = Integer.toString(connection.getFileDescriptor()); + String usbfsPath = device.getDeviceName(); + String serial = device.getSerialNumber(); + String man = device.getManufacturerName(); + String pn = device.getProductName(); + String vid = Integer.toString(device.getVendorId(),16); + String pid = Integer.toString(device.getProductId(),16); + + System.out.println("fd: " + fd + "\nusbfs: "+usbfsPath+"\nserial: "+serial+"\nvid: "+vid+" pid: "+ pid + "\nManufacturer: "+ man + "\nProduct Name: "+ pn); + return connection.getFileDescriptor(); + }catch(Exception e) {System.out.println(e);} + + //} + } + return -1; + } + + public String getUsbFs() { + return device.getDeviceName(); + } + + +}; + diff --git a/resources/icon_big.png b/resources/icon_big.png new file mode 100644 index 0000000000000000000000000000000000000000..3545c6ce893c5b6b2c1a43a4b868aaa81e07843b GIT binary patch literal 27511 zcmdSBWmuHo7dASy(v3*@Lqa80x>XPXk?vLj>CS-}kP=W3q-#K=JBJ#E5D<`%?vT!* zhnRRD|L1x?pX+=)A7JJhp1o)7wbx#8uX_`%{YI6F{4O~N1fo)VrK}4A5dv=sLAOYO zmoxu~YvAR!`zsSK5QzHczaN5R0cti7=pIN-`I)|d*8ZYT7RMwOe>}aAyxqI_MM~!_ zEl=n3ThE?{{Ix7EcoV8y|0d#sLP$4PJGZG-gZ|Yg+W!bW{6_ekPH)z}VR@B&?GF)8 zr#G=da);*jzPc$}Bs^Tk=dP%UjZ#Yns(ddeJ;J z!Ik8`#~lJOb-yxYWo7z}u9tlqMEt$rxNdOOLSK;kf;XIEj1$x}ydBH>>hr0&)MBXN zj!XQfPp|yVjW;@;Qp=s}61~>27yX(7vfl4;Ejy7L_F4@x4=l{5;^yXd*f=U6ieFDY z-PWLyDbs~sGs+xu!}U}+r={Kh(@q=kiv9HIeK9>d=YZE$_aKc_te($U5;5qVuwScD z(?ZDLtdixU;fLJZiM9hC{&Jf~^G1hWn-Rga+C(8WEUhZG)bJH|p_XG{mP?((j^&d_ zP3Hw<*#|b+JU>7kJ}<3${iipo)b|lr}Ogte1E69Sg@a}j%suTfNDAgSZGe&K9u9=R%C`xR&BU5owYy-zR=@P%h(yiw;(O!$f>RgK9H9N;5vq(`eqdkH*`sP? z+1wGR1FUmBylYvu_}4$<;4Ayo`3LfdG>{YocDfl++6Y!rR<7;L2--%E5e?7nN}TwA z_sd35+-z@(0JCtICN;|uyEf2VO_GB0Mc&DoPHF)%wMu2V|K-iH#PS*30oR!6-+h-u zRPoU*{S%;WXIlt~yk+3%EF@RNaF$4W(>?Zo>oh6z3HY2@cKw$C4C*yM^sOqkmRJER z-UaDHy`h9c3LOnyIz3NHSa`n#wTiaq{h^9#|-_UeKvovQf$T8=FlmG!( zlD0aZi72iJF5$l|?+_IKT9ffU1@zp{6tF=(9end`d7A)`Y`#>m_ie7I?GQb%1=Yz( zecMNy!Na=)T0!|_xpMKRqeE7}WYdhj1JZ7It$h|yQyj%sQHu(&E%XZfy(BkAWEebP z45?2*sX03iDgV|tP_UIYxMOv-Jkvh1xASj&szDN}bs9wlGAFwOE4%<6dPUwuy(SPv z@D|w2BDT>d6&3#k(3T!`oaph6KoErNJ$ZuA{P&r@`&-`;I4JiyK?pF6@n + Remove the comment if you do not require these default permissions. --> + + + Remove the comment if you do not require these default features. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java new file mode 100644 index 0000000000..087334e59f --- /dev/null +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Analog Devices Inc. + * + * This file is part of Scopy + * (see http://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.adi.scopy; + +import org.qtproject.qt5.android.bindings.QtActivity; + +import android.os.Bundle; +public class ScopyActivity extends QtActivity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() + { + super.onStart(); + } + + @Override + protected void onStop() + { + super.onStop(); + } +} diff --git a/android/src/org/adi/scopy/ScopyApplication.java b/android/src/org/adi/scopy/ScopyApplication.java new file mode 100644 index 0000000000..9b47be9203 --- /dev/null +++ b/android/src/org/adi/scopy/ScopyApplication.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 Analog Devices Inc. + * + * This file is part of Scopy + * (see http://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.adi.scopy; + +import org.qtproject.qt5.android.bindings.QtApplication; + +import java.io.File; +import java.io.IOException; +import android.system.Os; +import android.system.ErrnoException; + + +public class ScopyApplication extends QtApplication +{ + @Override + public void onCreate() + { + String src = getApplicationInfo().sourceDir; + System.out.println("sourcedir: "+ getApplicationInfo().sourceDir); + System.out.println("public sourcedir: "+ getApplicationInfo().publicSourceDir); + System.out.println("Hello application !"); + + try { + Os.setenv("PYTHONHOME",".",true); + Os.setenv("PYTHONPATH",src + "/assets/python3.8",true); + Os.setenv("SIGROKDECODE_DIR", src + "/assets/libsigrokdecode/decoders",true); + } + catch(ErrnoException x) { + System.out.println("Cannot set envvars"); + } + + super.onCreate(); + } +} From b4c60860aa7db0507583eeb4624170a672584c7f Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 1 Nov 2021 13:52:30 +0200 Subject: [PATCH 018/125] android: created libs folder for arm64-v8a Signed-off-by: Adrian Suciu --- android/libs/arm64-v8a/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 android/libs/arm64-v8a/.gitignore diff --git a/android/libs/arm64-v8a/.gitignore b/android/libs/arm64-v8a/.gitignore new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/android/libs/arm64-v8a/.gitignore @@ -0,0 +1 @@ +* From 411fa0ca9e1103633b87c738119f08685defb224 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 1 Nov 2021 13:58:20 +0200 Subject: [PATCH 019/125] android: make both apk and aab Signed-off-by: Adrian Suciu --- .github/workflows/androidbuild.yml | 11 +++++++---- CI/appveyor/build_scopy_apk.sh | 24 ++++++++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.github/workflows/androidbuild.yml b/.github/workflows/androidbuild.yml index 9c09ae04f3..1f3dcee429 100644 --- a/.github/workflows/androidbuild.yml +++ b/.github/workflows/androidbuild.yml @@ -10,15 +10,18 @@ jobs: steps: - uses: actions/checkout@v2 - name: Pull the Docker Image - run: docker pull ioanachelaruu/scopy-android:latest + run: docker pull analogdevices/scopy-build:android - name: Run Docker Image run: | docker run --privileged --net=host \ -v `pwd`:$GITHUB_WORKSPACE:rw \ -e "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ -e "BRANCH=${GITHUB_REF#refs/heads/}" \ - ioanachelaruu/scopy-android:latest /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/build_scopy_apk.sh + analogdevices/scopy-build:android /bin/bash -xe /home/runner/src/scopy-android-deps/clone_and_build.sh - uses: actions/upload-artifact@v2 with: - name: android-build-debug.apk - path: ${{ github.workspace }}/android-build-debug.apk + name: scopy-android + path: | + ${{ github.workspace }}/android-build-debug.apk + ${{ github.workspace }}/android-build-debug.aab + ${{ github.workspace }}/android-build-release.aab diff --git a/CI/appveyor/build_scopy_apk.sh b/CI/appveyor/build_scopy_apk.sh index 34585bf372..5a615f5928 100755 --- a/CI/appveyor/build_scopy_apk.sh +++ b/CI/appveyor/build_scopy_apk.sh @@ -1,16 +1,14 @@ #!/bin/bash set -xe -source $ANDROID_TOOLCHAIN_LOCATION/android_toolchain.sh $1 $2 +source ./android_toolchain.sh $1 $2 ARTIFACT_LOCATION=$GITHUB_WORKSPACE build_scopy() { - pushd $WORKDIR - rm -rf scopy git clone https://github.com/analogdevicesinc/scopy.git + pushd scopy - cd ${WORKDIR}/scopy git fetch origin $BRANCH git checkout FETCH_HEAD @@ -23,16 +21,30 @@ build_scopy() { cd build_$ABI make -j$JOBS - make -j$JOBS install cd .. ./android_deploy_qt.sh + cd build_$ABI + make apk + make aab + cd .. popd } move_artifact() { - sudo cp $WORKDIR/scopy/build_$ABI/android-build/build/outputs/apk/debug/android-build-debug.apk $ARTIFACT_LOCATION/ + pushd scopy + sudo cp ./build_$ABI/android-build/build/outputs/apk/debug/android-build-debug.apk $ARTIFACT_LOCATION/ + sudo cp ./build_$ABI/android-build/build/outputs/bundle/debug/android-build-debug.aab $ARTIFACT_LOCATION/ + sudo cp ./build_$ABI/android-build/build/outputs/bundle/release/android-build-release.aab $ARTIFACT_LOCATION/ + pushd $ARTIFACT_LOCATION + sudo chmod 644 ./android-build-debug.apk + sudo chmod 644 ./android-build-debug.aab + sudo chmod 644 ./android-build-release.aab + ls -la $ARTIFACT_LOCATION + popd + + popd } build_scopy From 3a6f6a74496971bd27f079bcfea550f7570b6eea Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 4 Nov 2021 10:04:47 +0200 Subject: [PATCH 020/125] ci: moved dockerfiles to their repositories https://github.com/analogdevicesinc/scopy-android-deps https://github.com/analogdevicesinc/scopy-mingw-build-deps https://github.com/analogdevicesinc/scopy-flatpak Signed-off-by: Adrian Suciu --- CI/appveyor/android-docker/Dockerfile | 83 --------------------------- CI/appveyor/docker/Dockerfile | 32 ----------- CI/appveyor/docker/README | 11 ---- CI/appveyor/mingw-docker/Dockerfile | 47 --------------- 4 files changed, 173 deletions(-) delete mode 100644 CI/appveyor/android-docker/Dockerfile delete mode 100644 CI/appveyor/docker/Dockerfile delete mode 100644 CI/appveyor/docker/README delete mode 100644 CI/appveyor/mingw-docker/Dockerfile diff --git a/CI/appveyor/android-docker/Dockerfile b/CI/appveyor/android-docker/Dockerfile deleted file mode 100644 index 32e91571b4..0000000000 --- a/CI/appveyor/android-docker/Dockerfile +++ /dev/null @@ -1,83 +0,0 @@ -FROM ubuntu:20.04 - -ARG USER=runner - -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update -RUN apt-get install -y git vim wget unzip sudo xserver-xorg openjdk-11-jre cmake libtool build-essential pkg-config autogen bison flex texinfo python-dev python-mako python-six swig3.0 python3-mako python3-numpy gettext libglib2.0-dev texinfo subversion libxkbcommon-x11-0 libxcb-xinerama0 libqt5gui5 libncurses5 - -RUN groupadd -g 1000 -r $USER -RUN useradd -u 1000 -g 1000 --create-home -r $USER - -#Change password -RUN echo "$USER:$USER" | chpasswd - -#Make sudo passwordless -RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER -RUN usermod -aG sudo $USER -RUN usermod -aG plugdev $USER - -USER $USER - -RUN mkdir -p /home/$USER/src -WORKDIR /home/$USER/src - -WORKDIR /home/$USER/src -RUN wget https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip -RUN unzip commandlinetools-linux-6200805_latest.zip - -RUN mkdir -p /home/$USER/Android/Sdk -ENV ANDROID_HOME /home/$USER/Android/Sdk -WORKDIR /home/$USER/src/tools/bin - -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} --licenses -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} "platforms;android-28" -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} "ndk;21.3.6528147" -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} "platform-tools" -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} "build-tools;30.0.3" -RUN yes | ./sdkmanager --sdk_root=${ANDROID_HOME} "cmdline-tools;latest" - -RUN sudo ln -s /home/$USER/Android/Sdk/platform-tools/adb /usr/local/bin/adb - -RUN wget https://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run -RUN chmod +x qt-unified-linux-x64-online.run - -ARG QT_USER -ARG QT_PASSWORD - -RUN ./qt-unified-linux-x64-online.run install --ao --al --aa DownloadError=Retry -c -m $QT_USER --pw $QT_PASSWORD qt.qt5.5152.android qt.tools.cmake -RUN sudo apt remove qtchooser - -RUN sudo ln -s /home/$USER/Qt/Tools/QtCreator/bin/qtcreator.sh /usr/local/bin/qtcreator - -WORKDIR /home/$USER/src -RUN git clone https://github.com/analogdevicesinc/scopy-android-deps.git - -WORKDIR /home/$USER/src/scopy-android-deps -RUN ./move_libs.sh aarch64 $USER -RUN ./rename_gradle.sh $USER - -RUN git submodule update --init gnuradio-android/ -RUN git submodule update --init gr-m2k/ -RUN git submodule update --init gr-scopy/ -RUN git submodule update --init libm2k/ -RUN git submodule update --init libsigrokdecode/ -RUN git submodule update --init --recursive libtinyiiod/ - -WORKDIR /home/$USER/src/scopy-android-deps/gnuradio-android -RUN git submodule update --init --recursive Boost-for-Android/ -RUN git submodule update --init libxml2/ -RUN git submodule update --init --recursive libiio/ -RUN git submodule update --init libad9361-iio/ -RUN git submodule update --init gr-iio/ -RUN git submodule update --init --recursive fftw3/ -RUN git submodule update --init --recursive gnuradio/ -RUN git submodule update --init --recursive libgmp/ -RUN git submodule update --init --recursive libusb/ -RUN git submodule update --init --recursive libzmq/ -RUN git submodule update --init --recursive volk/ - -WORKDIR /home/$USER/src/scopy-android-deps -ENV ANDROID_TOOLCHAIN_LOCATION=/home/$USER/src/scopy-android-deps -RUN ./download_deps_src.sh $USER -RUN ./android_deps_build.sh aarch64 $USER diff --git a/CI/appveyor/docker/Dockerfile b/CI/appveyor/docker/Dockerfile deleted file mode 100644 index 640fa68604..0000000000 --- a/CI/appveyor/docker/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:20.04 -ENV TZ=Europe/Bucharest -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update -y - -# Install base dependencies -RUN apt-get install -y software-properties-common build-essential git sudo apt-utils subversion - -# Install flatpak -RUN add-apt-repository ppa:alexlarsson/flatpak -y -RUN apt-get update -y -RUN apt install flatpak flatpak-builder -y - -# Install remote -RUN flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo -RUN flatpak install flathub org.kde.Platform//5.15 -y -RUN flatpak install flathub org.kde.Sdk//5.15 -y - -# Clean -RUN apt-get clean -y && apt-get autoclean -y - -RUN echo Cloning scopy-flatpak -ARG REPO_LINK=https://github.com/analogdevicesinc/scopy-flatpak -ARG REPO_LOCAL=/home/docker/scopy-flatpak -ARG REPO_BRANCH=master - -RUN git clone --recurse-submodules "$REPO_LINK" -b "$REPO_BRANCH" "$REPO_LOCAL" 2> /dev/null || (cd "$REPO_LOCAL"; git pull) - -#RUN cd /home/docker/scopy-flatpak && make -j4 -CMD ["/bin/bash"] - diff --git a/CI/appveyor/docker/README b/CI/appveyor/docker/README deleted file mode 100644 index 4e9a665fdd..0000000000 --- a/CI/appveyor/docker/README +++ /dev/null @@ -1,11 +0,0 @@ -KDE Runtime Docker image based on Ubuntu 18.04 - -The provided Dockerfile will install the KDE Runtime over the Ubuntu 18.04 base image. -Also, the obtained Docker image will contain the clone of https://github.com/analogdevicesinc/scopy-flatpak the scopy-flatpak recipe repository which can be found at /home/docker/scopy-flatpak - -In order to build the Flatpak package for Scopy inside this Docker image, it needs to be run using "--privileged" (otherwise there is a lack of access to necessary utilities). - -We built Scopy Flatpak in this image, then saved the newly created image as alexandratr/scopy-flatpak-bionic:scopy on Dockerhub. However, a clean image (without the Scopy Flatpak build) can be found as alexandratr/scopy-flatpak-bionic:clean on Dockerhub. - -Running make inside the scopy-flatpak folder will build the Scopy.flatpak artifact. An example of how to do this is illustrated in the "build_appveyor_flatpak.sh" and "inside_flatpak_docker.sh" scripts (which deploys a Scopy.flatpak for each Appveyor nightly build). - diff --git a/CI/appveyor/mingw-docker/Dockerfile b/CI/appveyor/mingw-docker/Dockerfile deleted file mode 100644 index 6ac104b969..0000000000 --- a/CI/appveyor/mingw-docker/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -# select as base image matching your host to get process isolation -FROM mcr.microsoft.com/windows/servercore:2004 - -SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] -# this creates an empty file named recycle.bin which essentialy disables the recycle bin -# this hack is needed because msys seems to create some files with wierd names in recycle bin -# that freeze the docker layer when commiting -# dirty hack -RUN echo . > 'C:\$RECYCLE.BIN'; - -RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \ -Invoke-WebRequest -UseBasicParsing -uri "https://github.com/msys2/msys2-installer/releases/download/2021-07-25/msys2-base-x86_64-20210725.sfx.exe" -OutFile msys2.exe; \ -.\msys2.exe -y -oC:\\; \ -Remove-Item msys2.exe; -RUN 'mkdir C:/msys64/home/docker/ && cd C:/msys64/home/docker/' - -SHELL ["cmd", "/S", "/C"] -# set envvars PATH HOME CHERE_INVOKING MSYS_SYSTEM - https://www.msys2.org/docs/ci/#other-systems -RUN setx PATH C:\msys64\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH% & \ -setx HOME C:\msys64\home\docker & \ -setx CHERE_INVOKING yes & \ -setx MSYSTEM MINGW64 - -WORKDIR C:\\msys64\\home\\docker -RUN C:\msys64\usr\bin\bash.exe -lc " " -SHELL ["cmd", "/S", "/C"] -RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu" & C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu" & C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Scc " -# install general packages needed to run build scripts -RUN C:\msys64\usr\bin\bash.exe -c 'pacman --noconfirm --needed -Sy git' -RUN C:\msys64\usr\bin\bash.exe -c "git clone https://github.com/analogdevicesinc/scopy-mingw-build-deps --branch refactor " - -# install packages and build scopy-deps -ARG BUILD_TARGET=x86_64 -ENV BUILD_TARGET=$BUILD_TARGET -RUN echo %BUILD_TARGET% - -# exit 0 is required because init_staging force terminates the msys process resulting in an error - it is not an error -RUN C:\msys64\usr\bin\bash.exe -c "cd /home/docker/scopy-mingw-build-deps && ls && echo Building for $BUILD_TARGET &&./init_staging.sh $BUILD_TARGET" & exit 0 -RUN C:\msys64\usr\bin\bash.exe -c "cd /home/docker/scopy-mingw-build-deps && source build.sh $BUILD_TARGET && install_tools && install_deps && recurse_submodules" -RUN C:\msys64\home\docker\scopy-mingw-build-deps\is.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART /LOG=C:\msys64\home\docker\iss.log /DIR=C:\innosetup -RUN C:\msys64\usr\bin\bash.exe -c "cd /home/docker/scopy-mingw-build-deps && source build.sh $BUILD_TARGET && build_deps" -# clone scopy - -# build scopy - - - From 2e7c9010a0a90e27da8330e29ec1114a6bd3be7c Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 4 Nov 2021 13:10:03 +0200 Subject: [PATCH 021/125] android: use nicer splash screen Signed-off-by: Adrian Suciu --- android/AndroidManifest.xml | 145 ++++++++++----------- android/res/drawable/splashscreen.xml | 4 +- android/res/drawable/splashscreen_land.xml | 4 +- android/res/drawable/splashscreen_port.xml | 4 +- 4 files changed, 76 insertions(+), 81 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 670f0069c1..c83544333d 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + Remove the comment if you do not require these default permissions. --> @@ -12,81 +12,76 @@ + Remove the comment if you do not require these default features. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/res/drawable/splashscreen.xml b/android/res/drawable/splashscreen.xml index 1abf5cd55d..405a7fae44 100644 --- a/android/res/drawable/splashscreen.xml +++ b/android/res/drawable/splashscreen.xml @@ -2,10 +2,10 @@ - + - + diff --git a/android/res/drawable/splashscreen_land.xml b/android/res/drawable/splashscreen_land.xml index cc418daa29..0a0b3e8e18 100644 --- a/android/res/drawable/splashscreen_land.xml +++ b/android/res/drawable/splashscreen_land.xml @@ -2,10 +2,10 @@ - + - + diff --git a/android/res/drawable/splashscreen_port.xml b/android/res/drawable/splashscreen_port.xml index 64830bb0cb..25fe888676 100644 --- a/android/res/drawable/splashscreen_port.xml +++ b/android/res/drawable/splashscreen_port.xml @@ -2,10 +2,10 @@ - + - + From cdaa56a59655a65eaa023c0ca9fbace2cdeff67b Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Wed, 6 Oct 2021 09:28:35 +0000 Subject: [PATCH 022/125] fixed checking for updates Signed-off-by: Ioana Chelaru --- android/AndroidManifest.xml | 4 ++-- src/preferences.cpp | 2 +- src/tool_launcher.cpp | 23 +++++++++++------------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index c83544333d..4b6537c56d 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,7 +1,7 @@ + Remove the comment if you do not require these default permissions. --> @@ -12,7 +12,7 @@ + Remove the comment if you do not require these default features. --> diff --git a/src/preferences.cpp b/src/preferences.cpp index 5c3008c6b1..82765e35e5 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -68,7 +68,7 @@ Preferences::Preferences(QWidget *parent) : m_skipCalIfCalibrated(true), automatical_version_checking_enabled(false), first_application_run(true), - check_updates_url("https://swdownloads.analog.com/cse/sw_versions.json"), + check_updates_url("http://swdownloads.analog.com/cse/sw_versions.json"), m_colorEditor(nullptr), m_logging_enabled(false) { diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index bedb801d7e..fcb34766f5 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -316,19 +316,18 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : } #if __ANDROID__ - auto result = QtAndroid::checkPermission(QString("android.permission.WRITE_EXTERNAL_STORAGE")); - if(result == QtAndroid::PermissionResult::Denied){ - QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync(QStringList({"android.permission.WRITE_EXTERNAL_STORAGE"})); - if(resultHash["android.permission.WRITE_EXTERNAL_STORAGE"] == QtAndroid::PermissionResult::Denied) - return; - } - result = QtAndroid::checkPermission(QString("android.permission.READ_EXTERNAL_STORAGE")); - if(result == QtAndroid::PermissionResult::Denied){ - QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync(QStringList({"android.permission.READ_EXTERNAL_STORAGE"})); - if(resultHash["android.permission.READ_EXTERNAL_STORAGE"] == QtAndroid::PermissionResult::Denied) - return; + const QVector permissions({"android.permission.READ_EXTERNAL_STORAGE", + "android.permission.WRITE_EXTERNAL_STORAGE", + "android.permission.INTERNET"}); + + for(const QString &permission : permissions) { + auto result = QtAndroid::checkPermission(permission); + if(result == QtAndroid::PermissionResult::Denied) { + auto resultHash = QtAndroid::requestPermissionsSync(QStringList({permission})); + if(resultHash[permission] == QtAndroid::PermissionResult::Denied) + return; + } } - #endif // skip_calibration=true; From a650e401ad390ca775f0598519c1d7562bda33f5 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 8 Oct 2021 15:08:02 +0300 Subject: [PATCH 023/125] gui: disabled tool detaching for android Signed-off-by: ioanachelaru --- src/networkanalyzerbufferviewer.cpp | 2 + src/preferences.cpp | 3 ++ src/tool.cpp | 6 +++ src/tool.hpp | 4 ++ src/tool_launcher.cpp | 4 ++ src/toolmenu.cpp | 6 ++- src/toolmenu.h | 3 ++ ui/preferences.ui | 74 +++++++++++++++-------------- 8 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/networkanalyzerbufferviewer.cpp b/src/networkanalyzerbufferviewer.cpp index cacda75e42..cf104f817a 100644 --- a/src/networkanalyzerbufferviewer.cpp +++ b/src/networkanalyzerbufferviewer.cpp @@ -194,7 +194,9 @@ void NetworkAnalyzerBufferViewer::sendBufferToOscilloscope() d_osc->add_ref_waveform("NA1", d_currentXdata, yData1, d_data[index].first.sampleRate); d_osc->add_ref_waveform("NA2", d_currentXdata, yData2, d_data[index].second.sampleRate); +#ifndef __ANDROID__ d_osc->detached(); +#endif } void NetworkAnalyzerBufferViewer::btnPreviousClicked() diff --git a/src/preferences.cpp b/src/preferences.cpp index 82765e35e5..33e54f6c1d 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -79,6 +79,9 @@ Preferences::Preferences(QWidget *parent) : ui->debugMessagesCheckbox->setVisible(false); ui->debugMessagesLbl->setVisible(false); +#ifdef __ANDROID__ + ui->doubleClickToDetachWidget->setDisabled(true); +#endif connect(ui->doubleClickCheckBox, &QCheckBox::stateChanged, [=](int state){ double_click_to_detach = (!state ? false : true); Q_EMIT notify(); diff --git a/src/tool.cpp b/src/tool.cpp index 8fc02f6b07..2d53ecb48d 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -20,6 +20,7 @@ #include "tool.hpp" #include "tool_launcher.hpp" + #include "detachedwindowsmanager.h" #include @@ -51,6 +52,7 @@ Tool::Tool(struct iio_context *ctx, ToolMenuItem *toolMenuItem, readPreferences(); +#ifndef __ANDROID__ if (api) { connect(api, &ApiObject::loadingFinished, this, &Tool::loadState); @@ -58,6 +60,7 @@ Tool::Tool(struct iio_context *ctx, ToolMenuItem *toolMenuItem, connect(toolMenuItem, &ToolMenuItem::detach, this, &Tool::detached); +#endif connect(this, &Tool::detachedState, toolMenuItem, &ToolMenuItem::setDetached); } @@ -123,6 +126,8 @@ void Tool::saveState() settings->sync(); } + +#ifndef __ANDROID__ void Tool::loadState() { bool isDetached = settings->value(name + "/detached").toBool(); @@ -160,6 +165,7 @@ void Tool::detached() this->window = window; } } +#endif void Tool::run() { diff --git a/src/tool.hpp b/src/tool.hpp index 70112e706b..ac9b509f5d 100644 --- a/src/tool.hpp +++ b/src/tool.hpp @@ -70,13 +70,17 @@ public Q_SLOTS: virtual void stop(); virtual void single(); virtual bool isRunning(); +#ifndef __ANDROID__ virtual void attached(); virtual void detached(); +#endif virtual void readPreferences(); private Q_SLOTS: void saveState(); +#ifndef __ANDROID__ void loadState(); +#endif protected: struct iio_context *ctx; diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index fcb34766f5..e796ccd6b1 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -369,8 +369,12 @@ void ToolLauncher::_setupToolMenu() menu->getButtonGroup()->addButton(ui->btnNotes); infoWidget = new InfoWidget(this); + +#ifndef __ANDROID__ connect(ui->homeWidget, &DetachDragZone::changeText, infoWidget, &InfoWidget::setText); +#endif + connect(menu, &ToolMenu::enableInfoWidget, infoWidget, &InfoWidget::enable); } diff --git a/src/toolmenu.cpp b/src/toolmenu.cpp index d57b7f33d9..f5e3610f21 100644 --- a/src/toolmenu.cpp +++ b/src/toolmenu.cpp @@ -54,11 +54,12 @@ ToolMenu::ToolMenu(Preferences *preferences, QWidget *parent): connect(this, &ToolMenu::itemMovedFromTo, this, &ToolMenu::_updateToolList); - +#ifndef __ANDROID__ connect(preferences, &Preferences::notify, this, &ToolMenu::_readPreferences); _readPreferences(); +#endif } ToolMenu::~ToolMenu() @@ -197,6 +198,8 @@ void ToolMenu::_loadState() settings.endArray(); } + +#ifndef __ANDROID__ void ToolMenu::_readPreferences() { for (int i = 0; i < d_tools.size(); ++i) { @@ -204,3 +207,4 @@ void ToolMenu::_readPreferences() item->enableDoubleClickToDetach(d_preferences->getDouble_click_to_detach()); } } +#endif diff --git a/src/toolmenu.h b/src/toolmenu.h index 5f8e82d98c..a5be3802c7 100644 --- a/src/toolmenu.h +++ b/src/toolmenu.h @@ -56,7 +56,10 @@ private Q_SLOTS: void _buildAllAvailableTools(); void _saveState(); void _loadState(); + +#ifndef __ANDROID__ void _readPreferences(); +#endif private: QStringList d_availableTools; diff --git a/ui/preferences.ui b/ui/preferences.ui index 12be93daf1..6fe641a81e 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -73,8 +73,8 @@ 0 0 - 1068 - 760 + 1082 + 752 @@ -1021,40 +1021,6 @@ - - - - - - - - - - - - - Double click to detach a tool - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -1706,6 +1672,42 @@ color: white; + + + + + + + + + + + + + + Double click to detach a tool + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + From db443fe70cdc2a8b57bf56a8f254f97281823a70 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 15 Oct 2021 11:17:02 +0300 Subject: [PATCH 024/125] android: close on back button pressed Signed-off-by: ioanachelaru --- src/tool_launcher.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index e796ccd6b1..fbfc55e079 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1950,5 +1950,42 @@ bool ToolLauncher::eventFilter(QObject *watched, QEvent *event) return true; } } + +#ifdef __ANDROID__ + else if(event->type() == QEvent::KeyRelease) { + QKeyEvent *ke = static_cast(event); + if (ke->key() == Qt::Key_Back) { + + QMessageBox* msgBox = new QMessageBox(this); + QSize mSize = msgBox->sizeHint(); // here's what you want, not m.width()/height() + QRect screenRect = QDesktopWidget().screenGeometry(); + + msgBox->setText("Are you sure you want to close Scopy?"); + + QPushButton* yesButton = new QPushButton("Yes"); + msgBox->addButton(yesButton ,QMessageBox::AcceptRole); + + QPushButton* noButton = new QPushButton("No"); + msgBox->addButton(noButton ,QMessageBox::RejectRole); + + msgBox->setModal(false); + msgBox->show(); + msgBox->activateWindow(); + msgBox->move( QPoint( screenRect.x() + screenRect.width()/2 - mSize.width()/2, + screenRect.y() + screenRect.height()/2 - mSize.height()/2 ) ); + + connect(yesButton, &QAbstractButton::clicked, [&] () { + // TODO: call save preferences + qApp->exit(); + }); + + connect(noButton, &QAbstractButton::clicked, [&] () { + event->ignore(); + }); + } + return true; + } +#endif + return QObject::eventFilter(watched, event); } From c85af0dc781fa6661170f20b69f94831d7f48153 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 22 Oct 2021 16:26:18 +0300 Subject: [PATCH 025/125] android: added zoom-out option on double click for all plots Signed-off-by: ioanachelaru --- src/DisplayPlot.cc | 12 ++++++++++++ src/DisplayPlot.h | 4 ++++ src/dbgraph.cpp | 12 ++++++++++++ src/dbgraph.hpp | 8 ++++++++ src/limitedplotzoomer.cpp | 5 ++++- src/limitedplotzoomer.h | 1 + src/network_analyzer.cpp | 4 ++++ src/nyquistGraph.cpp | 9 +++++++++ 8 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/DisplayPlot.cc b/src/DisplayPlot.cc index 7157c9d374..d2759bbd32 100644 --- a/src/DisplayPlot.cc +++ b/src/DisplayPlot.cc @@ -1972,6 +1972,18 @@ void DisplayPlot::_onYleftAxisWidgetScaleDivChanged() } } +#ifdef __ANDROID__ +void DisplayPlot::mousePressEvent(QMouseEvent *event) +{ + if (event->type() == QEvent::MouseButtonDblClick) { + for (unsigned int i = 0; i < d_zoomer.size(); ++i) { + OscPlotZoomer *zoomer = static_cast(d_zoomer[i]); + zoomer->popZoom(); + } + } +} +#endif + QwtPlotZoomer *DisplayPlot::getZoomer() const { if (d_zoomer.isEmpty()) diff --git a/src/DisplayPlot.h b/src/DisplayPlot.h index 91469c15c4..18359fbfe5 100644 --- a/src/DisplayPlot.h +++ b/src/DisplayPlot.h @@ -590,6 +590,10 @@ protected Q_SLOTS: void _onXbottomAxisWidgetScaleDivChanged(); void _onYleftAxisWidgetScaleDivChanged(); +#ifdef __ANDROID__ + void mousePressEvent(QMouseEvent *event); +#endif + protected: enum PlotMarker diff --git a/src/dbgraph.cpp b/src/dbgraph.cpp index fed6611956..f6a134ddd6 100644 --- a/src/dbgraph.cpp +++ b/src/dbgraph.cpp @@ -764,6 +764,11 @@ void dBgraph::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { Q_EMIT resetZoom(); +#ifdef __ANDROID__ + } else if (event->type() == QEvent::MouseButtonDblClick) { + zoomer->popZoom(); + Q_EMIT zoomOut(); +#endif } } @@ -772,6 +777,13 @@ void dBgraph::onResetZoom() zoomer->resetZoom(); } +#ifdef __ANDROID__ +void dBgraph::onZoomOut() +{ + zoomer->popZoom(); +} +#endif + void dBgraph::showEvent(QShowEvent *event) { d_hCursorHandle1->updatePosition(); diff --git a/src/dbgraph.hpp b/src/dbgraph.hpp index 42a5a8602c..8c993f7a5e 100644 --- a/src/dbgraph.hpp +++ b/src/dbgraph.hpp @@ -110,6 +110,10 @@ class dBgraph : public DisplayPlot void frequencySelected(double); void frequencyBarPositionChanged(int); +#ifdef __ANDROID__ + void zoomOut(); +#endif + public Q_SLOTS: void plot(double x, double y); void reset(); @@ -137,6 +141,10 @@ public Q_SLOTS: void mousePressEvent(QMouseEvent *event); void onResetZoom(); +#ifdef __ANDROID__ + void onZoomOut(); +#endif + void onFrequencyCursorPositionChanged(int pos); void onFrequencyBarMoved(double frequency); diff --git a/src/limitedplotzoomer.cpp b/src/limitedplotzoomer.cpp index bb39b54a1a..bc975c21cf 100644 --- a/src/limitedplotzoomer.cpp +++ b/src/limitedplotzoomer.cpp @@ -34,7 +34,10 @@ void LimitedPlotZoomer::resetZoom() QwtPlotZoomer::zoom(0); } - +void LimitedPlotZoomer::popZoom() +{ + QwtPlotZoomer::zoom(-1); +} void LimitedPlotZoomer::setBoundVertical(bool bound) { diff --git a/src/limitedplotzoomer.h b/src/limitedplotzoomer.h index d09125cba8..046bfa4b7e 100644 --- a/src/limitedplotzoomer.h +++ b/src/limitedplotzoomer.h @@ -28,6 +28,7 @@ class LimitedPlotZoomer : public QwtPlotZoomer public: LimitedPlotZoomer(QWidget*, bool doReplot = false); void resetZoom(); + void popZoom(); void setBoundVertical(bool bound); diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index bb223ed04c..dce50cd909 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -560,6 +560,10 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, connect(&m_dBgraph,SIGNAL(resetZoom()),&m_phaseGraph,SLOT(onResetZoom())); connect(&m_phaseGraph,SIGNAL(resetZoom()),&m_dBgraph,SLOT(onResetZoom())); +#ifdef __ANDROID__ + connect(&m_dBgraph, &dBgraph::zoomOut, &m_phaseGraph, &dBgraph::onZoomOut); + connect(&m_phaseGraph, &dBgraph::zoomOut, &m_dBgraph, &dBgraph::onZoomOut); +#endif connect(ui->rightMenu, &MenuAnim::finished, this, &NetworkAnalyzer::rightMenuFinished); diff --git a/src/nyquistGraph.cpp b/src/nyquistGraph.cpp index 078e634ed7..a97068a67e 100644 --- a/src/nyquistGraph.cpp +++ b/src/nyquistGraph.cpp @@ -256,6 +256,15 @@ void NyquistGraph::mousePressEvent(QMouseEvent *event) return; } +#ifdef __ANDROID__ + if (event->type() == QEvent::MouseButtonDblClick) { + zoomer->cancelZoom(); + QApplication::setOverrideCursor(Qt::CrossCursor); + QwtPolarPlot::mousePressEvent(event); + return; + } +#endif + if (zoomer->isZoomed()) QApplication::setOverrideCursor(Qt::ClosedHandCursor); From 8abbc1ddd7ddf4b1ffe7bc4dd84942700b280d5b Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 22 Oct 2021 16:26:43 +0300 Subject: [PATCH 026/125] updated .gitignore Signed-off-by: ioanachelaru --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index bf8218046e..28b186e8c4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ android/assets/python3.*/* !android/src/org/adi/scopy *.swp html/ +build_arm64-v8a/ +android_cmake.sh +android_deploy_qt.sh From d7d9508223d1a73e9ce3200dcf5f7f4e82d477ed Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 4 Nov 2021 18:50:51 +0200 Subject: [PATCH 027/125] android: enable iio-emu in android Signed-off-by: Adrian Suciu --- CI/appveyor/build_scopy_apk.sh | 1 + CMakeLists.txt | 4 +- .../src/org/adi/scopy/ScopyApplication.java | 44 +++++++++++-------- src/connectDialog.cpp | 16 +++++++ src/tool_launcher.cpp | 1 - 5 files changed, 43 insertions(+), 23 deletions(-) diff --git a/CI/appveyor/build_scopy_apk.sh b/CI/appveyor/build_scopy_apk.sh index 5a615f5928..bfc0e93fe7 100755 --- a/CI/appveyor/build_scopy_apk.sh +++ b/CI/appveyor/build_scopy_apk.sh @@ -8,6 +8,7 @@ build_scopy() { git clone https://github.com/analogdevicesinc/scopy.git pushd scopy + git submodule update --init --recursive iio-emu git fetch origin $BRANCH git checkout FETCH_HEAD diff --git a/CMakeLists.txt b/CMakeLists.txt index d2e2b81bf9..af407677dc 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,9 +368,7 @@ if (CLONE_IIO_EMU) if(NOT GIT_SUBMOD_RESULT EQUAL "0") message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") endif() - if(NOT ANDROID) - add_subdirectory(iio-emu) - endif() + add_subdirectory(iio-emu) endif() if(ANDROID) diff --git a/android/src/org/adi/scopy/ScopyApplication.java b/android/src/org/adi/scopy/ScopyApplication.java index 9b47be9203..2e3f7ecf71 100644 --- a/android/src/org/adi/scopy/ScopyApplication.java +++ b/android/src/org/adi/scopy/ScopyApplication.java @@ -30,23 +30,29 @@ public class ScopyApplication extends QtApplication { - @Override - public void onCreate() - { - String src = getApplicationInfo().sourceDir; - System.out.println("sourcedir: "+ getApplicationInfo().sourceDir); - System.out.println("public sourcedir: "+ getApplicationInfo().publicSourceDir); - System.out.println("Hello application !"); - - try { - Os.setenv("PYTHONHOME",".",true); - Os.setenv("PYTHONPATH",src + "/assets/python3.8",true); - Os.setenv("SIGROKDECODE_DIR", src + "/assets/libsigrokdecode/decoders",true); - } - catch(ErrnoException x) { - System.out.println("Cannot set envvars"); - } - - super.onCreate(); - } + @Override + public void onCreate() + { + String apk = getApplicationInfo().sourceDir; + System.out.println("sourcedir: "+ getApplicationInfo().sourceDir); + System.out.println("public sourcedir: "+ getApplicationInfo().publicSourceDir); + String libdir = getApplicationInfo().nativeLibraryDir; + System.out.println("native library dir:" + libdir); + System.out.println("Hello application !"); + + try { + Os.setenv("PYTHONHOME",".",true); + Os.setenv("PYTHONPATH",apk + "/assets/python3.8",true); + Os.setenv("SIGROKDECODE_DIR", apk + "/assets/libsigrokdecode/decoders",true); + Os.setenv("LD_LIBRARY_PATH", libdir, true); + Os.setenv("IIOEMU_BIN", libdir+"/iio-emu.so", true); + + } + + catch(ErrnoException x) { + System.out.println("Cannot set envvars"); + } + + super.onCreate(); + } } diff --git a/src/connectDialog.cpp b/src/connectDialog.cpp index bf811760cb..83bd74041d 100644 --- a/src/connectDialog.cpp +++ b/src/connectDialog.cpp @@ -102,20 +102,35 @@ void ConnectDialog::enableDemoBtn() QProcess killProcess; #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) killProcess.start("taskkill /im iio-emu /f"); +#else +#ifdef __ANDROID__ + killProcess.start("killall -9 iio-emu.so"); #else killProcess.start("pkill iio-emu"); +#endif #endif killProcess.waitForFinished(); // iio-emu found in system + +#ifdef __ANDROID__ + QString dirPath = qgetenv("IIOEMU_BIN"); + QString program =dirPath; +#else QString dirPath = QCoreApplication::applicationDirPath(); QString program = dirPath + "/iio-emu"; +#endif QStringList arguments; arguments.append(ui->demoDevicesComboBox->currentText()); process->setProgram(program); + //process->setArguments(QStringList("/"));//dirPath)); process->setArguments(arguments); process->start(); + auto started = process->waitForStarted(); + qDebug()<<"path"<readAllStandardOutput()); + qDebug()<<"stderr:"<readAllStandardError()); if (!started) { // retry to start the process // path for iio-emu when Scopy is built manually @@ -129,6 +144,7 @@ void ConnectDialog::enableDemoBtn() return; } } + qDebug()<<"Process " << program << "started"; ui->enableDemoBtn->setChecked(true); ui->enableDemoBtn->setText("Disable Demo"); diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index fbfc55e079..3ae33f54b0 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1523,7 +1523,6 @@ QPair adiscope::ToolLauncher::calibrate() ok = true; } } - ok = calib->calibrateAll(); } QMetaObject::invokeMethod(selectedDev->infoPage(), "setCalibrationStatusLabel", From 83ca3ddd5e95c95008bdc68ec45f0328ff4b48ef Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 5 Nov 2021 14:43:21 +0200 Subject: [PATCH 028/125] android: handle application restarts from Java Signed-off-by: Adrian Suciu --- android/src/org/adi/scopy/ScopyActivity.java | 19 +++++++++++++++++- src/application_restarter.cpp | 21 ++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java index 087334e59f..b3e0aeccaa 100644 --- a/android/src/org/adi/scopy/ScopyActivity.java +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -20,9 +20,15 @@ package org.adi.scopy; +import java.io.File; +import java.io.IOException; import org.qtproject.qt5.android.bindings.QtActivity; - +import android.content.pm.PackageManager; +import android.content.Intent; +import android.content.Context; +import android.content.ComponentName; import android.os.Bundle; + public class ScopyActivity extends QtActivity { @Override @@ -42,4 +48,15 @@ protected void onStop() { super.onStop(); } + + public void restart() { + System.out.println("-- ScopyActivity: Restarting "); + Context context = getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName()); + ComponentName componentName = intent.getComponent(); + Intent mainIntent = Intent.makeRestartActivityTask(componentName); + context.startActivity(mainIntent); + Runtime.getRuntime().exit(0); + } } diff --git a/src/application_restarter.cpp b/src/application_restarter.cpp index aa51d98690..e5d206c9a9 100644 --- a/src/application_restarter.cpp +++ b/src/application_restarter.cpp @@ -4,6 +4,10 @@ #include #include +#ifdef __ANDROID__ + #include +#endif + using namespace adiscope; ApplicationRestarter::ApplicationRestarter(const QString &executable) @@ -25,15 +29,20 @@ QStringList ApplicationRestarter::getArguments() const int ApplicationRestarter::restart(int exitCode) { - if (qApp->property("restart").toBool()) { - QProcess::startDetached(m_executable, m_arguments, m_currentPath); - } - - return exitCode; + if (qApp->property("restart").toBool()) { +#ifdef __ANDROID__ + QAndroidJniObject activity = QtAndroid::androidActivity(); + activity.callMethod("restart"); +#else + QProcess::startDetached(m_executable, m_arguments, m_currentPath); +#endif + } + + return exitCode; } void ApplicationRestarter::triggerRestart() { - qApp->setProperty("restart", QVariant(true)); + qApp->setProperty("restart", QVariant(true)); qApp->exit(); } From 532bf51578c55700e5842e3fa7b1b01dec1d23e2 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 8 Nov 2021 18:40:11 +0200 Subject: [PATCH 029/125] android: fix gnuradio in external storage bug Previously the application crashes if there is no gnuradio folder in external storage root. This fixes it by setting an envvar to the cache of the application. Signed-off-by: Adrian Suciu --- android/src/org/adi/scopy/ScopyApplication.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/src/org/adi/scopy/ScopyApplication.java b/android/src/org/adi/scopy/ScopyApplication.java index 2e3f7ecf71..5a5e1a3e34 100644 --- a/android/src/org/adi/scopy/ScopyApplication.java +++ b/android/src/org/adi/scopy/ScopyApplication.java @@ -34,16 +34,19 @@ public class ScopyApplication extends QtApplication public void onCreate() { String apk = getApplicationInfo().sourceDir; + String cache = getApplicationContext().getCacheDir().toString(); System.out.println("sourcedir: "+ getApplicationInfo().sourceDir); System.out.println("public sourcedir: "+ getApplicationInfo().publicSourceDir); String libdir = getApplicationInfo().nativeLibraryDir; System.out.println("native library dir:" + libdir); - System.out.println("Hello application !"); + System.out.println("applcation cache dir:" + cache); + System.out.println("Hello Scopy !"); try { Os.setenv("PYTHONHOME",".",true); Os.setenv("PYTHONPATH",apk + "/assets/python3.8",true); Os.setenv("SIGROKDECODE_DIR", apk + "/assets/libsigrokdecode/decoders",true); + Os.setenv("APPDATA", cache, true); Os.setenv("LD_LIBRARY_PATH", libdir, true); Os.setenv("IIOEMU_BIN", libdir+"/iio-emu.so", true); From 7ec371a12a7e644a78f9011ca8cb3cc9aa455788 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 8 Nov 2021 18:41:11 +0200 Subject: [PATCH 030/125] iio-emu: use IIOEMU_BIN envvar on all platforms Signed-off-by: Adrian Suciu --- src/connectDialog.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/connectDialog.cpp b/src/connectDialog.cpp index 83bd74041d..803700b6b3 100644 --- a/src/connectDialog.cpp +++ b/src/connectDialog.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include using namespace adiscope; @@ -112,18 +114,22 @@ void ConnectDialog::enableDemoBtn() killProcess.waitForFinished(); // iio-emu found in system + QString program = qgetenv("IIOEMU_BIN"); + if(program.isEmpty()) { + // Env Var not found, try local folder + QString dirPath = QCoreApplication::applicationDirPath(); + program = dirPath + "/iio-emu"; + QFileInfo fi(program); + if(!fi.exists() || fi.isDir()) { + // iio-emu is built in-tree + // development build + program = dirPath + "/iio-emu/iio-emu"; + } + } -#ifdef __ANDROID__ - QString dirPath = qgetenv("IIOEMU_BIN"); - QString program =dirPath; -#else - QString dirPath = QCoreApplication::applicationDirPath(); - QString program = dirPath + "/iio-emu"; -#endif QStringList arguments; arguments.append(ui->demoDevicesComboBox->currentText()); process->setProgram(program); - //process->setArguments(QStringList("/"));//dirPath)); process->setArguments(arguments); process->start(); @@ -131,18 +137,11 @@ void ConnectDialog::enableDemoBtn() qDebug()<<"path"<readAllStandardOutput()); qDebug()<<"stderr:"<readAllStandardError()); + if (!started) { - // retry to start the process - // path for iio-emu when Scopy is built manually - program = dirPath + "/iio-emu/iio-emu"; - process->setProgram(program); - process->start(); - started = process->waitForStarted(); - if (!started) { ui->description->setText("Server failed to start"); qDebug() << "Process failed to start"; return; - } } qDebug()<<"Process " << program << "started"; From 1eb00fb7039a85aff274ae68a501a2c6e9fc1764 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 2 Nov 2021 17:07:14 +0200 Subject: [PATCH 031/125] cmake: preprocess stylesheets using clang Signed-off-by: ioanachelaru --- CMakeLists.txt | 37 + resources/stylesheets/templates/default.qss.c | 891 ++++++++++++++++++ resources/stylesheets/templates/light.qss.c | 890 +++++++++++++++++ 3 files changed, 1818 insertions(+) create mode 100644 resources/stylesheets/templates/default.qss.c create mode 100644 resources/stylesheets/templates/light.qss.c diff --git a/CMakeLists.txt b/CMakeLists.txt index af407677dc..7a14e759b9 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,24 @@ endfunction() function(create_build_info_html_pages) endfunction() + + +function(configure_stylesheets DU_OPTION) + file(GLOB_RECURSE STYLESHEETS ${CMAKE_CURRENT_SOURCE_DIR}/resources/stylesheets/templates/*.qss.c) + + foreach(_stylesheet ${STYLESHEETS}) + string(REPLACE ".c" "" FILE_OUT ${_stylesheet}) + string(REPLACE "templates/" "" FILE_OUT ${FILE_OUT}) + + execute_process ( + COMMAND ${CMAKE_C_COMPILER} -E -P ${DU_OPTION} ${_stylesheet} -o ${FILE_OUT} + ) + + message(STATUS "Done preprocessing ${_stylesheet}, file written to: ${FILE_OUT}") + endforeach() + +endfunction() + # Get the GIT hash of the latest commit include(FindGit OPTIONAL) if (GIT_FOUND) @@ -267,6 +285,25 @@ if (APPLE) option(ENABLE_APPLICATION_BUNDLE "Enable application bundle for OSX" ON) endif(APPLE) +option(CONFIG_STYLESHEETS "Preprocess stylesheets using ${CMAKE_C_COMPILER}" OFF) +message(STATUS CONFIG_STYLESHEETS - ${CONFIG_STYLESHEETS}) +if (${CONFIG_STYLESHEETS}) + if (ANDROID) + set(PROCESSING_OPTIONS "-D__ANDROID__") + endif(ANDROID) + + if (APPLE) + set(PROCESSING_OPTIONS "-D__MACOS__") + endif(APPLE) + + if (WIN32) + set(PROCESSING_OPTIONS "-D__WINDOWS__") + endif(WIN32) + + configure_stylesheets("${PROCESSING_OPTIONS}") + +endif() + if (ENABLE_APPLICATION_BUNDLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") diff --git a/resources/stylesheets/templates/default.qss.c b/resources/stylesheets/templates/default.qss.c new file mode 100644 index 0000000000..1863df0a1b --- /dev/null +++ b/resources/stylesheets/templates/default.qss.c @@ -0,0 +1,891 @@ +/* Default background color */ +QMainWindow > .QWidget, adiscope--Sismograph > QwtPlotCanvas, QMessageBox, QToolTip { + background-color: #272730; +} + +/* Default settings for QWidget and its sub-classes */ +QWidget { + background-color: transparent; + color: #bebebe; + font-style: normal; + font-weight: normal; + font-size: 13px; +} + +QToolTip { + padding: 6px; + border: 1px solid rgba(149, 152, 154, 150); + color: white; +} + +QLabel { + color: rgba(255, 255, 255, 150); + font-size: 13px; + font-style: normal; + font-weight: normal; + text-align: left; +} + +QLabel:disabled, QRadioButton:disabled {color: rgba(255, 255, 255, 80); } + +QLabel[invalid=true] { color: red; } + +QLabel[valid=true] { color: rgba(255, 255, 255, 150) } + +QTextBrowser { background-color: rgba(0, 0, 0, 150); } + +/* Apply a gradient to the MenuAnim widgets */ +adiscope--MenuAnim, adiscope--DMM #widget_2, .adiscope--PowerController #rightMenu { + background-color: qlineargradient(spread:pad, x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba(0, 0, 0, 40), stop: 0.15 transparent, stop: 0.85 transparent, stop: 1.0 rgba(0, 0, 0, 40)); +} + +/* Except for the tool launcher's MenuAnim */ +QMainWindow > .QWidget > .adiscope--MenuAnim { background-color: none; } + +QwtPlot, QwtPolarPlot, pv--view--Viewport, QWidget[plot_container=true]{ + background-color: black; +} + +/* QwtPlot should have a black background, except the sismographs */ +adiscope--Sismograph { background-color: transparent; } + +QwtPlotCanvas { background-color: #141416; } + +QwtPolarCanvas { background-color: none; } + +QwtScaleWidget { color: rgba(255, 255, 255, 180); } + +QwtThermo { + color: #999999; + font-size: 26px; +} + +adiscope--BufferPreviewer { + border: 1px solid #7092be; + alternate-background-color: #4a64ff; + selection-background-color: #141416; + selection-color: #ff7200; + color: white; +} + +QDial { + background-color: black; + color: #4963ff; +} + +QTabWidget::tab-bar { left: 0; } + +QTabWidget::pane { border-top: 0px; } + +QTabBar::tab { + min-width: 100px; + min-height: 32px; + padding-bottom: 5px; + font: normal; +} + +QTabBar::tab:selected { + color: white; + border-bottom: 2px solid #f36d0a; + margin-top: 0px; +} + +QTabBar::tab:!selected { + border-bottom: 2px solid #373740; +} + +QRadioButton { + color: white; + spacing: 12px; +} + +QRadioButton::indicator { + width: 14px; + height: 14px; + border: 2px solid; + border-radius: 9px; + border-color: #4963FF; +} + +QRadioButton::indicator:checked { background-color: #4963FF; } + +QRadioButton::indicator:disabled { border-color: #555555; } + +QRadioButton::indicator:checked:disabled { background-color: #555555; } + +QLineEdit { + color: white; + font-size: 16px; + border: 0px solid gray; + border-bottom: 1px solid rgba(255, 255, 255, 102); + padding: 2px; +} + +QLineEdit[valid=true]{ color: white; } +QLineEdit[invalid=true]{ color: red; } + +QComboBox { + height: 24px; + border: none; + font-size: 14px; + + border-bottom: 1px solid rgba(255, 255, 255, 102); + padding-bottom: 4px; +} + +QComboBox:disabled, QLineEdit:disabled { color: #555555; } + +QComboBox QAbstractItemView { + border: none; + background-color: #1b1b21; + text-align: left; + color: #bebebe; + outline: none; + + border-bottom: 1px solid #bebebe; + border-top: 1px solid #bebebe; +} + +/* This only works on Windows */ +QComboBox QAbstractItemView { + selection-background-color: #272730; +} + +/* This does not work on Windows */ +QComboBox::item:selected { + font-weight: bold; + font-size: 18px; + border-bottom: 0px solid none; + background-color: #272730; +} + +QComboBox::drop-down { + subcontrol-position: center right; + border-image: url(:/icons/sba_cmb_box_arrow.svg); + width: 10px; + height: 6px; + font-size: 16px; + text-align: left; +} + +QComboBox::indicator { + background-color: transparent; + selection-background-color: transparent; + color: transparent; + selection-color: transparent; +} + +QMessageBox QLabel { + height: 420px; + width: 680px; + color: rgba(255,255,255,240); + font-size: 14px; + font: bold; +} + +QMessageBox QPushButton { + height: 20px; + width: 120px; + background-color: #4a64ff; + color: white; + font-size: 16px; + font: bold; + border-radius: 5px; + border-color: #4a64ff; + border-style: solid; + border-width: 1px; +} + +QAbstractSpinBox { + height: 45px; + font-size: 16px; + font-weight: bold; + border-bottom: 1px solid rgba(255, 255, 255, 102); +} + +QAbstractSpinBox::up-button, QAbstractSpinBox::down-button { + height: 20px; + width: 20px; +} + +QAbstractSpinBox::up-button { border-image: url(:/icons/sba_up_btn.svg); } + +QAbstractSpinBox::up-button:hover { border-image: url(:/icons/sba_up_btn_hover.svg); } + +QAbstractSpinBox::up-button:pressed { border-image: url(:/icons/sba_up_btn_pressed.svg); } + +QAbstractSpinBox::down-button { border-image: url(:/icons/sba_dn_btn.svg); } + +QAbstractSpinBox::down-button:hover { border-image: url(:/icons/sba_dn_btn_hover.svg); } + +QAbstractSpinBox::down-button:pressed { border-image: url(:/icons/sba_dn_btn_pressed.svg); } + +QGroupBox { + border: 2px solid rgba(255, 255, 255, 40); + border-radius: 8px; + color: rgba(255, 255, 255, 40); + font-size: 16px; + font-weight: bold; + margin-top: 9px; + padding-top: 5px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; /* position at the top center */ +} + +QScrollBar:vertical { + background: #262628; + max-width: 8px; +} + +QScrollBar::handle:vertical { + background: #404040; + border: 0; + border-radius: 3px; +} +QScrollBar::handle:vertical:hover { + background: #4a4a4b; + border: 0; + border-radius: 3px; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + border: none; + background: none; +} + +QScrollBar:horizontal { + background: #262628; + max-height: 6px; + border-radius: 3px; +} + +QScrollBar::handle:horizontal { + background: #404040; + border: 0; + border-radius: 3px; +} + +QScrollBar::handle:horizontal:hover { + background: #4a4a4b; + border: 0; + border-radius: 3px; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; } + +QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + border: none; + background: none; +} + +QDialog { background-color: #1b1b21; } + +ScopyAboutDialog QPushButton{ + background-color: #4a64ff; + color: #ffffff; +} + +QTextBrowser#aboutTextBrowser{ + background-color: transparent; + link-color: yellow; + selection-color: yellow; + color: white; +} + +/* Label, line and arrow button dividers between sections */ + +QLabel[subsection_label=true]{ + font-size: 12px; + color: rgba(255, 255, 255, 70); +} + +QFrame[subsection_line=true]{ border: 1px solid rgba(255, 255, 255, 70); } + +QPushButton[subsection_arrow_button=true]{ + max-height: 6px; + max-width: 10px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); +} + +QPushButton[subsection_arrow_button=true]:checked{ + max-height: 10px; + max-width: 6px; + border-image: url(:/icons/sba_cmb_box_arrow_right.svg); +} + + +/* Label associated with settings menu, right above a blue line divider */ + +QLabel[general_settings_label=true]{ + color: white; + font-size: 14px; + font-weight: normal; +} + +/* Blue line divider */ + +QFrame[blue_line=true]{ border: 2px solid #4A64FF; } + + +/* General style for blue buttons */ + +QPushButton[blue_button=true]{ + background-color: #4a64ff; + color: #ffffff; + border-radius: 4px; + font-size: 14px; +} + +QPushButton[blue_button=true]:pressed{ background-color: #2a44df; } + +#ifndef __ANDROID__ +QPushButton[blue_button=true]:hover{ background-color: #4a34ff; } +#endif + +QPushButton[blue_button=true]:disabled { background-color: grey; } + +/* Info button for each tool */ + +QPushButton[info_button=true]{ + min-height: 40px; + max-height: 40px; + min-width: 40px; + max-width: 40px; + background-color: transparent; + border-radius: 4px; +} + +#ifndef __ANDROID__ +QPushButton[info_button=true]:hover { background-color: rgba(0, 0, 0, 60); } +#endif + +/* Menu title label */ + +QLabel[menu_title_label=true]{ + color: white; + font-weight: normal; + font-size: 14px; +} + + +/* Style for all SpinBoxes */ + +QSpinBox{ + color: white; + border: 0px; + border-bottom: 1px solid rgba(255, 255, 255, 100); +} + + +QCheckBox { + spacing: 8px; + background-color: transparent; + font-size: 14px; + font-weight: bold; + color: rgba(255, 255, 255, 153); +} + +QCheckBox::indicator { + width: 14px; + height: 14px; + border: 2px solid rgb(74,100,255); + border-radius: 4px; +} + +QCheckBox::indicator:unchecked { background-color: transparent; } +QCheckBox::indicator:checked { background-color: rgb(74,100,255); } + + +QSlider::groove { + border: 1px solid #444444; + height: 2px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ + background: #999999; + margin: 2px 0; + border-radius: 2px; +} + +QSlider::handle { + background: rgb(73, 99, 255); + border: 0px solid; + width: 18px; + margin: -8px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + border-radius: 8px; +} + +QSlider::handle:disabled { background: #444444; } + +QSlider::sub-page:horizontal { background: #4A64FF; } + + +/* Style for settings and general settings buttons from all menus */ + +QPushButton[menu_icon_button=true] { + min-height: 40px; + max-height: 40px; + min-width: 60px; + max-width: 60px; + padding-top:5px +} + +QPushButton[menu_icon_button=true]:checked { border-top: 1px solid rgba(255,255,255,150); } + +QPushButton[menu_icon_button=true]:!checked { border-top: 1px solid rgba(255,255,255,0); } + +QPushButton[menu_icon_button=true]:!checked:hover { border-top: 1px solid rgba(255,255,255,0); } + +QPushButton[menu_icon_button=true]:checked:hover { border-top: 1px solid rgba(255,255,255,200); } + + +/* Style for all Tool launcher buttons, such as Home, Menu, Preferences, Load, Save, Notes, etc. */ + +QPushButton[tool_launcher_custom_widget=true] { + text-align:left; + border: none; + background-color: none; +} + +#ifndef __ANDROID__ +.QWidget[tool_launcher_custom_widget=true]:hover { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} +#endif + +#ifdef __ANDROID__ +.QWidget[tool_launcher_custom_widget=true]:pressed { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} +#endif + +QWidget[tool_launcher_custom_widget=true][selected=true] { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} + + +/* Style for icon-buttons such as Device, Home, Plus, etc. */ + +QWidget[menu_button=true][selected=true] { + opacity: 0.6; + border-radius: 4px; + background-color: #141416; +} + +QWidget[menu_button=true][selected=false] { background-color: transparent; } + + +/* Styles for DigitalIO widgets */ + +QWidget#page { + border-radius: 12px; + background-color: rgba(0, 0, 0, 80); +} + +QWidget#stackedWidgetPage1 { + border-radius: 12px; + background-color: #141416; +} + + +/* Background color for info page */ + +.adiscope--StackedHomepage#stackedWidget{ background-color: black; } + + +/* Style for all line separators */ + +QFrame[line_separator=true]{ background-color: rgba(255, 255, 255, 16); } + + +/* Background color for Voltmeter and Power Supply */ + +.adiscope--PowerController #leftPanel, .adiscope--DMM #leftPanel{ + background-color: rgba(0, 0, 0, 40); +} + + +/* Gradient background for all instruments */ + +.adiscope--Oscilloscope #mainWidget, .adiscope--SpectrumAnalyzer #mainWidget, +.adiscope--NetworkAnalyzer #mainWidget, .adiscope--SignalGenerator #mainWidget, +.adiscope--logic--LogicAnalyzer #mainWidget, .adiscope--logic--PatternGenerator #mainWidget, +.adiscope--DigitalIO #widget, .adiscope--DMM #hLayout_top_btn_area_voltmeter, .adiscope--PowerController #hWidget_top_area { + background-color: qlineargradient(spread:pad, x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 rgba(0, 0, 0, 40), stop: 0.1 transparent, stop: 1.0 transparent); +} + +/* Remove inherited gradient from the export setting of the Oscilloscope */ + +.adiscope--ExportSettings #mainWidget, .adiscope--ImportSettings #mainWidget { + background-color: none; +} + +adiscope--CursorReadouts { + background-color: black; +} + +QWidget[scopy_logo=true]{ + background-image: url(:/menu/scopy_title.png); + background-repeat: no-repeat; + background-position: left center; +} + +QPushButton[menu_icon=true]{ + background-image: url(:/menu/menu_button.png); + background-repeat: no-repeat; + background-position: left center; +} + +QWidget[adi_logo=true]{ + background-image: url(:/menu/adi.png); + background-repeat: no-repeat; + background-position: left center; + min-width: 104px; + min-height: 30px; +} + +QPushButton[save_logo=true]{ + background-image: url(:/icons/ic save.svg); + background-repeat: no-repeat; +} + +QPushButton[load_logo=true]{ + background-image: url(:/icons/ic load.svg); + background-repeat: no-repeat; + background-position: left center; +} + +QPushButton[preferences_icon=true]{ + background-image: url(:/menu/debugger.png); + background-repeat: no-repeat; + background-position: left center; + qproperty-text: "Preferences"; + text-align: center; +} + +QPushButton[general_settings_icon=true]:checked { + image: url(:/icons/gear_wheel_pressed.svg); +} + +QPushButton[general_settings_icon=true]:!checked { + image: url(:/icons/gear_wheel.svg); +} + + +QPushButton[general_settings_icon=true]:!checked:hover { + image: url(:/icons/gear_wheel_hover.svg); +} + +QPushButton[general_settings_icon=true]:checked:hover { + image: url(:/icons/gear_wheel_pressed.svg); +} + +QPushButton[settings_icon=true]:checked { + image: url(:/icons/setup3_checked_hover.svg); +} + +QPushButton[settings_icon=true]:!checked { + image: url(:/icons/setup3_unchecked.svg); +} + +QPushButton[settings_icon=true]:!checked:hover { + image: url(:/icons/setup3_unchecked_hover.svg); +} + +QPushButton[settings_icon=true]:checked:hover { + image: url(:/icons/setup3_checked_hover.svg); +} + +QHeaderView::section { + background-color: transparent; +} + +QHeaderView::section:checked { + background-color: transparent; +} + +/*************************************************************/ + + + +/************************** Channel widget **************************/ + +adiscope--ChannelWidget QWidget { + color: rgba(255, 255, 255, 153); + + border-width: 0px; + border-radius: 6px; +} + +/* Widget containing box, name, btn */ + +adiscope--ChannelWidget QWidget#widget { + background-color: transparent; + border-radius: 4px; +} +adiscope--ChannelWidget QWidget#widget[selected=true] { + background-color: rbga(20, 20, 22, 153); +} + +/* Round check box */ +adiscope--ChannelWidget QCheckBox#box{ + spacing: 0px; + background-color: none; + font-size: 14px; + font-weight: bold; +} +adiscope--ChannelWidget QCheckBox#box::indicator { + width: 14px; + height: 14px; + border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ + border-radius: 9px; +} +adiscope--ChannelWidget QCheckBox#box::indicator:unchecked { + background-color: transparent; +} +adiscope--ChannelWidget QCheckBox#box::indicator:checked { + background-color: #000000; /* Will be overwritted in the ChannelWidget constructor */ +} + +/* Name */ +adiscope--ChannelWidget QPushButton#name { + font-size: 14px; + font-weight: bold; + background-color: none; +} + +/* Delete Button */ +adiscope--ChannelWidget QPushButton#delBtn { + width: 24px; + height: 24px; + background-color: transparent; + background-position: center center; + background-repeat: no-repeat; + background-image: url(:/icons/redX.svg); +} +adiscope--ChannelWidget QPushButton#delBtn::hover { + background-image: url(:/icons/redX.svg); +} + +/* Menu button */ +adiscope--ChannelWidget QPushButton#btn { + width: 40px; + height: 20px; + background-color: transparent; +} +adiscope--ChannelWidget QPushButton#btn:pressed { + border-image: url(:/icons/setup_btn_checked.svg) +} +adiscope--ChannelWidget QPushButton#btn:!pressed { + border-image: url(:/icons/setup_btn_unchecked.svg) +} +adiscope--ChannelWidget QPushButton#btn:hover:!pressed:!checked { + border-image: url(:/icons/setup_btn_hover.svg) +} +adiscope--ChannelWidget QPushButton#btn:checked { + border-image: url(:/icons/setup_btn_checked.svg) +} + +/* Underline */ +adiscope--ChannelWidget QFrame#line { + border: 2px solid transparent; +} +adiscope--ChannelWidget QFrame#line[selected=true] { + border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ +} + +/*************************************************************/ + + + +/******************** Dropdown switch list ************************/ + +adiscope--DropdownSwitchList { + height: 30px; + border: 0px; + font-size: 18px; + border-radius: 4px; + padding-left: 20px; +} + +adiscope--DropdownSwitchList:editable{ + background-color: #141416; + color: white; +} + +adiscope--DropdownSwitchList::drop-down { + subcontrol-position: center right; + width: 10px; + height: 6px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); + margin-right: 20px; +} + +adiscope--DropdownSwitchList QAbstractItemView { + border: 0px; + background-color: #141416; + font-size: 18px; + outline: 0px; + + /* Add left space. Color should match background-color*/ + border-left: 0px solid #141416; /* setting to 0 for now */ +} + +adiscope--DropdownSwitchList QAbstractItemView::item { + color: #ffffff; + height: 60px; +} + +adiscope--DropdownSwitchList QAbstractItemView::item:hover { + background-color: #141416; + font-weight: bold; + border-bottom: 0px solid none; +} + +adiscope--DropdownSwitchList QHeaderView { + /* Cancel the effect of the QAbstractItemView border-left property. It's + necessary because the border (or padding) of the QAbstractItemView applies to + this element as well. */ + padding-left: -0px; +} + +adiscope--DropdownSwitchList QHeaderView:section { + color: rgba(255, 255, 255, 153); + background-color: #141416; + border: 0px; + font: 14px; +} + +adiscope--DropdownSwitchList QCheckBox { + background-color: #141416; +} + +adiscope--DropdownSwitchList QCheckBox::indicator { + width: 16px; + height: 16px; + subcontrol-position: center; +} + +/*************************************************************/ + + + +/************************* SpinBoxes *************************/ + +adiscope--SpinBoxA QPushButton#SBA_UpButton { + width: 30px; + height: 30px; + border-image: url(:/icons/sba_up_btn.svg); + border: 0px; +} + +adiscope--SpinBoxA QPushButton#SBA_UpButton:pressed { + border-image: url(:/icons/sba_up_btn_pressed.svg); +} +adiscope--SpinBoxA QPushButton#SBA_UpButton:hover:!pressed { + border-image: url(:/icons/sba_up_btn_hover.svg); +} + +adiscope--SpinBoxA QPushButton#SBA_DownButton { + width: 30px; + height: 30px; + border-image: url(:/icons/sba_dn_btn.svg); + border: 0px; +} +adiscope--SpinBoxA QPushButton#SBA_DownButton:pressed { + border-image: url(:/icons/sba_dn_btn_pressed.svg); +} +adiscope--SpinBoxA QPushButton#SBA_DownButton:hover:!pressed { + border-image: url(:/icons/sba_dn_btn_hover.svg); +} + +adiscope--SpinBoxA QLabel#SBA_Label { + color: rgba(255, 255, 255, 102); + font-size: 14px; +} + +adiscope--SpinBoxA QLineEdit#SBA_LineEdit { + height: 20px; + width: 75px; + font-size: 18px; + border: 0px; + bottom: 10px; +} + +adiscope--SpinBoxA QFrame#SBA_Line { + color: #4a64ff; +} + +adiscope--SpinBoxA QFrame#SBA_Line:disabled { + color: #555555; +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox { + height: 20px; + font-size: 12px; + font-weight: normal; + border-bottom: 0px; + padding-bottom: 0px; +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox::drop-down { + subcontrol-position: center right; + width: 10px; + height: 6px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox::drop-down:disabled { + subcontrol-position: center right; + width: 0px; + height: 0px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); +} + +adiscope--SpinBoxA QDial#SBA_CompletionCircle { + background-color: black; + color: #4963ff; +} +/*************************************************************/ + + +/******************** Stop buttons *************************/ + +QPushButton[stopButton=true] { + background-repeat: no-repeat; + background-position: center center; +} + +QPushButton[stopButton=true]:enabled { + background-image: url(:/icons/ico-stop.svg); +} + +QPushButton[stopButton=true][disabled=true][enabled=false] { + background-image: url(:/icons/ico-stop.svg); +} + +QPushButton[stopButton=true]:checked { + background-image: url(:/icons/ico-play-green.svg); +} + +/*************************************************************/ + +/******************** User Notes *************************/ + +QPushButton[userNote=true] { + background-image: url(:/icons/ic_note_unchecked.svg); +} + +QPushButton[userNote=true]:checked { + background-image: url(:/icons/ic_note_checked.svg); +} + +/*************************************************************/ diff --git a/resources/stylesheets/templates/light.qss.c b/resources/stylesheets/templates/light.qss.c new file mode 100644 index 0000000000..3dd72315a3 --- /dev/null +++ b/resources/stylesheets/templates/light.qss.c @@ -0,0 +1,890 @@ +/* Default background color */ +QMainWindow > .QWidget, adiscope--Sismograph > QwtPlotCanvas, QMessageBox, QToolTip { + background-color: #F7F7F7; +} + +/* Default settings for QWidget and its sub-classes */ +QWidget { + background-color: transparent; + color: #141416; + font-style: normal; + font-weight: normal; + font-size: 13px; +} + +QToolTip { + padding: 6px; + border: 1px solid rgba(149, 152, 154, 150); + color: rgba(0, 0, 0, 200); +} + +QLabel { + color: rgba(0, 0, 0, 200); + font-size: 13px; + font-style: normal; + font-weight: normal; + text-align: left; +} + +QLabel:disabled, QRadioButton:disabled {color: rgba(0, 0, 0, 70); } + +QLabel[invalid=true] { color: red; } + +QLabel[valid=true] { color: rgba(0, 0, 0, 200); } + +QTextBrowser { background-color: #EDEDED; } + +/* Apply a gradient to the MenuAnim widgets */ +adiscope--MenuAnim, adiscope--DMM #widget_2, .adiscope--PowerController #rightMenu { + background-color: qlineargradient(spread:pad, x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #EDEDED, stop: 0.15 #F7F7F7, stop: 0.85 #F7F7F7, stop: 1.0 #EDEDED); +} + +/* Except for the tool launcher's MenuAnim */ +QMainWindow > .QWidget > .adiscope--MenuAnim { background-color: none; } + +QwtPlot, QwtPolarPlot, pv--view--Viewport, QWidget[plot_container=true] { + background-color: white; +} + +/* QwtPlot should have a black background, except the sismographs */ +adiscope--Sismograph { background-color: transparent; } + +QwtPlotCanvas { background-color: white; } + +QwtPolarCanvas { background-color: none; } + +QwtScaleWidget { color: rgba(0, 0, 0, 180); } + +QwtThermo { + color: #999999; + font-size: 26px; +} + +adiscope--BufferPreviewer { + border: 1px solid #7092be; + alternate-background-color: #4a64ff; + selection-background-color: #F7F7F7; + selection-color: #ff7200; + color: white; +} + +QDial { + background-color: white; + color: #4963ff; +} + +QTabWidget::tab-bar { left: 0; } + +QTabWidget::pane { border-top: 0px; } + +QTabBar::tab { + min-width: 100px; + min-height: 32px; + padding-bottom: 5px; + font: normal; +} + +QTabBar::tab:selected { + color: #7D7D83; + border-bottom: 2px solid #f36d0a; + margin-top: 0px; +} + +QTabBar::tab:!selected { + border-bottom: 2px solid #7D7D83; +} + +QRadioButton { + color: rgba(0, 0, 0, 200); + spacing: 12px; +} + +QRadioButton::indicator { + width: 14px; + height: 14px; + border: 2px solid; + border-radius: 9px; + border-color: #4963FF; +} + +QRadioButton::indicator:checked { background-color: #4963FF; } + +QRadioButton::indicator:disabled { border-color: #C5C5C5; } + +QRadioButton::indicator:checked:disabled { background-color: #C5C5C5; } + +QLineEdit { + color: rgba(0, 0, 0, 200); + font-size: 16px; + border: 0px solid gray; + border-bottom: 1px solid rgba(0, 0, 0, 40); + padding: 2px; +} + +QLineEdit[valid=true]{ color: rgba(0, 0, 0, 200); } +QLineEdit[invalid=true]{ color: red; } + +QComboBox { + height: 24px; + border: none; + font-size: 14px; + + border-bottom: 1px solid rgba(0, 0, 0, 40); + padding-bottom: 4px; +} + +QComboBox:disabled, QLineEdit:disabled { color: rgba(0, 0, 0, 70); } + +QComboBox QAbstractItemView { + border: none; + background-color: white; + text-align: left; + color: black; + outline: none; + + border-bottom: 1px solid #bebebe; + border-top: 1px solid #bebebe; + + /* This only works on Windows */ + selection-color: black; + selection-background-color: #EDEDED; +} + +/* This does not work on Windows */ +QComboBox::item:selected { + font-weight: bold; + font-size: 18px; + border-bottom: 0px solid none; + background-color: #EDEDED; +} + +QComboBox::drop-down { + subcontrol-position: center right; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow.svg); + width: 10px; + height: 6px; + font-size: 16px; + text-align: left; +} + +QComboBox::indicator { + background-color: transparent; + selection-background-color: transparent; + color: transparent; + selection-color: transparent; +} + +QMessageBox QLabel { + height: 420px; + width: 680px; + color: rgba(0, 0, 0, 200); + font-size: 14px; + font: bold; +} + +QMessageBox QPushButton { + height: 20px; + width: 120px; + background-color: #4a64ff; + color: white; + font-size: 16px; + font: bold; + border-radius: 5px; + border-color: #4a64ff; + border-style: solid; + border-width: 1px; +} + +QAbstractSpinBox { + height: 45px; + font-size: 16px; + font-weight: bold; + border-bottom: 1px solid rgba(0, 0, 0, 40); +} + +QAbstractSpinBox::up-button, QAbstractSpinBox::down-button { + height: 20px; + width: 20px; +} + +QAbstractSpinBox::up-button { border-image: url(:/icons/sba_up_btn.svg); } + +QAbstractSpinBox::up-button:hover { border-image: url(:/icons/sba_up_btn_hover.svg); } + +QAbstractSpinBox::up-button:pressed { border-image: url(:/icons/sba_up_btn_pressed.svg); } + +QAbstractSpinBox::down-button { border-image: url(:/icons/sba_dn_btn.svg); } + +QAbstractSpinBox::down-button:hover { border-image: url(:/icons/sba_dn_btn_hover.svg); } + +QAbstractSpinBox::down-button:pressed { border-image: url(:/icons/sba_dn_btn_pressed.svg); } + +QGroupBox { + border: 2px solid rgba(255, 255, 255, 40); + border-radius: 8px; + color: rgba(255, 255, 255, 40); + font-size: 16px; + font-weight: bold; + margin-top: 9px; + padding-top: 5px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; /* position at the top center */ +} + +QScrollBar:vertical { + background: #F7F7F7; + max-width: 8px; +} + +QScrollBar::handle:vertical { + background: #BEBEC1; + border: 0; + border-radius: 3px; +} +QScrollBar::handle:vertical:hover { + background: #7D7D83; + border: 0; + border-radius: 3px; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + border: none; + background: none; +} + +QScrollBar:horizontal { + background: #F7F7F7; + max-height: 6px; + border-radius: 3px; +} + +QScrollBar::handle:horizontal { + background: #BEBEC1; + border: 0; + border-radius: 3px; +} + +QScrollBar::handle:horizontal:hover { + background: #7D7D83; + border: 0; + border-radius: 3px; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; } + +QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + border: none; + background: none; +} + +QDialog { background-color: white; } + +ScopyAboutDialog QPushButton{ + background-color: #4a64ff; + color: #ffffff; +} + +QTextBrowser#aboutTextBrowser{ + background-color: transparent; + link-color: yellow; + selection-color: yellow; + color: rgba(0, 0, 0, 200); +} + +/* Label, line and arrow button dividers between sections */ + +QLabel[subsection_label=true]{ + font-size: 12px; + color: rgba(0, 0, 0, 70); +} + +QFrame[subsection_line=true]{ border: 1px solid rgba(0, 0, 0, 50); } + +QPushButton[subsection_arrow_button=true]{ + max-height: 6px; + max-width: 10px; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow.svg); +} + +QPushButton[subsection_arrow_button=true]:checked{ + max-height: 10px; + max-width: 6px; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow_right.svg); +} + + +/* Label associated with settings menu, right above a blue line divider */ + +QLabel[general_settings_label=true]{ + color: rgba(0, 0, 0, 200); + font-size: 14px; + font-weight: normal; +} + +/* Blue line divider */ + +QFrame[blue_line=true]{ border: 2px solid #4A64FF; } + + +/* General style for blue buttons */ + +QPushButton[blue_button=true]{ + background-color: #4a64ff; + color: #ffffff; + border-radius: 4px; + font-size: 14px; +} + +QPushButton[blue_button=true]:pressed{ background-color: #2a44df; } + +#ifndef __ANDROID__ +QPushButton[blue_button=true]:hover{ background-color: #4a34ff; } +#endif + +QPushButton[blue_button=true]:disabled { background-color: grey; } + +/* Info button for each tool */ + +QPushButton[info_button=true]{ + min-height: 40px; + max-height: 40px; + min-width: 40px; + max-width: 40px; + background-color: transparent; + border-radius: 4px; +} + +#ifndef __ANDROID__ +QPushButton[info_button=true]:hover { background-color: rgba(0, 0, 0, 60); } +#endif + +/* Menu title label */ + +QLabel[menu_title_label=true]{ + color: rgba(0, 0, 0, 200); + font-weight: normal; + font-size: 14px; +} + + +/* Style for all SpinBoxes */ + +QSpinBox{ + color: rgba(0, 0, 0, 200); + border: 0px; + border-bottom: 1px solid rgba(0, 0, 0, 40); +} + + +QCheckBox { + spacing: 8px; + background-color: transparent; + font-size: 14px; + font-weight: normal; + color: rgba(0, 0, 0, 200); +} + +QCheckBox::indicator { + width: 14px; + height: 14px; + border: 2px solid rgb(74,100,255); + border-radius: 4px; +} + +QCheckBox::indicator:unchecked { background-color: transparent; } +QCheckBox::indicator:checked { background-color: rgb(74,100,255); } + + +QSlider::groove { + border: 1px solid #444444; + height: 2px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ + background: #999999; + margin: 2px 0; + border-radius: 2px; +} + +QSlider::handle { + background: rgb(73, 99, 255); + border: 0px solid; + width: 18px; + margin: -8px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ + border-radius: 8px; +} + +QSlider::handle:disabled { background: #444444; } + +QSlider::sub-page:horizontal { background: #4A64FF; } + + +/* Style for settings and general settings buttons from all menus */ + +QPushButton[menu_icon_button=true] { + min-height: 40px; + max-height: 40px; + min-width: 60px; + max-width: 60px; + padding-top:5px +} + +QPushButton[menu_icon_button=true]:checked { border-top: 1px solid rgba(0, 0, 0, 150); } + +QPushButton[menu_icon_button=true]:!checked { border-top: 1px solid rgba(0, 0, 0, 0); } + +QPushButton[menu_icon_button=true]:!checked:hover { border-top: 1px solid rgba(0, 0, 0, 0); } + +QPushButton[menu_icon_button=true]:checked:hover { border-top: 1px solid rgba(0, 0, 0, 200); } + + +/* Style for all Tool launcher buttons, such as Home, Menu, Preferences, Load, Save, Notes, etc. */ + +QPushButton[tool_launcher_custom_widget=true] { + text-align:left; + border: none; + background-color: none; +} + +#ifndef __ANDROID__ +.QWidget[tool_launcher_custom_widget=true]:hover { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} +#endif + +#ifdef __ANDROID__ +.QWidget[tool_launcher_custom_widget=true]:pressed { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} +#endif + +QWidget[tool_launcher_custom_widget=true][selected=true] { + background-color: rgba(0, 0, 0, 60); + border: 1px solid rgba(0, 0, 0, 30); + border-radius:5px; +} + + +/* Style for icon-buttons such as Device, Home, Plus, etc. */ + +QWidget[menu_button=true][selected=true] { + opacity: 0.6; + border-radius: 4px; + background-color: #C5C5C5; +} + +QWidget[menu_button=true][selected=false] { background-color: transparent; } + + +/* Styles for DigitalIO widgets */ + +QWidget#page { + border-radius: 12px; + background-color: rgba(0, 0, 0, 40); +} + +QWidget#stackedWidgetPage1 { + border-radius: 12px; + background-color: rgba(0, 0, 0, 80); +} + + +/* Background color for info page */ + +.adiscope--StackedHomepage#stackedWidget{ background-color: #EDEDED; } + + +/* Style for all line separators */ + +QFrame[line_separator=true]{ background-color: rgba(0, 0, 0, 20); } + + +/* Background color for Voltmeter and Power Supply */ + +.adiscope--PowerController #leftPanel, .adiscope--DMM #leftPanel{ + background-color: #EDEDED; +} + + +/* Gradient background for all instruments */ + +.adiscope--Oscilloscope #mainWidget, .adiscope--SpectrumAnalyzer #mainWidget, +.adiscope--NetworkAnalyzer #mainWidget, .adiscope--SignalGenerator #mainWidget, +.adiscope--logic--LogicAnalyzer #mainWidget, .adiscope--logic--PatternGenerator #mainWidget, +.adiscope--DigitalIO #widget, .adiscope--DMM #hLayout_top_btn_area_voltmeter, .adiscope--PowerController #hWidget_top_area { + background-color: qlineargradient(spread:pad, x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #EDEDED, stop: 0.1 #F7F7F7, stop: 1.0 #F7F7F7); +} + +/* Remove inherited gradient from the export setting of the Oscilloscope */ + +.adiscope--ExportSettings #mainWidget, .adiscope--ImportSettings #mainWidget { + background-color: none; +} + +adiscope--CursorReadouts { + background-color: white; +} + +QWidget[scopy_logo=true]{ + background-image: url(:/icons/scopy-light/icons/logo.png); + background-repeat: no-repeat; + background-position: left center; +} + +QPushButton[menu_icon=true]{ + background-image: url(:/icons/scopy-light/icons/menu.png); + background-repeat: no-repeat; + background-position: left center; +} + +QWidget[adi_logo=true]{ + background-image: url(:/icons/scopy-light/icons/logo analog.png); + background-repeat: no-repeat; + background-position: left center; + min-width: 104px; + min-height: 30px; +} + +QPushButton[save_logo=true]{ + background-image: url(:/icons/scopy-light/icons/ic save.svg); + background-repeat: no-repeat; +} + +QPushButton[load_logo=true]{ + background-image: url(:/icons/scopy-light/icons/ic load.svg); + background-repeat: no-repeat; + background-position: left center; +} + +QPushButton[preferences_icon=true]{ + background-image: url(:/icons/scopy-light/icons/Gear.png); + background-repeat: no-repeat; + background-position: left center; + qproperty-text: "Preferences"; + text-align: center; +} + +QPushButton[general_settings_icon=true]:checked { + image: url(:/icons/scopy-light/icons/gear_wheel_pressed.svg); +} + +QPushButton[general_settings_icon=true]:!checked { + image: url(:/icons/scopy-light/icons/Gear.png); +} + + +QPushButton[general_settings_icon=true]:!checked:hover { + image: url(:/icons/scopy-light/icons/gear_wheel_hover.png); +} + +QPushButton[general_settings_icon=true]:checked:hover { + image: url(:/icons/scopy-light/icons/gear_wheel_pressed.svg); +} + +QPushButton[settings_icon=true]:checked { + image: url(:/icons/scopy-light/icons/setup3_checked_hover.svg); +} + +QPushButton[settings_icon=true]:!checked { + image: url(:/icons/scopy-light/icons/setup3_unchecked.png); +} + +QPushButton[settings_icon=true]:!checked:hover { + image: url(:/icons/scopy-light/icons/setup3_unchecked_hover.png); +} + +QPushButton[settings_icon=true]:checked:hover { + image: url(:/icons/scopy-light/icons/setup3_checked_hover.svg); +} + +QHeaderView::section { + background-color: #D3D3D3; +} + +QHeaderView::section:checked { + background-color: #D3D3D3; +} + +/*************************************************************/ + + + +/*********************** Channel widget *******************/ + +adiscope--ChannelWidget QWidget { + color: rgba(0, 0, 0, 170); + + border-width: 0px; + border-radius: 6px; +} + +/* Widget containing box, name, btn */ +adiscope--ChannelWidget QWidget#widget { + background-color: transparent; + border-radius: 4px; +} +adiscope--ChannelWidget QWidget#widget[selected=true] { + background-color: rbga(0, 0, 0, 40); +} + +/* Round check box */ +adiscope--ChannelWidget QCheckBox#box{ + spacing: 0px; + background-color: none; + font-size: 14px; + font-weight: bold; +} +adiscope--ChannelWidget QCheckBox#box::indicator { + width: 14px; + height: 14px; + border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ + border-radius: 9px; +} +adiscope--ChannelWidget QCheckBox#box::indicator:unchecked { + background-color: transparent; +} +adiscope--ChannelWidget QCheckBox#box::indicator:checked { + background-color: #000000; /* Will be overwritted in the ChannelWidget constructor */ +} + +/* Name */ +adiscope--ChannelWidget QPushButton#name { + font-size: 14px; + font-weight: bold; + background-color: none; +} + +/* Delete Button */ +adiscope--ChannelWidget QPushButton#delBtn { + width: 24px; + height: 24px; + background-color: transparent; + background-position: center center; + background-repeat: no-repeat; + background-image: url(:/icons/redX.svg); +} +adiscope--ChannelWidget QPushButton#delBtn::hover { + background-image: url(:/icons/redX.svg); +} + +/* Menu button */ +adiscope--ChannelWidget QPushButton#btn { + width: 40px; + height: 20px; + background-color: transparent; +} +adiscope--ChannelWidget QPushButton#btn:pressed { + border-image: url(:/icons/setup_btn_checked.svg) +} +adiscope--ChannelWidget QPushButton#btn:!pressed { + border-image: url(:/icons/setup_btn_unchecked.svg) +} +adiscope--ChannelWidget QPushButton#btn:hover:!pressed:!checked { + border-image: url(:/icons/setup_btn_hover.svg) +} +adiscope--ChannelWidget QPushButton#btn:checked { + border-image: url(:/icons/setup_btn_checked.svg) +} + +/* Underline */ +adiscope--ChannelWidget QFrame#line { + border: 2px solid transparent; +} +adiscope--ChannelWidget QFrame#line[selected=true] { + border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ +} + +/*************************************************************/ + + + +/******************* Dropdown ***************/ + +adiscope--DropdownSwitchList { + height: 30px; + border: 0px; + font-size: 18px; + border-radius: 4px; + padding-left: 20px; +} + +adiscope--DropdownSwitchList:editable{ + background-color: white; + color: rgba(0, 0, 0, 200); +} + +adiscope--DropdownSwitchList::drop-down { + subcontrol-position: center right; + width: 10px; + height: 6px; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow.svg); + margin-right: 20px; +} + +adiscope--DropdownSwitchList QAbstractItemView { + border: 0px; + background-color: white; + font-size: 18px; + outline: 0px; + + /* Add left space. Color should match background-color*/ + border-left: 0px solid white; /* setting to 0 for now */ +} + +adiscope--DropdownSwitchList QAbstractItemView::item { + color: rgba(0, 0, 0, 200); + height: 60px; +} + +adiscope--DropdownSwitchList QAbstractItemView::item:hover { + background-color: #EDEDED; + font-weight: bold; + border-bottom: 0px solid none; +} + +adiscope--DropdownSwitchList QHeaderView { + /* Cancel the effect of the QAbstractItemView border-left property. It's + necessary because the border (or padding) of the QAbstractItemView applies to + this element as well. */ + padding-left: -0px; +} + +adiscope--DropdownSwitchList QHeaderView:section { + color: rgba(0, 0, 0, 200); + background-color: white; + border: 0px; + font: 14px; +} + +adiscope--DropdownSwitchList QCheckBox { + background-color: white; +} + +adiscope--DropdownSwitchList QCheckBox::indicator { + width: 16px; + height: 16px; + subcontrol-position: center; +} + +/*************************************************************/ + + + +/**************** SpinBoxes *****************/ + +adiscope--SpinBoxA QPushButton#SBA_UpButton { + width: 30px; + height: 30px; + border-image: url(:/icons/sba_up_btn.svg); + border: 0px; +} + +adiscope--SpinBoxA QPushButton#SBA_UpButton:pressed { + border-image: url(:/icons/sba_up_btn_pressed.svg); +} +adiscope--SpinBoxA QPushButton#SBA_UpButton:hover:!pressed { + border-image: url(:/icons/sba_up_btn_hover.svg); +} + +adiscope--SpinBoxA QPushButton#SBA_DownButton { + width: 30px; + height: 30px; + border-image: url(:/icons/sba_dn_btn.svg); + border: 0px; +} +adiscope--SpinBoxA QPushButton#SBA_DownButton:pressed { + border-image: url(:/icons/sba_dn_btn_pressed.svg); +} +adiscope--SpinBoxA QPushButton#SBA_DownButton:hover:!pressed { + border-image: url(:/icons/sba_dn_btn_hover.svg); +} + +adiscope--SpinBoxA QLabel#SBA_Label { + color: rgba(0, 0, 0, 200); + font-size: 14px; +} + +adiscope--SpinBoxA QLineEdit#SBA_LineEdit { + height: 20px; + width: 75px; + font-size: 18px; + border: 0px; + bottom: 10px; +} + +adiscope--SpinBoxA QFrame#SBA_Line { + color: #4a64ff; +} + +adiscope--SpinBoxA QFrame#SBA_Line:disabled { + color: #555555; +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox { + height: 20px; + font-size: 12px; + font-weight: normal; + border-bottom: 0px; + padding-bottom: 0px; +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox::drop-down { + subcontrol-position: center right; + width: 10px; + height: 6px; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow.svg); +} + +adiscope--SpinBoxA QComboBox#SBA_Combobox::drop-down:disabled { + subcontrol-position: center right; + width: 0px; + height: 0px; + border-image: url(:/icons/scopy-light/icons/sba_cmb_box_arrow.svg); +} + +adiscope--SpinBoxA QDial#SBA_CompletionCircle { + background-color: #C5C5C5; + color: #4963ff; +} + +/*************************************************************/ + + +/******************** Stop buttons *************************/ + +QPushButton[stopButton=true] { + background-repeat: no-repeat; + background-position: center center; +} + +QPushButton[stopButton=true]:enabled { + background-image: url(:/icons/scopy-light/icons/ico-stop.svg); +} + +QPushButton[stopButton=true][disabled=true][enabled=false] { + background-image: url(:/icons/scopy-light/icons/ico-stop.svg); +} + +QPushButton[stopButton=true]:checked { + background-image: url(:/icons/scopy-light/icons/ico-play-green.svg); +} + +/*************************************************************/ + +/******************** User Notes *************************/ + +QPushButton[userNote=true] { + background-image: url(:/icons/scopy-light/icons/ic_note_unchecked.svg); +} + +QPushButton[userNote=true]:checked { + background-image: url(:/icons/scopy-light/icons/ic_note_checked.svg); +} + +/*************************************************************/ From 3460cd84f0f6de944323cf9dee19517fc14eaa6e Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 10 Nov 2021 15:19:28 +0200 Subject: [PATCH 032/125] tool_launcher: fix decoders load path Signed-off-by: ioanachelaru --- src/tool_launcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 3ae33f54b0..9f012635a2 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1693,7 +1693,7 @@ bool adiscope::ToolLauncher::switchContext(const QString& uri) info.exec(); } else { - bool success = loadDecoders(":/assets/libsigrokdecode/decoders"); + bool success = loadDecoders("decoders"); if (!success) { search_timer->stop(); From d99adc354da31adbb7c349280ab58214852022f1 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 5 Nov 2021 17:17:15 +0200 Subject: [PATCH 033/125] spectrum_analyzer: update y axis labels Signed-off-by: ioanachelaru --- src/FftDisplayPlot.cc | 4 ++-- src/spectrum_analyzer.cpp | 19 +++++++++++++++++++ src/spectrum_analyzer.hpp | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/FftDisplayPlot.cc b/src/FftDisplayPlot.cc index 368ea53517..fd1b13ae7f 100644 --- a/src/FftDisplayPlot.cc +++ b/src/FftDisplayPlot.cc @@ -223,12 +223,12 @@ void FftDisplayPlot::replot() QString FftDisplayPlot::formatXValue(double value, int precision) const { - return d_formatter->format(value, "Hz", precision); + return d_formatter->format(value, d_xAxisUnit, precision); } QString FftDisplayPlot::formatYValue(double value, int precision) const { - return d_formatter->format(value, "dB", precision); + return d_formatter->format(value, d_yAxisUnit, precision); } void FftDisplayPlot::setupReadouts() diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 20b5f84bd9..579b84e137 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -224,6 +224,8 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, fft_plot->enableXaxisLabels(); fft_plot->enableYaxisLabels(); + setYAxisUnit(ui->cmb_units->currentText()); + fft_plot->setBtmHorAxisUnit("Hz"); // Initialize spectrum channels for (int i = 0 ; i < m_adc_nb_channels; i++) { @@ -476,6 +478,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->lblMagUnit->setText(ui->cmb_units->currentText()); ui->markerTable->hide(); + for (auto ch: qAsConst(channels)) { ch->setFftWindow(FftWinType::HAMMING, fft_size); } @@ -1072,6 +1075,15 @@ QString SpectrumAnalyzer::getReferenceChannelName() const return QString("REF %1").arg(current); } +void SpectrumAnalyzer::setYAxisUnit(const QString& unit) +{ + if (unit == "dBFS" || unit == "dBu" || unit == "dBV") { + fft_plot->setLeftVertAxisUnit("db"); + } else if (unit == "Vpeak" || unit == "Vrms" || unit == "V/√Hz") { + fft_plot->setLeftVertAxisUnit("V"); + } +} + void SpectrumAnalyzer::add_ref_waveform(QVector xData, QVector yData) { if (nb_ref_channels == MAX_REF_CHANNELS) { @@ -2231,6 +2243,11 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) top_scale->setValue(2.5E1); bottom_scale->setValue(1E-12); fft_plot->setAxisScale(QwtPlot::yLeft, bottom_scale->value(), top_scale->value()); + + fft_plot->replot(); + fft_plot->setYaxisMajorTicksPos(fft_plot->axisScaleDiv(QwtPlot::yLeft).ticks(2)); + fft_plot->leftHandlesArea()->repaint(); + break; default: ui->divisionWidget->setVisible(true); @@ -2243,6 +2260,8 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) fft_plot->setMagnitudeType((*it).second); fft_plot->recalculateMagnitudes(); + setYAxisUnit(unit); + fft_plot->replot(); fft_plot->leftHandlesArea()->repaint(); diff --git a/src/spectrum_analyzer.hpp b/src/spectrum_analyzer.hpp index 938e39bf05..d419b71ca7 100644 --- a/src/spectrum_analyzer.hpp +++ b/src/spectrum_analyzer.hpp @@ -184,6 +184,7 @@ private Q_SLOTS: void add_ref_waveform(QVector xData, QVector yData); void add_ref_waveform(unsigned int chIdx); QString getReferenceChannelName() const; + void setYAxisUnit(const QString& type); QList ch_api; QList marker_api; From 43c526f519de02b2076c2d5420620756808aa7f6 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 9 Nov 2021 15:21:28 +0200 Subject: [PATCH 034/125] spectrum_analyzer: enable more in-depth zoon on y axis Signed-off-by: ioanachelaru --- src/spectrum_analyzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 579b84e137..375a32b886 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -365,7 +365,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->cmbGainMode->setCurrentIndex(0); // Initialize vertical axis controls - unit_per_div->setMinValue(1); + unit_per_div->setMinValue(0.01); unit_per_div->setMaxValue(200/10); // Configure plot peak capabilities From f166b263e4ee0f53691906e8094adc7ad08e7721 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Thu, 11 Nov 2021 14:52:20 +0200 Subject: [PATCH 035/125] spectrum_analyzer: change behavior of the top, bottom and scale/div spin boxes Top and bottom influence scale/div, and scale/div influences top Signed-off-by: ioanachelaru --- src/spectrum_analyzer.cpp | 75 ++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 375a32b886..85ae692c06 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -366,7 +366,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, // Initialize vertical axis controls unit_per_div->setMinValue(0.01); - unit_per_div->setMaxValue(200/10); + unit_per_div->setMaxValue(500/10); // Configure plot peak capabilities for (uint i = 0; i < m_adc_nb_channels; i++) { @@ -467,8 +467,8 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->gridSweepControls->addWidget(startStopRange, 0, 0, 2, 2); - unit_per_div->setValue(20); top->setValue(0); + bottom->setValue(-200); marker_freq_pos->setMinValue(1); marker_freq_pos->setMaxValue(startStopRange->getStopValue()); @@ -2275,8 +2275,8 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) switch (magType) { case FftDisplayPlot::VPEAK: case FftDisplayPlot::VRMS: - unit_per_div->setValue(2); bottom->setValue(-10); + unit_per_div->setValue(2); fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); break; case FftDisplayPlot::VROOTHZ: @@ -2285,7 +2285,7 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) fft_plot->setAxisScale(QwtPlot::yLeft, bottom_scale->value(), top_scale->value()); break; default: - unit_per_div->setValue(20); + top->setValue(0); bottom->setValue(-200); fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); break; @@ -2354,17 +2354,21 @@ QPair SpectrumAnalyzer::getGridLayoutPosFromIndex(QGridLayout *layout, void SpectrumAnalyzer::onTopValueChanged(double top_value) { bool isScaleBtn = ui->topWidget->currentIndex(); - double perDiv = unit_per_div->value(); - double bottomValue = top_value - perDiv * 10; + double bottom_value; if (!isScaleBtn) { - bottom->blockSignals(true); - bottom->setValue(bottomValue); - bottom->blockSignals(false); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); + bottom_value = bottom->value(); + + unit_per_div->blockSignals(true); + unit_per_div->setValue(abs((top_value - bottom_value)/10)); + unit_per_div->blockSignals(false); + } else { - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_scale->value(), top_value); + bottom_value = bottom_scale->value(); } + + fft_plot->setAxisScale(QwtPlot::yLeft, bottom_value, top_value); + fft_plot->replot(); auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); fft_plot->setYaxisMajorTicksPos(div.ticks(2)); @@ -2374,42 +2378,55 @@ void SpectrumAnalyzer::onTopValueChanged(double top_value) void SpectrumAnalyzer::onScalePerDivValueChanged(double perDiv) { bool isScaleBtn = ui->topWidget->currentIndex(); + double topValue, bottomValue; + if (isScaleBtn) { - double topValue = bottom_scale->value() + perDiv * 10; + + bottomValue = bottom_scale->value(); + topValue = bottomValue + perDiv * 10; + + top_scale->blockSignals(true); top_scale->setValue(topValue); + top_scale->blockSignals(false); - double bottomValue = top_scale->value() - perDiv * 10; - if (bottomValue != bottom_scale->value()) { - bottom_scale->setValue(bottomValue); - } } else { bottom->setMaxValue(m_mag_min_max.second - perDiv * 10); top->setMinValue(m_mag_min_max.first + perDiv * 10); - double topValue = bottom->value() + perDiv * 10; - top->setValue(topValue); + bottomValue = bottom->value(); + topValue = bottomValue + perDiv * 10; - double bottomValue = top->value() - perDiv * 10; - if (bottomValue != bottom->value()) { - bottom->setValue(bottomValue); - } + top->blockSignals(true); + top->setValue(topValue); + top->blockSignals(false); } + + fft_plot->setAxisScale(QwtPlot::yLeft, bottomValue, topValue); + + fft_plot->replot(); + auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); + fft_plot->setYaxisMajorTicksPos(div.ticks(2)); + fft_plot->leftHandlesArea()->repaint(); } void SpectrumAnalyzer::onBottomValueChanged(double bottom_value) { bool isScaleBtn = ui->topWidget->currentIndex(); - double perDiv = unit_per_div->value(); - double topValue = bottom_value + perDiv * 10; + double top_value; if (!isScaleBtn) { - top->blockSignals(true); - top->setValue(topValue); - top->blockSignals(false); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); + top_value = top->value(); + + unit_per_div->blockSignals(true); + unit_per_div->setValue(abs((top_value - bottom_value)/10)); + unit_per_div->blockSignals(false); + } else { - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_value, top_scale->value()); + top_value = top_scale->value(); } + + fft_plot->setAxisScale(QwtPlot::yLeft, bottom_value, top_value); + fft_plot->replot(); auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); fft_plot->setYaxisMajorTicksPos(div.ticks(2)); From 6b331316619e363d64d01579ba8b8bfded2d5ad6 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 17 Nov 2021 12:41:14 +0200 Subject: [PATCH 036/125] cmake: enable stylesheets preprocessing Signed-off-by: ioanachelaru --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a14e759b9..3c71d184e4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,7 @@ if (APPLE) option(ENABLE_APPLICATION_BUNDLE "Enable application bundle for OSX" ON) endif(APPLE) -option(CONFIG_STYLESHEETS "Preprocess stylesheets using ${CMAKE_C_COMPILER}" OFF) +option(CONFIG_STYLESHEETS "Preprocess stylesheets using ${CMAKE_C_COMPILER}" ON) message(STATUS CONFIG_STYLESHEETS - ${CONFIG_STYLESHEETS}) if (${CONFIG_STYLESHEETS}) if (ANDROID) From 5ed4faf2cf1cb38373d90cccc6d79523c96ce5f1 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Thu, 17 Jun 2021 10:53:27 +0300 Subject: [PATCH 037/125] gui: moved gui files Signed-off-by: Ioana Chelaru --- CMakeLists.txt | 1 + src/DisplayPlot.h | 2 +- src/FftDisplayPlot.h | 2 +- src/dbgraph.hpp | 2 +- src/debugger.h | 6 ++-- src/device_widget.cpp | 4 +-- src/digitalio.cpp | 2 +- src/dmm.cpp | 2 +- src/dmm.hpp | 2 +- src/{ => gui}/ComboBoxLineEdit.cpp | 0 src/{ => gui}/ComboBoxLineEdit.h | 0 src/{ => gui}/animationmanager.cpp | 0 src/{ => gui}/animationmanager.h | 2 +- src/{ => gui}/basemenu.cpp | 0 src/{ => gui}/basemenu.h | 0 src/{ => gui}/basemenuitem.cpp | 0 src/{ => gui}/basemenuitem.h | 0 src/{ => gui}/bitfieldwidget.cpp | 0 src/{ => gui}/bitfieldwidget.h | 0 src/{ => gui}/channel_widget.cpp | 0 src/{ => gui}/channel_widget.hpp | 0 src/{ => gui}/checkbox_delegate.cpp | 0 src/{ => gui}/checkbox_delegate.h | 0 src/{ => gui}/coloredQWidget.cpp | 0 src/{ => gui}/coloredQWidget.hpp | 0 src/{ => gui}/completion_circle.cpp | 0 src/{ => gui}/completion_circle.h | 0 src/{ => gui}/connectDialog.cpp | 0 src/{ => gui}/connectDialog.hpp | 0 src/{ => gui}/cursor_readouts.cpp | 0 src/{ => gui}/cursor_readouts.h | 2 +- src/{ => gui}/customPushButton.cpp | 0 src/{ => gui}/customPushButton.hpp | 0 src/{ => gui}/customSwitch.cpp | 0 src/{ => gui}/customSwitch.hpp | 2 +- src/{ => gui}/customanimation.cpp | 2 +- src/{ => gui}/customanimation.h | 0 src/{ => gui}/customplotpositionbutton.cpp | 0 src/{ => gui}/customplotpositionbutton.h | 2 +- src/{ => gui}/db_click_buttons.cpp | 0 src/{ => gui}/db_click_buttons.hpp | 0 src/{ => gui}/detachdragzone.cpp | 2 +- src/{ => gui}/detachdragzone.h | 2 +- src/{ => gui}/detachedWindow.cpp | 0 src/{ => gui}/detachedWindow.hpp | 0 src/{ => gui}/detachedwindowsmanager.cpp | 0 src/{ => gui}/detachedwindowsmanager.h | 0 src/{ => gui}/dragzone.cpp | 0 src/{ => gui}/dragzone.h | 0 src/{ => gui}/dropdown_switch_list.cpp | 2 +- src/{ => gui}/dropdown_switch_list.h | 2 +- src/{ => gui}/dynamicWidget.cpp | 0 src/{ => gui}/dynamicWidget.hpp | 0 src/{ => gui}/homepage_controls.cpp | 0 src/{ => gui}/homepage_controls.h | 0 src/{ => gui}/info_page.cpp | 2 +- src/{ => gui}/info_page.hpp | 0 src/{ => gui}/info_widget.cpp | 0 src/{ => gui}/info_widget.h | 0 src/{ => gui}/instrumentnotes.cpp | 0 src/{ => gui}/instrumentnotes.h | 0 src/{ => gui}/linked_button.cpp | 2 +- src/{ => gui}/linked_button.hpp | 0 src/{ => gui}/measure.cpp | 0 src/{ => gui}/measure.h | 0 src/{ => gui}/measure_settings.cpp | 2 +- src/{ => gui}/measure_settings.h | 0 src/{ => gui}/menu_anim.cpp | 0 src/{ => gui}/menu_anim.hpp | 4 +-- src/{ => gui}/osc_custom_scroll.cpp | 0 src/{ => gui}/osc_custom_scroll.h | 0 src/{ => gui}/osc_export_settings.cpp | 0 src/{ => gui}/osc_export_settings.h | 2 +- src/{ => gui}/osc_import_settings.cpp | 0 src/{ => gui}/osc_import_settings.h | 0 src/{ => gui}/registerwidget.cpp | 0 src/{ => gui}/registerwidget.h | 2 +- src/{ => gui}/runsinglewidget.cpp | 4 +-- src/{ => gui}/runsinglewidget.h | 0 src/{ => gui}/smallOnOffSwitch.cpp | 2 +- src/{ => gui}/smallOnOffSwitch.hpp | 2 +- src/{ => gui}/spinbox_a.cpp | 2 +- src/{ => gui}/spinbox_a.hpp | 0 src/{ => gui}/stacked_homepage.cpp | 2 +- src/{ => gui}/stacked_homepage.h | 2 +- src/{ => gui}/startstoprangewidget.cpp | 0 src/{ => gui}/startstoprangewidget.h | 0 src/{ => gui}/user_notes.cpp | 2 +- src/{ => gui}/user_notes.hpp | 0 src/{ => gui}/user_notes_api.cpp | 0 src/{ => gui}/user_notes_api.hpp | 0 src/logicanalyzer/logic_analyzer.cpp | 6 ++-- src/logicanalyzer/logic_analyzer.h | 2 +- src/logicanalyzer/logicgroupitem.cpp | 2 +- src/logicanalyzer/logicgroupitem.h | 2 +- src/manualcalibration.h | 2 +- src/math.cpp | 2 +- src/measurement_gui.cpp | 2 +- src/network_analyzer.cpp | 4 +-- src/network_analyzer.hpp | 6 ++-- src/oscilloscope.cpp | 12 +++---- src/oscilloscope.hpp | 4 +-- src/oscilloscope_api.cpp | 4 +-- src/oscilloscope_plot.hpp | 4 +-- src/patterngenerator/pattern_generator.cpp | 4 +-- src/patterngenerator/pattern_generator.h | 2 +- src/patterngenerator/patterns/patterns.cpp | 2 +- src/patterngenerator/patterns/patterns.hpp | 4 +-- src/power_controller.cpp | 2 +- src/power_controller.hpp | 2 +- src/preferences.cpp | 2 +- src/signal_generator.cpp | 6 ++-- src/signal_generator_api.cpp | 4 +-- src/spectrum_analyzer.cpp | 6 ++-- src/spectrum_analyzer.hpp | 6 ++-- src/spectrum_analyzer_api.cpp | 4 +-- src/statistic_widget.cpp | 2 +- src/tool.cpp | 3 +- src/tool_launcher.cpp | 10 +++--- src/tool_launcher.hpp | 8 ++--- src/tool_launcher_api.cpp | 2 +- src/toolmenu.h | 2 +- src/toolmenuitem.cpp | 3 +- src/toolmenuitem.h | 4 +-- src/trigger_settings.cpp | 2 +- ui/channel.ui | 2 +- ui/channel_settings.ui | 2 +- ui/cursors_settings.ui | 4 +-- ui/debugger.ui | 2 +- ui/digitalIoChannel.ui | 4 +-- ui/digitalIoElement.ui | 4 +-- ui/digitalio.ui | 6 ++-- ui/dmm.ui | 17 ++++----- ui/logic_analyzer.ui | 26 +++++++------- ui/manualcalibration.ui | 2 +- ui/measure_panel.ui | 2 +- ui/measure_settings.ui | 2 +- ui/network_analyzer.ui | 40 +++++++++++----------- ui/osc_export_settings.ui | 2 +- ui/osc_general_settings.ui | 2 +- ui/oscilloscope.ui | 20 +++++------ ui/pattern_generator.ui | 32 ++++++++--------- ui/patterns/i2cpatternui.ui | 2 +- ui/patterns/spipatternui.ui | 4 +-- ui/powercontrol.ui | 16 ++++----- ui/signal_generator.ui | 16 ++++----- ui/spectrum_analyzer.ui | 26 +++++++------- ui/spinbox_a.ui | 6 ++-- ui/statistics_panel.ui | 2 +- ui/tool_launcher.ui | 8 ++--- ui/trigger_settings.ui | 4 +-- ui/user_notes.ui | 13 +++---- 152 files changed, 233 insertions(+), 236 deletions(-) rename src/{ => gui}/ComboBoxLineEdit.cpp (100%) rename src/{ => gui}/ComboBoxLineEdit.h (100%) rename src/{ => gui}/animationmanager.cpp (100%) rename src/{ => gui}/animationmanager.h (97%) rename src/{ => gui}/basemenu.cpp (100%) rename src/{ => gui}/basemenu.h (100%) rename src/{ => gui}/basemenuitem.cpp (100%) rename src/{ => gui}/basemenuitem.h (100%) rename src/{ => gui}/bitfieldwidget.cpp (100%) rename src/{ => gui}/bitfieldwidget.h (100%) rename src/{ => gui}/channel_widget.cpp (100%) rename src/{ => gui}/channel_widget.hpp (100%) rename src/{ => gui}/checkbox_delegate.cpp (100%) rename src/{ => gui}/checkbox_delegate.h (100%) rename src/{ => gui}/coloredQWidget.cpp (100%) rename src/{ => gui}/coloredQWidget.hpp (100%) rename src/{ => gui}/completion_circle.cpp (100%) rename src/{ => gui}/completion_circle.h (100%) rename src/{ => gui}/connectDialog.cpp (100%) rename src/{ => gui}/connectDialog.hpp (100%) rename src/{ => gui}/cursor_readouts.cpp (100%) rename src/{ => gui}/cursor_readouts.h (99%) rename src/{ => gui}/customPushButton.cpp (100%) rename src/{ => gui}/customPushButton.hpp (100%) rename src/{ => gui}/customSwitch.cpp (100%) rename src/{ => gui}/customSwitch.hpp (97%) rename src/{ => gui}/customanimation.cpp (97%) rename src/{ => gui}/customanimation.h (100%) rename src/{ => gui}/customplotpositionbutton.cpp (100%) rename src/{ => gui}/customplotpositionbutton.h (97%) rename src/{ => gui}/db_click_buttons.cpp (100%) rename src/{ => gui}/db_click_buttons.hpp (100%) rename src/{ => gui}/detachdragzone.cpp (98%) rename src/{ => gui}/detachdragzone.h (97%) rename src/{ => gui}/detachedWindow.cpp (100%) rename src/{ => gui}/detachedWindow.hpp (100%) rename src/{ => gui}/detachedwindowsmanager.cpp (100%) rename src/{ => gui}/detachedwindowsmanager.h (100%) rename src/{ => gui}/dragzone.cpp (100%) rename src/{ => gui}/dragzone.h (100%) rename src/{ => gui}/dropdown_switch_list.cpp (99%) rename src/{ => gui}/dropdown_switch_list.h (98%) rename src/{ => gui}/dynamicWidget.cpp (100%) rename src/{ => gui}/dynamicWidget.hpp (100%) rename src/{ => gui}/homepage_controls.cpp (100%) rename src/{ => gui}/homepage_controls.h (100%) rename src/{ => gui}/info_page.cpp (99%) rename src/{ => gui}/info_page.hpp (100%) rename src/{ => gui}/info_widget.cpp (100%) rename src/{ => gui}/info_widget.h (100%) rename src/{ => gui}/instrumentnotes.cpp (100%) rename src/{ => gui}/instrumentnotes.h (100%) rename src/{ => gui}/linked_button.cpp (94%) rename src/{ => gui}/linked_button.hpp (100%) rename src/{ => gui}/measure.cpp (100%) rename src/{ => gui}/measure.h (100%) rename src/{ => gui}/measure_settings.cpp (99%) rename src/{ => gui}/measure_settings.h (100%) rename src/{ => gui}/menu_anim.cpp (100%) rename src/{ => gui}/menu_anim.hpp (95%) rename src/{ => gui}/osc_custom_scroll.cpp (100%) rename src/{ => gui}/osc_custom_scroll.h (100%) rename src/{ => gui}/osc_export_settings.cpp (100%) rename src/{ => gui}/osc_export_settings.h (97%) rename src/{ => gui}/osc_import_settings.cpp (100%) rename src/{ => gui}/osc_import_settings.h (100%) rename src/{ => gui}/registerwidget.cpp (100%) rename src/{ => gui}/registerwidget.h (98%) rename src/{ => gui}/runsinglewidget.cpp (98%) rename src/{ => gui}/runsinglewidget.h (100%) rename src/{ => gui}/smallOnOffSwitch.cpp (99%) rename src/{ => gui}/smallOnOffSwitch.hpp (98%) rename src/{ => gui}/spinbox_a.cpp (99%) rename src/{ => gui}/spinbox_a.hpp (100%) rename src/{ => gui}/stacked_homepage.cpp (99%) rename src/{ => gui}/stacked_homepage.h (98%) rename src/{ => gui}/startstoprangewidget.cpp (100%) rename src/{ => gui}/startstoprangewidget.h (100%) rename src/{ => gui}/user_notes.cpp (99%) rename src/{ => gui}/user_notes.hpp (100%) rename src/{ => gui}/user_notes_api.cpp (100%) rename src/{ => gui}/user_notes_api.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c71d184e4..b265be624f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,7 @@ FILE(GLOB SRC_LIST src/*.cpp src/*.cc src/patterngenerator/*/*.cpp src/logicanalyzer/*.cpp src/logicanalyzer/*/*.cpp + src/gui/*.cpp ) FILE(GLOB M2KSCOPE_UIS ui/patterns/*.ui ui/*.ui) diff --git a/src/DisplayPlot.h b/src/DisplayPlot.h index 18359fbfe5..a9fc666aa2 100644 --- a/src/DisplayPlot.h +++ b/src/DisplayPlot.h @@ -67,7 +67,7 @@ #include "printableplot.h" #include "symbol_controller.h" #include "plot_line_handle.h" -#include "cursor_readouts.h" +#include "gui/cursor_readouts.h" #include "handles_area.hpp" #include "plotpickerwrapper.h" #include diff --git a/src/FftDisplayPlot.h b/src/FftDisplayPlot.h index ebf3435bcc..8019cffbbe 100644 --- a/src/FftDisplayPlot.h +++ b/src/FftDisplayPlot.h @@ -26,7 +26,7 @@ #include "symbol_controller.h" #include "plot_line_handle.h" #include "handles_area.hpp" -#include "cursor_readouts.h" +#include "gui/cursor_readouts.h" #include namespace adiscope { diff --git a/src/dbgraph.hpp b/src/dbgraph.hpp index 8c993f7a5e..faf97a47b4 100644 --- a/src/dbgraph.hpp +++ b/src/dbgraph.hpp @@ -28,7 +28,7 @@ #include "customFifo.hpp" #include "symbol_controller.h" #include "plot_line_handle.h" -#include "cursor_readouts.h" +#include "gui/cursor_readouts.h" #include "DisplayPlot.h" namespace adiscope { diff --git a/src/debugger.h b/src/debugger.h index 9a02b49bb3..cbf2a1040a 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -28,11 +28,11 @@ /* Local includes */ #include "debug.h" -#include "bitfieldwidget.h" -#include "registerwidget.h" +#include "gui/bitfieldwidget.h" +#include "gui/registerwidget.h" #include "filter.hpp" #include "tool.hpp" -#include "detachedWindow.hpp" +#include "gui/detachedWindow.hpp" #include "tool_launcher.hpp" class QJSEngine; diff --git a/src/device_widget.cpp b/src/device_widget.cpp index ccdcbaec4c..afa6c5f278 100644 --- a/src/device_widget.cpp +++ b/src/device_widget.cpp @@ -19,8 +19,8 @@ */ #include "device_widget.hpp" -#include "dynamicWidget.hpp" -#include "info_page.hpp" +#include "gui/dynamicWidget.hpp" +#include "gui/info_page.hpp" #include "tool_launcher.hpp" #include "ui_device.h" diff --git a/src/digitalio.cpp b/src/digitalio.cpp index 42985394a0..6415132b61 100644 --- a/src/digitalio.cpp +++ b/src/digitalio.cpp @@ -31,7 +31,7 @@ #include "logging_categories.h" #include "digitalio.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "digitalio_api.hpp" // Generated UI diff --git a/src/dmm.cpp b/src/dmm.cpp index 442fe01039..26b4c90942 100644 --- a/src/dmm.cpp +++ b/src/dmm.cpp @@ -19,7 +19,7 @@ */ #include "dmm.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "ui_dmm.h" #include #include "utils.h" diff --git a/src/dmm.hpp b/src/dmm.hpp index 4b0375dae5..e4ecffea86 100644 --- a/src/dmm.hpp +++ b/src/dmm.hpp @@ -32,7 +32,7 @@ #include "tool.hpp" #include "scroll_filter.hpp" #include -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include #include #include diff --git a/src/ComboBoxLineEdit.cpp b/src/gui/ComboBoxLineEdit.cpp similarity index 100% rename from src/ComboBoxLineEdit.cpp rename to src/gui/ComboBoxLineEdit.cpp diff --git a/src/ComboBoxLineEdit.h b/src/gui/ComboBoxLineEdit.h similarity index 100% rename from src/ComboBoxLineEdit.h rename to src/gui/ComboBoxLineEdit.h diff --git a/src/animationmanager.cpp b/src/gui/animationmanager.cpp similarity index 100% rename from src/animationmanager.cpp rename to src/gui/animationmanager.cpp diff --git a/src/animationmanager.h b/src/gui/animationmanager.h similarity index 97% rename from src/animationmanager.h rename to src/gui/animationmanager.h index 7a533337a7..33bebc7a39 100644 --- a/src/animationmanager.h +++ b/src/gui/animationmanager.h @@ -21,7 +21,7 @@ #ifndef ANIMATIONMANAGER_H #define ANIMATIONMANAGER_H -#include +#include #include namespace adiscope { diff --git a/src/basemenu.cpp b/src/gui/basemenu.cpp similarity index 100% rename from src/basemenu.cpp rename to src/gui/basemenu.cpp diff --git a/src/basemenu.h b/src/gui/basemenu.h similarity index 100% rename from src/basemenu.h rename to src/gui/basemenu.h diff --git a/src/basemenuitem.cpp b/src/gui/basemenuitem.cpp similarity index 100% rename from src/basemenuitem.cpp rename to src/gui/basemenuitem.cpp diff --git a/src/basemenuitem.h b/src/gui/basemenuitem.h similarity index 100% rename from src/basemenuitem.h rename to src/gui/basemenuitem.h diff --git a/src/bitfieldwidget.cpp b/src/gui/bitfieldwidget.cpp similarity index 100% rename from src/bitfieldwidget.cpp rename to src/gui/bitfieldwidget.cpp diff --git a/src/bitfieldwidget.h b/src/gui/bitfieldwidget.h similarity index 100% rename from src/bitfieldwidget.h rename to src/gui/bitfieldwidget.h diff --git a/src/channel_widget.cpp b/src/gui/channel_widget.cpp similarity index 100% rename from src/channel_widget.cpp rename to src/gui/channel_widget.cpp diff --git a/src/channel_widget.hpp b/src/gui/channel_widget.hpp similarity index 100% rename from src/channel_widget.hpp rename to src/gui/channel_widget.hpp diff --git a/src/checkbox_delegate.cpp b/src/gui/checkbox_delegate.cpp similarity index 100% rename from src/checkbox_delegate.cpp rename to src/gui/checkbox_delegate.cpp diff --git a/src/checkbox_delegate.h b/src/gui/checkbox_delegate.h similarity index 100% rename from src/checkbox_delegate.h rename to src/gui/checkbox_delegate.h diff --git a/src/coloredQWidget.cpp b/src/gui/coloredQWidget.cpp similarity index 100% rename from src/coloredQWidget.cpp rename to src/gui/coloredQWidget.cpp diff --git a/src/coloredQWidget.hpp b/src/gui/coloredQWidget.hpp similarity index 100% rename from src/coloredQWidget.hpp rename to src/gui/coloredQWidget.hpp diff --git a/src/completion_circle.cpp b/src/gui/completion_circle.cpp similarity index 100% rename from src/completion_circle.cpp rename to src/gui/completion_circle.cpp diff --git a/src/completion_circle.h b/src/gui/completion_circle.h similarity index 100% rename from src/completion_circle.h rename to src/gui/completion_circle.h diff --git a/src/connectDialog.cpp b/src/gui/connectDialog.cpp similarity index 100% rename from src/connectDialog.cpp rename to src/gui/connectDialog.cpp diff --git a/src/connectDialog.hpp b/src/gui/connectDialog.hpp similarity index 100% rename from src/connectDialog.hpp rename to src/gui/connectDialog.hpp diff --git a/src/cursor_readouts.cpp b/src/gui/cursor_readouts.cpp similarity index 100% rename from src/cursor_readouts.cpp rename to src/gui/cursor_readouts.cpp diff --git a/src/cursor_readouts.h b/src/gui/cursor_readouts.h similarity index 99% rename from src/cursor_readouts.h rename to src/gui/cursor_readouts.h index c462a2ca52..8f64a10e52 100644 --- a/src/cursor_readouts.h +++ b/src/gui/cursor_readouts.h @@ -22,7 +22,7 @@ #define CURSOR_READOUTS_H #include -#include "customanimation.h" +#include "gui/customanimation.h" #include #include #include "customplotpositionbutton.h" diff --git a/src/customPushButton.cpp b/src/gui/customPushButton.cpp similarity index 100% rename from src/customPushButton.cpp rename to src/gui/customPushButton.cpp diff --git a/src/customPushButton.hpp b/src/gui/customPushButton.hpp similarity index 100% rename from src/customPushButton.hpp rename to src/gui/customPushButton.hpp diff --git a/src/customSwitch.cpp b/src/gui/customSwitch.cpp similarity index 100% rename from src/customSwitch.cpp rename to src/gui/customSwitch.cpp diff --git a/src/customSwitch.hpp b/src/gui/customSwitch.hpp similarity index 97% rename from src/customSwitch.hpp rename to src/gui/customSwitch.hpp index 4a419b2dff..1a3721502d 100644 --- a/src/customSwitch.hpp +++ b/src/gui/customSwitch.hpp @@ -22,7 +22,7 @@ #define CUSTOM_SWITCH_HPP #include -#include "customanimation.h" +#include "gui/customanimation.h" #include #include diff --git a/src/customanimation.cpp b/src/gui/customanimation.cpp similarity index 97% rename from src/customanimation.cpp rename to src/gui/customanimation.cpp index 9eb830eaa6..25b0d369ff 100644 --- a/src/customanimation.cpp +++ b/src/gui/customanimation.cpp @@ -19,7 +19,7 @@ */ #include "customanimation.h" -#include +#include using namespace adiscope; diff --git a/src/customanimation.h b/src/gui/customanimation.h similarity index 100% rename from src/customanimation.h rename to src/gui/customanimation.h diff --git a/src/customplotpositionbutton.cpp b/src/gui/customplotpositionbutton.cpp similarity index 100% rename from src/customplotpositionbutton.cpp rename to src/gui/customplotpositionbutton.cpp diff --git a/src/customplotpositionbutton.h b/src/gui/customplotpositionbutton.h similarity index 97% rename from src/customplotpositionbutton.h rename to src/gui/customplotpositionbutton.h index 999221de22..66af71601f 100644 --- a/src/customplotpositionbutton.h +++ b/src/gui/customplotpositionbutton.h @@ -21,7 +21,7 @@ #define CUSTOMPLOTPOSITIONBUTTON_H #include -#include "coloredQWidget.hpp" +#include "gui/coloredQWidget.hpp" #include namespace Ui { diff --git a/src/db_click_buttons.cpp b/src/gui/db_click_buttons.cpp similarity index 100% rename from src/db_click_buttons.cpp rename to src/gui/db_click_buttons.cpp diff --git a/src/db_click_buttons.hpp b/src/gui/db_click_buttons.hpp similarity index 100% rename from src/db_click_buttons.hpp rename to src/gui/db_click_buttons.hpp diff --git a/src/detachdragzone.cpp b/src/gui/detachdragzone.cpp similarity index 98% rename from src/detachdragzone.cpp rename to src/gui/detachdragzone.cpp index 58c5bccc25..6121da74be 100644 --- a/src/detachdragzone.cpp +++ b/src/gui/detachdragzone.cpp @@ -21,7 +21,7 @@ #include "tool_launcher.hpp" #include -#include "basemenuitem.h" +#include "gui/basemenuitem.h" using namespace adiscope; diff --git a/src/detachdragzone.h b/src/gui/detachdragzone.h similarity index 97% rename from src/detachdragzone.h rename to src/gui/detachdragzone.h index 338cd5e0d5..31260839f7 100644 --- a/src/detachdragzone.h +++ b/src/gui/detachdragzone.h @@ -26,7 +26,7 @@ #include #include -#include "coloredQWidget.hpp" +#include "gui/coloredQWidget.hpp" namespace adiscope { class DetachDragZone : public ColoredQWidget diff --git a/src/detachedWindow.cpp b/src/gui/detachedWindow.cpp similarity index 100% rename from src/detachedWindow.cpp rename to src/gui/detachedWindow.cpp diff --git a/src/detachedWindow.hpp b/src/gui/detachedWindow.hpp similarity index 100% rename from src/detachedWindow.hpp rename to src/gui/detachedWindow.hpp diff --git a/src/detachedwindowsmanager.cpp b/src/gui/detachedwindowsmanager.cpp similarity index 100% rename from src/detachedwindowsmanager.cpp rename to src/gui/detachedwindowsmanager.cpp diff --git a/src/detachedwindowsmanager.h b/src/gui/detachedwindowsmanager.h similarity index 100% rename from src/detachedwindowsmanager.h rename to src/gui/detachedwindowsmanager.h diff --git a/src/dragzone.cpp b/src/gui/dragzone.cpp similarity index 100% rename from src/dragzone.cpp rename to src/gui/dragzone.cpp diff --git a/src/dragzone.h b/src/gui/dragzone.h similarity index 100% rename from src/dragzone.h rename to src/gui/dragzone.h diff --git a/src/dropdown_switch_list.cpp b/src/gui/dropdown_switch_list.cpp similarity index 99% rename from src/dropdown_switch_list.cpp rename to src/gui/dropdown_switch_list.cpp index 05f8affad8..cd59290577 100644 --- a/src/dropdown_switch_list.cpp +++ b/src/gui/dropdown_switch_list.cpp @@ -19,7 +19,7 @@ */ #include "dropdown_switch_list.h" -#include "checkbox_delegate.h" +#include "gui/checkbox_delegate.h" #include #include diff --git a/src/dropdown_switch_list.h b/src/gui/dropdown_switch_list.h similarity index 98% rename from src/dropdown_switch_list.h rename to src/gui/dropdown_switch_list.h index 469b1ed808..f0a5451a38 100644 --- a/src/dropdown_switch_list.h +++ b/src/gui/dropdown_switch_list.h @@ -22,7 +22,7 @@ #define DROPDOWN_SWITCH_LIST_H #include -#include "ComboBoxLineEdit.h" +#include "gui/ComboBoxLineEdit.h" class QStandardItemModel; class QTreeView; diff --git a/src/dynamicWidget.cpp b/src/gui/dynamicWidget.cpp similarity index 100% rename from src/dynamicWidget.cpp rename to src/gui/dynamicWidget.cpp diff --git a/src/dynamicWidget.hpp b/src/gui/dynamicWidget.hpp similarity index 100% rename from src/dynamicWidget.hpp rename to src/gui/dynamicWidget.hpp diff --git a/src/homepage_controls.cpp b/src/gui/homepage_controls.cpp similarity index 100% rename from src/homepage_controls.cpp rename to src/gui/homepage_controls.cpp diff --git a/src/homepage_controls.h b/src/gui/homepage_controls.h similarity index 100% rename from src/homepage_controls.h rename to src/gui/homepage_controls.h diff --git a/src/info_page.cpp b/src/gui/info_page.cpp similarity index 99% rename from src/info_page.cpp rename to src/gui/info_page.cpp index 23d6e5511d..16b09912f5 100644 --- a/src/info_page.cpp +++ b/src/gui/info_page.cpp @@ -24,7 +24,7 @@ #include #include #include "libm2k/analog/dmm.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include #include #include diff --git a/src/info_page.hpp b/src/gui/info_page.hpp similarity index 100% rename from src/info_page.hpp rename to src/gui/info_page.hpp diff --git a/src/info_widget.cpp b/src/gui/info_widget.cpp similarity index 100% rename from src/info_widget.cpp rename to src/gui/info_widget.cpp diff --git a/src/info_widget.h b/src/gui/info_widget.h similarity index 100% rename from src/info_widget.h rename to src/gui/info_widget.h diff --git a/src/instrumentnotes.cpp b/src/gui/instrumentnotes.cpp similarity index 100% rename from src/instrumentnotes.cpp rename to src/gui/instrumentnotes.cpp diff --git a/src/instrumentnotes.h b/src/gui/instrumentnotes.h similarity index 100% rename from src/instrumentnotes.h rename to src/gui/instrumentnotes.h diff --git a/src/linked_button.cpp b/src/gui/linked_button.cpp similarity index 94% rename from src/linked_button.cpp rename to src/gui/linked_button.cpp index 344884d7ef..424111f4be 100644 --- a/src/linked_button.cpp +++ b/src/gui/linked_button.cpp @@ -1,5 +1,5 @@ #include "linked_button.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include #include diff --git a/src/linked_button.hpp b/src/gui/linked_button.hpp similarity index 100% rename from src/linked_button.hpp rename to src/gui/linked_button.hpp diff --git a/src/measure.cpp b/src/gui/measure.cpp similarity index 100% rename from src/measure.cpp rename to src/gui/measure.cpp diff --git a/src/measure.h b/src/gui/measure.h similarity index 100% rename from src/measure.h rename to src/gui/measure.h diff --git a/src/measure_settings.cpp b/src/gui/measure_settings.cpp similarity index 99% rename from src/measure_settings.cpp rename to src/gui/measure_settings.cpp index 588eafbf0d..73038156c1 100644 --- a/src/measure_settings.cpp +++ b/src/gui/measure_settings.cpp @@ -19,7 +19,7 @@ */ #include "measure_settings.h" #include "oscilloscope_plot.hpp" -#include "dropdown_switch_list.h" +#include "gui/dropdown_switch_list.h" #include "ui_measure_settings.h" #include diff --git a/src/measure_settings.h b/src/gui/measure_settings.h similarity index 100% rename from src/measure_settings.h rename to src/gui/measure_settings.h diff --git a/src/menu_anim.cpp b/src/gui/menu_anim.cpp similarity index 100% rename from src/menu_anim.cpp rename to src/gui/menu_anim.cpp diff --git a/src/menu_anim.hpp b/src/gui/menu_anim.hpp similarity index 95% rename from src/menu_anim.hpp rename to src/gui/menu_anim.hpp index 6b5e3f2adf..0c5b6a4c40 100644 --- a/src/menu_anim.hpp +++ b/src/gui/menu_anim.hpp @@ -21,9 +21,9 @@ #ifndef MENU_ANIM_HPP #define MENU_ANIM_HPP -#include "coloredQWidget.hpp" +#include "gui/coloredQWidget.hpp" -#include "customanimation.h" +#include "gui/customanimation.h" #include #include diff --git a/src/osc_custom_scroll.cpp b/src/gui/osc_custom_scroll.cpp similarity index 100% rename from src/osc_custom_scroll.cpp rename to src/gui/osc_custom_scroll.cpp diff --git a/src/osc_custom_scroll.h b/src/gui/osc_custom_scroll.h similarity index 100% rename from src/osc_custom_scroll.h rename to src/gui/osc_custom_scroll.h diff --git a/src/osc_export_settings.cpp b/src/gui/osc_export_settings.cpp similarity index 100% rename from src/osc_export_settings.cpp rename to src/gui/osc_export_settings.cpp diff --git a/src/osc_export_settings.h b/src/gui/osc_export_settings.h similarity index 97% rename from src/osc_export_settings.h rename to src/gui/osc_export_settings.h index 6e470aa56d..fd5a4476d7 100644 --- a/src/osc_export_settings.h +++ b/src/gui/osc_export_settings.h @@ -28,7 +28,7 @@ #include /* Local includes */ -#include "dropdown_switch_list.h" +#include "gui/dropdown_switch_list.h" namespace Ui { class ExportSettings; diff --git a/src/osc_import_settings.cpp b/src/gui/osc_import_settings.cpp similarity index 100% rename from src/osc_import_settings.cpp rename to src/gui/osc_import_settings.cpp diff --git a/src/osc_import_settings.h b/src/gui/osc_import_settings.h similarity index 100% rename from src/osc_import_settings.h rename to src/gui/osc_import_settings.h diff --git a/src/registerwidget.cpp b/src/gui/registerwidget.cpp similarity index 100% rename from src/registerwidget.cpp rename to src/gui/registerwidget.cpp diff --git a/src/registerwidget.h b/src/gui/registerwidget.h similarity index 98% rename from src/registerwidget.h rename to src/gui/registerwidget.h index 8f2a4e015f..caf2a8e0cd 100644 --- a/src/registerwidget.h +++ b/src/gui/registerwidget.h @@ -25,7 +25,7 @@ #include #include "regmapparser.h" -#include "bitfieldwidget.h" +#include "gui/bitfieldwidget.h" #include "debug.h" namespace Ui { diff --git a/src/runsinglewidget.cpp b/src/gui/runsinglewidget.cpp similarity index 98% rename from src/runsinglewidget.cpp rename to src/gui/runsinglewidget.cpp index 6178672001..d609cc6c6e 100644 --- a/src/runsinglewidget.cpp +++ b/src/gui/runsinglewidget.cpp @@ -22,8 +22,8 @@ #include "ui_runsinglewidget.h" #include "utils.h" -#include "dynamicWidget.hpp" -#include "customPushButton.hpp" +#include "gui/dynamicWidget.hpp" +#include "gui/customPushButton.hpp" using namespace adiscope; diff --git a/src/runsinglewidget.h b/src/gui/runsinglewidget.h similarity index 100% rename from src/runsinglewidget.h rename to src/gui/runsinglewidget.h diff --git a/src/smallOnOffSwitch.cpp b/src/gui/smallOnOffSwitch.cpp similarity index 99% rename from src/smallOnOffSwitch.cpp rename to src/gui/smallOnOffSwitch.cpp index ac993cee20..9d3692b280 100644 --- a/src/smallOnOffSwitch.cpp +++ b/src/gui/smallOnOffSwitch.cpp @@ -19,7 +19,7 @@ */ #include "smallOnOffSwitch.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include #include diff --git a/src/smallOnOffSwitch.hpp b/src/gui/smallOnOffSwitch.hpp similarity index 98% rename from src/smallOnOffSwitch.hpp rename to src/gui/smallOnOffSwitch.hpp index 1ea0cbc088..1338603d4f 100644 --- a/src/smallOnOffSwitch.hpp +++ b/src/gui/smallOnOffSwitch.hpp @@ -22,7 +22,7 @@ #define SMALL_ON_OFF_SWITCH_HPP #include -#include "customanimation.h" +#include "gui/customanimation.h" #include #include #include diff --git a/src/spinbox_a.cpp b/src/gui/spinbox_a.cpp similarity index 99% rename from src/spinbox_a.cpp rename to src/gui/spinbox_a.cpp index 307f6324e4..f4ff7d4d56 100644 --- a/src/spinbox_a.cpp +++ b/src/gui/spinbox_a.cpp @@ -19,7 +19,7 @@ */ #include "spinbox_a.hpp" -#include "completion_circle.h" +#include "gui/completion_circle.h" #include "apiobjectmanager.h" #include "singletone_wrapper.h" diff --git a/src/spinbox_a.hpp b/src/gui/spinbox_a.hpp similarity index 100% rename from src/spinbox_a.hpp rename to src/gui/spinbox_a.hpp diff --git a/src/stacked_homepage.cpp b/src/gui/stacked_homepage.cpp similarity index 99% rename from src/stacked_homepage.cpp rename to src/gui/stacked_homepage.cpp index 1bb4aeef62..c25e27057a 100644 --- a/src/stacked_homepage.cpp +++ b/src/gui/stacked_homepage.cpp @@ -21,7 +21,7 @@ #include "stacked_homepage.h" #include -#include "customanimation.h" +#include "gui/customanimation.h" #include #include #include diff --git a/src/stacked_homepage.h b/src/gui/stacked_homepage.h similarity index 98% rename from src/stacked_homepage.h rename to src/gui/stacked_homepage.h index ad3ddc7a75..060a1736d3 100644 --- a/src/stacked_homepage.h +++ b/src/gui/stacked_homepage.h @@ -25,7 +25,7 @@ #include #include -#include "homepage_controls.h" +#include "gui/homepage_controls.h" namespace Ui { class StackedHomepage; diff --git a/src/startstoprangewidget.cpp b/src/gui/startstoprangewidget.cpp similarity index 100% rename from src/startstoprangewidget.cpp rename to src/gui/startstoprangewidget.cpp diff --git a/src/startstoprangewidget.h b/src/gui/startstoprangewidget.h similarity index 100% rename from src/startstoprangewidget.h rename to src/gui/startstoprangewidget.h diff --git a/src/user_notes.cpp b/src/gui/user_notes.cpp similarity index 99% rename from src/user_notes.cpp rename to src/gui/user_notes.cpp index 97713420e3..f375889864 100644 --- a/src/user_notes.cpp +++ b/src/gui/user_notes.cpp @@ -22,7 +22,7 @@ #include "ui_user_notes.h" #include "ui_note.h" #include "ui_user_note_page.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include #include diff --git a/src/user_notes.hpp b/src/gui/user_notes.hpp similarity index 100% rename from src/user_notes.hpp rename to src/gui/user_notes.hpp diff --git a/src/user_notes_api.cpp b/src/gui/user_notes_api.cpp similarity index 100% rename from src/user_notes_api.cpp rename to src/gui/user_notes_api.cpp diff --git a/src/user_notes_api.hpp b/src/gui/user_notes_api.hpp similarity index 100% rename from src/user_notes_api.hpp rename to src/gui/user_notes_api.hpp diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 92c24d290d..08376df645 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -32,11 +32,11 @@ #include "logicanalyzer/annotationcurve.h" #include "logicanalyzer/decoder.h" -#include "basemenu.h" +#include "gui/basemenu.h" #include "logicgroupitem.h" #include "logicanalyzer_api.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include @@ -50,7 +50,7 @@ #include #include "scopyExceptionHandler.h" -#include "osc_export_settings.h" +#include "gui/osc_export_settings.h" #include "filemanager.h" #include "config.h" #include "state_updater.h" diff --git a/src/logicanalyzer/logic_analyzer.h b/src/logicanalyzer/logic_analyzer.h index 13cb0e4b41..ee88a93a50 100644 --- a/src/logicanalyzer/logic_analyzer.h +++ b/src/logicanalyzer/logic_analyzer.h @@ -34,7 +34,7 @@ #include "logic_tool.h" #include "oscilloscope_plot.hpp" #include "buffer_previewer.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "scroll_filter.hpp" #include "saverestoretoolsettings.h" diff --git a/src/logicanalyzer/logicgroupitem.cpp b/src/logicanalyzer/logicgroupitem.cpp index fd74fff99e..05ff0904bf 100644 --- a/src/logicanalyzer/logicgroupitem.cpp +++ b/src/logicanalyzer/logicgroupitem.cpp @@ -26,7 +26,7 @@ #include #include -#include "basemenu.h" +#include "gui/basemenu.h" using namespace adiscope; diff --git a/src/logicanalyzer/logicgroupitem.h b/src/logicanalyzer/logicgroupitem.h index 71a24a2f9a..6c22a86064 100644 --- a/src/logicanalyzer/logicgroupitem.h +++ b/src/logicanalyzer/logicgroupitem.h @@ -22,7 +22,7 @@ #ifndef LOGICGROUPITEM_H #define LOGICGROUPITEM_H -#include "basemenuitem.h" +#include "gui/basemenuitem.h" #include diff --git a/src/manualcalibration.h b/src/manualcalibration.h index c343b55406..16b2a5f3a9 100644 --- a/src/manualcalibration.h +++ b/src/manualcalibration.h @@ -31,7 +31,7 @@ /*Local includes*/ #include "tool.hpp" -#include "detachedWindow.hpp" +#include "gui/detachedWindow.hpp" #include "tool_launcher.hpp" #include "filter.hpp" diff --git a/src/math.cpp b/src/math.cpp index 190b1395e8..63234bd177 100644 --- a/src/math.cpp +++ b/src/math.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "math.hpp" #include diff --git a/src/measurement_gui.cpp b/src/measurement_gui.cpp index 1cf18314dc..c4641b78fc 100644 --- a/src/measurement_gui.cpp +++ b/src/measurement_gui.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ #include "measurement_gui.h" -#include "measure.h" +#include "gui/measure.h" #include diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index dce50cd909..722aa470dd 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -19,10 +19,10 @@ */ #include "logging_categories.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "network_analyzer.hpp" #include "signal_generator.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "hardware_trigger.hpp" #include "ui_network_analyzer.h" #include "filemanager.h" diff --git a/src/network_analyzer.hpp b/src/network_analyzer.hpp index 26a699645d..9dbd21eb76 100644 --- a/src/network_analyzer.hpp +++ b/src/network_analyzer.hpp @@ -21,7 +21,7 @@ #ifndef SCOPY_NETWORK_ANALYZER_HPP #define SCOPY_NETWORK_ANALYZER_HPP -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "apiObject.hpp" #include "iio_manager.hpp" #include "signal_sample.hpp" @@ -29,7 +29,7 @@ #include "dbgraph.hpp" #include "handles_area.hpp" #include -#include "customPushButton.hpp" +#include "gui/customPushButton.hpp" #include "scroll_filter.hpp" #include #include @@ -58,7 +58,7 @@ #include "TimeDomainDisplayPlot.h" #include "networkanalyzerbufferviewer.h" -#include "startstoprangewidget.h" +#include "gui/startstoprangewidget.h" extern "C" { struct iio_context; diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index cb0928c6dc..5479fd7f47 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -47,25 +47,25 @@ /* Local includes */ #include "logging_categories.h" #include "adc_sample_conv.hpp" -#include "customPushButton.hpp" +#include "gui/customPushButton.hpp" #include "oscilloscope.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "measurement_gui.h" -#include "measure_settings.h" +#include "gui/measure_settings.h" #include "statistic_widget.h" #include "state_updater.h" #include "osc_capture_params.hpp" #include "buffer_previewer.hpp" #include "config.h" -#include "customplotpositionbutton.h" -#include "channel_widget.hpp" +#include "gui/customplotpositionbutton.h" +#include "gui/channel_widget.hpp" #include "signal_sample.hpp" #include "filemanager.h" #include "scopyExceptionHandler.h" #include "oscilloscope_api.hpp" #include "mixed_signal_sink.h" -#include "runsinglewidget.h" +#include "gui/runsinglewidget.h" /* Generated UI */ #include "ui_channel_settings.h" diff --git a/src/oscilloscope.hpp b/src/oscilloscope.hpp index a12c9ea2fa..fa62a19fe4 100644 --- a/src/oscilloscope.hpp +++ b/src/oscilloscope.hpp @@ -57,11 +57,11 @@ #include "ConstellationDisplayPlot.h" #include "FftDisplayPlot.h" #include "HistogramDisplayPlot.h" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "trigger_settings.hpp" #include "plot_utils.hpp" #include "tool.hpp" -#include "osc_import_settings.h" +#include "gui/osc_import_settings.h" #include "math.hpp" #include "scroll_filter.hpp" #include "cancel_dc_offset_block.h" diff --git a/src/oscilloscope_api.cpp b/src/oscilloscope_api.cpp index a5323e4323..ee9937e850 100644 --- a/src/oscilloscope_api.cpp +++ b/src/oscilloscope_api.cpp @@ -20,11 +20,11 @@ #include "oscilloscope_api.hpp" #include "ui_oscilloscope.h" -#include "measure_settings.h" +#include "gui/measure_settings.h" #include "ui_measure_settings.h" #include "ui_cursors_settings.h" #include "ui_trigger_settings.h" -#include "channel_widget.hpp" +#include "gui/channel_widget.hpp" #include "ui_channel_settings.h" #include "ui_osc_general_settings.h" diff --git a/src/oscilloscope_plot.hpp b/src/oscilloscope_plot.hpp index d5432a7439..65c2b4eea6 100644 --- a/src/oscilloscope_plot.hpp +++ b/src/oscilloscope_plot.hpp @@ -22,8 +22,8 @@ #define M2K_OSCILLOSCOPE_PLOT_H #include "TimeDomainDisplayPlot.h" -#include "measure.h" -#include "customplotpositionbutton.h" +#include "gui/measure.h" +#include "gui/customplotpositionbutton.h" #include "graticule.h" #include diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index f5c61533bd..953a5c48fc 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -24,8 +24,8 @@ #include #include "ui_pattern_generator.h" #include "digitalchannel_manager.hpp" -#include "dynamicWidget.hpp" -#include "basemenu.h" +#include "gui/dynamicWidget.hpp" +#include "gui/basemenu.h" #include "../logicanalyzer/logicgroupitem.h" #include "../logicanalyzer/logicdatacurve.h" diff --git a/src/patterngenerator/pattern_generator.h b/src/patterngenerator/pattern_generator.h index 3c8a100839..d5f61e50db 100644 --- a/src/patterngenerator/pattern_generator.h +++ b/src/patterngenerator/pattern_generator.h @@ -25,7 +25,7 @@ #include "logic_tool.h" #include "oscilloscope_plot.hpp" #include "buffer_previewer.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "scroll_filter.hpp" #include "../logicanalyzer/genericlogicplotcurve.h" diff --git a/src/patterngenerator/patterns/patterns.cpp b/src/patterngenerator/patterns/patterns.cpp index c64c7d905f..9a10145d24 100644 --- a/src/patterngenerator/patterns/patterns.cpp +++ b/src/patterngenerator/patterns/patterns.cpp @@ -34,7 +34,7 @@ #include "../pattern_generator.h" #include "../../logicanalyzer/annotationcurve.h" #include "../../logicanalyzer/annotationdecoder.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include diff --git a/src/patterngenerator/patterns/patterns.hpp b/src/patterngenerator/patterns/patterns.hpp index 88717c9947..75b75c89d0 100644 --- a/src/patterngenerator/patterns/patterns.hpp +++ b/src/patterngenerator/patterns/patterns.hpp @@ -32,8 +32,8 @@ #include #include #include -#include "spinbox_a.hpp" -#include "osc_import_settings.h" +#include "gui/spinbox_a.hpp" +#include "gui/osc_import_settings.h" // Generated UI #include "ui_binarycounterpatternui.h" diff --git a/src/power_controller.cpp b/src/power_controller.cpp index 10a069bf58..afee9e3bbe 100644 --- a/src/power_controller.cpp +++ b/src/power_controller.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "power_controller.hpp" #include "filter.hpp" diff --git a/src/power_controller.hpp b/src/power_controller.hpp index 8053366f07..0235f259b7 100644 --- a/src/power_controller.hpp +++ b/src/power_controller.hpp @@ -25,7 +25,7 @@ #include #include "apiObject.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "tool.hpp" extern "C" { diff --git a/src/preferences.cpp b/src/preferences.cpp index 33e54f6c1d..8e2934f84e 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -20,7 +20,7 @@ #include "preferences.h" #include "ui_preferences.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include #include diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 7f3ab5b72e..f746539e12 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -19,11 +19,11 @@ */ #include "logging_categories.h" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "signal_generator.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "ui_signal_generator.h" -#include "channel_widget.hpp" +#include "gui/channel_widget.hpp" #include diff --git a/src/signal_generator_api.cpp b/src/signal_generator_api.cpp index a339ee4c0c..ae00906597 100644 --- a/src/signal_generator_api.cpp +++ b/src/signal_generator_api.cpp @@ -19,8 +19,8 @@ */ #include "signal_generator_api.hpp" #include "ui_signal_generator.h" -#include "channel_widget.hpp" -#include "spinbox_a.hpp" +#include "gui/channel_widget.hpp" +#include "gui/spinbox_a.hpp" namespace adiscope { void SignalGenerator_API::show() diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 85ae692c06..4e0224afdb 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -41,9 +41,9 @@ #include "filter.hpp" #include "math.hpp" #include "fft_block.hpp" -#include "dynamicWidget.hpp" -#include "channel_widget.hpp" -#include "db_click_buttons.hpp" +#include "gui/dynamicWidget.hpp" +#include "gui/channel_widget.hpp" +#include "gui/db_click_buttons.hpp" #include "filemanager.h" #include "spectrum_analyzer_api.hpp" #include "stream_to_vector_overlap.h" diff --git a/src/spectrum_analyzer.hpp b/src/spectrum_analyzer.hpp index d419b71ca7..9b71ad2229 100644 --- a/src/spectrum_analyzer.hpp +++ b/src/spectrum_analyzer.hpp @@ -32,9 +32,9 @@ #include "FftDisplayPlot.h" #include "tool.hpp" #include "plot_utils.hpp" -#include "spinbox_a.hpp" -#include "customPushButton.hpp" -#include "startstoprangewidget.h" +#include "gui/spinbox_a.hpp" +#include "gui/customPushButton.hpp" +#include "gui/startstoprangewidget.h" #include #include diff --git a/src/spectrum_analyzer_api.cpp b/src/spectrum_analyzer_api.cpp index fc15c2612e..58f00d48eb 100644 --- a/src/spectrum_analyzer_api.cpp +++ b/src/spectrum_analyzer_api.cpp @@ -20,8 +20,8 @@ #include "spectrum_analyzer_api.hpp" #include "ui_spectrum_analyzer.h" #include "ui_cursors_settings.h" -#include "channel_widget.hpp" -#include "db_click_buttons.hpp" +#include "gui/channel_widget.hpp" +#include "gui/db_click_buttons.hpp" namespace adiscope { int SpectrumChannel_API::type() diff --git a/src/statistic_widget.cpp b/src/statistic_widget.cpp index 4ece129e9a..a567597333 100644 --- a/src/statistic_widget.cpp +++ b/src/statistic_widget.cpp @@ -19,7 +19,7 @@ */ #include "statistic_widget.h" -#include "measure.h" +#include "gui/measure.h" #include "plot_utils.hpp" #include "ui_statistic.h" diff --git a/src/tool.cpp b/src/tool.cpp index 2d53ecb48d..5df86adf30 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -20,8 +20,7 @@ #include "tool.hpp" #include "tool_launcher.hpp" - -#include "detachedwindowsmanager.h" +#include "gui/detachedwindowsmanager.h" #include diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 9f012635a2..83afdeefc7 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -20,21 +20,21 @@ #include "logging_categories.h" #include "config.h" -#include "connectDialog.hpp" -#include "dynamicWidget.hpp" +#include "gui/connectDialog.hpp" +#include "gui/dynamicWidget.hpp" #include "oscilloscope.hpp" #include "spectrum_analyzer.hpp" #include "tool_launcher.hpp" #include "qtjs.hpp" #include "jsfileio.h" -#include "dragzone.h" +#include "gui/dragzone.h" #include "debugger.h" #include "manualcalibration.h" #include "apiobjectmanager.h" #include "device_widget.hpp" -#include "user_notes.hpp" +#include "gui/user_notes.hpp" #include "external_script_api.hpp" -#include "animationmanager.h" +#include "gui/animationmanager.h" #include "singletone_wrapper.h" #include "phonehome.h" diff --git a/src/tool_launcher.hpp b/src/tool_launcher.hpp index 003e867122..cb22fa8136 100644 --- a/src/tool_launcher.hpp +++ b/src/tool_launcher.hpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #ifdef __ANDROID__ @@ -53,11 +53,11 @@ #include "network_analyzer.hpp" #include "digitalio.hpp" -#include "detachedWindow.hpp" +#include "gui/detachedWindow.hpp" #include "preferences.h" -#include "info_page.hpp" +#include "gui/info_page.hpp" #include "device_widget.hpp" -#include "connectDialog.hpp" +#include "gui/connectDialog.hpp" #include "toolmenu.h" #include "session_info.h" diff --git a/src/tool_launcher_api.cpp b/src/tool_launcher_api.cpp index adaade2851..af2647ae2d 100644 --- a/src/tool_launcher_api.cpp +++ b/src/tool_launcher_api.cpp @@ -19,7 +19,7 @@ */ #include "tool_launcher_api.hpp" #include "ui_tool_launcher.h" -#include "user_notes.hpp" +#include "gui/user_notes.hpp" #include "spectrum_analyzer.hpp" #include "apiobjectmanager.h" #include "debugger.h" diff --git a/src/toolmenu.h b/src/toolmenu.h index a5be3802c7..d622b6e633 100644 --- a/src/toolmenu.h +++ b/src/toolmenu.h @@ -21,7 +21,7 @@ #ifndef TOOLMENU_H #define TOOLMENU_H -#include "basemenu.h" +#include "gui/basemenu.h" #include "filter.hpp" #include "toolmenuitem.h" #include "preferences.h" diff --git a/src/toolmenuitem.cpp b/src/toolmenuitem.cpp index 063b3eb77d..24c937da93 100644 --- a/src/toolmenuitem.cpp +++ b/src/toolmenuitem.cpp @@ -26,8 +26,7 @@ #include #include #include -#include "dynamicWidget.hpp" -#include "dynamicWidget.hpp" +#include "gui/dynamicWidget.hpp" #include "utils.h" using namespace adiscope; diff --git a/src/toolmenuitem.h b/src/toolmenuitem.h index 44776a57b7..9ea7bd2094 100644 --- a/src/toolmenuitem.h +++ b/src/toolmenuitem.h @@ -21,8 +21,8 @@ #ifndef TOOLMENUITEM_H #define TOOLMENUITEM_H -#include "basemenuitem.h" -#include "customPushButton.hpp" +#include "gui/basemenuitem.h" +#include "gui/customPushButton.hpp" namespace adiscope { class ToolMenuItem : public BaseMenuItem diff --git a/src/trigger_settings.cpp b/src/trigger_settings.cpp index ff4f152c3c..77f08a99fb 100644 --- a/src/trigger_settings.cpp +++ b/src/trigger_settings.cpp @@ -26,7 +26,7 @@ #include #include "trigger_settings.hpp" -#include "spinbox_a.hpp" +#include "gui/spinbox_a.hpp" #include "scroll_filter.hpp" #include "ui_trigger_settings.h" diff --git a/ui/channel.ui b/ui/channel.ui index 9ed812c8a1..420d5811ca 100644 --- a/ui/channel.ui +++ b/ui/channel.ui @@ -198,7 +198,7 @@ adiscope::CustomPushButton QPushButton -

customPushButton.hpp
+
gui/customPushButton.hpp
diff --git a/ui/channel_settings.ui b/ui/channel_settings.ui index 071b2dcbdd..9f74674596 100644 --- a/ui/channel_settings.ui +++ b/ui/channel_settings.ui @@ -1321,7 +1321,7 @@ AC Coupling adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/cursors_settings.ui b/ui/cursors_settings.ui index f831c36fba..a08c755f35 100644 --- a/ui/cursors_settings.ui +++ b/ui/cursors_settings.ui @@ -776,12 +776,12 @@ color: rgba(255,255,255,51); adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
adiscope::SmallOnOffSwitch QPushButton -
smallOnOffSwitch.hpp
+
gui/smallOnOffSwitch.hpp
diff --git a/ui/debugger.ui b/ui/debugger.ui index 9cd85849e5..87577303cd 100644 --- a/ui/debugger.ui +++ b/ui/debugger.ui @@ -907,7 +907,7 @@ background-color: transparent; adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
diff --git a/ui/digitalIoChannel.ui b/ui/digitalIoChannel.ui index 88c8ffb8d8..926367e74f 100644 --- a/ui/digitalIoChannel.ui +++ b/ui/digitalIoChannel.ui @@ -364,12 +364,12 @@ background-color: red; adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
adiscope::SmallOnOffSwitch QPushButton -
smallOnOffSwitch.hpp
+
gui/smallOnOffSwitch.hpp
diff --git a/ui/digitalIoElement.ui b/ui/digitalIoElement.ui index 1120a30827..3be9abafaa 100644 --- a/ui/digitalIoElement.ui +++ b/ui/digitalIoElement.ui @@ -145,7 +145,7 @@ width: 1310px; } - 0 + 1 @@ -441,7 +441,7 @@ QSlider::handle:horizontal:enabled { adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/digitalio.ui b/ui/digitalio.ui index 2b31a9361c..cf3a52c33b 100644 --- a/ui/digitalio.ui +++ b/ui/digitalio.ui @@ -260,19 +260,19 @@ background-color: grey; adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
diff --git a/ui/dmm.ui b/ui/dmm.ui index 2b568c2960..a3991606e5 100644 --- a/ui/dmm.ui +++ b/ui/dmm.ui @@ -1976,24 +1976,24 @@ line-height: 14px; adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
- adiscope::LinkedButton + adiscope::CustomSwitch QPushButton -
linked_button.hpp
+
gui/customSwitch.hpp
- adiscope::CustomSwitch + adiscope::LinkedButton QPushButton -
customSwitch.hpp
+
gui/linked_button.hpp
QwtThermo @@ -2020,9 +2020,6 @@ line-height: 14px; - - - @@ -2059,7 +2056,7 @@ line-height: 14px; - + diff --git a/ui/logic_analyzer.ui b/ui/logic_analyzer.ui index 1579a8795e..060b07fea0 100644 --- a/ui/logic_analyzer.ui +++ b/ui/logic_analyzer.ui @@ -464,7 +464,7 @@ - 3 + 2 @@ -1365,7 +1365,7 @@ color: rgba(255,255,255,51); 0 0 420 - 474 + 304 @@ -1952,44 +1952,44 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
- - adiscope::CustomPushButton - QPushButton -
customPushButton.hpp
-
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
diff --git a/ui/manualcalibration.ui b/ui/manualcalibration.ui index 1f01ff5ec2..255903aaf3 100644 --- a/ui/manualcalibration.ui +++ b/ui/manualcalibration.ui @@ -291,7 +291,7 @@ QListWidget{ adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
diff --git a/ui/measure_panel.ui b/ui/measure_panel.ui index 2292f4a1c8..9f308152f7 100644 --- a/ui/measure_panel.ui +++ b/ui/measure_panel.ui @@ -275,7 +275,7 @@ adiscope::OscCustomScrollArea QScrollArea -
osc_custom_scroll.h
+
gui/osc_custom_scroll.h
1
diff --git a/ui/measure_settings.ui b/ui/measure_settings.ui index f0a465b591..8ead1d5fca 100644 --- a/ui/measure_settings.ui +++ b/ui/measure_settings.ui @@ -667,7 +667,7 @@ adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/network_analyzer.ui b/ui/network_analyzer.ui index 8e04fbbf07..e2b844f076 100644 --- a/ui/network_analyzer.ui +++ b/ui/network_analyzer.ui @@ -796,7 +796,7 @@ border: 5px solid white; - 2 + 0 @@ -2055,8 +2055,8 @@ padding-left: 20px; 0 0 - 300 - 531 + 261 + 529 @@ -3124,43 +3124,43 @@ QLabel { - adiscope::CustomSwitch + adiscope::CustomPushButton QPushButton -
customSwitch.hpp
+
gui/customPushButton.hpp
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
- adiscope::InstrumentNotes + adiscope::MenuAnim QWidget -
instrumentnotes.h
+
gui/menu_anim.hpp
1
- adiscope::LinkedButton - QPushButton -
linked_button.hpp
-
- - adiscope::MenuAnim + adiscope::InstrumentNotes QWidget -
menu_anim.hpp
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
- adiscope::CustomPushButton + adiscope::CustomSwitch + QPushButton +
gui/customSwitch.hpp
+
+ + adiscope::LinkedButton QPushButton -
customPushButton.hpp
+
gui/linked_button.hpp
adiscope::dBgraph @@ -3177,13 +3177,13 @@ QLabel { adiscope::CustomPlotPositionButton QWidget -
customplotpositionbutton.h
+
gui/customplotpositionbutton.h
1
adiscope::SmallOnOffSwitch QPushButton -
smallOnOffSwitch.hpp
+
gui/smallOnOffSwitch.hpp
diff --git a/ui/osc_export_settings.ui b/ui/osc_export_settings.ui index 2268971dec..354f11ef6f 100644 --- a/ui/osc_export_settings.ui +++ b/ui/osc_export_settings.ui @@ -292,7 +292,7 @@ adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/osc_general_settings.ui b/ui/osc_general_settings.ui index ffec3aff2b..5fc6ffc2bb 100644 --- a/ui/osc_general_settings.ui +++ b/ui/osc_general_settings.ui @@ -504,7 +504,7 @@ adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/oscilloscope.ui b/ui/oscilloscope.ui index e08ae438c0..f4d74b8217 100644 --- a/ui/oscilloscope.ui +++ b/ui/oscilloscope.ui @@ -984,39 +984,39 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); }
+ + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
- - adiscope::CustomPushButton - QPushButton -
customPushButton.hpp
-
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
diff --git a/ui/pattern_generator.ui b/ui/pattern_generator.ui index f5fd868151..70d63d4f08 100644 --- a/ui/pattern_generator.ui +++ b/ui/pattern_generator.ui @@ -469,7 +469,7 @@ - 3 + 0 @@ -519,8 +519,8 @@ 0 0 - 200 - 300 + 300 + 474 @@ -1381,8 +1381,8 @@ color: rgba(255,255,255,51); 0 0 - 280 - 474 + 220 + 134 @@ -1782,44 +1782,44 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
- - adiscope::CustomPushButton - QPushButton -
customPushButton.hpp
-
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
diff --git a/ui/patterns/i2cpatternui.ui b/ui/patterns/i2cpatternui.ui index df60fefddc..806d09e773 100644 --- a/ui/patterns/i2cpatternui.ui +++ b/ui/patterns/i2cpatternui.ui @@ -176,7 +176,7 @@ adiscope::SmallOnOffSwitch QPushButton -
smallOnOffSwitch.hpp
+
gui/smallOnOffSwitch.hpp
diff --git a/ui/patterns/spipatternui.ui b/ui/patterns/spipatternui.ui index 40ea4441b5..978fde7730 100644 --- a/ui/patterns/spipatternui.ui +++ b/ui/patterns/spipatternui.ui @@ -6,7 +6,7 @@ 0 0 - 262 + 282 391 @@ -283,7 +283,7 @@ adiscope::SmallOnOffSwitch QPushButton -
smallOnOffSwitch.hpp
+
gui/smallOnOffSwitch.hpp
diff --git a/ui/powercontrol.ui b/ui/powercontrol.ui index 021506bc68..c4e5df3208 100644 --- a/ui/powercontrol.ui +++ b/ui/powercontrol.ui @@ -1091,27 +1091,27 @@ QPushButton:disabled { - - adiscope::CustomSwitch - QPushButton -
customSwitch.hpp
-
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
+ + adiscope::CustomSwitch + QPushButton +
gui/customSwitch.hpp
+
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
QwtThermo diff --git a/ui/signal_generator.ui b/ui/signal_generator.ui index 66f6bd9ef3..8f3371ff1c 100644 --- a/ui/signal_generator.ui +++ b/ui/signal_generator.ui @@ -1414,36 +1414,36 @@ background-repeat: no-repeat; adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
- adiscope::LinkedButton + adiscope::CustomSwitch QPushButton -
linked_button.hpp
+
gui/customSwitch.hpp
- adiscope::CustomSwitch + adiscope::LinkedButton QPushButton -
customSwitch.hpp
+
gui/linked_button.hpp
adiscope::Math diff --git a/ui/spectrum_analyzer.ui b/ui/spectrum_analyzer.ui index fec2e2c3a7..de2dc1fa45 100644 --- a/ui/spectrum_analyzer.ui +++ b/ui/spectrum_analyzer.ui @@ -1027,7 +1027,7 @@ min-width: 64px; 0 0 298 - 428 + 356 @@ -2391,44 +2391,44 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
- - adiscope::CustomPushButton - QPushButton -
customPushButton.hpp
-
adiscope::InstrumentNotes QWidget -
instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
runsinglewidget.h
+
gui/runsinglewidget.h
1
adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
adiscope::LinkedButton QPushButton -
linked_button.hpp
+
gui/linked_button.hpp
adiscope::MarkerTable @@ -2439,7 +2439,7 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } adiscope::ImportSettings QWidget -
osc_import_settings.h
+
gui/osc_import_settings.h
1
diff --git a/ui/spinbox_a.ui b/ui/spinbox_a.ui index a7ee78cac8..3293311d09 100644 --- a/ui/spinbox_a.ui +++ b/ui/spinbox_a.ui @@ -6,8 +6,8 @@ 0 0 - 180 - 65 + 274 + 74
@@ -138,7 +138,7 @@ adiscope::CompletionCircle QDial -
completion_circle.h
+
gui/completion_circle.h
diff --git a/ui/statistics_panel.ui b/ui/statistics_panel.ui index 483c36e71f..ef7a37e233 100644 --- a/ui/statistics_panel.ui +++ b/ui/statistics_panel.ui @@ -190,7 +190,7 @@ QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { adiscope::OscCustomScrollArea QScrollArea -
osc_custom_scroll.h
+
gui/osc_custom_scroll.h
1
diff --git a/ui/tool_launcher.ui b/ui/tool_launcher.ui index 64a7fe0dd4..b141bf6f6b 100644 --- a/ui/tool_launcher.ui +++ b/ui/tool_launcher.ui @@ -1631,25 +1631,25 @@ border-width: 0px; adiscope::DetachDragZone QWidget -
detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
menu_anim.hpp
+
gui/menu_anim.hpp
1
adiscope::OscCustomScrollArea QScrollArea -
osc_custom_scroll.h
+
gui/osc_custom_scroll.h
1
adiscope::StackedHomepage QStackedWidget -
stacked_homepage.h
+
gui/stacked_homepage.h
1
diff --git a/ui/trigger_settings.ui b/ui/trigger_settings.ui index 83445a51cb..efd179e848 100644 --- a/ui/trigger_settings.ui +++ b/ui/trigger_settings.ui @@ -110,7 +110,7 @@ 0 - -18 + 0 266 958 @@ -958,7 +958,7 @@ color: rgba(255,255,255,51); adiscope::CustomSwitch QPushButton -
customSwitch.hpp
+
gui/customSwitch.hpp
diff --git a/ui/user_notes.ui b/ui/user_notes.ui index 6996283c7b..3923473c08 100644 --- a/ui/user_notes.ui +++ b/ui/user_notes.ui @@ -519,20 +519,21 @@ border-width: 0px; - adiscope::StackedHomepage - QStackedWidget -
stacked_homepage.h
+ adiscope::OscCustomScrollArea + QScrollArea +
gui/osc_custom_scroll.h
1
- adiscope::OscCustomScrollArea - QScrollArea -
osc_custom_scroll.h
+ adiscope::StackedHomepage + QStackedWidget +
gui/stacked_homepage.h
1
+ From feb4d78a5e2f1d9b24c70a62c879b840f985fa2e Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 17:31:27 +0300 Subject: [PATCH 038/125] gui: added ThemeManager Signed-off-by: Ioana Chelaru --- src/gui/theme_manager.cpp | 40 +++++++++++++++++++++++++++++++++++++++ src/gui/theme_manager.hpp | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/gui/theme_manager.cpp create mode 100644 src/gui/theme_manager.hpp diff --git a/src/gui/theme_manager.cpp b/src/gui/theme_manager.cpp new file mode 100644 index 0000000000..fedc183873 --- /dev/null +++ b/src/gui/theme_manager.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "theme_manager.hpp" +#include "utils.h" + +using namespace scopy::gui; + +ThemeManager::ThemeManager() + : m_app(nullptr) +{ + m_availableThemes.append("default"); + m_availableThemes.append("light"); +} + +ThemeManager& ThemeManager::getInstance() +{ + static ThemeManager INSTANCE; + return INSTANCE; +} + +void ThemeManager::setApplication(QApplication* app) { m_app = app; } + +void ThemeManager::setCurrentTheme(const QString& theme) +{ + m_currentTheme = theme; + + Util util; + QString stylesheet = util.loadStylesheetFromFile(":/stylesheets/themes/" + theme + ".qss"); + + if (m_app) { + m_app->setStyleSheet(stylesheet); + } + + QIcon::setThemeName(theme); +} + +QString ThemeManager::getCurrentTheme() const { return m_currentTheme; } + +const QStringList ThemeManager::getAvailableThemes() { return m_availableThemes; } diff --git a/src/gui/theme_manager.hpp b/src/gui/theme_manager.hpp new file mode 100644 index 0000000000..c63584c894 --- /dev/null +++ b/src/gui/theme_manager.hpp @@ -0,0 +1,33 @@ +#ifndef THEMEMANAGER_HPP +#define THEMEMANAGER_HPP + +#include + +namespace scopy { +namespace gui { + +class ThemeManager +{ +public: + static ThemeManager& getInstance(); + + ThemeManager(const ThemeManager&) = delete; + ThemeManager& operator=(const ThemeManager&) = delete; + + void setApplication(QApplication* app); + const QStringList getAvailableThemes(); + + void setCurrentTheme(const QString& theme); + QString getCurrentTheme() const; + +private: + ThemeManager(); + + QApplication* m_app; + QString m_currentTheme; + QStringList m_availableThemes; +}; +} // namespace gui +} // namespace scopy + +#endif // THEMEMANAGER_HPP From 0f707bd460a23726815485c9fbdc4e929d920d01 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 18:02:37 +0300 Subject: [PATCH 039/125] gui: added SubsectionSeparator Signed-off-by: Ioana Chelaru --- src/gui/subsection_separator.cpp | 48 ++++++++++++ src/gui/subsection_separator.hpp | 44 +++++++++++ ui/subsection_separator.ui | 124 +++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 src/gui/subsection_separator.cpp create mode 100644 src/gui/subsection_separator.hpp create mode 100644 ui/subsection_separator.ui diff --git a/src/gui/subsection_separator.cpp b/src/gui/subsection_separator.cpp new file mode 100644 index 0000000000..bedc4e15d7 --- /dev/null +++ b/src/gui/subsection_separator.cpp @@ -0,0 +1,48 @@ +#include "ui_subsection_separator.h" + +#include + +#include "subsection_separator.hpp" + +using namespace scopy::gui; + +SubsectionSeparator::SubsectionSeparator(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::SubsectionSeparator) +{ + m_ui->setupUi(this); + + m_ui->widgetSubsectionContent->setVisible(false); + connect(m_ui->btnSubsectionSeparator, &QPushButton::toggled, + [=](bool toggled) { m_ui->widgetSubsectionContent->setVisible(toggled); }); +} + +SubsectionSeparator::SubsectionSeparator(const QString& text, const bool buttonVisible, QWidget* parent) + : SubsectionSeparator(parent) +{ + setLabel(text); + setButtonVisible(buttonVisible); +} + +SubsectionSeparator::~SubsectionSeparator() { delete m_ui; } + +QPushButton* SubsectionSeparator::getButton() { return m_ui->btnSubsectionSeparator; } + +void SubsectionSeparator::setButtonVisible(bool buttonVisible) +{ + m_ui->btnSubsectionSeparator->setVisible(buttonVisible); +} + +bool SubsectionSeparator::getButtonChecked() { return m_ui->btnSubsectionSeparator->isChecked(); } + +void SubsectionSeparator::setButtonChecked(bool checked) { m_ui->btnSubsectionSeparator->setChecked(checked); } + +QLabel* SubsectionSeparator::getLabel() { return m_ui->lblSubsectionSeparator; } + +void SubsectionSeparator::setLabel(const QString& text) { m_ui->lblSubsectionSeparator->setText(text); } + +void SubsectionSeparator::setLabelVisible(bool visible) { m_ui->lblSubsectionSeparator->setVisible(visible); } + +void SubsectionSeparator::setLineVisible(bool visible) { m_ui->lineSubsectionSeparator->setVisible(visible); } + +void SubsectionSeparator::setContent(QWidget* content) { m_ui->vLayoutContent->addWidget(content); } diff --git a/src/gui/subsection_separator.hpp b/src/gui/subsection_separator.hpp new file mode 100644 index 0000000000..24b35ce651 --- /dev/null +++ b/src/gui/subsection_separator.hpp @@ -0,0 +1,44 @@ +#ifndef SUBSECTIONSEPARATOR_H +#define SUBSECTIONSEPARATOR_H + +#include +#include +#include + +namespace Ui { +class SubsectionSeparator; +} + +namespace scopy { +namespace gui { +class SubsectionSeparator : public QWidget +{ + Q_OBJECT + +public: + explicit SubsectionSeparator(QWidget* parent = nullptr); + explicit SubsectionSeparator(const QString& text, const bool buttonVisible = false, QWidget* parent = nullptr); + ~SubsectionSeparator(); + +private: + Ui::SubsectionSeparator* m_ui; + +public: + QPushButton* getButton(); + void setButtonVisible(bool buttonVisible); + + bool getButtonChecked(); + void setButtonChecked(bool checked); + + QLabel* getLabel(); + void setLabel(const QString& text); + + void setLabelVisible(bool visible); + void setLineVisible(bool visible); + + void setContent(QWidget* content); +}; +} // namespace gui +} // namespace scopy + +#endif // SUBSECTIONSEPARATOR_H diff --git a/ui/subsection_separator.ui b/ui/subsection_separator.ui new file mode 100644 index 0000000000..5b478f1cc4 --- /dev/null +++ b/ui/subsection_separator.ui @@ -0,0 +1,124 @@ + + + SubsectionSeparator + + + + 0 + 0 + 399 + 75 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + QLayout::SetMinimumSize + + + 0 + + + 25 + + + 15 + + + + + + + + true + + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + + 16777215 + 1 + + + + QFrame::Sunken + + + 1 + + + Qt::Horizontal + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + From 9a0f265a40cb7cacadd8414c61a70aebd916ab57 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 18:02:58 +0300 Subject: [PATCH 040/125] gui: added MenuHeader Signed-off-by: Ioana Chelaru --- src/gui/menu_header.cpp | 40 +++++++++++++++ src/gui/menu_header.hpp | 43 ++++++++++++++++ ui/menu_header.ui | 105 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/gui/menu_header.cpp create mode 100644 src/gui/menu_header.hpp create mode 100644 ui/menu_header.ui diff --git a/src/gui/menu_header.cpp b/src/gui/menu_header.cpp new file mode 100644 index 0000000000..affd780e2f --- /dev/null +++ b/src/gui/menu_header.cpp @@ -0,0 +1,40 @@ +#include "ui_menu_header.h" + +#include +#include + +#include "menu_header.hpp" + +using namespace scopy::gui; + +MenuHeader::MenuHeader(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::MenuHeader) +{ + m_ui->setupUi(this); + + this->setEnableBtnVisible(false); +} + +MenuHeader::MenuHeader(const QString& label, const QColor* color, bool enableBtnVisible, QWidget* parent) + : MenuHeader(parent) +{ + this->setLabel(label); + this->setLineColor(color); + this->setEnableBtnVisible(enableBtnVisible); +} + +MenuHeader::~MenuHeader() { delete m_ui; } + +void MenuHeader::setEnabledBtnState(bool state) { m_ui->btnEnabled->setChecked(state); } + +void MenuHeader::setLabel(const QString& text) { m_ui->lblTitle->setText(text); } + +void MenuHeader::setLineColor(const QColor* color) +{ + m_ui->lineSeparator->setStyleSheet("border: 2px solid " + color->name()); +} + +void MenuHeader::setEnableBtnVisible(bool visible) { m_ui->btnEnabled->setVisible(visible); } + +QPushButton* MenuHeader::getEnableBtn() { return m_ui->btnEnabled; } diff --git a/src/gui/menu_header.hpp b/src/gui/menu_header.hpp new file mode 100644 index 0000000000..32a032fc43 --- /dev/null +++ b/src/gui/menu_header.hpp @@ -0,0 +1,43 @@ +#ifndef MENU_HEADER_HPP +#define MENU_HEADER_HPP + +#include +#include + +namespace Ui { +class MenuHeader; +} + +namespace scopy { +namespace gui { + +class MenuHeader : public QWidget +{ + Q_OBJECT + +public: + explicit MenuHeader(QWidget* parent = nullptr); + explicit MenuHeader(const QString& label = nullptr, const QColor* color = new QColor("#4A64FF"), + bool enableBtnVisible = false, QWidget* parent = nullptr); + ~MenuHeader(); + +private: + Ui::MenuHeader* m_ui; + +public Q_SLOTS: + void setEnabledBtnState(bool state); + +Q_SIGNALS: + void enableBtnToggled(bool state); + +public: + void setLabel(const QString& text); + void setLineColor(const QColor* color); + void setEnableBtnVisible(bool visible); + + QPushButton* getEnableBtn(); +}; +} // namespace gui +} // namespace scopy + +#endif // MENU_HEADER_HPP diff --git a/ui/menu_header.ui b/ui/menu_header.ui new file mode 100644 index 0000000000..80d20436c8 --- /dev/null +++ b/ui/menu_header.ui @@ -0,0 +1,105 @@ + + + MenuHeader + + + + 0 + 0 + 278 + 67 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 10 + + + + + 10 + + + 0 + + + 15 + + + + + TextLabel + + + true + + + + + + + + + + true + + + true + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 10 + + + + + + + + + 0 + 2 + + + + Qt::Horizontal + + + + + + + + adiscope::CustomSwitch + QPushButton +
gui/customSwitch.hpp
+
+
+ + +
From a107939b54be560ca690681dab9170a43b53f597 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 18:03:19 +0300 Subject: [PATCH 041/125] gui: added CustomMenuButton Signed-off-by: Ioana Chelaru --- src/gui/custom_menu_button.cpp | 54 ++++++++++++++++++++++++++++++++++ src/gui/custom_menu_button.hpp | 49 ++++++++++++++++++++++++++++++ ui/custom_menu_button.ui | 52 ++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 src/gui/custom_menu_button.cpp create mode 100644 src/gui/custom_menu_button.hpp create mode 100644 ui/custom_menu_button.ui diff --git a/src/gui/custom_menu_button.cpp b/src/gui/custom_menu_button.cpp new file mode 100644 index 0000000000..c80a421986 --- /dev/null +++ b/src/gui/custom_menu_button.cpp @@ -0,0 +1,54 @@ +#include "ui_custom_menu_button.h" + +#include "custom_menu_button.hpp" + +using namespace scopy::gui; + +CustomMenuButton::CustomMenuButton(QString labelText, bool checkboxVisible, bool checkBoxChecked, QWidget* parent) + : CustomMenuButton(parent) +{ + + m_ui->lblCustomMenuButton->setText(labelText); + m_ui->checkBoxCustomMenuButton->setVisible(checkboxVisible); + + if (checkboxVisible) { + checkBoxToggled(checkBoxChecked); + m_ui->checkBoxCustomMenuButton->setChecked(checkBoxChecked); + } +} + +CustomMenuButton::CustomMenuButton(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::CustomMenuButton) + , m_floatingMenu(false) +{ + m_ui->setupUi(this); + connect(m_ui->checkBoxCustomMenuButton, &QCheckBox::toggled, this, &CustomMenuButton::checkBoxToggled); +} + +CustomMenuButton::~CustomMenuButton() { delete m_ui; } + +void CustomMenuButton::setLabel(const QString& text) { m_ui->lblCustomMenuButton->setText(text); } + +void CustomMenuButton::setCheckboxVisible(bool visible) { m_ui->checkBoxCustomMenuButton->setVisible(visible); } + +CustomPushButton* CustomMenuButton::getBtn() { return m_ui->btnCustomMenuButton; } + +QCheckBox* CustomMenuButton::getCheckBox() { return m_ui->checkBoxCustomMenuButton; } + +bool CustomMenuButton::getCheckBoxState() { return getCheckBox()->isChecked(); } + +void CustomMenuButton::setCheckBoxState(bool checked) { m_ui->checkBoxCustomMenuButton->setChecked(checked); } + +void CustomMenuButton::setMenuFloating(bool floating) { m_floatingMenu = floating; } + +void CustomMenuButton::checkBoxToggled(bool toggled) +{ + if (!toggled) { + m_ui->btnCustomMenuButton->setChecked(false); + } + + if (!m_floatingMenu) { + m_ui->btnCustomMenuButton->setEnabled(toggled); + } +} diff --git a/src/gui/custom_menu_button.hpp b/src/gui/custom_menu_button.hpp new file mode 100644 index 0000000000..c21d6289e8 --- /dev/null +++ b/src/gui/custom_menu_button.hpp @@ -0,0 +1,49 @@ +#ifndef CUSTOMMENUBUTTON_H +#define CUSTOMMENUBUTTON_H + +#include +#include +#include + +#include "customPushButton.hpp" + +using namespace adiscope; + +namespace Ui { +class CustomMenuButton; +} + +namespace scopy { +namespace gui { + +class CustomMenuButton : public QWidget +{ + Q_OBJECT + +public: + explicit CustomMenuButton(QString labelText = nullptr, bool checkboxVisible = false, + bool checkBoxChecked = false, QWidget* parent = nullptr); + explicit CustomMenuButton(QWidget* parent = nullptr); + ~CustomMenuButton(); + + void setLabel(const QString& text); + void setCheckboxVisible(bool visible); + + CustomPushButton* getBtn(); + QCheckBox* getCheckBox(); + bool getCheckBoxState(); + void setCheckBoxState(bool checked); + void setMenuFloating(bool floating); + +public Q_SLOTS: + void checkBoxToggled(bool toggled); + +private: + Ui::CustomMenuButton* m_ui; + + bool m_floatingMenu; +}; +} // namespace gui +} // namespace scopy + +#endif // CUSTOMMENUBUTTON_H diff --git a/ui/custom_menu_button.ui b/ui/custom_menu_button.ui new file mode 100644 index 0000000000..d5bd44d2d1 --- /dev/null +++ b/ui/custom_menu_button.ui @@ -0,0 +1,52 @@ + + + CustomMenuButton + + + + 0 + 0 + 141 + 50 + + + + Form + + + + + + + + + + + + + Trigger + + + + + + + + + + true + + + + + + + + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
+
+ + +
From eea69c580cfdbf29970bd26f9798ac1c4e8da8a8 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 18:03:38 +0300 Subject: [PATCH 042/125] gui: added SettingsPairWidget Signed-off-by: Ioana Chelaru --- src/gui/settings_pair_widget.cpp | 18 +++++++ src/gui/settings_pair_widget.hpp | 34 ++++++++++++ ui/settings_pair_widget.ui | 93 ++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/gui/settings_pair_widget.cpp create mode 100644 src/gui/settings_pair_widget.hpp create mode 100644 ui/settings_pair_widget.ui diff --git a/src/gui/settings_pair_widget.cpp b/src/gui/settings_pair_widget.cpp new file mode 100644 index 0000000000..14c96a368d --- /dev/null +++ b/src/gui/settings_pair_widget.cpp @@ -0,0 +1,18 @@ +#include "ui_settings_pair_widget.h" + +#include "settings_pair_widget.hpp" + +using namespace scopy::gui; + +SettingsPairWidget::SettingsPairWidget(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::SettingsPairWidget) +{ + m_ui->setupUi(this); +} + +SettingsPairWidget::~SettingsPairWidget() { delete m_ui; } + +CustomPushButton* SettingsPairWidget::getGeneralSettingsBtn() { return m_ui->btnGenSettings; } + +QPushButton* SettingsPairWidget::getSettingsBtn() { return m_ui->btnSettings; } diff --git a/src/gui/settings_pair_widget.hpp b/src/gui/settings_pair_widget.hpp new file mode 100644 index 0000000000..4372830c8b --- /dev/null +++ b/src/gui/settings_pair_widget.hpp @@ -0,0 +1,34 @@ +#ifndef SETTINGSCUSTOMWIDGET_H +#define SETTINGSCUSTOMWIDGET_H + +#include +#include + +#include "customPushButton.hpp" + +using namespace adiscope; + +namespace Ui { +class SettingsPairWidget; +} + +namespace scopy { +namespace gui { +class SettingsPairWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SettingsPairWidget(QWidget* parent = nullptr); + ~SettingsPairWidget(); + + CustomPushButton* getGeneralSettingsBtn(); + QPushButton* getSettingsBtn(); + +private: + Ui::SettingsPairWidget* m_ui; +}; +} // namespace gui +} // namespace scopy + +#endif // SETTINGSPAIRWIDGET_H diff --git a/ui/settings_pair_widget.ui b/ui/settings_pair_widget.ui new file mode 100644 index 0000000000..4ea377013e --- /dev/null +++ b/ui/settings_pair_widget.ui @@ -0,0 +1,93 @@ + + + SettingsPairWidget + + + + 0 + 0 + 100 + 42 + + + + + 0 + 0 + + + + + 100 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + true + + + true + + + true + + + + + + + + + + + + + true + + + true + + + true + + + + + + + + adiscope::CustomPushButton + QPushButton +
gui/customPushButton.hpp
+
+
+ + +
From f9dbb33a79ff5a114e22c2d6b80afdd3e5d7ef5d Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 18 Jun 2021 18:03:59 +0300 Subject: [PATCH 043/125] gui: added GenericMenu Signed-off-by: Ioana Chelaru --- src/gui/generic_menu.cpp | 49 ++++++++++++++++++++++++++++++++++++++++ src/gui/generic_menu.hpp | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/gui/generic_menu.cpp create mode 100644 src/gui/generic_menu.hpp diff --git a/src/gui/generic_menu.cpp b/src/gui/generic_menu.cpp new file mode 100644 index 0000000000..96612ae469 --- /dev/null +++ b/src/gui/generic_menu.cpp @@ -0,0 +1,49 @@ +#include + +#include "generic_menu.hpp" + +using namespace scopy::gui; + +GenericMenu::GenericMenu(QWidget* parent) + : QWidget(parent) + , m_menuHeader(new MenuHeader(parent)) + , m_menu(new BaseMenu(parent)) + , m_lastOpenPosition(0) +{} + +GenericMenu::~GenericMenu() +{ + delete m_menuHeader; + delete m_menu; +} + +void GenericMenu::initInteractiveMenu() +{ + this->setStyleSheet(".QWidget { background-color: none; }"); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(10); + layout->setContentsMargins(18, 20, 18, 9); + + layout->addWidget(m_menuHeader); + layout->addWidget(m_menu); + + this->setLayout(layout); +} + +void GenericMenu::setMenuHeader(const QString& title, const QColor* lineColor, bool hasEnableBtn) +{ + m_menuHeader->setLabel(title); + m_menuHeader->setLineColor(lineColor); + m_menuHeader->setEnabledBtnState(hasEnableBtn); +} + +void GenericMenu::insertSection(SubsectionSeparator* section) +{ + BaseMenuItem* item = new BaseMenuItem(this); + item->setWidget(section); + + m_menu->insertMenuItem(item, m_lastOpenPosition); + + m_lastOpenPosition++; +} diff --git a/src/gui/generic_menu.hpp b/src/gui/generic_menu.hpp new file mode 100644 index 0000000000..7fdb391383 --- /dev/null +++ b/src/gui/generic_menu.hpp @@ -0,0 +1,41 @@ +#ifndef GENERICMENU_HPP +#define GENERICMENU_HPP + +#include + +#include "basemenu.h" +#include "menu_header.hpp" +#include "subsection_separator.hpp" + +using namespace adiscope; + +namespace scopy { +namespace gui { + +class GenericMenu : public QWidget +{ + Q_OBJECT + +public: + explicit GenericMenu(QWidget* parent = nullptr); + ~GenericMenu(); + + virtual void setMenuButton(bool toggled){}; + + void initInteractiveMenu(); + void setMenuHeader(const QString& title, const QColor* lineColor, bool hasEnableBtn); + void insertSection(SubsectionSeparator* section); + +Q_SIGNALS: + void enableBtnToggled(bool toggled); + +private: + MenuHeader* m_menuHeader; + BaseMenu* m_menu; + + int m_lastOpenPosition; +}; +} // namespace gui +} // namespace scopy + +#endif // GENERICMENU_HPP From d9c7af09251d3a682ee1c22f46d0322c6e2572a1 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Tue, 22 Jun 2021 15:11:14 +0300 Subject: [PATCH 044/125] gui: added ToolView, ToolViewBuilder and ChannelManager Signed-off-by: Ioana Chelaru --- src/gui/channel_manager.cpp | 190 ++++++++++ src/gui/channel_manager.hpp | 65 ++++ src/gui/channel_widget.cpp | 2 + src/gui/channel_widget.hpp | 9 +- src/gui/runsinglewidget.cpp | 4 + src/gui/runsinglewidget.h | 4 + src/gui/tool_view.cpp | 446 +++++++++++++++++++++++ src/gui/tool_view.hpp | 111 ++++++ src/gui/tool_view_builder.cpp | 47 +++ src/gui/tool_view_builder.hpp | 38 ++ ui/tool_view.ui | 653 ++++++++++++++++++++++++++++++++++ 11 files changed, 1567 insertions(+), 2 deletions(-) create mode 100644 src/gui/channel_manager.cpp create mode 100644 src/gui/channel_manager.hpp create mode 100644 src/gui/tool_view.cpp create mode 100644 src/gui/tool_view.hpp create mode 100644 src/gui/tool_view_builder.cpp create mode 100644 src/gui/tool_view_builder.hpp create mode 100644 ui/tool_view.ui diff --git a/src/gui/channel_manager.cpp b/src/gui/channel_manager.cpp new file mode 100644 index 0000000000..d6aff53fff --- /dev/null +++ b/src/gui/channel_manager.cpp @@ -0,0 +1,190 @@ +#include "dynamicWidget.hpp" + +#include + +#include "channel_manager.hpp" + +using namespace scopy::gui; +using namespace adiscope; + +ChannelManager::ChannelManager(ChannelsPositionEnum position, QWidget* parent) + : QWidget(parent) + , m_scrollArea(new QScrollArea(parent)) + , m_channelsWidget(new QWidget(m_scrollArea)) + , m_switchBtn(new QPushButton(m_scrollArea)) + , m_hasAddBtn(false) + , m_addChannelBtn(new CustomPushButton(m_scrollArea)) + , m_position(position) +{ + if (m_position == ChannelsPositionEnum::VERTICAL) { + m_channelsWidget->setLayout(new QVBoxLayout(m_channelsWidget)); + + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + } else { + m_channelsWidget->setLayout(new QHBoxLayout(m_channelsWidget)); + + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + } + + m_channelsWidget->layout()->setSpacing(0); + m_channelsWidget->layout()->setMargin(0); + m_channelsWidget->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); + + m_scrollArea->setWidget(m_channelsWidget); + + m_scrollArea->setStyleSheet("background-color:orange"); + m_channelsWidget->setStyleSheet("background-color:red"); +} + +ChannelManager::~ChannelManager() +{ + delete m_channelsWidget; + delete m_addChannelBtn; + delete m_switchBtn; + delete m_parent; +} + +void ChannelManager::build(QWidget* parent) +{ + m_parent = parent; + + // Experimental - change orientation at runtime + setDynamicProperty(m_switchBtn, "blue_button", true); + m_switchBtn->setCheckable(true); + m_switchBtn->setFlat(true); + m_switchBtn->setText("Switch"); + connect(m_switchBtn, &QPushButton::toggled, [=]() { + if (m_position == ChannelsPositionEnum::VERTICAL) { + m_position = ChannelsPositionEnum::HORIZONTAL; + } else { + m_position = ChannelsPositionEnum::VERTICAL; + } + Q_EMIT positionChanged(m_position); + }); + + m_parent->layout()->addWidget(m_switchBtn); + m_parent->layout()->addWidget(m_scrollArea); +} + +ChannelWidget* ChannelManager::buildNewChannel(int chId, bool deletable, bool simplefied, QColor color, + const QString& fullName, const QString& shortName) +{ + ChannelWidget* ch = new ChannelWidget(chId, deletable, simplefied, color); + + m_channelsWidget->layout()->addWidget(ch); + ch->setFullName(fullName + QString(" %1").arg(chId + 1)); + ch->setShortName(shortName + QString(" %1").arg(chId + 1)); + ch->nameButton()->setText(ch->shortName()); + + m_channelsList.append(ch); + + if (m_position == ChannelsPositionEnum::VERTICAL) { + m_channelsWidget->setMinimumHeight(m_channelsList.size() * m_channelsList.first()->height()); + m_channelsWidget->setMaximumHeight(m_channelsList.size() * m_channelsList.first()->height()); + + m_channelsWidget->setMinimumWidth(m_channelsList.last()->width()); + m_channelsWidget->setMaximumWidth(m_channelsList.last()->width()); + } else { + m_channelsWidget->setMinimumWidth(m_channelsList.size() * m_channelsList.first()->width()); + m_channelsWidget->setMaximumWidth(m_channelsList.size() * m_channelsList.first()->width()); + + m_channelsWidget->setMinimumHeight(m_channelsList.first()->height()); + m_channelsWidget->setMaximumHeight(m_channelsList.first()->height()); + } + + return ch; +} + +void ChannelManager::removeChannel(ChannelWidget* ch) +{ + m_channelsList.removeOne(ch); + m_channelsWidget->layout()->removeWidget(ch); + delete ch; +} + +CustomPushButton* ChannelManager::getAddChannelBtn() +{ + if (m_hasAddBtn) { + return m_addChannelBtn; + } else { + return nullptr; + } +} + +QList ChannelManager::getChannelsList() { return m_channelsList; } + +void ChannelManager::changeParent(QWidget* newParent) +{ + delete m_channelsWidget->layout(); + + if (m_position == ChannelsPositionEnum::VERTICAL) { + m_channelsWidget->setLayout(new QVBoxLayout(m_channelsWidget)); + + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + } else { + m_channelsWidget->setLayout(new QHBoxLayout(m_channelsWidget)); + + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + } + + m_channelsWidget->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); + m_channelsWidget->layout()->setSpacing(0); + m_channelsWidget->layout()->setMargin(0); + + for (ChannelWidget* channel : m_channelsList) { + m_channelsWidget->layout()->addWidget(channel); + } + + if (m_position == ChannelsPositionEnum::VERTICAL) { + m_channelsWidget->setMinimumHeight(m_channelsList.size() * m_channelsList.first()->height()); + m_channelsWidget->setMaximumHeight(m_channelsList.size() * m_channelsList.first()->height()); + + m_channelsWidget->setMinimumWidth(m_channelsList.last()->width() + 25); + m_channelsWidget->setMaximumWidth(m_channelsList.last()->width() + 25); + } else { + m_channelsWidget->setMinimumWidth(m_channelsList.size() * m_channelsList.first()->width()); + m_channelsWidget->setMaximumWidth(m_channelsList.size() * m_channelsList.first()->width()); + + m_channelsWidget->setMinimumHeight(m_channelsList.first()->height()); + m_channelsWidget->setMaximumHeight(m_channelsList.first()->height()); + } + + m_parent = newParent; + m_parent->layout()->addWidget(m_switchBtn); + m_parent->layout()->addWidget(m_scrollArea); + m_parent->layout()->addWidget(m_addChannelBtn); +} + +QList ChannelManager::getEnabledChannels() +{ + QList lst; + + for (auto widget : m_channelsWidget->children()) { + ChannelWidget* channel = qobject_cast(widget); + if (channel->enableButton()->isChecked()) { + lst.append(channel); + } + } + + return lst; +} + +void ChannelManager::insertAddBtn(QWidget* menu, bool dockable) +{ + m_hasAddBtn = true; + + // TO DO: center + btn when position is vertical + m_addChannelBtn->setCheckable(true); + m_addChannelBtn->setFlat(true); + m_addChannelBtn->setIcon(QIcon(":/icons/common/add.svg")); + m_addChannelBtn->setIconSize(QSize(16, 16)); + m_addChannelBtn->setMaximumSize(25, 25); + + m_parent->layout()->addWidget(m_addChannelBtn); + + Q_EMIT(configureAddBtn(menu, dockable)); +} diff --git a/src/gui/channel_manager.hpp b/src/gui/channel_manager.hpp new file mode 100644 index 0000000000..f5bc544e35 --- /dev/null +++ b/src/gui/channel_manager.hpp @@ -0,0 +1,65 @@ +#ifndef CHANNEL_MANAGER_HPP +#define CHANNEL_MANAGER_HPP + +#include +#include +#include + +#include "channel_widget.hpp" +#include "customPushButton.hpp" + +using namespace adiscope; + +namespace scopy { +namespace gui { + +enum class ChannelsPositionEnum +{ + VERTICAL, + HORIZONTAL +}; + +class ChannelManager : public QWidget +{ + Q_OBJECT + +public: + explicit ChannelManager(ChannelsPositionEnum position = ChannelsPositionEnum::HORIZONTAL, + QWidget* parent = nullptr); + ~ChannelManager(); + + void build(QWidget* parent); + void insertAddBtn(QWidget* menu, bool dockable); + + ChannelWidget* buildNewChannel(int chId, bool deletable, bool simplefied, QColor color, const QString& fullName, + const QString& shortName); + void removeChannel(ChannelWidget* ch); + QList getEnabledChannels(); + + CustomPushButton* getAddChannelBtn(); + QList getChannelsList(); + +public Q_SLOTS: + void changeParent(QWidget* newParent); + +Q_SIGNALS: + void configureAddBtn(QWidget* menu, bool dockable); + void positionChanged(ChannelsPositionEnum position); + +private: + QWidget* m_parent; + QScrollArea* m_scrollArea; + QWidget* m_channelsWidget; + QPushButton* m_switchBtn; + + bool m_hasAddBtn; + CustomPushButton* m_addChannelBtn; + + ChannelsPositionEnum m_position; + + QList m_channelsList; +}; +} // namespace gui +} // namespace scopy + +#endif // CHANNEL_MANAGER_HPP diff --git a/src/gui/channel_widget.cpp b/src/gui/channel_widget.cpp index cebf28b855..4b226f613e 100644 --- a/src/gui/channel_widget.cpp +++ b/src/gui/channel_widget.cpp @@ -198,6 +198,8 @@ void ChannelWidget::setReferenceChannel(const bool &ref) m_ref = ref; } +void ChannelWidget::setMenuFloating(bool floating) { m_floatingMenu = floating; } + bool ChannelWidget::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { diff --git a/src/gui/channel_widget.hpp b/src/gui/channel_widget.hpp index 2b30d38579..babeb17ce7 100644 --- a/src/gui/channel_widget.hpp +++ b/src/gui/channel_widget.hpp @@ -64,9 +64,15 @@ class ChannelWidget: public QWidget bool isReferenceChannel() const; void setReferenceChannel(const bool&); + void setMenuFloating(bool floating); + protected: bool eventFilter(QObject *object, QEvent *event) override; +private: + void init(); + void setButtonNoGroup(QAbstractButton *btn); + Q_SIGNALS: void enabled(bool en); void selected(bool on); @@ -94,8 +100,7 @@ private Q_SLOTS: QString m_function; bool m_ref; - void init(); - void setButtonNoGroup(QAbstractButton *btn); + bool m_floatingMenu; }; } /* namespace adiscope */ diff --git a/src/gui/runsinglewidget.cpp b/src/gui/runsinglewidget.cpp index d609cc6c6e..df51ead675 100644 --- a/src/gui/runsinglewidget.cpp +++ b/src/gui/runsinglewidget.cpp @@ -84,6 +84,10 @@ bool RunSingleWidget::runButtonChecked() const return d_ui->runButton->isChecked(); } +QPushButton* RunSingleWidget::getRunButton() { return d_ui->runButton; } + +QPushButton* RunSingleWidget::getSingleButton() { return d_ui->singleButton; } + void RunSingleWidget::toggle(bool checked) { if (!checked) { diff --git a/src/gui/runsinglewidget.h b/src/gui/runsinglewidget.h index b0cf92eae0..7cedef11b3 100644 --- a/src/gui/runsinglewidget.h +++ b/src/gui/runsinglewidget.h @@ -21,6 +21,7 @@ #ifndef RUNSINGLEWIDGET_H #define RUNSINGLEWIDGET_H +#include #include namespace Ui { @@ -45,6 +46,9 @@ class RunSingleWidget : public QWidget bool singleButtonChecked() const; bool runButtonChecked() const; + QPushButton* getRunButton(); + QPushButton* getSingleButton(); + public Q_SLOTS: void toggle(bool); void single(); diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp new file mode 100644 index 0000000000..fa315e9b5c --- /dev/null +++ b/src/gui/tool_view.cpp @@ -0,0 +1,446 @@ +#include "ui_tool_view.h" + +#include + +#include "channel_widget.hpp" +#include "menu_header.hpp" +#include "tool_view.hpp" + +using namespace scopy::gui; + +ToolView::ToolView(QWidget* parent) + : QWidget(parent) + , m_ui(new Ui::ToolView) + , m_dockables(0) +{ + m_ui->setupUi(this); + + m_centralMainWindow = new QMainWindow(m_ui->widgetCentral); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + m_ui->widgetPlotContainer->layout()->addWidget(m_centralMainWindow); + + m_ui->widgetRunSingleBtns->enableRunButton(false); + m_ui->widgetRunSingleBtns->enableSingleButton(false); + m_ui->widgetSettingsPairBtns->setVisible(false); + m_ui->btnHelp->setVisible(false); + m_ui->btnPrint->setVisible(false); + + m_ui->widgetInstrumentNotes->setVisible(false); + m_ui->widgetVerticalChannels->setVisible(false); + m_ui->widgetFooter->setVisible(false); + m_ui->widgetMenuBtns->setVisible(false); + + m_ui->widgetMenuAnim->setMaximumWidth(0); + + connect(m_ui->widgetMenuAnim, &MenuAnim::finished, this, &ToolView::rightMenuFinished); +} + +ToolView::~ToolView() { delete m_ui; } + +void ToolView::configureLastOpenedMenu() +{ + QPushButton* settingsBtn = m_ui->widgetSettingsPairBtns->getSettingsBtn(); + + connect(settingsBtn, &QPushButton::clicked, this, [=](bool checked) { + if (!m_menuOrder.isEmpty()) { + CustomPushButton* btn = nullptr; + + if (checked) { + btn = m_menuOrder.back(); + m_menuOrder.pop_back(); + } else { + btn = static_cast(m_group.checkedButton()); + } + + btn->setChecked(checked); + } else { + getSettingsBtn()->setChecked(false); + } + }); +} + +void ToolView::rightMenuFinished(bool opened) +{ + Q_UNUSED(opened) + + // At the end of each animation, check if there are other button check + // actions that might have happened while animating and execute all + // these queued actions + while (m_menuButtonActions.size()) { + auto pair = m_menuButtonActions.dequeue(); + toggleRightMenu(pair.first, pair.second); + } +} + +void ToolView::triggerRightMenuToggle(bool checked) +{ + // Queue the action, if right menu animation is in progress. This way + // the action will be remembered and performed right after the animation + // finishes + CustomPushButton* btn = static_cast(QObject::sender()); + if (m_ui->widgetMenuAnim->animInProgress()) { + m_menuButtonActions.enqueue(QPair(btn, checked)); + } else { + toggleRightMenu(btn, checked); + } +} + +void ToolView::toggleRightMenu(CustomPushButton* btn, bool checked) +{ + int id = btn->property("id").toInt(); + + if (id != -m_generalSettingsMenuId) { + if (!m_menuOrder.contains(btn)) { + m_menuOrder.push_back(btn); + } else { + m_menuOrder.removeOne(btn); + m_menuOrder.push_back(btn); + } + } + + if (checked) { + settingsPanelUpdate(id); + } + + m_ui->widgetMenuAnim->toggleMenu(checked); +} + +void ToolView::settingsPanelUpdate(int id) +{ + if (id >= 0) { + m_ui->stackedWidget->setCurrentIndex(0); + } else { + m_ui->stackedWidget->setCurrentIndex(-id); + } + + for (int i = 0; i < m_ui->stackedWidget->count(); i++) { + QSizePolicy::Policy policy = QSizePolicy::Ignored; + + if (i == m_ui->stackedWidget->currentIndex()) { + policy = QSizePolicy::Expanding; + } + QWidget* widget = m_ui->stackedWidget->widget(i); + widget->setSizePolicy(policy, policy); + } + m_ui->stackedWidget->adjustSize(); +} + +void ToolView::buildChannelsContainer(ChannelManager* cm, ChannelsPositionEnum position) +{ + connect(cm, &ChannelManager::configureAddBtn, this, &ToolView::configureAddMathBtn); + + connect(this, &ToolView::changeParent, cm, &ChannelManager::changeParent); + connect(cm, &ChannelManager::positionChanged, this, [=](ChannelsPositionEnum position) { + if (position == ChannelsPositionEnum::VERTICAL) { + m_ui->widgetHorizontalChannels->setVisible(false); + m_ui->widgetVerticalChannels->setVisible(true); + + if (!m_ui->widgetMenuBtns->isVisible()) { + m_ui->widgetFooter->setVisible(false); + } + + Q_EMIT changeParent(m_ui->widgetVerticalChannelsContainer); + } else { + m_ui->widgetVerticalChannels->setVisible(false); + m_ui->widgetFooter->setVisible(true); + m_ui->widgetHorizontalChannels->setVisible(true); + + Q_EMIT changeParent(m_ui->widgetHorizontalChannelsContainer); + } + }); + + if (position == ChannelsPositionEnum::HORIZONTAL) { + m_ui->widgetFooter->setVisible(true); + cm->build(m_ui->widgetHorizontalChannelsContainer); + } else { + m_ui->widgetVerticalChannels->setVisible(true); + cm->build(m_ui->widgetVerticalChannelsContainer); + } +} + +QDockWidget* ToolView::createDetachableMenu(QWidget* menu, int& id) +{ + QMainWindow* subWindow = new QMainWindow(this); + QDockWidget* docker = new QDockWidget(subWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + subWindow->addDockWidget(Qt::RightDockWidgetArea, docker); + docker->setWidget(menu); + + id = m_ui->stackedWidget->addWidget(subWindow); + + return docker; +} + +void ToolView::configureAddMathBtn(QWidget* menu, bool dockable) +{ + ChannelManager* cm = static_cast(QObject::sender()); + CustomPushButton* addBtn = cm->getAddChannelBtn(); + int id; + + if (dockable) { + QDockWidget* docker = this->createDetachableMenu(menu, id); + + connect(docker, &QDockWidget::topLevelChanged, addBtn, [=](bool topLevel) { + addBtn->setChecked(!topLevel); + addBtn->setDisabled(topLevel); + + if (topLevel) { + m_menuOrder.removeOne(addBtn); + } + }); + } else { + id = m_ui->stackedWidget->addWidget(menu); + } + + m_group.addButton(addBtn); + addBtn->setProperty("id", QVariant(-id)); + + connect(addBtn, &CustomPushButton::toggled, this, &ToolView::triggerRightMenuToggle); + connect(addBtn, &CustomPushButton::toggled, m_ui->widgetSettingsPairBtns->getSettingsBtn(), + &QPushButton::setChecked); +} + +ChannelWidget* ToolView::buildNewChannel(ChannelManager* channelManager, GenericMenu* menu, bool dockable, int chId, + bool deletable, bool simplefied, QColor color, const QString& fullName, + const QString& shortName) +{ + ChannelWidget* ch = channelManager->buildNewChannel(chId, deletable, simplefied, color, fullName, shortName); + int id; + + if (dockable) { + QDockWidget* docker = this->createDetachableMenu(menu, id); + + connect(docker, &QDockWidget::topLevelChanged, ch->menuButton(), [=](bool topLevel) { + CustomPushButton* btn = static_cast(ch->menuButton()); + btn->setChecked(!topLevel); + btn->setDisabled(topLevel); + + ch->setMenuFloating(topLevel); + + if (topLevel) { + m_menuOrder.removeOne(btn); + } + }); + + if (deletable) { + connect(ch, &ChannelWidget::deleteClicked, this, [=]() { docker->close(); }); + } + + } else { + id = m_ui->stackedWidget->addWidget(menu); + } + + m_group.addButton(ch->menuButton()); + m_channelsGroup.addButton(ch->nameButton()); + ch->menuButton()->setProperty("id", QVariant(-id)); + + connect(ch->menuButton(), &CustomPushButton::toggled, this, &ToolView::triggerRightMenuToggle); + connect(ch->menuButton(), &CustomPushButton::toggled, m_ui->widgetSettingsPairBtns->getSettingsBtn(), + &QPushButton::setChecked); + connect(ch->enableButton(), &QAbstractButton::toggled, [=](bool toggled) { + if (!toggled) { + // we also remove the button from the history + // so that the last menu opened button on top + // won't open the menu when it is disabled + m_menuOrder.removeOne(qobject_cast(ch->menuButton())); + } + + // mirror menu btn + menu->setMenuButton(toggled); + }); + + connect(menu, &GenericMenu::enableBtnToggled, [=](bool toggled) { ch->enableButton()->setChecked(toggled); }); + + if (deletable) { + connect(ch, &ChannelWidget::deleteClicked, this, [=]() { + if (ch->menuButton()->isChecked()) { + m_menuButtonActions.removeAll(QPair( + qobject_cast(ch->menuButton()), true)); + toggleRightMenu(qobject_cast(ch->menuButton()), false); + } + m_menuOrder.removeOne(qobject_cast(ch->menuButton())); + + channelManager->removeChannel(ch); + }); + } + + return ch; +} + +void ToolView::buildNewInstrumentMenu(GenericMenu* menu, bool dockable, const QString& name, bool checkBoxVisible, + bool checkBoxChecked) +{ + m_ui->widgetFooter->setVisible(true); + m_ui->widgetMenuBtns->setVisible(true); + + CustomMenuButton* btn = new CustomMenuButton(name, checkBoxVisible, checkBoxChecked); + int id; + + if (dockable) { + QDockWidget* docker = this->createDetachableMenu(menu, id); + + connect(docker, &QDockWidget::topLevelChanged, btn, [=](bool topLevel) { + btn->getBtn()->setChecked(!topLevel); + btn->getBtn()->setDisabled(topLevel); + + btn->setMenuFloating(topLevel); + + if (topLevel) { + m_menuOrder.removeOne(btn->getBtn()); + } + }); + + } else { + id = m_ui->stackedWidget->addWidget(menu); + } + + m_ui->hLayoutMenuBtnsContainer->addWidget(btn); + m_group.addButton(btn->getBtn()); + btn->getBtn()->setProperty("id", QVariant(-id)); + + connect(btn->getBtn(), &CustomPushButton::toggled, this, &ToolView::triggerRightMenuToggle); + connect(btn->getBtn(), &CustomPushButton::toggled, m_ui->widgetSettingsPairBtns->getSettingsBtn(), + &QPushButton::setChecked); + connect(btn->getCheckBox(), &QCheckBox::toggled, [=](bool toggled) { + if (!toggled) { + // we also remove the button from the history + // so that the last menu opened button on top + // won't open the menu when it is disabled + m_menuOrder.removeOne(btn->getBtn()); + } + // mirror menu btn + menu->setMenuButton(toggled); + }); + + connect(menu, &GenericMenu::enableBtnToggled, [=](bool toggled) { btn->getCheckBox()->setChecked(toggled); }); + + if ((checkBoxVisible && checkBoxChecked) || !checkBoxVisible) { + m_menuOrder.push_back(btn->getBtn()); + } +} + +void ToolView::addCentralWidget(QWidget* widget, bool dockable, const QString& dockerName, int row, int column, + int rowspan, int columnspan) +{ + if (dockable) { + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setWindowTitle(dockerName); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(widget); + + if (m_dockables < 3) { + if (m_dockables % 3 == 0) { + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + } else if (m_dockables % 3 == 1) { + m_centralMainWindow->addDockWidget(Qt::RightDockWidgetArea, docker); + } else { + m_centralMainWindow->addDockWidget(Qt::BottomDockWidgetArea, docker); + } + + m_firstDocks.push_back(docker); + } else { + m_centralMainWindow->tabifyDockWidget(m_firstDocks.at(m_dockables % 3), docker); + } + + m_dockables++; + + } else { + if (row == -1 || column == -1) { + m_ui->gridWidgetCentral->addWidget(widget); + } else { + if (rowspan == -1 || columnspan == -1) { + m_ui->gridWidgetCentral->addWidget(widget, row, column); + } else { + m_ui->gridWidgetCentral->addWidget(widget, row, column, rowspan, columnspan); + } + } + } +} + +void ToolView::setGeneralSettingsMenu(QWidget* menu, bool dockable) +{ + CustomPushButton* generalSettingsBtn = m_ui->widgetSettingsPairBtns->getGeneralSettingsBtn(); + + if (dockable) { + QDockWidget* docker = this->createDetachableMenu(menu, m_generalSettingsMenuId); + + connect(docker, &QDockWidget::topLevelChanged, generalSettingsBtn, [=](bool topLevel) { + generalSettingsBtn->setChecked(!topLevel); + generalSettingsBtn->setDisabled(topLevel); + }); + } else { + m_generalSettingsMenuId = m_ui->stackedWidget->addWidget(menu); + } + + generalSettingsBtn->setProperty("id", QVariant(-m_generalSettingsMenuId)); + m_group.addButton(generalSettingsBtn); + + connect(generalSettingsBtn, &CustomPushButton::toggled, this, [=](bool toggled) { + triggerRightMenuToggle(toggled); + if (toggled) { + m_ui->widgetSettingsPairBtns->getSettingsBtn()->setChecked(!toggled); + } + }); +} + +void ToolView::setFixedMenu(QWidget* menu, bool dockable) +{ + int id; + + if (dockable) { + QDockWidget* docker = this->createDetachableMenu(menu, id); + + connect(docker, &QDockWidget::topLevelChanged, + [=](bool topLevel) { m_ui->widgetMenuAnim->toggleMenu(!topLevel); }); + } else { + id = m_ui->stackedWidget->addWidget(menu); + } + + settingsPanelUpdate(-id); + m_ui->widgetMenuAnim->toggleMenu(true); +} + +QWidget* ToolView::getTopExtraWidget() { return m_ui->widgetTopExtra; } + +void ToolView::setVisibleTopExtraWidget(bool visible) { m_ui->widgetTopExtra->setVisible(visible); } + +void ToolView::addTopExtraWidget(QWidget* widget) { m_ui->widgetTopExtra->layout()->addWidget(widget); } + +QWidget* ToolView::getBottomExtraWidget() { return m_ui->widgetBottomExtra; } + +void ToolView::setVisibleBottomExtraWidget(bool visible) { m_ui->widgetBottomExtra->setVisible(visible); } + +void ToolView::addBottomExtraWidget(QWidget* widget) { m_ui->widgetBottomExtra->layout()->addWidget(widget); } + +QWidget* ToolView::getCentralWidget() { return m_ui->widgetCentral; } + +QStackedWidget* ToolView::getStackedWidget() { return m_ui->stackedWidget; } + +void ToolView::setInstrumentNotesVisible(bool visible) { m_ui->widgetInstrumentNotes->setVisible(visible); } + +LinkedButton* ToolView::getHelpBtn() { return m_ui->btnHelp; } + +void ToolView::setHelpBtnVisible(bool visible) { m_ui->btnHelp->setVisible(visible); } + +void ToolView::setUrlHelpBtn(const QString& url) { m_ui->btnHelp->setUrl(url); } + +QPushButton* ToolView::getRunBtn() { return m_ui->widgetRunSingleBtns->getRunButton(); } + +void ToolView::setRunBtnVisible(bool visible) { m_ui->widgetRunSingleBtns->enableRunButton(visible); } + +QPushButton* ToolView::getSingleBtn() { return m_ui->widgetRunSingleBtns->getSingleButton(); } + +void ToolView::setSingleBtnVisible(bool visible) { m_ui->widgetRunSingleBtns->enableSingleButton(visible); } + +QPushButton* ToolView::getPrintBtn() { return m_ui->btnPrint; } + +void ToolView::setPrintBtnVisible(bool visible) { m_ui->btnPrint->setVisible(visible); } + +void ToolView::setPairSettingsVisible(bool visible) { m_ui->widgetSettingsPairBtns->setVisible(visible); } + +CustomPushButton* ToolView::getGeneralSettingsBtn() { return m_ui->widgetSettingsPairBtns->getGeneralSettingsBtn(); } + +QPushButton* ToolView::getSettingsBtn() { return m_ui->widgetSettingsPairBtns->getSettingsBtn(); } diff --git a/src/gui/tool_view.hpp b/src/gui/tool_view.hpp new file mode 100644 index 0000000000..8261d2560b --- /dev/null +++ b/src/gui/tool_view.hpp @@ -0,0 +1,111 @@ +#ifndef TOOL_VIEW_HPP +#define TOOL_VIEW_HPP + +#include +#include +#include +#include +#include +#include + +#include "channel_manager.hpp" +#include "channel_widget.hpp" +#include "custom_menu_button.hpp" +#include "customPushButton.hpp" +#include "generic_menu.hpp" +#include "linked_button.hpp" + +namespace Ui { +class ToolView; +} + +namespace scopy { +namespace gui { + +class ToolView : public QWidget +{ + friend class ToolViewBuilder; + + Q_OBJECT + +public: + explicit ToolView(QWidget* parent = nullptr); + ~ToolView(); + + LinkedButton* getHelpBtn(); + void setHelpBtnVisible(bool visible); + void setUrlHelpBtn(const QString& url); + + QPushButton* getPrintBtn(); + void setPrintBtnVisible(bool visible); + + QPushButton* getRunBtn(); + void setRunBtnVisible(bool visible); + + QPushButton* getSingleBtn(); + void setSingleBtnVisible(bool visible); + + QWidget* getTopExtraWidget(); + void setVisibleTopExtraWidget(bool visible); + void addTopExtraWidget(QWidget* widget); + + QWidget* getBottomExtraWidget(); + void setVisibleBottomExtraWidget(bool visible); + void addBottomExtraWidget(QWidget* widget); + + QWidget* getCentralWidget(); + QStackedWidget* getStackedWidget(); + + CustomPushButton* getGeneralSettingsBtn(); + QPushButton* getSettingsBtn(); + void setPairSettingsVisible(bool visible); + + void setInstrumentNotesVisible(bool visible); + + void setFixedMenu(QWidget* menu, bool dockable); + void setGeneralSettingsMenu(QWidget* menu, bool dockable); + + ChannelWidget* buildNewChannel(ChannelManager* channelManager, GenericMenu* menu, bool dockable, int chId, + bool deletable, bool simplified, QColor color, const QString& fullName, + const QString& shortName); + void buildNewInstrumentMenu(GenericMenu* menu, bool dockable, const QString& name, bool checkBoxVisible = false, + bool checkBoxChecked = false); + void addCentralWidget(QWidget* widget, bool dockable, const QString& dockerName, int row = -1, int column = -1, + int rowspan = -1, int columnspan = -1); + +private: + void configureLastOpenedMenu(); + void buildChannelsContainer(ChannelManager* channelManager, ChannelsPositionEnum position); + void toggleRightMenu(CustomPushButton* btn, bool checked); + void settingsPanelUpdate(int id); + void rightMenuFinished(bool opened); + QDockWidget* createDetachableMenu(QWidget* menu, int& id); + +public Q_SLOTS: + void triggerRightMenuToggle(bool checked); + void configureAddMathBtn(QWidget* menu, bool dockable); + +Q_SIGNALS: + void changeParent(QWidget* newParent); + void channelDisabled(bool disabled); + void instrumentMenuDisabled(bool disabled); + +private: + Ui::ToolView* m_ui; + + QButtonGroup m_group; + QButtonGroup m_channelsGroup; // selected state of each channel + + QQueue> m_menuButtonActions; + QList m_menuOrder; + + int m_generalSettingsMenuId; + + QMainWindow* m_centralMainWindow; + unsigned int m_dockables; + std::vector m_firstDocks; +}; +} // namespace gui +} // namespace scopy + +#endif // TOOL_VIEW_HPP diff --git a/src/gui/tool_view_builder.cpp b/src/gui/tool_view_builder.cpp new file mode 100644 index 0000000000..0367db659c --- /dev/null +++ b/src/gui/tool_view_builder.cpp @@ -0,0 +1,47 @@ +#include "dynamicWidget.hpp" + +#include "tool_view_builder.hpp" + +using namespace scopy::gui; + +ToolViewBuilder::ToolViewBuilder(const ToolViewRecipe& recipe, ChannelManager* channelManager) +{ + m_toolView = new ToolView(); + + if (recipe.hasRunBtn) { + m_toolView->setRunBtnVisible(true); + } + if (recipe.hasSingleBtn) { + m_toolView->setSingleBtnVisible(true); + } + if (recipe.hasHelpBtn) { + m_toolView->setHelpBtnVisible(true); + m_toolView->setUrlHelpBtn(recipe.helpBtnUrl); + } + if (recipe.hasPrintBtn) { + m_toolView->setPrintBtnVisible(true); + } + if (recipe.hasGroupBtn) { + QPushButton* btn = new QPushButton; + btn->setStyleSheet("QPushButton{ width: 80px;" + "height: 40px;" + "text-align: left;" + "font-weight: bold;" + "padding-left: 15px;" + "padding-right: 15px;}"); + btn->setText("Group"); + adiscope::setDynamicProperty(btn, "blue_button", true); + m_toolView->addTopExtraWidget(btn); + } + + if (recipe.hasPairSettingsBtn) { + m_toolView->setPairSettingsVisible(true); + m_toolView->configureLastOpenedMenu(); + } + + if (recipe.hasChannels) { + m_toolView->buildChannelsContainer(channelManager, recipe.channelsPosition); + } +} + +ToolView* ToolViewBuilder::build() { return m_toolView; } diff --git a/src/gui/tool_view_builder.hpp b/src/gui/tool_view_builder.hpp new file mode 100644 index 0000000000..63479b9066 --- /dev/null +++ b/src/gui/tool_view_builder.hpp @@ -0,0 +1,38 @@ +#ifndef TOOLBUILDER_HPP +#define TOOLBUILDER_HPP + +#include "tool_view.hpp" + +namespace scopy { +namespace gui { + +struct ToolViewRecipe +{ + QString helpBtnUrl{""}; + bool hasHelpBtn{true}; + bool hasPrintBtn{false}; + bool hasGroupBtn{false}; + + bool hasRunBtn{false}; + bool hasSingleBtn{false}; + + bool hasPairSettingsBtn{false}; + + bool hasChannels{false}; + ChannelsPositionEnum channelsPosition{ChannelsPositionEnum::HORIZONTAL}; +}; + +class ToolViewBuilder +{ +public: + ToolViewBuilder(const ToolViewRecipe& recipe, ChannelManager* channelManager = nullptr); + + ToolView* build(); + +private: + ToolView* m_toolView; +}; +} // namespace gui +} // namespace scopy + +#endif // TOOLVIEWBUILDER_HPP diff --git a/ui/tool_view.ui b/ui/tool_view.ui new file mode 100644 index 0000000000..facb2bdb66 --- /dev/null +++ b/ui/tool_view.ui @@ -0,0 +1,653 @@ + + + ToolView + + + + 0 + 0 + 1280 + 654 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 40 + + + QLayout::SetDefaultConstraint + + + 15 + + + 40 + + + 9 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 10 + + + + + + + + + + + false + + + + + + + Qt::RightToLeft + + + QPushButton{ + width: 80px; + height: 40px; + + text-align: left; + font-weight: bold; + padding-left: 15px; + padding-right: 15px; +} + + + Print + + + + :/icons/common/ic printer.svg:/icons/common/ic printer.svg + + + + 10 + 10 + + + + true + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 40 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 5 + + + + + + + + + + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 190 + 0 + + + + + 190 + 16777215 + + + + + 0 + + + 10 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + 0 + + + + + + + + + + + + + + 0 + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 340 + 0 + + + + -1 + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 1 + + + 1 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 510 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + + + + + 40 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 48 + + + + QPushButton { + width: 40px; + height: 20px; + background-color: transparent; +} +QPushButton:pressed { border-image: url(:/icons/common/setup_btn_checked.svg); } +QPushButton:!pressed { border-image: url(:/icons/common/setup_btn_unchecked.svg); } +QPushButton:hover:!pressed:!checked { border-image: url(:/icons/common/setup_btn_hover.svg); } +QPushButton:checked { border-image: url(:/icons/common/setup_btn_checked.svg); } + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + adiscope::MenuAnim + QWidget +
gui/menu_anim.hpp
+ 1 +
+ + adiscope::InstrumentNotes + QWidget +
gui/instrumentnotes.h
+ 1 +
+ + adiscope::RunSingleWidget + QWidget +
gui/runsinglewidget.h
+ 1 +
+ + adiscope::LinkedButton + QPushButton +
gui/linked_button.hpp
+
+ + scopy::gui::SettingsPairWidget + QWidget +
gui/settings_pair_widget.hpp
+ 1 +
+
+ + +
From 67f4a06ffa3bbe6189a3fbbf97d72d3aea984af3 Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 25 Jun 2021 14:17:56 +0300 Subject: [PATCH 045/125] gui: added dockable plots Signed-off-by: Ioana Chelaru --- resources/stylesheets/default.qss | 4 + resources/stylesheets/light.qss | 4 + src/logicanalyzer/logic_analyzer.cpp | 66 +++- src/network_analyzer.cpp | 56 ++- src/oscilloscope.cpp | 175 +++++++-- src/oscilloscope.hpp | 6 + src/oscilloscope_plot.cpp | 4 +- src/patterngenerator/pattern_generator.cpp | 47 ++- src/signal_generator.cpp | 34 +- src/spectrum_analyzer.cpp | 68 +++- ui/logic_analyzer.ui | 7 +- ui/network_analyzer.ui | 6 + ui/oscilloscope.ui | 401 +++------------------ ui/signal_generator.ui | 3 + ui/spectrum_analyzer.ui | 233 ++++++------ 15 files changed, 569 insertions(+), 545 deletions(-) diff --git a/resources/stylesheets/default.qss b/resources/stylesheets/default.qss index 1ac70060d6..77889503d8 100644 --- a/resources/stylesheets/default.qss +++ b/resources/stylesheets/default.qss @@ -12,6 +12,10 @@ QWidget { font-size: 13px; } +QDockWidget { + titlebar-normal-icon: ""; +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/resources/stylesheets/light.qss b/resources/stylesheets/light.qss index 77a251b5f9..c811742496 100644 --- a/resources/stylesheets/light.qss +++ b/resources/stylesheets/light.qss @@ -12,6 +12,10 @@ QWidget { font-size: 13px; } +QDockWidget { + titlebar-normal-icon: ""; +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 08376df645..7c7c06f9cd 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -39,7 +39,7 @@ #include "gui/dynamicWidget.hpp" #include - +#include #include #include @@ -1292,23 +1292,67 @@ void LogicAnalyzer::setupUi() // Plot positioning and settings m_plot.disableLegend(); + + // Build central widget + + QWidget* centralWidget = new QWidget(this); + QVBoxLayout* vLayout = new QVBoxLayout(centralWidget); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(0); + + // add the buffer previewer + ui->plot_and_buffPreviewer->removeWidget(ui->hLayoutBufferPreview); + vLayout->addWidget(ui->hLayoutBufferPreview); + + // add plot elements + QWidget* plotWidget = new QWidget(this); + plotWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); + QGridLayout* gridLayout = new QGridLayout(plotWidget); + gridLayout->setVerticalSpacing(0); + gridLayout->setHorizontalSpacing(0); + gridLayout->setContentsMargins(25, 0, 25, 0); + plotWidget->setLayout(gridLayout); + QSpacerItem *plotSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); -// ui->gridLayoutPlot->addWidget(measurePanel, 0, 1, 1, 1); - ui->gridLayoutPlot->addWidget(m_plot.topArea(), 0, 0, 1, 4); - ui->gridLayoutPlot->addWidget(m_plot.topHandlesArea(), 1, 0, 1, 4); + gridLayout->addWidget(m_plot.topArea(), 0, 0, 1, 4); + gridLayout->addWidget(m_plot.topHandlesArea(), 1, 0, 1, 4); + + gridLayout->addWidget(m_plot.leftHandlesArea(), 0, 0, 4, 1); + gridLayout->addWidget(m_plot.rightHandlesArea(), 0, 3, 4, 1); + + gridLayout->addWidget(&m_plot, 2, 1, 1, 1); + gridLayout->addWidget(m_plotScrollBar, 2, 5, 1, 1); - ui->gridLayoutPlot->addWidget(m_plot.leftHandlesArea(), 0, 0, 4, 1); - ui->gridLayoutPlot->addWidget(m_plot.rightHandlesArea(), 0, 3, 4, 1); + gridLayout->addWidget(m_plot.bottomHandlesArea(), 3, 0, 1, 4); + gridLayout->addItem(plotSpacer, 4, 0, 1, 4); + vLayout->addWidget(plotWidget); + centralWidget->setLayout(vLayout); + + // Add dockable plot + + QMainWindow* m_centralMainWindow = new QMainWindow(this); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + ui->gridLayoutPlot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(centralWidget); + + connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + docker->setContentsMargins(10, 0, 10, 10); + } else { + docker->setContentsMargins(0, 0, 0, 0); + } + }); - ui->gridLayoutPlot->addWidget(&m_plot, 2, 1, 1, 1); - ui->gridLayoutPlot->addWidget(m_plotScrollBar, 2, 5, 1, 1); + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - ui->gridLayoutPlot->addWidget(m_plot.bottomHandlesArea(), 3, 0, 1, 4); - ui->gridLayoutPlot->addItem(plotSpacer, 4, 0, 1, 4); -// ui->gridLayoutPlot->addWidget(statisticsPanel, 6, 1, 1, 1); m_plot.enableAxis(QwtPlot::yLeft, false); m_plot.enableAxis(QwtPlot::xBottom, false); diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 722aa470dd..46a1d4fdd4 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -422,18 +423,53 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, m_phaseGraph.setVertCursorsHandleEnabled(false); - ui->gridLayout_plots->addWidget(bufferPreviewer, 0, 1, 1, 1); - ui->gridLayout_plots->addWidget(ui->statusWidget, 1, 1, 1, 1); - ui->gridLayout_plots->addWidget(m_dBgraph.rightHandlesArea(), 0, 2, 6, 1); - ui->gridLayout_plots->addWidget(m_dBgraph.topHandlesArea(), 2, 0, 1, 2); - ui->gridLayout_plots->addWidget(m_dBgraph.leftHandlesArea(), 3, 0, 1, 1); - ui->gridLayout_plots->addWidget(&m_dBgraph, 3, 1, 1, 1); + // Add dockable plot - ui->gridLayout_plots->addWidget(m_phaseGraph.topHandlesArea(), 4, 0, 1, 2); - ui->gridLayout_plots->addWidget(m_phaseGraph.leftHandlesArea(), 5, 0, 1, 1); - ui->gridLayout_plots->addWidget(&m_phaseGraph, 5, 1, 1, 1); + QWidget* widget = new QWidget(this); + QGridLayout* gridLayout = new QGridLayout(widget); + gridLayout->setVerticalSpacing(0); + gridLayout->setHorizontalSpacing(10); + gridLayout->setContentsMargins(0, 0, 0, 0); + + gridLayout->addWidget(bufferPreviewer, 0, 1, 1, 1); + gridLayout->addWidget(ui->statusWidget, 1, 1, 1, 1); + gridLayout->addWidget(m_dBgraph.rightHandlesArea(), 0, 2, 6, 1); + gridLayout->addWidget(m_dBgraph.topHandlesArea(), 2, 0, 1, 2); + gridLayout->addWidget(m_dBgraph.leftHandlesArea(), 3, 0, 1, 1); + gridLayout->addWidget(&m_dBgraph, 3, 1, 1, 1); + + gridLayout->addWidget(m_phaseGraph.topHandlesArea(), 4, 0, 1, 2); + gridLayout->addWidget(m_phaseGraph.leftHandlesArea(), 5, 0, 1, 1); + gridLayout->addWidget(&m_phaseGraph, 5, 1, 1, 1); + + gridLayout->addWidget(m_dBgraph.bottomHandlesArea(), 6, 0, 1, 3); + + widget->setLayout(gridLayout); + + + QMainWindow* m_centralMainWindow = new QMainWindow(this); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + ui->gridLayout_plots->addWidget(m_centralMainWindow, 0, 0); + + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(widget); + + connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + docker->setContentsMargins(10, 0, 10, 10); +// ui->stackedWidget->hide(); + } else { + docker->setContentsMargins(0, 0, 0, 0); +// ui->stackedWidget->show(); + } + }); + + + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - ui->gridLayout_plots->addWidget(m_dBgraph.bottomHandlesArea(), 6, 0, 1, 3); m_phaseGraph.enableXaxisLabels(); m_dBgraph.enableXaxisLabels(); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index 5479fd7f47..bcb21cb38c 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -39,6 +39,7 @@ #include #include #include +#include /* libm2k includes */ #include @@ -315,27 +316,89 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, buffer_previewer->setCursorPos(0.5); - /* Plot layout */ + + // Build central widget + QWidget* centralWidget = new QWidget(this); + QVBoxLayout* vLayout = new QVBoxLayout(centralWidget); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(0); + + // add the buffer previewer + ui->plot_and_buffPreviewer->removeWidget(ui->hLayoutBufferPreview); + vLayout->addWidget(ui->hLayoutBufferPreview); + + // add plot elements + QWidget* plotWidget = new QWidget(this); + gridPlot = new QGridLayout(plotWidget); + gridPlot->setVerticalSpacing(0); + gridPlot->setHorizontalSpacing(0); + gridPlot->setContentsMargins(0, 0, 0, 0); + plotWidget->setLayout(gridPlot); QSpacerItem *plotSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); - ui->gridLayoutPlot->addWidget(measurePanel, 0, 1, 1, 1); - ui->gridLayoutPlot->addWidget(plot.topArea(), 1, 0, 1, 4); - ui->gridLayoutPlot->addWidget(plot.topHandlesArea(), 2, 0, 1, 4); + gridPlot->addWidget(measurePanel, 0, 1, 1, 1); + gridPlot->addWidget(plot.topArea(), 1, 0, 1, 4); + gridPlot->addWidget(plot.topHandlesArea(), 2, 0, 1, 4); + + gridPlot->addWidget(plot.leftHandlesArea(), 1, 0, 4, 1); + gridPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); + + gridPlot->addWidget(&plot, 3, 1, 1, 1); + gridPlot->addWidget(&hist_plot, 3, 2, 1, 1); + + gridPlot->addWidget(plot.bottomHandlesArea(), 4, 0, 1, 4); + gridPlot->addItem(plotSpacer, 5, 0, 1, 4); + gridPlot->addWidget(statisticsPanel, 6, 1, 1, 1); + + vLayout->addWidget(plotWidget); + centralWidget->setLayout(vLayout); + + ui->plot_and_scales->removeWidget(ui->scalesWidget); + vLayout->addWidget(ui->scalesWidget); + - ui->gridLayoutPlot->addWidget(plot.leftHandlesArea(), 1, 0, 4, 1); - ui->gridLayoutPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); + // Create main window + QMainWindow* centralWindow = new QMainWindow(this); + centralWindow->setCentralWidget(0); + centralWindow->setWindowFlags(Qt::Widget); + ui->gridLayoutPlot->addWidget(centralWindow, 1, 0, 1, 1); - ui->gridLayoutPlot->addWidget(&plot, 3, 1, 1, 1); - ui->gridLayoutPlot->addWidget(&hist_plot, 3, 2, 1, 1); + // Create default plot docker + QDockWidget* plotDocker = new QDockWidget(centralWindow); + plotDocker->setFeatures(plotDocker->features() & ~QDockWidget::DockWidgetClosable); + plotDocker->setAllowedAreas(Qt::AllDockWidgetAreas); + plotDocker->setWidget(centralWidget); + plotDocker->setWindowTitle("TimeDomain"); - ui->gridLayoutPlot->addWidget(plot.bottomHandlesArea(), 4, 0, 1, 4); - ui->gridLayoutPlot->addItem(plotSpacer, 5, 0, 1, 4); - ui->gridLayoutPlot->addWidget(statisticsPanel, 6, 1, 1, 1); + connect(plotDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + plotDocker->setContentsMargins(10, 0, 10, 10); + + } else { + plotDocker->setContentsMargins(0, 0, 0, 0); + } + }); plot.setBonusWidthForHistogram(25); + + // setup docker for histogram plot + histDocker = new QDockWidget(centralWindow); + histDocker->setFeatures(plotDocker->features() & ~QDockWidget::DockWidgetClosable); + histDocker->setAllowedAreas(Qt::AllDockWidgetAreas); + histDocker->setWindowTitle("Histogram"); + + // build histogram widget + histWidget = new QWidget(histDocker); + QVBoxLayout* histLayout = new QVBoxLayout(histWidget); + histWidget->setLayout(histLayout); + histDocker->setWidget(histWidget); + + histDocker->hide(); + + /* Default plot settings */ plot.setSampleRate(active_sample_rate, 1, ""); plot.setActiveVertAxis(0); @@ -379,8 +442,8 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, hist_plot.setMinimumWidth(25); hist_plot.setMaximumWidth(25); - xy_plot.setMinimumHeight(50); - xy_plot.setMinimumWidth(50); +// xy_plot.setMinimumHeight(50); +// xy_plot.setMinimumWidth(50); xy_plot.setVertUnitsPerDiv(5); xy_plot.setHorizUnitsPerDiv(5); @@ -397,16 +460,61 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, xy_plot.setLineColor(0, QColor("#4a64ff")); - ui->hlayout_fft->addWidget(&fft_plot); - ui->container_fft_plot->hide(); - QGridLayout *gridL = static_cast( - ui->xy_plot_container->layout()); - QWidget *w = gridL->itemAtPosition(1, 0)->widget(); + // Add dockable fft plot + fftDocker = new QDockWidget(centralWindow); + fftDocker->setFeatures(fftDocker->features() & ~QDockWidget::DockWidgetClosable); + fftDocker->setAllowedAreas(Qt::AllDockWidgetAreas); + fftDocker->setWindowTitle("FFT"); + fftDocker->setWidget(&fft_plot); + + connect(fftDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + fftDocker->setContentsMargins(10, 0, 10, 10); + } else { + fftDocker->setContentsMargins(0, 0, 0, 10); + } + }); + + fftDocker->hide(); + + + // setup XYPlot docker + QWidget *xyWidget = new QWidget(this); + QGridLayout *gridL = new QGridLayout(xyWidget); + gridL->setVerticalSpacing(0); + gridL->setHorizontalSpacing(0); + gridL->setContentsMargins(0, 0, 0, 0); + gridL->addWidget(&xy_plot, 1, 0); - gridL->addWidget(w, 2, 0); + gridL->addWidget(gridL->itemAtPosition(1, 0)->widget(), 2, 0); + xyWidget->setLayout(gridL); + + xyDocker = new QDockWidget(centralWindow); + xyDocker->setFeatures(xyDocker->features() & ~QDockWidget::DockWidgetClosable); + xyDocker->setAllowedAreas(Qt::AllDockWidgetAreas); + xyDocker->setWindowTitle("XY"); + xyDocker->setWidget(xyWidget); + + connect(xyDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + xyDocker->setContentsMargins(10, 0, 10, 10); + } else { + xyDocker->setContentsMargins(0, 0, 0, 0); + } + }); + + xyDocker->hide(); + + // arange all dockers + centralWindow->addDockWidget(Qt::LeftDockWidgetArea, fftDocker); + centralWindow->addDockWidget(Qt::LeftDockWidgetArea, histDocker); + centralWindow->addDockWidget(Qt::LeftDockWidgetArea, plotDocker); + centralWindow->addDockWidget(Qt::RightDockWidgetArea, xyDocker); + + centralWindow->tabifyDockWidget(plotDocker, fftDocker); + centralWindow->tabifyDockWidget(plotDocker, histDocker); - ui->xy_plot_container->hide(); ui->rightMenu->setMaximumWidth(0); @@ -2880,9 +2988,10 @@ void Oscilloscope::onFFT_view_toggled(bool visible) iio->connect(ctm_blocks.at(i), 0, qt_fft_block, i); } - ui->container_fft_plot->show(); + fftDocker->show(); } else { - ui->container_fft_plot->hide(); + fftDocker->setFloating(false); + fftDocker->hide(); if (fft_is_visible && (fft_channels.size() > 0)) { for (unsigned int i = 0; i < nb_channels; i++) { @@ -2908,28 +3017,31 @@ void Oscilloscope::onHistogram_view_toggled(bool visible) cancelZoom(); if(!visible){ + histDocker->setFloating(false); + histDocker->hide(); + hist_plot.setOrientation(Qt::Horizontal); hist_plot.setMinimumWidth(25); hist_plot.setMaximumWidth(25); hist_plot.setMaximumHeight(1000); - ui->hist_layout->removeWidget(&hist_plot); - ui->gridLayoutPlot->addWidget(&hist_plot, 3, 2, 1, 1); - ui->gridLayoutPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); + histWidget->layout()->removeWidget(&hist_plot); + gridPlot->addWidget(&hist_plot, 3, 2, 1, 1); + gridPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); plot.setBonusWidthForHistogram(25); scaleHistogramPlot(); toggleMiniHistogramPlotVisible(miniHistogram); } else { + histDocker->show(); hist_plot.setOrientation(Qt::Vertical); hist_plot.setMinimumWidth(plot.width()); hist_plot.setMaximumHeight(300); hist_plot.setMaximumWidth(2000); - ui->gridLayoutPlot->removeWidget(&hist_plot); - ui->gridLayoutPlot->addWidget(plot.rightHandlesArea(), 1, 2, 3, 1); - ui->hist_layout->addWidget(&hist_plot); + gridPlot->removeWidget(&hist_plot); + gridPlot->addWidget(plot.rightHandlesArea(), 1, 2, 4, 1); + histWidget->layout()->addWidget(&hist_plot); plot.setBonusWidthForHistogram(0); scaleHistogramPlot(); - hist_plot.setVisible(true); } hist_is_visible = visible; } @@ -2939,6 +3051,7 @@ void Oscilloscope::onXY_view_toggled(bool visible) if (visible) { gsettings_ui->xySettings->show(); } else { + xyDocker->setFloating(false); gsettings_ui->xySettings->hide(); } @@ -3002,9 +3115,9 @@ void Oscilloscope::onXY_view_toggled(bool visible) if(!xy_is_visible) iio->connect(ftc, 0, this->qt_xy_block, 0); - ui->xy_plot_container->show(); + xyDocker->show(); } else { - ui->xy_plot_container->hide(); + xyDocker->hide(); // Disconnect the XY section from the running flowgraph iio->disconnect(xy_channels.at(index_x).first, xy_channels.at(index_x).second, diff --git a/src/oscilloscope.hpp b/src/oscilloscope.hpp index fa62a19fe4..4811f40965 100644 --- a/src/oscilloscope.hpp +++ b/src/oscilloscope.hpp @@ -312,6 +312,12 @@ namespace adiscope { ExportSettings *exportSettings; CustomPlotPositionButton *cursorsPositionButton; + QGridLayout* gridPlot; + QDockWidget* fftDocker; + QDockWidget* xyDocker; + QDockWidget* histDocker; + QWidget* histWidget; + MouseWheelWidgetGuard *wheelEventGuard; QPair *math_pair; diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index 497a7fc80a..f2a57d9318 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -78,8 +78,8 @@ CapturePlot::CapturePlot(QWidget *parent, bool isdBgraph, unsigned int xNumDivs d_currentHandleInitPx(30), d_maxBufferError(nullptr) { - setMinimumHeight(200); - setMinimumWidth(450); +// setMinimumHeight(200); +// setMinimumWidth(450); /* Initial colors scheme */ d_trigAactiveLinePen = QPen(QColor(255, 255, 255), 2, Qt::SolidLine); diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 953a5c48fc..3a7e4ca3c9 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -22,6 +22,7 @@ #include "pattern_generator.h" #include +#include #include "ui_pattern_generator.h" #include "digitalchannel_manager.hpp" #include "gui/dynamicWidget.hpp" @@ -228,21 +229,51 @@ void PatternGenerator::setupUi() // set default menu width to 0 m_ui->rightMenu->setMaximumWidth(0); + // Add docking plot + + QWidget* widget = new QWidget(this); + QGridLayout* gridLayout = new QGridLayout(widget); + gridLayout->setVerticalSpacing(0); + gridLayout->setHorizontalSpacing(0); + gridLayout->setContentsMargins(0, 0, 0, 0); + widget->setLayout(gridLayout); + QSpacerItem *plotSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); - m_ui->gridLayoutPlot->addWidget(m_plot.topArea(), 0, 0, 1, 4); - m_ui->gridLayoutPlot->addWidget(m_plot.topHandlesArea(), 1, 0, 1, 4); + gridLayout->addWidget(m_plot.topArea(), 0, 0, 1, 4); + gridLayout->addWidget(m_plot.topHandlesArea(), 1, 0, 1, 4); + + gridLayout->addWidget(m_plot.leftHandlesArea(), 0, 0, 4, 1); + gridLayout->addWidget(m_plot.rightHandlesArea(), 0, 3, 4, 1); + + gridLayout->addWidget(&m_plot, 2, 1, 1, 1); + gridLayout->addWidget(m_plotScrollBar, 2, 5, 1, 1); + + gridLayout->addWidget(m_plot.bottomHandlesArea(), 3, 0, 1, 4); + gridLayout->addItem(plotSpacer, 4, 0, 1, 4); - m_ui->gridLayoutPlot->addWidget(m_plot.leftHandlesArea(), 0, 0, 4, 1); - m_ui->gridLayoutPlot->addWidget(m_plot.rightHandlesArea(), 0, 3, 4, 1); + QMainWindow* m_centralMainWindow = new QMainWindow(this); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + m_ui->gridLayoutPlot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(widget); + + connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + docker->setContentsMargins(10, 0, 10, 0); + } else { + docker->setContentsMargins(0, 0, 0, 0); + } + }); - m_ui->gridLayoutPlot->addWidget(&m_plot, 2, 1, 1, 1); - m_ui->gridLayoutPlot->addWidget(m_plotScrollBar, 2, 5, 1, 1); - m_ui->gridLayoutPlot->addWidget(m_plot.bottomHandlesArea(), 3, 0, 1, 4); - m_ui->gridLayoutPlot->addItem(plotSpacer, 4, 0, 1, 4); + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); m_plot.enableAxis(QwtPlot::yLeft, false); diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index f746539e12..e2209b7a7b 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -602,11 +603,36 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, m_plot->enableTimeTrigger(false); - ui->plot->addWidget(m_plot->topArea(), 0, 0, 1, 4); - ui->plot->addWidget(m_plot->topHandlesArea(), 1, 0, 1, 4); + // Add docking plot + + QWidget* widget = new QWidget(); + QGridLayout *gridplot = new QGridLayout(); + + gridplot->addWidget(m_plot->topArea(), 0, 0); + gridplot->addWidget(m_plot->topHandlesArea(), 1, 0); + gridplot->addWidget(m_plot, 2, 0); + + gridplot->setVerticalSpacing(0); + gridplot->setHorizontalSpacing(0); + gridplot->setContentsMargins(0, 0, 0, 0); + widget->setLayout(gridplot); + ui->plot->removeWidget(ui->instrumentNotes); - ui->plot->addWidget(ui->instrumentNotes,3,0,1,4); - ui->plot->addWidget(m_plot, 2, 1, 1, 1); + + QMainWindow* m_centralMainWindow = new QMainWindow(this); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + ui->plot->addWidget(m_centralMainWindow, 0, 0); + + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(widget); + docker->setContentsMargins(0, 0, 0, 10); + + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + + ui->plot->addWidget(ui->instrumentNotes, 3, 0); } SignalGenerator::~SignalGenerator() diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 4e0224afdb..c52bd67e84 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -34,6 +34,7 @@ #include #include #include +#include /* Local includes */ #include "logging_categories.h" @@ -220,7 +221,44 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, fft_plot->setYaxisMouseGesturesEnabled(i, false); } - ui->gridLayout_plot->addWidget(fft_plot->getPlotwithElements(), 1, 0, 1, 1); + // Add dockable plot + + QWidget* centralWidget = new QWidget(this); + QVBoxLayout* vLayout = new QVBoxLayout(centralWidget); + vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setSpacing(6); + centralWidget->setLayout(vLayout); + + ui->widgetPlotContainer->layout()->removeWidget(ui->topPlotWidget); + vLayout->addWidget(ui->topPlotWidget); + + vLayout->addWidget(fft_plot->getPlotwithElements()); + + ui->widgetPlotContainer->layout()->removeWidget(ui->markerTable); + vLayout->addWidget(ui->markerTable); + + + QMainWindow* m_centralMainWindow = new QMainWindow(this); + m_centralMainWindow->setCentralWidget(0); + m_centralMainWindow->setWindowFlags(Qt::Widget); + ui->gridLayout_plot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setWidget(centralWidget); + + connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + docker->setContentsMargins(10, 0, 10, 10); + } else { + docker->setContentsMargins(0, 0, 0, 0); + } + }); + + + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + fft_plot->enableXaxisLabels(); fft_plot->enableYaxisLabels(); @@ -2320,20 +2358,20 @@ void SpectrumAnalyzer::on_btnMarkerTable_toggled(bool checked) ui->markerTable->setVisible(checked); // Set the Plot 3 times taller than the Marker Table (when visible) - QGridLayout *layout = static_cast( - ui->widgetPlotContainer->layout()); - int row1 = getGridLayoutPosFromIndex(layout, - layout->indexOf(ui->markerTable)).first; - int row2 = getGridLayoutPosFromIndex(layout, - layout->indexOf(ui->gridLayout_plot)).first; - - if (checked) { - layout->setRowStretch(row1, 1); - layout->setRowStretch(row2, 3); - } else { - layout->setRowStretch(row1, 0); - layout->setRowStretch(row2, 0); - } +// QGridLayout *layout = static_cast( +// ui->widgetPlotContainer->layout()); +// int row1 = getGridLayoutPosFromIndex(layout, +// layout->indexOf(ui->markerTable)).first; +// int row2 = getGridLayoutPosFromIndex(layout, +// layout->indexOf(ui->gridLayout_plot)).first; + +// if (checked) { +// layout->setRowStretch(row1, 1); +// layout->setRowStretch(row2, 3); +// } else { +// layout->setRowStretch(row1, 0); +// layout->setRowStretch(row2, 0); +// } } QPair SpectrumAnalyzer::getGridLayoutPosFromIndex(QGridLayout *layout, diff --git a/ui/logic_analyzer.ui b/ui/logic_analyzer.ui index 060b07fea0..0211f3f4f0 100644 --- a/ui/logic_analyzer.ui +++ b/ui/logic_analyzer.ui @@ -266,6 +266,9 @@ true
+ + 0 + @@ -390,13 +393,13 @@ - 25 + 0 0 - 25 + 0 0 diff --git a/ui/network_analyzer.ui b/ui/network_analyzer.ui index e2b844f076..40fe5402d1 100644 --- a/ui/network_analyzer.ui +++ b/ui/network_analyzer.ui @@ -262,6 +262,12 @@ 0 + + 0 + + + 0 + diff --git a/ui/oscilloscope.ui b/ui/oscilloscope.ui index f4d74b8217..492d5e5c7b 100644 --- a/ui/oscilloscope.ui +++ b/ui/oscilloscope.ui @@ -259,6 +259,18 @@ Signal View
true + + 0 + + + 0 + + + 0 + + + 0 + @@ -319,7 +331,10 @@ Signal View 0 - + + + + 0 @@ -334,369 +349,69 @@ Signal View 0 - - - 25 - - - 0 - - - 25 - - - 0 - - - 0 - - - - - - - 0 - - - 0 - - - 0 - - - 3 - - - - - 15 - - - 10 - - - - - - - Qt::Horizontal - - - - 0 - 15 - - - - - - - - - - - - - 20 - - - 0 - - - 0 - - - 0 - - - 2 - - - 0 - - - + + + + 25 + + + 1 + + + 25 + + + 1 + 0 - - - - - - - - 0 - - - 20 - - - 20 - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - + - - - - 0 - 0 - - - - - 20 - 0 - - - + + 0 - 0 + 1 - 0 - - - 0 + 1 0 + + + + 15 + + + 10 + + + + + + + Qt::Horizontal + + + + 0 + 15 + + + + - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - - - - - - - - - - - 0 - 0 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 58 - - - - - 16777215 - 58 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 399 - 0 - - - - - - - - 0 - - - 0 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 0 - - - - - - - - - - - - 0 - 0 - - - - - 0 - 45 - - - - - 16777215 - 45 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - 3 - - - 0 - - - 3 - - - - - - - Qt::Horizontal - - - - 0 - 10 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 2 - 10 - - - - diff --git a/ui/signal_generator.ui b/ui/signal_generator.ui index 8f3371ff1c..04c0d173f1 100644 --- a/ui/signal_generator.ui +++ b/ui/signal_generator.ui @@ -134,6 +134,9 @@ 0 + + 0 + diff --git a/ui/spectrum_analyzer.ui b/ui/spectrum_analyzer.ui index de2dc1fa45..c26388dd95 100644 --- a/ui/spectrum_analyzer.ui +++ b/ui/spectrum_analyzer.ui @@ -6,7 +6,7 @@ 0 0 - 989 + 1003 661 @@ -296,13 +296,13 @@ min-width: 6em; - 50 + 20 - 50 + 0 - 50 + 20 20 @@ -314,126 +314,24 @@ min-width: 6em; 10 - - - - - - 0 + + + 6 - - - - - 200 - 200 - - - - - - - - 0 - - - - - dBFS - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 0 - - - - - - - - - 200 - 0 - - - - - 200 - 16777215 - - - - Sample: - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 0 - - - - - - - - Average Sample: - - - - - - - Qt::Horizontal - - - - 0 - 10 - - - - - - - - Marker_n 0.000 Hz 0dB - - - - - - - - - - + + + + 0 + 0 + + + - 200 - 200 + 16777215 + 120 @@ -448,6 +346,103 @@ min-width: 6em; + + + + + 0 + + + 0 + + + + + dBFS + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 0 + + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + Sample: + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 0 + + + + + + + + Average Sample: + + + + + + + Qt::Horizontal + + + + 0 + 10 + + + + + + + + Marker_n 0.000 Hz 0dB + + + + + + From f78975e6839a541413e147e1b3d1dda6505e190c Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Thu, 1 Jul 2021 12:08:05 +0300 Subject: [PATCH 046/125] gui: added menu bar to plot dockers Signed-off-by: Ioana Chelaru --- resources/stylesheets/default.qss | 6 +++- resources/stylesheets/light.qss | 7 +++- resources/stylesheets/templates/default.qss.c | 8 +++++ resources/stylesheets/templates/light.qss.c | 9 +++++ src/logicanalyzer/logic_analyzer.cpp | 12 +++---- src/network_analyzer.cpp | 15 +++------ src/oscilloscope.cpp | 33 +++++-------------- src/patterngenerator/pattern_generator.cpp | 12 +++---- src/qtgui_util.cc | 21 ++++++++++++ src/signal_generator.cpp | 6 +++- src/spectrum_analyzer.cpp | 12 +++---- src/utils.h | 9 +++++ ui/cursor_readouts.ui | 3 -- ui/pattern_generator.ui | 12 +++---- 14 files changed, 93 insertions(+), 72 deletions(-) diff --git a/resources/stylesheets/default.qss b/resources/stylesheets/default.qss index 77889503d8..6bb801699b 100644 --- a/resources/stylesheets/default.qss +++ b/resources/stylesheets/default.qss @@ -13,7 +13,11 @@ QWidget { } QDockWidget { - titlebar-normal-icon: ""; + titlebar-normal-icon: url(); +} + +QDockWidget::title { + background-color: transparent; } QToolTip { diff --git a/resources/stylesheets/light.qss b/resources/stylesheets/light.qss index c811742496..0ca9e6a097 100644 --- a/resources/stylesheets/light.qss +++ b/resources/stylesheets/light.qss @@ -13,7 +13,12 @@ QWidget { } QDockWidget { - titlebar-normal-icon: ""; + titlebar-normal-icon: url(); + background-color: #F7F7F7; +} + +QDockWidget::title { + background-color: transparent; } QToolTip { diff --git a/resources/stylesheets/templates/default.qss.c b/resources/stylesheets/templates/default.qss.c index 1863df0a1b..8eec694f11 100644 --- a/resources/stylesheets/templates/default.qss.c +++ b/resources/stylesheets/templates/default.qss.c @@ -12,6 +12,14 @@ QWidget { font-size: 13px; } +QDockWidget { + titlebar-normal-icon: url(); +} + +QDockWidget::title { + background-color: transparent; +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/resources/stylesheets/templates/light.qss.c b/resources/stylesheets/templates/light.qss.c index 3dd72315a3..3265c9bcb4 100644 --- a/resources/stylesheets/templates/light.qss.c +++ b/resources/stylesheets/templates/light.qss.c @@ -12,6 +12,15 @@ QWidget { font-size: 13px; } +QDockWidget { + titlebar-normal-icon: url(); + background-color: #F7F7F7; +} + +QDockWidget::title { + background-color: transparent; +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 7c7c06f9cd..fdfa480e04 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1340,16 +1340,12 @@ void LogicAnalyzer::setupUi() QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::AllDockWidgetAreas); docker->setWidget(centralWidget); - connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - docker->setContentsMargins(10, 0, 10, 10); - } else { - docker->setContentsMargins(0, 0, 0, 0); - } - }); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 46a1d4fdd4..0c43c0ed86 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -454,19 +454,12 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::AllDockWidgetAreas); docker->setWidget(widget); - connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - docker->setContentsMargins(10, 0, 10, 10); -// ui->stackedWidget->hide(); - } else { - docker->setContentsMargins(0, 0, 0, 0); -// ui->stackedWidget->show(); - } - }); - +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index bcb21cb38c..ce0543c5bf 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -372,15 +372,6 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, plotDocker->setWidget(centralWidget); plotDocker->setWindowTitle("TimeDomain"); - connect(plotDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - plotDocker->setContentsMargins(10, 0, 10, 10); - - } else { - plotDocker->setContentsMargins(0, 0, 0, 0); - } - }); - plot.setBonusWidthForHistogram(25); @@ -390,6 +381,7 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, histDocker->setAllowedAreas(Qt::AllDockWidgetAreas); histDocker->setWindowTitle("Histogram"); + // build histogram widget histWidget = new QWidget(histDocker); QVBoxLayout* histLayout = new QVBoxLayout(histWidget); @@ -468,14 +460,6 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, fftDocker->setWindowTitle("FFT"); fftDocker->setWidget(&fft_plot); - connect(fftDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - fftDocker->setContentsMargins(10, 0, 10, 10); - } else { - fftDocker->setContentsMargins(0, 0, 0, 10); - } - }); - fftDocker->hide(); @@ -496,14 +480,6 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, xyDocker->setWindowTitle("XY"); xyDocker->setWidget(xyWidget); - connect(xyDocker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - xyDocker->setContentsMargins(10, 0, 10, 10); - } else { - xyDocker->setContentsMargins(0, 0, 0, 0); - } - }); - xyDocker->hide(); // arange all dockers @@ -515,6 +491,12 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, centralWindow->tabifyDockWidget(plotDocker, fftDocker); centralWindow->tabifyDockWidget(plotDocker, histDocker); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(plotDocker); + DockerUtils::configureTopBar(histDocker); + DockerUtils::configureTopBar(fftDocker); + DockerUtils::configureTopBar(xyDocker); +#endif ui->rightMenu->setMaximumWidth(0); @@ -3042,6 +3024,7 @@ void Oscilloscope::onHistogram_view_toggled(bool visible) histWidget->layout()->addWidget(&hist_plot); plot.setBonusWidthForHistogram(0); scaleHistogramPlot(); + hist_plot.setVisible(true); } hist_is_visible = visible; } diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 3a7e4ca3c9..aa12d55922 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -261,16 +261,12 @@ void PatternGenerator::setupUi() QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::AllDockWidgetAreas); docker->setWidget(widget); - connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - docker->setContentsMargins(10, 0, 10, 0); - } else { - docker->setContentsMargins(0, 0, 0, 0); - } - }); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); diff --git a/src/qtgui_util.cc b/src/qtgui_util.cc index 78c7507838..4951b99e2b 100644 --- a/src/qtgui_util.cc +++ b/src/qtgui_util.cc @@ -170,3 +170,24 @@ bool Util::compareNatural(const std::string& a, const std::string& b) { return (compareNatural(a_new, b_new)); } +void DockerUtils::configureTopBar(QDockWidget *docker) +{ + connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + if(topLevel) { + docker->setWindowFlags(Qt::CustomizeWindowHint | + Qt::Window | + Qt::WindowMinimizeButtonHint | + Qt::WindowMaximizeButtonHint); + docker->show(); + docker->setStyleSheet("QDockWidget {" + "titlebar-normal-icon: url(:/icons/close_hovered.svg);" + "}"); + docker->setContentsMargins(10, 0, 10, 10); + } else { + docker->setStyleSheet("QDockWidget {" + "titlebar-normal-icon: url();" + "}"); + docker->setContentsMargins(0, 0, 0, 0); + } + }); +} diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index e2209b7a7b..3f98deeef3 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -626,10 +626,14 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::AllDockWidgetAreas); docker->setWidget(widget); docker->setContentsMargins(0, 0, 0, 10); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif + m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); ui->plot->addWidget(ui->instrumentNotes, 3, 0); diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index c52bd67e84..77d8e33268 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -245,16 +245,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::AllDockWidgetAreas); docker->setWidget(centralWidget); - connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ - if(topLevel) { - docker->setContentsMargins(10, 0, 10, 10); - } else { - docker->setContentsMargins(0, 0, 0, 0); - } - }); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); diff --git a/src/utils.h b/src/utils.h index 405189f961..f0a2d83673 100644 --- a/src/utils.h +++ b/src/utils.h @@ -25,6 +25,7 @@ #include #include #include +#include class QwtDblClickPlotPicker: public QwtPlotPicker { @@ -64,4 +65,12 @@ class Util static bool compareNatural(const std::string &a, const std::string &b); }; +#define PLOT_MENU_BAR_ENABLED + +class DockerUtils : public QObject +{ +public: + static void configureTopBar(QDockWidget* docker); +}; + #endif /* M2K_UTILS_H */ diff --git a/ui/cursor_readouts.ui b/ui/cursor_readouts.ui index ef1a1381b3..4cfc262383 100644 --- a/ui/cursor_readouts.ui +++ b/ui/cursor_readouts.ui @@ -419,9 +419,6 @@ 0 - - color: rgba(255, 255, 255, 153); - ΔV = diff --git a/ui/pattern_generator.ui b/ui/pattern_generator.ui index 70d63d4f08..6b93715f59 100644 --- a/ui/pattern_generator.ui +++ b/ui/pattern_generator.ui @@ -288,7 +288,7 @@ 20 - 20 + 10 @@ -337,7 +337,7 @@ 20 - 20 + 10 @@ -1812,14 +1812,14 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); }1 - adiscope::CustomSwitch + adiscope::LinkedButton QPushButton -
gui/customSwitch.hpp
+
gui/linked_button.hpp
- adiscope::LinkedButton + adiscope::CustomSwitch QPushButton -
gui/linked_button.hpp
+
gui/customSwitch.hpp
From ed43846518c9a93425dadd5e7166100f0258b38d Mon Sep 17 00:00:00 2001 From: Ioana Chelaru Date: Fri, 2 Jul 2021 14:18:10 +0300 Subject: [PATCH 047/125] ToolView: added top bar to dockable widgets Signed-off-by: Ioana Chelaru --- src/gui/tool_view.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp index fa315e9b5c..a2225f79a4 100644 --- a/src/gui/tool_view.cpp +++ b/src/gui/tool_view.cpp @@ -5,6 +5,7 @@ #include "channel_widget.hpp" #include "menu_header.hpp" #include "tool_view.hpp" +#include "utils.h" using namespace scopy::gui; @@ -168,6 +169,10 @@ QDockWidget* ToolView::createDetachableMenu(QWidget* menu, int& id) subWindow->addDockWidget(Qt::RightDockWidgetArea, docker); docker->setWidget(menu); +#ifdef SETTINGS_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif + id = m_ui->stackedWidget->addWidget(subWindow); return docker; @@ -328,9 +333,13 @@ void ToolView::addCentralWidget(QWidget* widget, bool dockable, const QString& d QDockWidget* docker = new QDockWidget(m_centralMainWindow); docker->setWindowTitle(dockerName); docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::NoDockWidgetArea); + docker->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas); docker->setWidget(widget); +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif + if (m_dockables < 3) { if (m_dockables % 3 == 0) { m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); From 1cfe1e22696490c73252b96b7b638cad25a0205b Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 3 Sep 2021 16:47:50 +0300 Subject: [PATCH 048/125] gui: reworked adding central widgets into ToolView Signed-off-by: ioanachelaru --- src/gui/tool_view.cpp | 80 +++++++++++++++++++++++-------------------- src/gui/tool_view.hpp | 11 +++--- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp index a2225f79a4..b2adfa635f 100644 --- a/src/gui/tool_view.cpp +++ b/src/gui/tool_view.cpp @@ -1,4 +1,4 @@ -#include "ui_tool_view.h" +#include "ui_tool_view.h" #include @@ -12,14 +12,13 @@ using namespace scopy::gui; ToolView::ToolView(QWidget* parent) : QWidget(parent) , m_ui(new Ui::ToolView) - , m_dockables(0) { m_ui->setupUi(this); m_centralMainWindow = new QMainWindow(m_ui->widgetCentral); m_centralMainWindow->setCentralWidget(0); m_centralMainWindow->setWindowFlags(Qt::Widget); - m_ui->widgetPlotContainer->layout()->addWidget(m_centralMainWindow); + m_ui->widgetCentral->layout()->addWidget(m_centralMainWindow); m_ui->widgetRunSingleBtns->enableRunButton(false); m_ui->widgetRunSingleBtns->enableSingleButton(false); @@ -178,6 +177,20 @@ QDockWidget* ToolView::createDetachableMenu(QWidget* menu, int& id) return docker; } +QDockWidget *ToolView::createDockableWidget(QWidget *widget, const QString& dockerName) +{ + QDockWidget* docker = new QDockWidget(m_centralMainWindow); + docker->setWindowTitle(dockerName); + docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); + docker->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas); + docker->setWidget(widget); + +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(docker); +#endif + return docker; +} + void ToolView::configureAddMathBtn(QWidget* menu, bool dockable) { ChannelManager* cm = static_cast(QObject::sender()); @@ -326,47 +339,38 @@ void ToolView::buildNewInstrumentMenu(GenericMenu* menu, bool dockable, const QS } } -void ToolView::addCentralWidget(QWidget* widget, bool dockable, const QString& dockerName, int row, int column, - int rowspan, int columnspan) +void ToolView::addFixedCentralWidget(QWidget *widget, int row, int column, int rowspan, int columnspan) { - if (dockable) { - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setWindowTitle(dockerName); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas); - docker->setWidget(widget); + if (row == -1 || column == -1) { + m_ui->gridWidgetCentral->addWidget(widget); + } else { + if (rowspan == -1 || columnspan == -1) { + m_ui->gridWidgetCentral->addWidget(widget, row, column); + } else { + m_ui->gridWidgetCentral->addWidget(widget, row, column, rowspan, columnspan); + } + } +} -#ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); -#endif +int ToolView::addDockableCentralWidget(QWidget *widget, Qt::DockWidgetArea area, const QString &dockerName) +{ + QDockWidget* docker = this->createDockableWidget(widget, dockerName); - if (m_dockables < 3) { - if (m_dockables % 3 == 0) { - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - } else if (m_dockables % 3 == 1) { - m_centralMainWindow->addDockWidget(Qt::RightDockWidgetArea, docker); - } else { - m_centralMainWindow->addDockWidget(Qt::BottomDockWidgetArea, docker); - } + m_centralMainWindow->addDockWidget(area, docker); + m_docksList.append(docker); - m_firstDocks.push_back(docker); - } else { - m_centralMainWindow->tabifyDockWidget(m_firstDocks.at(m_dockables % 3), docker); - } + return m_docksList.size() - 1; +} - m_dockables++; +void ToolView::addDockableTabedWidget(QWidget *widget, const QString &dockerName, int plotId) +{ + QDockWidget* docker = this->createDockableWidget(widget, dockerName); + m_centralMainWindow->tabifyDockWidget(m_docksList.at(plotId), docker); +} - } else { - if (row == -1 || column == -1) { - m_ui->gridWidgetCentral->addWidget(widget); - } else { - if (rowspan == -1 || columnspan == -1) { - m_ui->gridWidgetCentral->addWidget(widget, row, column); - } else { - m_ui->gridWidgetCentral->addWidget(widget, row, column, rowspan, columnspan); - } - } - } +void ToolView::addFixedTabedWidget(QWidget *widget, int plotId) +{ + // TODO } void ToolView::setGeneralSettingsMenu(QWidget* menu, bool dockable) diff --git a/src/gui/tool_view.hpp b/src/gui/tool_view.hpp index 8261d2560b..a72040da86 100644 --- a/src/gui/tool_view.hpp +++ b/src/gui/tool_view.hpp @@ -70,8 +70,11 @@ class ToolView : public QWidget const QString& shortName); void buildNewInstrumentMenu(GenericMenu* menu, bool dockable, const QString& name, bool checkBoxVisible = false, bool checkBoxChecked = false); - void addCentralWidget(QWidget* widget, bool dockable, const QString& dockerName, int row = -1, int column = -1, - int rowspan = -1, int columnspan = -1); + + void addFixedCentralWidget(QWidget* widget, int row = -1, int column = -1,int rowspan = -1, int columnspan = -1); + int addDockableCentralWidget(QWidget* widget, Qt::DockWidgetArea area, const QString& dockerName); + void addDockableTabedWidget(QWidget* widget, const QString &dockerName, int plotId); + void addFixedTabedWidget(QWidget* widget, int plotId); private: void configureLastOpenedMenu(); @@ -80,6 +83,7 @@ class ToolView : public QWidget void settingsPanelUpdate(int id); void rightMenuFinished(bool opened); QDockWidget* createDetachableMenu(QWidget* menu, int& id); + QDockWidget* createDockableWidget(QWidget* widget, const QString& dockerName); public Q_SLOTS: void triggerRightMenuToggle(bool checked); @@ -102,8 +106,7 @@ public Q_SLOTS: int m_generalSettingsMenuId; QMainWindow* m_centralMainWindow; - unsigned int m_dockables; - std::vector m_firstDocks; + QList m_docksList; }; } // namespace gui } // namespace scopy From 97286c9102710bc88c5c61e785609292917f567a Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 7 Sep 2021 12:50:54 +0300 Subject: [PATCH 049/125] gui: implemented method for adding fixed tabbed widgets in ToolView Signed-off-by: ioanachelaru --- src/gui/tool_view.cpp | 26 ++++++++++++++++++++++---- src/gui/tool_view.hpp | 5 +++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp index b2adfa635f..cbe13dfd49 100644 --- a/src/gui/tool_view.cpp +++ b/src/gui/tool_view.cpp @@ -1,11 +1,11 @@ #include "ui_tool_view.h" #include - #include "channel_widget.hpp" #include "menu_header.hpp" #include "tool_view.hpp" #include "utils.h" +#include using namespace scopy::gui; @@ -362,15 +362,33 @@ int ToolView::addDockableCentralWidget(QWidget *widget, Qt::DockWidgetArea area, return m_docksList.size() - 1; } -void ToolView::addDockableTabedWidget(QWidget *widget, const QString &dockerName, int plotId) +void ToolView::addDockableTabbedWidget(QWidget *widget, const QString &dockerName, int plotId) { QDockWidget* docker = this->createDockableWidget(widget, dockerName); m_centralMainWindow->tabifyDockWidget(m_docksList.at(plotId), docker); } -void ToolView::addFixedTabedWidget(QWidget *widget, int plotId) +int ToolView::addFixedTabbedWidget(QWidget *widget, const QString& title, int plotId, int row, int column, int rowspan, int columnspan) { - // TODO + if(m_centralFixedWidgets.size() > plotId && plotId != -1) { + // adding widget to an existing TabWidget + + QTabWidget* tabWidget = static_cast(m_centralFixedWidgets.at(plotId)); + tabWidget->addTab(widget, title); + + return plotId; + + } else { + // creating new TabWidget + + QTabWidget* tabWidget = new QTabWidget(m_ui->widgetCentral); + tabWidget->addTab(widget, title); + m_centralFixedWidgets.append(tabWidget); + + this->addFixedCentralWidget(tabWidget, row, column, rowspan, columnspan); + + return m_centralFixedWidgets.size() - 1; + } } void ToolView::setGeneralSettingsMenu(QWidget* menu, bool dockable) diff --git a/src/gui/tool_view.hpp b/src/gui/tool_view.hpp index a72040da86..3692b3145a 100644 --- a/src/gui/tool_view.hpp +++ b/src/gui/tool_view.hpp @@ -73,8 +73,8 @@ class ToolView : public QWidget void addFixedCentralWidget(QWidget* widget, int row = -1, int column = -1,int rowspan = -1, int columnspan = -1); int addDockableCentralWidget(QWidget* widget, Qt::DockWidgetArea area, const QString& dockerName); - void addDockableTabedWidget(QWidget* widget, const QString &dockerName, int plotId); - void addFixedTabedWidget(QWidget* widget, int plotId); + void addDockableTabbedWidget(QWidget* widget, const QString &dockerName, int plotId); + int addFixedTabbedWidget(QWidget* widget, const QString& title, int plotId = -1, int row = -1, int column = -1,int rowspan = -1, int columnspan = -1); private: void configureLastOpenedMenu(); @@ -107,6 +107,7 @@ public Q_SLOTS: QMainWindow* m_centralMainWindow; QList m_docksList; + QList m_centralFixedWidgets; }; } // namespace gui } // namespace scopy From 9603216a9ab85b88683ee5ecd3338e3f5cc9116f Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 5 Oct 2021 16:21:16 +0300 Subject: [PATCH 050/125] gui: added style for SubsectionSeparator Signed-off-by: ioanachelaru --- resources/stylesheets/default.qss | 21 +++++++++++++++++++++ resources/stylesheets/light.qss | 21 +++++++++++++++++++++ src/gui/channel_manager.cpp | 2 +- ui/subsection_separator.ui | 13 ++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/resources/stylesheets/default.qss b/resources/stylesheets/default.qss index 6bb801699b..66bea6b5a3 100644 --- a/resources/stylesheets/default.qss +++ b/resources/stylesheets/default.qss @@ -355,6 +355,27 @@ QPushButton[blue_button=true]:hover{ background-color: #4a34ff; } QPushButton[blue_button=true]:disabled { background-color: grey; } +/*********************************** SubsectionSeparator ***************************************/ + +QLabel#lblSubsectionSeparator{ + font-size: 12px; + color: rgba(255, 255, 255, 70); +} + +QFrame#lineSubsectionSeparator{ border: 1px solid rgba(255, 255, 255, 70); } + +QPushButton#btnSubsectionSeparator{ + max-height: 6px; + max-width: 10px; + border-image: url(:/icons/sba_cmb_box_arrow_right.svg); +} + +QPushButton#btnSubsectionSeparator:checked{ + max-height: 10px; + max-width: 6px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); +} + /* Info button for each tool */ QPushButton[info_button=true]{ diff --git a/resources/stylesheets/light.qss b/resources/stylesheets/light.qss index 0ca9e6a097..a8b4b297d9 100644 --- a/resources/stylesheets/light.qss +++ b/resources/stylesheets/light.qss @@ -355,6 +355,27 @@ QPushButton[blue_button=true]:hover{ background-color: #4a34ff; } QPushButton[blue_button=true]:disabled { background-color: grey; } +/*********************************** SubsectionSeparator ***************************************/ + +QLabel#lblSubsectionSeparator{ + font-size: 12px; + color: rgba(0, 0, 0, 70); +} + +QFrame#lineSubsectionSeparator{ border: 1px solid rgba(0, 0, 0, 70); } + +QPushButton#btnSubsectionSeparator{ + max-height: 6px; + max-width: 10px; + border-image: url(:/icons/sba_cmb_box_arrow_right.svg); +} + +QPushButton#btnSubsectionSeparator:checked{ + max-height: 10px; + max-width: 6px; + border-image: url(:/icons/sba_cmb_box_arrow.svg); +} + /* Info button for each tool */ QPushButton[info_button=true]{ diff --git a/src/gui/channel_manager.cpp b/src/gui/channel_manager.cpp index d6aff53fff..b20e2f5e02 100644 --- a/src/gui/channel_manager.cpp +++ b/src/gui/channel_manager.cpp @@ -180,7 +180,7 @@ void ChannelManager::insertAddBtn(QWidget* menu, bool dockable) // TO DO: center + btn when position is vertical m_addChannelBtn->setCheckable(true); m_addChannelBtn->setFlat(true); - m_addChannelBtn->setIcon(QIcon(":/icons/common/add.svg")); + m_addChannelBtn->setIcon(QIcon(":/menu/add.svg")); m_addChannelBtn->setIconSize(QSize(16, 16)); m_addChannelBtn->setMaximumSize(25, 25); diff --git a/ui/subsection_separator.ui b/ui/subsection_separator.ui index 5b478f1cc4..a30c73598c 100644 --- a/ui/subsection_separator.ui +++ b/ui/subsection_separator.ui @@ -57,6 +57,11 @@ + + + :/icons/sba_cmb_box_arrow_right.svg + :/icons/sba_cmb_box_arrow.svg:/icons/sba_cmb_box_arrow_right.svg + true @@ -119,6 +124,12 @@
- + + + + + + + From c67dc6e5c80268488fba00b6087d4b48ba881a1f Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Thu, 4 Nov 2021 14:17:21 +0200 Subject: [PATCH 051/125] ChannelManager: create addChannelBtn only when needed Signed-off-by: ioanachelaru --- src/gui/channel_manager.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/channel_manager.cpp b/src/gui/channel_manager.cpp index b20e2f5e02..ad703ef7b2 100644 --- a/src/gui/channel_manager.cpp +++ b/src/gui/channel_manager.cpp @@ -13,7 +13,6 @@ ChannelManager::ChannelManager(ChannelsPositionEnum position, QWidget* parent) , m_channelsWidget(new QWidget(m_scrollArea)) , m_switchBtn(new QPushButton(m_scrollArea)) , m_hasAddBtn(false) - , m_addChannelBtn(new CustomPushButton(m_scrollArea)) , m_position(position) { if (m_position == ChannelsPositionEnum::VERTICAL) { @@ -41,7 +40,11 @@ ChannelManager::ChannelManager(ChannelsPositionEnum position, QWidget* parent) ChannelManager::~ChannelManager() { delete m_channelsWidget; - delete m_addChannelBtn; + + if (m_addChannelBtn) { + delete m_addChannelBtn; + } + delete m_switchBtn; delete m_parent; } @@ -175,7 +178,9 @@ QList ChannelManager::getEnabledChannels() void ChannelManager::insertAddBtn(QWidget* menu, bool dockable) { + m_hasAddBtn = true; + m_addChannelBtn = new CustomPushButton(m_scrollArea); // TO DO: center + btn when position is vertical m_addChannelBtn->setCheckable(true); From edbf15181f0157c41bd6c9681fff9d4e51f1041b Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 3 Sep 2021 10:32:29 +0300 Subject: [PATCH 052/125] spectrum analyzer: added measurements Signed-off-by: ioanachelaru --- src/FftDisplayPlot.cc | 40 ++- src/FftDisplayPlot.h | 11 + src/gui/measure.cpp | 555 +++++++++++++++++++++++++++++++---- src/gui/measure.h | 34 ++- src/gui/measure_settings.cpp | 325 +++++++++++++++----- src/gui/measure_settings.h | 11 +- src/measurement_gui.cpp | 80 +++++ src/measurement_gui.h | 18 ++ src/oscilloscope.cpp | 3 +- src/oscilloscope_plot.cpp | 7 +- src/oscilloscope_plot.hpp | 1 + src/spectrum_analyzer.cpp | 525 ++++++++++++++++++++++++++++++++- src/spectrum_analyzer.hpp | 48 +++ ui/measure_settings.ui | 124 ++++++-- ui/spectrum_analyzer.ui | 46 ++- 15 files changed, 1660 insertions(+), 168 deletions(-) diff --git a/src/FftDisplayPlot.cc b/src/FftDisplayPlot.cc index fd1b13ae7f..b8adaa0da8 100644 --- a/src/FftDisplayPlot.cc +++ b/src/FftDisplayPlot.cc @@ -209,6 +209,10 @@ FftDisplayPlot::~FftDisplayPlot() canvas()->removeEventFilter(d_symbolCtrl); } +void FftDisplayPlot::initChannelMeasurement(int nplots) { + Q_EMIT channelAdded(nplots); +} + void FftDisplayPlot::replot() { if (!d_leftHandlesArea || !d_bottomHandlesArea) { @@ -221,6 +225,17 @@ void FftDisplayPlot::replot() QwtPlot::replot(); } +bool FftDisplayPlot::isReferenceWaveform(unsigned int chnIdx) +{ + QwtPlotCurve *curve = Curve(chnIdx); + return d_ref_curves.values().contains(curve); +} + +size_t FftDisplayPlot::getCurveSize(unsigned int chnIdx) +{ + return Curve(chnIdx)->data()->size(); +} + QString FftDisplayPlot::formatXValue(double value, int precision) const { return d_formatter->format(value, d_xAxisUnit, precision); @@ -474,7 +489,6 @@ void FftDisplayPlot::registerReferenceWaveform(QString name, QVector xDa d_ref_curves.insert(name, curve); d_plot_curve.push_back(curve); - n_ref_curves++; d_num_markers.push_back(0); d_markers.push_back(QList()); @@ -491,6 +505,9 @@ void FftDisplayPlot::registerReferenceWaveform(QString name, QVector xDa findPeaks(d_plot_curve.size() - 1); + Q_EMIT channelAdded(y_data.size() + n_ref_curves); + n_ref_curves++; + replot(); } @@ -583,6 +600,27 @@ void FftDisplayPlot::useLogFreq(bool use_log_freq) replot(); } +std::vector FftDisplayPlot::getOrginal_data() { + return y_original_data; +} + +int64_t FftDisplayPlot::getYdata_size() { + return y_data.size(); +} + +std::vector FftDisplayPlot::getRef_data() { + return d_refYdata; +} + +std::vector FftDisplayPlot::getScaleFactor() { + return y_scale_factor; +} + +int64_t FftDisplayPlot::getNumPoints() +{ + return d_numPoints; +} + void FftDisplayPlot::plotData(const std::vector &pts, uint64_t num_points) { diff --git a/src/FftDisplayPlot.h b/src/FftDisplayPlot.h index 8019cffbbe..d713c19352 100644 --- a/src/FftDisplayPlot.h +++ b/src/FftDisplayPlot.h @@ -179,6 +179,16 @@ namespace adiscope { explicit FftDisplayPlot(int nplots, QWidget *parent = nullptr); ~FftDisplayPlot(); + void initChannelMeasurement(int nplots); + std::vector getOrginal_data(); + std::vector getRef_data(); + int64_t getYdata_size(); + std::vector getScaleFactor(); + int64_t getNumPoints(); + + bool isReferenceWaveform(unsigned int chnIdx); + size_t getCurveSize(unsigned int chnIdx); + // Scaling factors for plot samples (one per channel) double channelScaleFactor(int chIdx) const; void setScaleFactor(int chIdx, double scale); @@ -252,6 +262,7 @@ namespace adiscope { QString formatYValue(double value, int precision) const; Q_SIGNALS: + void channelAdded(int); void newData(); void sampleRateUpdated(double); void sampleCountUpdated(uint); diff --git a/src/gui/measure.cpp b/src/gui/measure.cpp index f70d6c413a..4a7d064a1b 100644 --- a/src/gui/measure.cpp +++ b/src/gui/measure.cpp @@ -347,10 +347,378 @@ namespace adiscope { QString m_name; }; + + class SpectralDetection { + public: + SpectralDetection(double *data, ssize_t data_length, int harmonics_number): + m_data(data), + m_data_length(data_length), + m_harmonics_number(harmonics_number) + { + } + + void calculateSpectralDetectionParameters(double &spur, double &harm_dist, double &noise, + double &average_noise, double &signal) { + struct harmonic_tuple { + double harm_value; + int harm_bin; + }; + + findHarmonics(m_harmonics_number); + + int spur_bw; + findSpur(spur, spur_bw, false, m_harmonic_bins[0]); + + if(m_mask.empty()) + m_mask = calculateAutoMask(); + int noise_bins; + maskedSumOfSquares(noise_bins, noise, m_mask); + + noise_bins = noise_bins > 1 ? noise_bins : 1; + noise = average_noise * (m_data_length - 1); + + std::map harmonic; + for(int i = 0; i < m_harmonics_number; i++) + { + struct harmonic_tuple harm; + harm.harm_bin = m_harmonic_bins[i]; + harm.harm_value = m_harmonics[i]; + harm.harm_value -= average_noise * m_harmonic_bw[i]; + harmonic[i+1] = harm; + } + + spur -= average_noise * spur_bw; + signal = harmonic[1].harm_value; + + harm_dist = harmonic[2].harm_value + harmonic[3].harm_value + + harmonic[4].harm_value + harmonic[5].harm_value; + + } + + private: + + std::vector calculateAutoMask() { + const int BANDWIDTH_DIVIDER = 80; + const int NUM_INITAL_NOISE_HARMS = 5; + + int bw = int(m_data_length / BANDWIDTH_DIVIDER); + std::vector mask; + mask.resize(m_data_length, 1); + for (int i = 0; i < NUM_INITAL_NOISE_HARMS; i++) + clearMask(mask, m_harmonic_bins[i] - bw, m_harmonic_bins[i] + bw); + mask[0] = 0; + + double noise_est; + int noise_bins; + maskedSum(noise_bins, noise_est, mask); + noise_est /= noise_bins; + + std::fill(mask.begin(), mask.end(), 1); + //clear mask la dc. + clearMask(mask, 0, 1); + int j; + int low, high; + + double sum; + + for(int i = 0; i < m_harmonic_bins.size(); i++) + { + int h = m_harmonic_bins.at(i); + if(mask[h] == 0) + continue; + j = 1; + sum = 0.0F; + for(int s = h-j; s < h-j+3; s++) + { + sum += m_data[s]; + } + sum /= 3; + while ( h - j > 0 && mask[h-j] == 1 && + sum > noise_est ) { + j++; + sum = 0.0F; + for(int s = h-j; s < h-j+3; s++) + { + sum += m_data[s]; + } + sum /= 3; + } + low = h - j + 1; + + j = 1; + sum = 0.0F; + for(int s = h+j-2; s < h+j+1; s++) + { + sum += m_data[s]; + } + sum /= 3; + while ( h + j < m_data_length && mask[h+j] == 1 && + sum > noise_est ) { + j++; + sum = 0.0F; + for(int s = h+j-2; s < h+j+1; s++) + { + sum += m_data[s]; + } + sum /= 3; + } + high = h + j - 1; + clearMask(mask, low, high); + } + + return mask; + } + + void setMask(std::vector &mask, int start, int end, int value) + { + int nyq = mask.size(); + int n = 2 * (nyq - 1); + std::vector indices; + for(int i = start; i <= end; i++) + { + int index_value = (i + n) % n; + if (index_value > nyq) + index_value = n - index_value; + indices.push_back(index_value); + } + + for(std::vector::iterator it = indices.begin(); it != indices.end(); ++it) + { + mask.at(*it) = value; + } + } + + void clearMask(std::vector &mask, int start, int end) + { + setMask(mask, start, end, 0); + } + + void maskedSubset(std::vector &indices, std::vector &new_mask, std::vector mask, int start, int end) + { + int nyq = mask.size(); + int n = 2 * (nyq - 1); + std::vector mapped_subset; + for(int i = start; i < end; i++) + { + int index_value = (i + n) % n; + if (index_value > nyq) + index_value = n - index_value; + mapped_subset.push_back(index_value); + } + + for(int i = 0; i < mapped_subset.size(); i++) + { + if(mask.at(mapped_subset.at(i)) == 1) + { + indices.push_back(mapped_subset.at(i)); + new_mask.push_back(mask.at(mapped_subset.at(i))); + } + + } + } + + void maskedSumOfSquares(int &index, double &value, std::vector mask, int start = 0, int end = 0) + { + if(end == 0 || end >= m_data_length) + end = m_data_length - 1; + + std::vector indices; + std::vector new_mask; + maskedSubset(indices, new_mask, mask, start, end); + double sum = 0; + + + for(std::vector::iterator it = indices.begin(); it != indices.end(); ++it) + { + sum = sum + (m_data[*it] * m_data[*it]); + } + + index = indices.size(); + value = sum; + } + + void maskedSum(int &index, double &value, std::vector mask, int start = 0, int end = 0) + { + if(end == 0 || end >= m_data_length) + end = m_data_length - 1; + + std::vector indices; + std::vector new_mask; + maskedSubset(indices, new_mask, mask, start, end); + double sum = 0; + + + for(std::vector::iterator it = indices.begin(); it != indices.end(); ++it) + { + sum = sum + (m_data[*it]); + } + + index = indices.size(); + value = sum; + } + + void maskedMax(int &index, double &value, std::vector mask, int start = 0, int end = 0) + { + if(end == 0) + end = m_data_length - 1; + double *data_at_index = new double[end](); + double data_at_index_size; + int i = 0; + + std::vector indices; + std::vector new_mask; + maskedSubset(indices, new_mask, mask, start, end); + + for(int i=0; i value) + { + index = i; + value = data[i]; + } + } + } + + void findHarmonics(int max_harmonics) + { + m_harmonic_bins.resize(max_harmonics); + m_harmonic_bw.resize(max_harmonics); + m_harmonics.resize(max_harmonics); + + std::fill(m_harmonic_bins.begin(), m_harmonic_bins.end(), 0); + std::fill(m_harmonic_bw.begin(), m_harmonic_bw.end(), 0); + std::fill(m_harmonics.begin(), m_harmonics.end(), 0.0); + + //find the fundamental bin (max value) from the data + int fund_bin = 0; + double max = 0; + getMax(m_data, m_data_length, fund_bin, max); + + m_harmonic_bins.at(0) = fund_bin; + + for(int h = 1; h < max_harmonics + 1; h++) + { + std::vector mask; + mask.resize(m_data_length, 0); + + int nominal_bin = h * fund_bin; + int h_2 = static_cast(h / 2); + if(h > 1) + { + setMask(mask, nominal_bin - h_2, nominal_bin + h_2, 1); + for(int i = 0; i < h - 1; i++) + { + clearMask(mask, m_harmonic_bins.at(i), m_harmonic_bins.at(i)); + } + int index; + double value; + maskedMax(index, value, mask); + m_harmonic_bins.at(h-1) = index; + } + clearMask(mask, nominal_bin - h_2, nominal_bin + h_2); + setMask(mask, m_harmonic_bins.at(h-1) - BW, m_harmonic_bins.at(h-1) + BW, 1); + for(int i = 0; i < h - 1; i++) + { + clearMask(mask, m_harmonic_bins.at(i) - BW, m_harmonic_bins.at(i) + BW); + } + double val_harm; + int val_hbws; + maskedSumOfSquares(val_hbws, val_harm, mask); + m_harmonics[h-1] = val_harm; + m_harmonic_bw[h-1] = val_hbws; + + } + + } + + void findSpur(double &spur, int& spur_bw, bool find_in_harmonics, int fund_bin) { + if(find_in_harmonics == true) { + int index = std::max_element(m_harmonics.begin() + 1, m_harmonics.end()) - m_harmonics.begin(); + spur = *std::max_element(m_harmonics.begin() + 1, m_harmonics.end()); + spur_bw = m_harmonic_bw[index + 1]; + } + else + { + findSpurInData(spur, spur_bw, fund_bin); + } + } + + void findSpurInData(double &spur, int& spur_bw, int fund_bin) { + std::vector mask; + mask.resize(m_data_length, 1); + //clear mask la dc. + clearMask(mask, 0, 1); + clearMask(mask, fund_bin - BW, fund_bin + BW); + int index = 0; + for(int i = 0; i < mask.size(); i++) + { + if(mask.at(i) == 1) + { + index = i; + break; + } + } + double max_value; + int max_index; + maskedSumOfSquares(max_index, max_value, mask, index - BW, index + BW); + max_index = index; + int masked_index = 0; + + double value; + while(index < m_data_length) + { + if(mask.at(index) == 1) + { + maskedSumOfSquares(masked_index, value, mask, index - BW, index + BW); + if(value > max_value) + { + max_value = value; + max_index = index; + } + } + index++; + } + + int spur_bin; + double spur_value; + maskedMax(spur_bin, spur_value, mask, max_index - BW, max_index + BW); + maskedSumOfSquares(spur_bw, spur, mask, spur_bin - BW, spur_bin + BW); + } + + private: + double *m_data; + ssize_t m_data_length; + + const int BW = 3; + std::vector m_harmonic_bins; + std::vector m_harmonic_bw; + std::vector m_harmonics; + int m_harmonics_number; + std::vector m_mask; + + }; } Measure::Measure(int channel, double *buffer, size_t length, - const std::function &conversion_fct): + const std::function &conversion_fct, bool isTimeDomain): m_channel(channel), m_buffer(buffer), m_buf_length(length), @@ -361,58 +729,77 @@ Measure::Measure(int channel, double *buffer, size_t length, m_histogram(nullptr), m_cross_detect(nullptr), m_gatingEnabled(false), - m_conversion_function(conversion_fct) + m_conversion_function(conversion_fct), + m_isTimeDomain(isTimeDomain), + m_harmonics_number(5) { - // Create a set of measurements - m_measurements.push_back(std::make_shared(QObject::tr("Period"), - MeasurementData::HORIZONTAL, "s", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Frequency"), - MeasurementData::HORIZONTAL, "Hz", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Min"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Max"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Peak-peak"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Mean"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Cycle Mean"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("RMS"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Cycle RMS"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("AC RMS"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Area"), - MeasurementData::VERTICAL, "Vs", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Cycle Area"), - MeasurementData::VERTICAL, "Vs", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Low"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("High"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Amplitude"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Middle"), - MeasurementData::VERTICAL, "V", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("+Over"), - MeasurementData::VERTICAL, "%", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("-Over"), - MeasurementData::VERTICAL, "%", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Rise"), - MeasurementData::HORIZONTAL, "s", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("Fall"), - MeasurementData::HORIZONTAL, "s", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("+Width"), - MeasurementData::HORIZONTAL, "s", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("-Width"), - MeasurementData::HORIZONTAL, "s", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("+Duty"), - MeasurementData::HORIZONTAL, "%", channel)); - m_measurements.push_back(std::make_shared(QObject::tr("-Duty"), - MeasurementData::HORIZONTAL, "%", channel)); + if(m_isTimeDomain) { + + // Create a set of measurements + m_measurements.push_back(std::make_shared(QObject::tr("Period"), + MeasurementData::HORIZONTAL, "s", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Frequency"), + MeasurementData::HORIZONTAL, "Hz", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Min"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Max"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Peak-peak"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Mean"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Cycle Mean"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("RMS"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Cycle RMS"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("AC RMS"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Area"), + MeasurementData::VERTICAL, "Vs", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Cycle Area"), + MeasurementData::VERTICAL, "Vs", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Low"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("High"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Amplitude"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Middle"), + MeasurementData::VERTICAL, "V", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("+Over"), + MeasurementData::VERTICAL, "%", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("-Over"), + MeasurementData::VERTICAL, "%", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Rise"), + MeasurementData::HORIZONTAL, "s", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("Fall"), + MeasurementData::HORIZONTAL, "s", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("+Width"), + MeasurementData::HORIZONTAL, "s", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("-Width"), + MeasurementData::HORIZONTAL, "s", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("+Duty"), + MeasurementData::HORIZONTAL, "%", channel)); + m_measurements.push_back(std::make_shared(QObject::tr("-Duty"), + MeasurementData::HORIZONTAL, "%", channel)); + } else { + //Spectral Measurements + m_measurements.push_back(std::make_shared("Noise_Floor", + MeasurementData::HORIZONTAL_F, "dB", channel)); + m_measurements.push_back(std::make_shared("SINAD", + MeasurementData::HORIZONTAL_F, "dB", channel)); + m_measurements.push_back(std::make_shared("SNR", + MeasurementData::HORIZONTAL_F, "dB", channel)); + m_measurements.push_back(std::make_shared("THD", + MeasurementData::HORIZONTAL_F, "dB", channel)); + m_measurements.push_back(std::make_shared("THDN", + MeasurementData::HORIZONTAL_F, "dB", channel)); + m_measurements.push_back(std::make_shared("SFDR", + MeasurementData::VERTICAL_F, "dBc", channel)); + } } @@ -486,6 +873,15 @@ void Measure::measure() if (!m_buffer || m_buf_length == 0) return; + if(m_isTimeDomain) { + measureTimeDomain(); + } else { + measureSpectral(); + } +} + +void Measure::measureTimeDomain() +{ double period; double frequency; double rise_time; @@ -803,6 +1199,34 @@ void Measure::measure() } +void Measure::measureSpectral() { + double spur, harm_dist, noise, average_noise, signal; + + SpectralDetection detection(m_buffer, m_buf_length, m_harmonics_number); + + detection.calculateSpectralDetectionParameters(spur, harm_dist, noise, average_noise, signal); + + double noise_floor, snr, thd, thdn, sinad, sfdr; + noise_floor = 10 * log10(average_noise / signal); + m_measurements[NOISE_FLOOR]->setValue(noise_floor); + + snr = 10 * log10(signal / noise); + m_measurements[SNR]->setValue(snr); + + thd = harm_dist > 0 ? 10 * log10(harm_dist / signal) : 0; + m_measurements[THD]->setValue(thd); + + sinad = 10 * log10(signal / (harm_dist + noise)); + m_measurements[SINAD]->setValue(sinad); + + thdn = harm_dist > 0 ? 10 * log10((harm_dist + noise) / signal) : 0; + m_measurements[THDN]->setValue(thdn); + + sfdr = spur > 0 ? 10 * log10(signal / spur) : 0; + m_measurements[SFDR]->setValue(sfdr); +} + + double Measure::sampleRate() { return m_sample_rate; @@ -872,6 +1296,31 @@ void Measure::setGatingEnabled(bool enable){ m_gatingEnabled = enable; } +std::vector Measure::LoadMaskfromFile(std::string file_name) +{ + std::vector mask; + std::ifstream mask_file(file_name.c_str()); + if(!mask_file.is_open()) throw std::runtime_error("Could not open freq file"); + + std::string line = ""; + while(std::getline(mask_file, line)) { + + int val = std::atoi(line.c_str()); + mask.push_back(val); + } + return mask; +} + +void Measure::setHarmonicNumber(int harmonics_number) +{ + m_harmonics_number = harmonics_number; +} + +void Measure::setMask(std::vector mask) +{ + std::vector m_mask = mask; +} + QList> Measure::measurments() { return m_measurements; @@ -914,6 +1363,10 @@ MeasurementData::MeasurementData(const QString& name, axisType axis, m_unitType = PERCENTAGE; else if (unit.toLower() == "s" || unit.toLower() == "seconds") m_unitType = TIME; + else if ((unit.toLower() == "db" || unit.toLower() == "decibels")) + m_unitType = DECIBELS; + else if ((unit.toLower() == "dbc" || unit.toLower() == "decibels_to_carrier")) + m_unitType = DECIBELS_TO_CARRIER; else m_unitType = METRIC; } diff --git a/src/gui/measure.h b/src/gui/measure.h index 0fc3480eb0..2882eca52a 100644 --- a/src/gui/measure.h +++ b/src/gui/measure.h @@ -35,12 +35,16 @@ namespace adiscope { METRIC, TIME, PERCENTAGE, - DIMENSIONLESS + DIMENSIONLESS, + DECIBELS, + DECIBELS_TO_CARRIER }; enum axisType { HORIZONTAL, - VERTICAL + VERTICAL, + HORIZONTAL_F, + VERTICAL_F }; MeasurementData(const QString& name, axisType axis, @@ -101,11 +105,25 @@ namespace adiscope { DEFAULT_MEASUREMENT_COUNT }; + enum defaultSpectralMeasurements { + NOISE_FLOOR = 0, + SINAD, + SNR, + THD, + THDN, + SFDR, + DEFAULT_SPECTRAL_MEASUREMENT_COUNT + }; + Measure(int channel, double *buffer = NULL, size_t length = 0, - const std::function &conversion = nullptr); + const std::function &conversion = nullptr, bool isTimeDomain = true); void setDataSource(double *buffer, size_t length); void measure(); + + void measureTimeDomain(); + void measureSpectral(); + double sampleRate(); void setSampleRate(double); unsigned int adcBitCount(); @@ -120,11 +138,17 @@ namespace adiscope { void setEndIndex(int); void setGatingEnabled(bool); + void setHarmonicNumber(int harmonics_number); + void setMask(std::vector mask); + QList> measurments(); std::shared_ptr measurement(int id); int activeMeasurementsCount() const; void setConversionFunction(const std::function &fp); + + std::vector LoadMaskfromFile(std::string file_name); + private: bool highLowFromHistogram(double &low, double &high, double min, double max); @@ -144,6 +168,10 @@ namespace adiscope { int *m_histogram; CrossingDetection *m_cross_detect; + int m_harmonics_number; + std::vector m_mask; + bool m_isTimeDomain; + QList> m_measurements; std::function m_conversion_function; }; diff --git a/src/gui/measure_settings.cpp b/src/gui/measure_settings.cpp index 73038156c1..88c7d7b9ce 100644 --- a/src/gui/measure_settings.cpp +++ b/src/gui/measure_settings.cpp @@ -55,25 +55,41 @@ static const std::map icons_lut = { {Measure::N_DUTY, "://icons/measurements/n_duty.svg"}, }; -MeasureSettings::MeasureSettings(CapturePlot *plot, QWidget *parent) : - QWidget(parent), - m_ui(new Ui::MeasureSettings), - m_channelName(""), - m_chnUnderlineColor(), - m_horizMeasurements(new DropdownSwitchList(2, this)), - m_vertMeasurements(new DropdownSwitchList(2, this)), - m_emitActivated(true), - m_emitStatsChanged(true), - m_emitStatsDeleteAll(true), - m_emitDeleteAll(true), - m_are_dropdowns_filled(false), - m_plot(plot), - m_selectedChannel(-1), - m_enableDisplayAll(false) -{ - QTreeView *treeView; - - m_ui->setupUi(this); +static const std::map icons_spect = { + {Measure::NOISE_FLOOR, "://icons/measurements/period.svg"}, + {Measure::SINAD, "://icons/measurements/frequency.svg"}, + {Measure::SNR, "://icons/measurements/frequency.svg"}, + {Measure::THD, "://icons/measurements/frequency.svg"}, + {Measure::THDN, "://icons/measurements/frequency.svg"}, + {Measure::SFDR, "://icons/measurements/frequency.svg"}, +}; + +MeasureSettings::MeasureSettings( QList* measures_list, QWidget *parent, bool is_time_domain) : + QWidget(parent), + m_ui(new Ui::MeasureSettings), + m_channelName(""), + m_chnUnderlineColor(), + m_horizMeasurements(nullptr), + m_vertMeasurements(nullptr), + m_is_time_domain(is_time_domain), + m_emitActivated(true), + m_emitStatsChanged(true), + m_emitStatsDeleteAll(true), + m_emitDeleteAll(true), + m_are_dropdowns_filled(false), + m_measures_list(measures_list), + m_selectedChannel(-1), + m_enableDisplayAll(false) +{ + QTreeView *treeView; + + m_ui->setupUi(this); + hide_measure_settings(m_is_time_domain); + + if(m_is_time_domain) + { + m_horizMeasurements = new DropdownSwitchList(2, this); + m_vertMeasurements = new DropdownSwitchList(2, this); m_ui->hLayout_hMeasurements->addWidget(m_horizMeasurements); m_ui->hLayout_vMeasurements->addWidget(m_vertMeasurements); @@ -87,8 +103,8 @@ MeasureSettings::MeasureSettings(CapturePlot *plot, QWidget *parent) : treeView->setIconSize(QSize(30, 20)); connect(m_horizMeasurements->model(), - SIGNAL(itemChanged(QStandardItem*)), - SLOT(onMeasurementPropertyChanged(QStandardItem*))); + SIGNAL(itemChanged(QStandardItem*)), + SLOT(onMeasurementPropertyChanged(QStandardItem*))); m_vertMeasurements->setTitle(tr("Vertical")); m_vertMeasurements->setColumnTitle(0, tr("Name")); @@ -100,8 +116,45 @@ MeasureSettings::MeasureSettings(CapturePlot *plot, QWidget *parent) : treeView->setIconSize(QSize(30, 20)); connect(m_vertMeasurements->model(), - SIGNAL(itemChanged(QStandardItem*)), - SLOT(onMeasurementPropertyChanged(QStandardItem*))); + SIGNAL(itemChanged(QStandardItem*)), + SLOT(onMeasurementPropertyChanged(QStandardItem*))); + } + else + { + m_horizMeasurements = new DropdownSwitchList(1, this); + m_vertMeasurements = new DropdownSwitchList(1, this); + + connect(m_ui->comboBox_harm, SIGNAL(currentIndexChanged(int)), + SLOT(onharmValueChanged(int))); + + m_ui->hLayout_hMeasurements->addWidget(m_horizMeasurements); + m_ui->hLayout_vMeasurements->addWidget(m_vertMeasurements); + + m_horizMeasurements->setTitle(tr("Horizontals")); + m_horizMeasurements->setColumnTitle(0, tr("Name")); + m_horizMeasurements->setColumnTitle(1, tr("Measure")); + m_horizMeasurements->setMaxVisibleItems(4); + treeView = static_cast(m_horizMeasurements->view()); + treeView->header()->resizeSection(0, 150); + treeView->setIconSize(QSize(30, 20)); + + connect(m_horizMeasurements->model(), + SIGNAL(itemChanged(QStandardItem*)), + SLOT(onMeasurementPropertyChanged(QStandardItem*))); + + m_vertMeasurements->setTitle(tr("Verticals")); + m_vertMeasurements->setColumnTitle(0, tr("Name")); + m_vertMeasurements->setColumnTitle(1, tr("Measure")); + + m_vertMeasurements->setMaxVisibleItems(4); + treeView = static_cast(m_vertMeasurements->view()); + treeView->header()->resizeSection(0, 150); + treeView->setIconSize(QSize(30, 20)); + + connect(m_vertMeasurements->model(), + SIGNAL(itemChanged(QStandardItem*)), + SLOT(onMeasurementPropertyChanged(QStandardItem*))); + } } MeasureSettings::~MeasureSettings() @@ -109,6 +162,50 @@ MeasureSettings::~MeasureSettings() delete m_ui; } +void MeasureSettings::onharmValueChanged(int id) +{ + Measure *measure = measureOfChannel(m_selectedChannel); + measure->setHarmonicNumber(id + 1); +} + +void MeasureSettings::hide_measure_settings(bool is_time_domain) { + if(is_time_domain) + { + m_ui->comboBox_harm->hide(); + m_ui->label_9->hide(); + m_ui->line_6->hide(); + m_ui->label_10->hide(); + } + else + { + m_ui->label_7->hide(); + m_ui->line_3->hide(); + m_ui->verticalSpacer_6->changeSize(0,0); + m_ui->verticalSpacer_7->changeSize(0,0); + + m_ui->button_StatisticsEn->hide(); + m_ui->button_statsDeleteAll->hide(); + m_ui->button_StatisticsReset->hide(); + + m_ui->button_GatingEnable->hide(); + m_ui->label_4->hide(); + m_ui->label_5->hide(); + m_ui->line_5->hide(); + + m_ui->comboBox_harm->addItem("1"); + m_ui->comboBox_harm->addItem("2"); + m_ui->comboBox_harm->addItem("3"); + m_ui->comboBox_harm->addItem("4"); + m_ui->comboBox_harm->addItem("5"); + m_ui->comboBox_harm->addItem("6"); + m_ui->comboBox_harm->addItem("7"); + m_ui->comboBox_harm->addItem("8"); + m_ui->comboBox_harm->addItem("9"); + + m_ui->comboBox_harm->setCurrentIndex(6); + } +} + QString MeasureSettings::channelName() const { return m_channelName; @@ -224,34 +321,68 @@ void MeasureSettings::on_button_measDeleteAll_toggled(bool checked) } } +Measure* MeasureSettings::measureOfChannel(int chnIdx) const +{ + Measure *measure = nullptr; + + auto it = std::find_if(m_measures_list->begin(), m_measures_list->end(), + [&](Measure *m) { return m->channel() == chnIdx; }); + if (it != m_measures_list->end()) + measure = *it; + + return measure; +} + void MeasureSettings::onChannelAdded(int chnIdx) { - // Use the measurements of the 1st channel to construct the dropdowns. - // All channels have the same set of measurements. - if (m_are_dropdowns_filled) - return; + // Use the measurements of the 1st channel to construct the dropdowns. + // All channels have the same set of measurements. + if (m_are_dropdowns_filled) + return; - auto measurements = m_plot->measurements(chnIdx); - int h = 0; - int v = 0; + Measure *measure = measureOfChannel(chnIdx); + auto measurements = QList>(); + if (measure) + measurements = measure->measurments(); + int h = 0; + int v = 0; + if(m_is_time_domain) + { for (int i = 0; i < measurements.size(); i++) { - enum MeasurementData::axisType axis = measurements[i]->axis(); - - if (axis == MeasurementData::HORIZONTAL) { - m_horizMeasurements->addDropdownElement( - QIcon(icons_lut.at(i)), - measurements[i]->name(), QVariant(i)); - m_measurePosInDropdowns.append(h++); - } else if (axis == MeasurementData::VERTICAL) { - m_vertMeasurements->addDropdownElement( - QIcon(icons_lut.at(i)), - measurements[i]->name(), QVariant(i)); - m_measurePosInDropdowns.append(v++); - } + enum MeasurementData::axisType axis = measurements[i]->axis(); + + if (axis == MeasurementData::HORIZONTAL) { + m_horizMeasurements->addDropdownElement( + QIcon(icons_lut.at(i)), + measurements[i]->name(), QVariant(i)); + m_measurePosInDropdowns.append(h++); + } else if (axis == MeasurementData::VERTICAL) { + m_vertMeasurements->addDropdownElement( + QIcon(icons_lut.at(i)), + measurements[i]->name(), QVariant(i)); + m_measurePosInDropdowns.append(v++); + } } + } + else + { + for (int i = 0; i < measurements.size(); i++) { + enum MeasurementData::axisType axis = measurements[i]->axis(); + + if (axis == MeasurementData::HORIZONTAL_F) { + m_horizMeasurements->addDropdownElement( + QIcon(icons_spect.at(i)), + measurements[i]->name(), QVariant(i)); + } else if (axis == MeasurementData::VERTICAL_F) { + m_vertMeasurements->addDropdownElement( + QIcon(icons_spect.at(i)), + measurements[i]->name(), QVariant(i)); + } + } + } - m_are_dropdowns_filled = true; + m_are_dropdowns_filled = true; } void MeasureSettings::onChannelRemoved(int chnIdx) @@ -284,31 +415,53 @@ void MeasureSettings::setSelectedChannel(int chnIdx) void MeasureSettings::loadMeasurementStatesFromData() { - auto measurements = m_plot->measurements(m_selectedChannel); - int h_idx = 0; - int v_idx = 0; + Measure *measure = measureOfChannel(m_selectedChannel); + auto measurements = QList>(); + if (measure) + measurements = measure->measurments(); + + int h_idx = 0; + int v_idx = 0; - QStandardItemModel *horiz_model = + QStandardItemModel *horiz_model = static_cast(m_horizMeasurements->model()); - QStandardItemModel *vert_model = + QStandardItemModel *vert_model = static_cast(m_vertMeasurements->model()); - setEmitActivated(false); + setEmitActivated(false); - for (int i = 0; i < measurements.size(); i++) { - int axis = measurements[i]->axis(); - int state = measurements[i]->enabled(); + if(m_is_time_domain) + { + for (int i = 0; i < measurements.size(); i++) { + int axis = measurements[i]->axis(); + int state = measurements[i]->enabled(); - if (axis == MeasurementData::HORIZONTAL) { + if (axis == MeasurementData::HORIZONTAL) { horiz_model->item(h_idx++, 1)->setData( - QVariant((int) state), Qt::EditRole); - } else if (axis == MeasurementData::VERTICAL) { + QVariant((int) state), Qt::EditRole); + } else if (axis == MeasurementData::VERTICAL) { vert_model->item(v_idx++, 1)->setData( - QVariant((int) state), Qt::EditRole); + QVariant((int) state), Qt::EditRole); + } } - } + } + else + { + for (int i = 0; i < measurements.size(); i++) { + enum MeasurementData::axisType axis = measurements[i]->axis(); + int state = measurements[i]->enabled(); + + if (axis == MeasurementData::HORIZONTAL_F) { + horiz_model->item(h_idx++, 1)->setData( + QVariant((int) state), Qt::EditRole); + } else if (axis == MeasurementData::VERTICAL_F) { + vert_model->item(v_idx++, 1)->setData( + QVariant((int) state), Qt::EditRole); + } + } + } - setEmitActivated(true); + setEmitActivated(true); } void MeasureSettings::deleteAllMeasurements() @@ -331,7 +484,12 @@ void MeasureSettings::displayAllMeasurements() { m_displayAllBackup = m_selectedMeasurements; m_selectedMeasurements.clear(); - auto measurements = m_plot->measurements(m_selectedChannel); + + Measure *measure = measureOfChannel(m_selectedChannel); + auto measurements = QList>(); + if (measure) + measurements = measure->measurments(); + for (int i = 0; i < measurements.size(); i++) { MeasurementItem item(i, measurements[i]->channel()); m_selectedMeasurements.push_back(MeasurementItem(item)); @@ -352,7 +510,11 @@ void MeasureSettings::onMeasurementActivated(int chnIdx, int id, bool en) if (chnIdx < 0) return; - auto measurements = m_plot->measurements(chnIdx); + Measure *measure = measureOfChannel(chnIdx); + auto measurements = QList>(); + if (measure) + measurements = measure->measurments(); + MeasurementItem mItem(id, measurements[id]->channel()); if (en) { m_selectedMeasurements.push_back(mItem); @@ -380,28 +542,31 @@ void MeasureSettings::onStatisticActivated(DropdownSwitchList *dropdown, void MeasureSettings::loadStatisticStatesForChannel(int chnIdx) { - const int stats_col = 2; + // makes sense only for the oscilloscope + if (m_is_time_domain) { + const int stats_col = 2; - setEmitStatsChanged(false); + setEmitStatsChanged(false); - // Start with all selections cleared - setAllMeasurements(stats_col, false); + // Start with all selections cleared + setAllMeasurements(stats_col, false); - // Restore selections that are present in the selected statistics list - for (int i = 0; i < m_selectedStatistics.size(); i++) { - if (m_selectedStatistics[i].measurementItem.channel_id() != - chnIdx) { - continue; - } - QStandardItemModel *model = static_cast - (m_selectedStatistics[i].dropdown->model()); + // Restore selections that are present in the selected statistics list + for (int i = 0; i < m_selectedStatistics.size(); i++) { + if (m_selectedStatistics[i].measurementItem.channel_id() != + chnIdx) { + continue; + } + QStandardItemModel *model = static_cast + (m_selectedStatistics[i].dropdown->model()); - int measId = m_selectedStatistics[i].measurementItem.id(); + int measId = m_selectedStatistics[i].measurementItem.id(); - model->item(m_measurePosInDropdowns[measId], stats_col)-> - setData(QVariant(1), Qt::EditRole); + model->item(m_measurePosInDropdowns[measId], stats_col)-> + setData(QVariant(1), Qt::EditRole); + } + setEmitStatsChanged(true); } - setEmitStatsChanged(true); } void MeasureSettings::on_button_StatisticsEn_toggled(bool checked) @@ -544,7 +709,11 @@ void MeasureSettings::recoverAllStatistics() void MeasureSettings::addStatistic(int measure_id, int ch_id) { - auto measurements = m_plot->measurements(ch_id); + Measure *measure = measureOfChannel(ch_id); + auto measurements = QList>(); + if (measure) + measurements = measure->measurments(); + MeasurementData::axisType axis = measurements[measure_id]->axis(); struct StatisticSelection selection; diff --git a/src/gui/measure_settings.h b/src/gui/measure_settings.h index 43199e704d..5253b9ba10 100644 --- a/src/gui/measure_settings.h +++ b/src/gui/measure_settings.h @@ -22,6 +22,7 @@ #include #include +#include "measure.h" namespace Ui { class MeasureSettings; @@ -72,11 +73,12 @@ struct StatisticSelection { class MeasureSettings : public QWidget { friend class Oscilloscope_API; + friend class SpectrumAnalyzer_API; Q_OBJECT public: - explicit MeasureSettings(CapturePlot *plot, QWidget *parent = 0); + explicit MeasureSettings(QList* measures_list, QWidget *parent = 0, bool is_time_domain = true); ~MeasureSettings(); QString channelName() const; @@ -119,6 +121,7 @@ public Q_SLOTS: void onChannelRemoved(int); void setSelectedChannel(int); void onMeasurementActivated(int chnIdx, int id, bool en); + void onharmValueChanged(int id); private Q_SLOTS: void onMeasurementPropertyChanged(QStandardItem *item); void on_button_measDisplayAll_toggled(bool checked); @@ -131,7 +134,9 @@ private Q_SLOTS: void on_button_GatingEnable_toggled(bool checked); private: + void hide_measure_settings(bool is_time_domain); void deleteAllMeasurements(); + Measure* measureOfChannel(int chnIdx) const; void recoverAllMeasurements(); void displayAllMeasurements(); void disableDisplayAllMeasurements(); @@ -161,8 +166,10 @@ private Q_SLOTS: bool m_emitStatsDeleteAll; bool m_are_dropdowns_filled; bool m_enableDisplayAll; + bool m_is_time_domain; + + QList* m_measures_list; - CapturePlot* m_plot; int m_selectedChannel; QList m_selectedMeasurements; QList m_deleteAllBackup; diff --git a/src/measurement_gui.cpp b/src/measurement_gui.cpp index c4641b78fc..a4a5ad5483 100644 --- a/src/measurement_gui.cpp +++ b/src/measurement_gui.cpp @@ -182,6 +182,86 @@ void PercentageMeasurementGui::update(const MeasurementData& data, double displa m_valueLabel->setText(m_value); } +/* + * Class DecibelsMeasurementGui implementation + */ + + DecibelsMeasurementGui::DecibelsMeasurementGui(): + MeasurementGui() +{ +} + +void DecibelsMeasurementGui::init(QLabel *name, QLabel *value) +{ + // Get the necessary label width so that the label will never resize + QLabel *label = new QLabel(value); + label->setText("-999.999 dB"); + m_minValLableWidth = label->minimumSizeHint().width(); + value->setMinimumWidth(m_minValLableWidth); + delete label; + + MeasurementGui::init(name, value); +} + +void DecibelsMeasurementGui::update(const MeasurementData& data, double displayScale) +{ + m_name = data.name() + ":"; + + if (data.measured() && data.enabled()) { + double value = data.value(); + if (data.axis() == MeasurementData::VERTICAL_F) { + value *= displayScale; + } + m_value.setNum(value, 'f', 3); + m_value += "dB"; + } else { + m_value = "--"; + } + + m_nameLabel->setText(m_name); + m_valueLabel->setText(m_value); +} + +/* + * Class DecibelstoCarrierMeasurementGui implementation + */ + + DecibelstoCarrierMeasurementGui::DecibelstoCarrierMeasurementGui(): + MeasurementGui() +{ +} + +void DecibelstoCarrierMeasurementGui::init(QLabel *name, QLabel *value) +{ + // Get the necessary label width so that the label will never resize + QLabel *label = new QLabel(value); + label->setText("-999.999 dB"); + m_minValLableWidth = label->minimumSizeHint().width(); + value->setMinimumWidth(m_minValLableWidth); + delete label; + + MeasurementGui::init(name, value); +} + +void DecibelstoCarrierMeasurementGui::update(const MeasurementData& data, double displayScale) +{ + m_name = data.name() + ":"; + + if (data.measured() && data.enabled()) { + double value = data.value(); + if (data.axis() == MeasurementData::VERTICAL_F) { + value *= displayScale; + } + m_value.setNum(value, 'f', 3); + m_value += "dB"; + } else { + m_value = "--"; + } + + m_nameLabel->setText(m_name); + m_valueLabel->setText(m_value); +} + /* * Class DimensionlessMeasurementGui implementation */ diff --git a/src/measurement_gui.h b/src/measurement_gui.h index aac46beaa7..59a71de283 100644 --- a/src/measurement_gui.h +++ b/src/measurement_gui.h @@ -83,6 +83,24 @@ class PercentageMeasurementGui: public MeasurementGui virtual void update(const MeasurementData& data, double displayScale); }; +class DecibelsMeasurementGui: public MeasurementGui +{ +public: + DecibelsMeasurementGui(); + + virtual void init(QLabel *name, QLabel *value); + virtual void update(const MeasurementData& data, double displayScale); +}; + +class DecibelstoCarrierMeasurementGui: public MeasurementGui +{ +public: + DecibelstoCarrierMeasurementGui(); + + virtual void init(QLabel *name, QLabel *value); + virtual void update(const MeasurementData& data, double displayScale); +}; + class DimensionlessMeasurementGui: public MeasurementGui { public: diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index ce0543c5bf..d6f193a72e 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -4296,7 +4296,8 @@ void Oscilloscope::measureUpdateValues() void Oscilloscope::measure_settings_init() { - measure_settings = new MeasureSettings(&plot, this); + QList* measure_obj = plot.getMeasurements(); + measure_settings = new MeasureSettings(measure_obj, this); int measure_panel = ui->stackedWidget->insertWidget(-1, measure_settings); diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index f2a57d9318..b0dcf35e3c 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -442,6 +442,11 @@ void CapturePlot::replot() } } +QList* CapturePlot::getMeasurements() +{ + return &d_measureObjs; +} + HorizBar *CapturePlot::levelTriggerA() { return d_levelTriggerABar; @@ -2026,5 +2031,3 @@ void CapturePlot::removeLeftVertAxis(unsigned int axis) DisplayPlot::removeLeftVertAxis(axis); } - - diff --git a/src/oscilloscope_plot.hpp b/src/oscilloscope_plot.hpp index 65c2b4eea6..83a20dbc4f 100644 --- a/src/oscilloscope_plot.hpp +++ b/src/oscilloscope_plot.hpp @@ -86,6 +86,7 @@ namespace adiscope { void removeOffsetWidgets(int chnIdx); void removeLeftVertAxis(unsigned int axis); + QList* getMeasurements(); void measure(); int activeMeasurementsCount(int chnIdx); QList> measurements(int chnIdx); diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 77d8e33268..c483885998 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include /* Local includes */ #include "logging_categories.h" @@ -48,11 +50,16 @@ #include "filemanager.h" #include "spectrum_analyzer_api.hpp" #include "stream_to_vector_overlap.h" +#include "gui/measure.h" +#include "measurement_gui.h" +#include "gui/measure_settings.h" /* Generated UI */ #include "ui_spectrum_analyzer.h" #include "ui_cursors_settings.h" #include "ui_cursor_readouts.h" +#include "ui_measure_panel.h" +#include "ui_measure_settings.h" #include #include @@ -209,8 +216,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, settings_group->addButton(ui->btnMarkers); settings_group->addButton(ui->btnAddRef); settings_group->addButton(ui->btnCursors); + settings_group->addButton(ui->btnMeasure); settings_group->setExclusive(true); + /* Measure panel */ + measure_panel_init(); + fft_plot = new FftDisplayPlot(m_adc_nb_channels, this); fft_plot->disableLegend(); @@ -218,9 +229,16 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, fft_plot->setXaxisMouseGesturesEnabled(false); for (uint i = 0; i < m_adc_nb_channels; i++) { +// ui->gridLayout_plot->addWidget(measurePanel, 0, 1, 1, 1); fft_plot->setYaxisMouseGesturesEnabled(i, false); } + connect(fft_plot, SIGNAL(channelAdded(int)), + SLOT(onChannelAdded(int))); + + /* Measurements Settings */ + measure_settings_init(); + // Add dockable plot QWidget* centralWidget = new QWidget(this); @@ -229,6 +247,8 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, vLayout->setSpacing(6); centralWidget->setLayout(vLayout); + vLayout->addWidget(measurePanel); + ui->widgetPlotContainer->layout()->removeWidget(ui->topPlotWidget); vLayout->addWidget(ui->topPlotWidget); @@ -475,6 +495,13 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, const bool visible = (channels[crt_channel_id]->averageType() != FftDisplayPlot::AverageType::SAMPLE); setCurrentAverageIndexLabel(crt_channel_id); + /* Apply measurements for every new batch of data */ + connect(fft_plot, SIGNAL(newData()), SLOT(onNewDataReceived())); + + for (int i = 0; i < m_adc_nb_channels; i++) { + fft_plot->initChannelMeasurement(i); + } + connect(top, SIGNAL(valueChanged(double)), SLOT(onTopValueChanged(double))); connect(unit_per_div, SIGNAL(valueChanged(double)), @@ -517,6 +544,14 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ch->setFftWindow(FftWinType::HAMMING, fft_size); } + if (!runButton()->isChecked() && measurementsEnabled()) { + measureUpdateValues(); + } + + for (unsigned int i = 0; i < m_adc_nb_channels; i++) { + init_selected_measurements(i, {0, 1, 4, 5}); + } + connect(ui->logBtn, &QPushButton::toggled, fft_plot, &FftDisplayPlot::useLogFreq); connect(ui->logBtn, &QPushButton::toggled, @@ -660,6 +695,11 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, readPreferences(); ui->btnHelp->setUrl("https://wiki.analog.com/university/tools/m2k/scopy/spectrumanalyzer"); + + // TODO: enable measurements + ui->boxMeasure->setChecked(false); + ui->boxMeasure->setVisible(false); + ui->btnMeasure->setVisible(false); } SpectrumAnalyzer::~SpectrumAnalyzer() @@ -682,6 +722,10 @@ SpectrumAnalyzer::~SpectrumAnalyzer() delete *it; } + for (auto it = d_measureObjs.begin(); it != d_measureObjs.end(); ++it) { + delete *it; + } + if (iio) { bool started = isIioManagerStarted(); @@ -843,8 +887,10 @@ void SpectrumAnalyzer::toggleRightMenu(CustomPushButton *btn, bool checked) index = 3; } else if (btn == ui->btnAddRef) { index = 4; - } else if (btn == ui->btnCursors) { + } else if (btn == ui->btnMeasure) { index = 5; + } else if (btn == ui->btnCursors) { + index = 6; } } @@ -940,6 +986,410 @@ void SpectrumAnalyzer::on_btnMarkers_toggled(bool checked) static_cast(QObject::sender()), checked); } +void SpectrumAnalyzer::on_btnMeasure_toggled(bool checked) { + triggerRightMenuToggle(static_cast(QObject::sender()), + checked); +} + +void SpectrumAnalyzer::measure_settings_init() +{ + measure_settings = new MeasureSettings(&d_measureObjs, this, false); + int measure_panel = ui->stackedWidget->insertWidget(5, measure_settings); + + ui->btnMeasure->setDisabled(true); + + connect(measure_settings, + SIGNAL(measurementActivated(int, int)), + SLOT(onMeasurementActivated(int, int))); + + connect(measure_settings, + SIGNAL(measurementDeactivated(int, int)), + SLOT(onMeasurementDeactivated(int, int))); + + connect(measure_settings, + SIGNAL(measurementSelectionListChanged()), + SLOT(onMeasurementSelectionListChanged())); + + connect(fft_plot, SIGNAL(channelAdded(int)), + measure_settings, SLOT(onChannelAdded(int))); + + connect(this, SIGNAL(selectedChannelChanged(int)), + measure_settings, SLOT(setSelectedChannel(int))); + + connect(ui->boxMeasure, SIGNAL(toggled(bool)), + SLOT(setMeasuremensEnabled(bool))); + +} + +void SpectrumAnalyzer::onChannelAdded(int chnIdx) +{ + Measure *measure = nullptr; + + if(fft_plot->isReferenceWaveform(chnIdx)) + { + int idx = chnIdx - fft_plot->getYdata_size(); + size_t curve_size = fft_plot->getCurveSize(chnIdx); + double* data = new double [curve_size](); + measure = new Measure(chnIdx, data, curve_size, + nullptr, false); + } + else + { + int64_t numPoints = fft_plot->getNumPoints() / 2; + std::vector scale_factor = fft_plot->getScaleFactor(); + + //std::vector data = fft_plot->getOrginal_data(); + //int count = fft_plot->countReferenceWaveform(chnIdx); + double* data = new double [numPoints](); + measure = new Measure(chnIdx, data, + numPoints, nullptr, false); + } + measure->setAdcBitCount(12); + d_measureObjs.push_back(measure); + +} + + +void SpectrumAnalyzer::update_measure_for_channel(int ch_idx) { + ChannelWidget *chn_widget = getChannelWidgetAt(ch_idx); + + measure_settings->setChannelName(chn_widget->fullName()); + measure_settings->setChannelUnderlineColor(chn_widget->color()); +} + + +void SpectrumAnalyzer::measure_panel_init() { + measurePanel = new QWidget(this); + measure_panel_ui = new Ui::MeasurementsPanel(); + measure_panel_ui->setupUi(measurePanel); + + connect(measure_panel_ui->scrollArea->horizontalScrollBar(), &QScrollBar::rangeChanged, + measure_panel_ui->scrollArea_2->horizontalScrollBar(), &QScrollBar::setRange); + + connect(measure_panel_ui->scrollArea_2->horizontalScrollBar(), &QScrollBar::valueChanged, + measure_panel_ui->scrollArea->horizontalScrollBar(), &QScrollBar::setValue); + connect(measure_panel_ui->scrollArea->horizontalScrollBar(), &QScrollBar::valueChanged, + measure_panel_ui->scrollArea_2->horizontalScrollBar(), &QScrollBar::setValue); + + connect(measure_panel_ui->scrollArea->horizontalScrollBar(), &QScrollBar::rangeChanged, + [=](double v1, double v2){ + measure_panel_ui->scrollArea_2->widget()->setFixedWidth(measure_panel_ui->scrollAreaWidgetContents->width()); + }); + + measurePanel->hide(); + + connect(this, SIGNAL(measurementsAvailable()), + SLOT(onMeasuremetsAvailable())); + +} + +void SpectrumAnalyzer::init_selected_measurements(int chnIdx, + std::vector measureIdx) +{ + auto measurements_val = measurements(chnIdx); + for (int i = 0; i < measureIdx.size(); i++) { + measurements_val[measureIdx[i]]->setEnabled(true); + measure_settings->onMeasurementActivated( + chnIdx, measureIdx[i], true); + } + measure_settings->loadMeasurementStatesFromData(); + onMeasurementSelectionListChanged(); +} + +std::shared_ptr SpectrumAnalyzer::measurement(int id, int chnIdx) +{ + Measure *measure = measureOfChannel(chnIdx); + if (measure) + return measure->measurement(id); + else + return std::shared_ptr(); +} + +void SpectrumAnalyzer::onMeasurementActivated(int id, int chnIdx) +{ + int oldActiveMeasCount = activeMeasurementsCount(chnIdx); + + auto mList = measurements(chnIdx); + mList[id]->setEnabled(true); + measurements_data.push_back(mList[id]); + measureCreateAndAppendGuiFrom(*mList[id]); + + if (oldActiveMeasCount == 0) { + measure(); + } + + measureLabelsRearrange(); +} + +void SpectrumAnalyzer::onMeasurementDeactivated(int id, int chnIdx) +{ + auto mList = measurements(chnIdx); + QString name = mList[id]->name(); + + mList[id]->setEnabled(false); + + auto it = find_if(measurements_data.begin(), measurements_data.end(), + [&](std::shared_ptr const& p) + { return (p->name() == name) && (p->channel() == chnIdx); }); + if (it != measurements_data.end()) { + int i = it - measurements_data.begin(); + measurements_data.removeAt(i); + measurements_gui.removeAt(i); + measureLabelsRearrange(); + } +} + +void SpectrumAnalyzer::onMeasurementSelectionListChanged() +{ + // Clear all measurements in list + for (int i = 0; i < measurements_data.size(); i++) { + measurements_data[i]->setEnabled(false); + } + measurements_data.clear(); + measurements_gui.clear(); + + // Use the new list from MeasureSettings + auto newList = measure_settings->measurementSelection(); + for (int i = 0; i < newList.size(); i++) { + auto pMeasurement = measurement(newList[i].id(), + newList[i].channel_id()); + if (pMeasurement) { + pMeasurement->setEnabled(true); + measurements_data.push_back(pMeasurement); + measureCreateAndAppendGuiFrom(*pMeasurement); + } + } + measureLabelsRearrange(); +} + +void SpectrumAnalyzer::measureCreateAndAppendGuiFrom(const MeasurementData& + measurement) +{ + std::shared_ptr p; + + switch(measurement.unitType()) { + + case MeasurementData::DECIBELS: + p = std::make_shared(); + break; + case MeasurementData::DECIBELS_TO_CARRIER: + p = std::make_shared(); + break; + case MeasurementData::DIMENSIONLESS: + p = std::make_shared(); + break; + default: + break; + } + if (p) + measurements_gui.push_back(p); +} + +void SpectrumAnalyzer::measureLabelsRearrange() +{ + QWidget *container = measure_panel_ui->measurements-> + findChild("container"); + + if (container) { + measure_panel_ui->measurements->layout()->removeWidget(container); + delete container; + } + + container = new QWidget(); + container->setObjectName("container"); + if (!measure_panel_ui->measurements->layout()) { + QVBoxLayout *measurementsLayout = new + QVBoxLayout(measure_panel_ui->measurements); + measurementsLayout->addWidget(container); + measurementsLayout->setContentsMargins(0, 0, 0, 0); + } else { + measure_panel_ui->measurements->layout()->addWidget(container); + } + + QGridLayout *gLayout = new QGridLayout(container); + + gLayout->setContentsMargins(0, 0, 0, 0); + gLayout->setVerticalSpacing(5); + gLayout->setHorizontalSpacing(5); + int max_rows = 4; + int nb_meas_added = 0; + + for (int i = 0; i < measurements_data.size(); i++) { + + int channel = measurements_data[i]->channel(); + if (channel >= m_adc_nb_channels + nb_ref_channels) { + continue; + } + + ChannelWidget *chn_widget = getChannelWidgetAt(channel); + if (!chn_widget->enableButton()->isChecked()) { + continue; + } + + QLabel *name = new QLabel(); + QLabel *value = new QLabel(); + + + int row = nb_meas_added % max_rows; + int col = nb_meas_added / max_rows; + + gLayout->addWidget(name, row, 2 * col); + + QHBoxLayout *value_layout = new QHBoxLayout(); + value_layout->setContentsMargins(0, 0, 10, 0); + value_layout->addWidget(value); + gLayout->addLayout(value_layout, row, 2 * col + 1); + + measurements_gui[i]->init(name, value); + double pb_atten = 1; + measurements_gui[i]->update(*(measurements_data[i]), + pb_atten); + measurements_gui[i]->setLabelsColor(fft_plot->getLineColor(channel)); + + nb_meas_added++; + } +} + +QList> SpectrumAnalyzer::measurements(int chnIdx) +{ + //POATE O SA POT PUNE DIRECT MEASURE OF CHANNALE, IN LOC DE FCT ASTA?? + Measure *measure = measureOfChannel(chnIdx); + + if (measure) + return measure->measurments(); + else + return QList>(); +} + +Measure* SpectrumAnalyzer::measureOfChannel(int chnIdx) const +{ + Measure *measure = nullptr; + + auto it = std::find_if(d_measureObjs.begin(), d_measureObjs.end(), + [&](Measure *m) { return m->channel() == chnIdx; }); + if (it != d_measureObjs.end()) + measure = *it; + + return measure; +} + +int SpectrumAnalyzer::activeMeasurementsCount(int chnIdx) +{ + int count = -1; + Measure *measure = measureOfChannel(chnIdx); + + if (measure) + count = measure->activeMeasurementsCount(); + + return count; +} + +bool SpectrumAnalyzer::measurementsEnabled() +{ + return d_measurementsEnabled; +} + +void SpectrumAnalyzer::setMeasuremensEnabled(bool en) +{ + d_measurementsEnabled = en; + + if (en) { + ui->btnMeasure->setEnabled(true); + } else { + if (ui->btnMeasure->isChecked()) + ui->btnMeasure->setChecked(false); + + ui->btnMeasure->setEnabled(false); + + menuOrder.removeOne(ui->btnMeasure); + } +} + +void SpectrumAnalyzer::computeMeasurementsForChannel(unsigned int chnIdx, unsigned int sampleRate) +{ + if (chnIdx >= d_measureObjs.size()) { + return; + } + + Measure *measure = d_measureObjs[chnIdx]; + measure->setSampleRate(sampleRate); + measure->measure(); + + Q_EMIT measurementsAvailable(); +} + +void SpectrumAnalyzer::measure() +{ + for (int i = 0; i < d_measureObjs.size(); i++) { + Measure *measure = d_measureObjs[i]; + if (measure->activeMeasurementsCount() > 0) { + measure->setSampleRate(fft_plot->sampleRate()); + measure->measure(); + } + } +} + +void SpectrumAnalyzer::onNewDataReceived() +{ + int ref_idx = 0; + for (int i = 0; i < d_measureObjs.size(); i++) { + Measure *measure = d_measureObjs[i]; + int chn = measure->channel(); + if (fft_plot->isReferenceWaveform(chn)) { + size_t curve_size = fft_plot->getCurveSize(chn); + measure->setDataSource(fft_plot->getRef_data()[ref_idx], + curve_size / 2); + ref_idx++; + } + else { + int64_t numPoints = fft_plot->getNumPoints() / 2; + std::vector data = fft_plot->getOrginal_data(); + std::vector scale_factor = fft_plot->getScaleFactor(); + + //int count = fft_plot->countReferenceWaveform(chn); + //chn = chn - count; + for (int s = 0; s < numPoints; s++) { + data[chn][s] = sqrt(data[chn][s]) * scale_factor[chn] / numPoints; + } + measure->setDataSource(data[chn], numPoints); + } + measure->setSampleRate(sample_rate); + measure->measure(); + } + + Q_EMIT measurementsAvailable(); +} + +void SpectrumAnalyzer::onMeasuremetsAvailable() +{ + measureUpdateValues(); +} + +void SpectrumAnalyzer::measureUpdateValues() +{ + + for (int i = 0; i < measurements_data.size(); i++) { + int channel = measurements_data[i]->channel(); + ChannelWidget *chn_widget = getChannelWidgetAt(channel); + if (!chn_widget->enableButton()->isChecked()) { + continue; + } + measurements_gui[i]->update(*(measurements_data[i]), + 1.0f); // de modificat scale-ul + } +} + +void SpectrumAnalyzer::on_boxMeasure_toggled(bool checked) { + if (checked) { + update_measure_for_channel(crt_channel_id); + } else { + if (ui->btnMeasure->isChecked()) + ui->btnMeasure->setChecked(false); + menuOrder.removeOne(ui->btnMeasure); + } + measurePanel->setVisible(checked); +} + void SpectrumAnalyzer::on_btnAddRef_toggled(bool checked) { triggerRightMenuToggle( @@ -1163,6 +1613,10 @@ void SpectrumAnalyzer::add_ref_waveform(QVector xData, QVector y if (nb_ref_channels == MAX_REF_CHANNELS) { ui->btnAddRef->hide(); } + + init_selected_measurements(curve_id, {0, 1, 4, 5}); + + computeMeasurementsForChannel(curve_id, sample_rate); } void SpectrumAnalyzer::add_ref_waveform(unsigned int chIdx) @@ -1178,6 +1632,20 @@ void SpectrumAnalyzer::add_ref_waveform(unsigned int chIdx) add_ref_waveform(xData, yData); } +void SpectrumAnalyzer::cleanUpMeasurementsBeforeChannelRemoval(int chnIdx) +{ + Measure *measure = measureOfChannel(chnIdx); + if (measure) { + int pos = d_measureObjs.indexOf(measure); + for (int i = pos + 1; i < d_measureObjs.size(); i++) { + d_measureObjs[i]->setChannel( + d_measureObjs[i]->channel() - 1); + } + d_measureObjs.removeOne(measure); + delete measure; + } +} + void SpectrumAnalyzer::onReferenceChannelDeleted() { if (nb_ref_channels - 1 < MAX_REF_CHANNELS) { @@ -1187,6 +1655,27 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() ChannelWidget *channelWidget = static_cast(QObject::sender()); QAbstractButton *delBtn = channelWidget->deleteButton(); QString qname = delBtn->property("curve_name").toString(); + int curve_id = channelWidget->id(); + + /*If there are no more channels enabled, we should + disable the measurements.*/ + bool shouldDisable = true; + + for (unsigned int i = 0; i < m_adc_nb_channels + nb_ref_channels; i++) { + ChannelWidget *cw = static_cast( + ui->channelsList->itemAt(i)->widget()); + if (curve_id == cw->id()) + continue; + if (cw->enableButton()->isChecked()) + shouldDisable = false; + } + + if (shouldDisable) + measure_settings->disableDisplayAll(); + + measure_settings->onChannelRemoved(channelWidget->id()); + + cleanUpMeasurementsBeforeChannelRemoval(channelWidget->id()); fft_plot->unregisterReferenceWaveform(qname); ui->channelsList->removeWidget(channelWidget); @@ -1210,6 +1699,7 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() if (channelWidget->id() < crt_channel_id) { crt_channel_id--; Q_EMIT selectedChannelChanged(crt_channel_id); + update_measure_for_channel(crt_channel_id); } else if (channelWidget->id() == crt_channel_id) { for (int i = 0; i < m_adc_nb_channels + nb_ref_channels; ++i) { auto cw = getChannelWidgetAt(i); @@ -1219,10 +1709,17 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() if (cw->enableButton()->isChecked()) { channelsEnabled = true; Q_EMIT selectedChannelChanged(0); + update_measure_for_channel(0); cw->nameButton()->setChecked(true); + Q_EMIT selectedChannelChanged(cw->id()); + update_measure_for_channel(cw->id()); break; } } + if (!channelsEnabled) { + crt_channel_id = 0; + Q_EMIT selectedChannelChanged(0); + } } if (ui->btnMarkers->isChecked() && !channelsEnabled) { @@ -1242,6 +1739,7 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() menuOrder.removeAll(static_cast(channelWidget->menuButton())); } + onMeasurementSelectionListChanged(); delete channelWidget; } @@ -1659,6 +2157,10 @@ void SpectrumAnalyzer::onChannelSelected(bool en) triggerRightMenuToggle( static_cast(ui->btnMarkers), en); } + + if (measurementsEnabled()) { + update_measure_for_channel(chIdx); + } } void SpectrumAnalyzer::updateMarkerMenu(unsigned int id) @@ -1708,17 +2210,37 @@ void SpectrumAnalyzer::onChannelEnabled(bool en) ui->btnMarkers->setEnabled(true); ui->btnMarkers->blockSignals(false); } + + bool shouldActivate = true; + if (cw->enableButton()->isChecked()) { + shouldActivate = false; + } + + if (shouldActivate) { + //nu trebe asta + Q_EMIT selectedChannelChanged(cw->id()); + update_measure_for_channel(cw->id()); + measure_settings->activateDisplayAll(); + } + } else { bool allDisabled = true; + bool shouldDisable = true; for (int i = 0; i < channels.size() + nb_ref_channels; i++) { ChannelWidget *cw = getChannelWidgetAt(i); if (cw->enableButton()->isChecked()) { cw->nameButton()->setChecked(true); allDisabled = false; + shouldDisable = false; break; } } + + if (shouldDisable) { + measure_settings->disableDisplayAll(); + } + if (allDisabled) { QSignalBlocker(ui->btnMarkers); if (!ui->btnSweep->isChecked()) { @@ -1740,6 +2262,7 @@ void SpectrumAnalyzer::onChannelEnabled(bool en) } } + measureLabelsRearrange(); fft_plot->replot(); updateRunButton(en); diff --git a/src/spectrum_analyzer.hpp b/src/spectrum_analyzer.hpp index 9b71ad2229..f75dc52dae 100644 --- a/src/spectrum_analyzer.hpp +++ b/src/spectrum_analyzer.hpp @@ -35,6 +35,7 @@ #include "gui/spinbox_a.hpp" #include "gui/customPushButton.hpp" #include "gui/startstoprangewidget.h" +#include "gui/measure.h" #include #include @@ -56,6 +57,7 @@ namespace Ui { class SpectrumAnalyzer; class CursorReadouts; class CursorsSettings; +class MeasurementsPanel; } namespace adiscope { @@ -63,6 +65,8 @@ class SpectrumChannel; class Filter; class ChannelWidget; class DbClickButtons; +class MeasurementData; +class MeasurementGui; } class QPushButton; @@ -73,6 +77,7 @@ namespace adiscope { class SpectrumAnalyzer_API; class SpectrumChannel_API; class SpectrumMarker_API; +class MeasureSettings; class SpectrumAnalyzer: public Tool { @@ -114,6 +119,7 @@ public Q_SLOTS: void started(bool); void showTool(); void selectedChannelChanged(int); + void measurementsAvailable(); private Q_SLOTS: void on_btnHistory_toggled(bool checked); @@ -123,6 +129,18 @@ private Q_SLOTS: void on_btnSweep_toggled(bool checked); void on_btnMarkers_toggled(bool checked); + void on_btnMeasure_toggled(bool); + void on_boxMeasure_toggled(bool); + + void onMeasuremetsAvailable(); + void onMeasurementActivated(int id, int chnIdx); + void onMeasurementDeactivated(int id, int chnIdx); + void onMeasurementSelectionListChanged(); + + void setMeasuremensEnabled(bool en); + void onChannelAdded(int); + void onNewDataReceived(); + void on_boxCursors_toggled(bool on); void on_btnCursors_toggled(bool); void onCursorReadoutsChanged(struct cursorReadoutsText); @@ -208,6 +226,13 @@ private Q_SLOTS: libm2k::analog::GenericAnalogIn* m_generic_analogin; Ui::SpectrumAnalyzer *ui; + QWidget *measurePanel; + Ui::MeasurementsPanel *measure_panel_ui; + adiscope::MeasureSettings *measure_settings; + QList> measurements_data; + QList> measurements_gui; + QList d_measureObjs; + bool d_measurementsEnabled; Ui::CursorReadouts *cursor_readouts_ui; QWidget *cursorReadouts; @@ -277,6 +302,29 @@ private Q_SLOTS: void fillCursorReadouts(const struct cursorReadoutsText &); bool canSwitchAverageHistory(FftDisplayPlot::AverageType avg_type); + + //din capture plot + QList> measurements(int chnIdx); + std::shared_ptr measurement(int id, int chnIdx); + void measure(); + int activeMeasurementsCount(int chnIdx); + Measure* measureOfChannel(int chnIdx) const; + bool measurementsEnabled(); + void computeMeasurementsForChannel(unsigned int chnIdx, unsigned int sampleRate); + + void cleanUpMeasurementsBeforeChannelRemoval(int chnIdx); + + //functii normale + void settings_panel_update(int id); + void settings_panel_size_adjust(); + void update_measure_for_channel(int ch_idx); + + void measure_panel_init(); + void measure_settings_init(); + void init_selected_measurements(int, std::vector); + void measureUpdateValues(); + void measureLabelsRearrange(); + void measureCreateAndAppendGuiFrom(const MeasurementData&); }; class SpectrumChannel: public QObject diff --git a/ui/measure_settings.ui b/ui/measure_settings.ui index 8ead1d5fca..5c2f2255cc 100644 --- a/ui/measure_settings.ui +++ b/ui/measure_settings.ui @@ -6,10 +6,16 @@ 0 0 - 284 - 588 + 281 + 727 + + + 0 + 0 + + Form @@ -21,7 +27,7 @@ 18 - 0 + 10 8 @@ -126,6 +132,12 @@ + + + 0 + 0 + + QFrame::NoFrame @@ -140,10 +152,16 @@ 0 0 - 252 - 563 + 311 + 674 + + + 0 + 0 + + 0 @@ -627,14 +645,88 @@ - + + + 25 + + + 15 + + + 6 + + + 16 + + + + + QFrame::Sunken + + + MEASUREMENT PARAMETERS + + + true + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + true + + + + + + + Harmonitc Number + + + + + + + + 0 + 0 + + + + true + + + + + + + Qt::Vertical + + QSizePolicy::Expanding + - 0 - 197 + 10 + 10 @@ -643,22 +735,6 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 0 - 10 - - - - diff --git a/ui/spectrum_analyzer.ui b/ui/spectrum_analyzer.ui index c26388dd95..04dd3bdf2b 100644 --- a/ui/spectrum_analyzer.ui +++ b/ui/spectrum_analyzer.ui @@ -2257,6 +2257,13 @@ border-width: 0px; + + + + Measure + + + @@ -2283,6 +2290,32 @@ QPushButton:hover:!pressed:!checked { border-image: url(:/icons/setup_btn_hover. QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + + + + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 10 + + + + @@ -2386,6 +2419,11 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + adiscope::CustomSwitch + QPushButton +
gui/customSwitch.hpp
+
adiscope::CustomPushButton QPushButton @@ -2415,11 +2453,6 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); }gui/runsinglewidget.h 1 - - adiscope::CustomSwitch - QPushButton -
gui/customSwitch.hpp
-
adiscope::LinkedButton QPushButton @@ -2441,6 +2474,9 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + +
From 44761418cd8c0f599d59c41c8fe025d58716af9f Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 15 Nov 2021 17:56:59 +0200 Subject: [PATCH 053/125] oscilloscope: fix gui header spacing Signed-off-by: ioanachelaru --- ui/oscilloscope.ui | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/ui/oscilloscope.ui b/ui/oscilloscope.ui index 492d5e5c7b..c60b7c6b64 100644 --- a/ui/oscilloscope.ui +++ b/ui/oscilloscope.ui @@ -64,6 +64,9 @@ 40 + + 40 + 15 @@ -73,22 +76,6 @@ 9 - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 10 - - - - @@ -163,7 +150,7 @@ Signal View - 40 + 10 20 @@ -176,7 +163,7 @@ Signal View - 30 + 95 0 @@ -737,6 +724,9 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } + + +
From 050252d85b9c6991a3baa818c37d0cbb61a32815 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 15 Nov 2021 18:13:04 +0200 Subject: [PATCH 054/125] android: fix info page button sizes Signed-off-by: ioanachelaru --- src/gui/info_page.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/info_page.cpp b/src/gui/info_page.cpp index 16b09912f5..aa922de66f 100644 --- a/src/gui/info_page.cpp +++ b/src/gui/info_page.cpp @@ -70,6 +70,14 @@ InfoPage::InfoPage(QString uri, Preferences *pref, PhoneHome* phoneHome, setStatusLabel(ui->lblCalibrationStatus); setStatusLabel(ui->lblConnectionStatus); setStatusLabel(ui->lblIdentifyStatus); + +#ifdef __ANDROID__ + ui->btnCalibrate->setMinimumWidth(150); + ui->btnConnect->setMinimumWidth(150); + ui->btnForget->setMinimumWidth(150); + ui->btnIdentify->setMinimumWidth(150); + ui->btnRegister->setMinimumWidth(150); +#endif } void InfoPage::readPreferences() { From 2a6c41ef7916627a3fd6742db5061d49cef5d0f6 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 12 Nov 2021 16:55:25 +0200 Subject: [PATCH 055/125] android: fix indicator sizes Signed-off-by: ioanachelaru --- resources/stylesheets/templates/default.qss.c | 16 ++++++++++------ resources/stylesheets/templates/light.qss.c | 16 ++++++++++------ resources/stylesheets/templates/values.h | 7 +++++++ 3 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 resources/stylesheets/templates/values.h diff --git a/resources/stylesheets/templates/default.qss.c b/resources/stylesheets/templates/default.qss.c index 8eec694f11..215d430853 100644 --- a/resources/stylesheets/templates/default.qss.c +++ b/resources/stylesheets/templates/default.qss.c @@ -1,3 +1,5 @@ +#include "values.h" + /* Default background color */ QMainWindow > .QWidget, adiscope--Sismograph > QwtPlotCanvas, QMessageBox, QToolTip { background-color: #272730; @@ -108,8 +110,8 @@ QRadioButton { } QRadioButton::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid; border-radius: 9px; border-color: #4963FF; @@ -399,8 +401,8 @@ QCheckBox { } QCheckBox::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid rgb(74,100,255); border-radius: 4px; } @@ -650,12 +652,14 @@ adiscope--ChannelWidget QCheckBox#box{ font-size: 14px; font-weight: bold; } + adiscope--ChannelWidget QCheckBox#box::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ border-radius: 9px; } + adiscope--ChannelWidget QCheckBox#box::indicator:unchecked { background-color: transparent; } diff --git a/resources/stylesheets/templates/light.qss.c b/resources/stylesheets/templates/light.qss.c index 3265c9bcb4..d1f436dc53 100644 --- a/resources/stylesheets/templates/light.qss.c +++ b/resources/stylesheets/templates/light.qss.c @@ -1,3 +1,5 @@ +#include "values.h" + /* Default background color */ QMainWindow > .QWidget, adiscope--Sismograph > QwtPlotCanvas, QMessageBox, QToolTip { background-color: #F7F7F7; @@ -109,8 +111,8 @@ QRadioButton { } QRadioButton::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid; border-radius: 9px; border-color: #4963FF; @@ -399,8 +401,8 @@ QCheckBox { } QCheckBox::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid rgb(74,100,255); border-radius: 4px; } @@ -649,12 +651,14 @@ adiscope--ChannelWidget QCheckBox#box{ font-size: 14px; font-weight: bold; } + adiscope--ChannelWidget QCheckBox#box::indicator { - width: 14px; - height: 14px; + width: INDICATOR_SIZE; + height: INDICATOR_SIZE; border: 2px solid #000000; /* Will be overwritted in the ChannelWidget constructor */ border-radius: 9px; } + adiscope--ChannelWidget QCheckBox#box::indicator:unchecked { background-color: transparent; } diff --git a/resources/stylesheets/templates/values.h b/resources/stylesheets/templates/values.h new file mode 100644 index 0000000000..5f2b3486b7 --- /dev/null +++ b/resources/stylesheets/templates/values.h @@ -0,0 +1,7 @@ +#ifdef __ANDROID__ + #define INDICATOR_SIZE 18px +#endif + +#ifndef __ANDROID__ + #define INDICATOR_SIZE 14px +#endif From 0f7365ea3877cbb2bc13af61ae29e030b66956da Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 17 Nov 2021 15:32:32 +0200 Subject: [PATCH 056/125] android: increase size of add buttons Signed-off-by: ioanachelaru --- src/oscilloscope.cpp | 4 ++++ src/spectrum_analyzer.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index d6f193a72e..2301f35431 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -929,6 +929,10 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, ui->btnHelp->setUrl("https://wiki.analog.com/university/tools/m2k/scopy/oscilloscope"); +#ifdef __ANDROID__ + ui->btnAddMath->setIconSize(QSize(24, 24)); +#endif + } int Oscilloscope::binSearchPointOnXaxis(double time) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index c483885998..ea231f3b5e 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -700,6 +700,11 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->boxMeasure->setChecked(false); ui->boxMeasure->setVisible(false); ui->btnMeasure->setVisible(false); + +#ifdef __ANDROID__ + ui->btnAddRef->setIconSize(QSize(24, 24)); +#endif + } SpectrumAnalyzer::~SpectrumAnalyzer() From 8cea04f3277265cd6397a2b05be6c05bc02359cd Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 19 Nov 2021 17:21:13 +0200 Subject: [PATCH 057/125] tool_launcher: fix ping function with adequate iio call Signed-off-by: ioanachelaru --- src/gui/info_page.cpp | 15 ++++++++++----- src/tool_launcher.cpp | 8 ++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/gui/info_page.cpp b/src/gui/info_page.cpp index aa922de66f..bc34ef75b7 100644 --- a/src/gui/info_page.cpp +++ b/src/gui/info_page.cpp @@ -23,6 +23,7 @@ #include "preferences.h" #include #include +#include #include "libm2k/analog/dmm.hpp" #include "gui/dynamicWidget.hpp" #include @@ -494,7 +495,6 @@ void M2kInfoPage::getDeviceInfo() void M2kInfoPage::refreshTemperature() { - libm2k::context::M2k *temp_m2k = nullptr; if(!m_ctx) { temp_m2k = libm2k::context::m2kOpen(m_uri.toLocal8Bit().constData()); @@ -503,10 +503,15 @@ void M2kInfoPage::refreshTemperature() } if(temp_m2k) { - auto dmm = temp_m2k->getDMM("ad9963"); - auto ch = dmm->readChannel("temp0"); - auto val = ch.value; - m_info_params["Temperature"] = QString::number(val); + try { + auto dmm = temp_m2k->getDMM("ad9963"); + auto ch = dmm->readChannel("temp0"); + auto val = ch.value; + m_info_params["Temperature"] = QString::number(val); + + } catch (libm2k::m2k_exception &e) { + qDebug() << e.what(); + } } if(!m_ctx) { diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 83afdeefc7..d769f3f857 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1185,10 +1185,14 @@ void adiscope::ToolLauncher::disconnect() void adiscope::ToolLauncher::ping() { - int ret = iio_context_get_version(ctx, nullptr, nullptr, nullptr); + auto dev = iio_context_get_device(ctx, 0); + const iio_device* test_device = nullptr; - if (ret < 0) + int ret = iio_device_get_trigger(dev, &test_device); + + if (ret < 0 && ret != -ENOENT) { disconnect(); + } } void adiscope::ToolLauncher::connectBtn_clicked(bool pressed) From 7a29ceab953d1524bd67f1183619bfc8b26519f5 Mon Sep 17 00:00:00 2001 From: Alexandra Trifan Date: Tue, 23 Nov 2021 11:54:24 +0200 Subject: [PATCH 058/125] src/tool_launcher.cpp: Provide the full path to the decoders location. Signed-off-by: Alexandra Trifan --- src/tool_launcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index d769f3f857..3e96baa444 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1697,7 +1697,7 @@ bool adiscope::ToolLauncher::switchContext(const QString& uri) info.exec(); } else { - bool success = loadDecoders("decoders"); + bool success = loadDecoders(QCoreApplication::applicationDirPath() + "/decoders"); if (!success) { search_timer->stop(); From 75022b2849589e7d26da885142339fba147bfca9 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Wed, 17 Nov 2021 16:29:58 +0200 Subject: [PATCH 059/125] ci: stopped building for macos-mojave start building for macos-bigsur Signed-off-by: Adrian Suciu --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 36ed02bfdd..c92cba5406 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ environment: matrix: - ARCH: x86_64 ARCH_BIT: 64 - APPVEYOR_BUILD_WORKER_IMAGE: macos-mojave + APPVEYOR_BUILD_WORKER_IMAGE: macos-bigsur BUILD_DEPS_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/install_macos_deps.sh" BUILD_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/build_appveyor_macos.sh" PACKAGE_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/package_darwin.sh" From af66fbf2d9fea226b6ffffc2fa761c127f1e9661 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 24 Nov 2021 14:27:41 +0200 Subject: [PATCH 060/125] spectral measurements: temporary fix crash Signed-off-by: ioanachelaru --- src/gui/measure.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/measure.cpp b/src/gui/measure.cpp index 4a7d064a1b..2fa5e6316d 100644 --- a/src/gui/measure.cpp +++ b/src/gui/measure.cpp @@ -1200,7 +1200,9 @@ void Measure::measureTimeDomain() } void Measure::measureSpectral() { - double spur, harm_dist, noise, average_noise, signal; + + //TODO - reconsider computation of measurements + double spur, harm_dist, noise, average_noise = 0, signal; SpectralDetection detection(m_buffer, m_buf_length, m_harmonics_number); From 2ec47c1c1b8b910768707f40e6ac7a8ab4710369 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Thu, 6 Jan 2022 15:52:03 +0200 Subject: [PATCH 061/125] spectrum analyzer: disabled measurements Signed-off-by: ioanachelaru --- src/spectrum_analyzer.cpp | 57 +++++++++++++++++++++++++++++++++++++++ src/spectrum_analyzer.hpp | 21 +++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index ea231f3b5e..623451f3f6 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -50,9 +50,12 @@ #include "filemanager.h" #include "spectrum_analyzer_api.hpp" #include "stream_to_vector_overlap.h" + +#ifdef SPECTRAL_MSR #include "gui/measure.h" #include "measurement_gui.h" #include "gui/measure_settings.h" +#endif /* Generated UI */ #include "ui_spectrum_analyzer.h" @@ -216,11 +219,16 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, settings_group->addButton(ui->btnMarkers); settings_group->addButton(ui->btnAddRef); settings_group->addButton(ui->btnCursors); + +#ifdef SPECTRAL_MSR settings_group->addButton(ui->btnMeasure); +#endif settings_group->setExclusive(true); +#ifdef SPECTRAL_MSR /* Measure panel */ measure_panel_init(); +#endif fft_plot = new FftDisplayPlot(m_adc_nb_channels, this); fft_plot->disableLegend(); @@ -236,8 +244,10 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, connect(fft_plot, SIGNAL(channelAdded(int)), SLOT(onChannelAdded(int))); +#ifdef SPECTRAL_MSR /* Measurements Settings */ measure_settings_init(); +#endif // Add dockable plot @@ -247,7 +257,9 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, vLayout->setSpacing(6); centralWidget->setLayout(vLayout); +#ifdef SPECTRAL_MSR vLayout->addWidget(measurePanel); +#endif ui->widgetPlotContainer->layout()->removeWidget(ui->topPlotWidget); vLayout->addWidget(ui->topPlotWidget); @@ -495,12 +507,14 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, const bool visible = (channels[crt_channel_id]->averageType() != FftDisplayPlot::AverageType::SAMPLE); setCurrentAverageIndexLabel(crt_channel_id); +#ifdef SPECTRAL_MSR /* Apply measurements for every new batch of data */ connect(fft_plot, SIGNAL(newData()), SLOT(onNewDataReceived())); for (int i = 0; i < m_adc_nb_channels; i++) { fft_plot->initChannelMeasurement(i); } +#endif connect(top, SIGNAL(valueChanged(double)), SLOT(onTopValueChanged(double))); @@ -544,6 +558,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ch->setFftWindow(FftWinType::HAMMING, fft_size); } +#ifdef SPECTRAL_MSR if (!runButton()->isChecked() && measurementsEnabled()) { measureUpdateValues(); } @@ -551,6 +566,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, for (unsigned int i = 0; i < m_adc_nb_channels; i++) { init_selected_measurements(i, {0, 1, 4, 5}); } +#endif connect(ui->logBtn, &QPushButton::toggled, fft_plot, &FftDisplayPlot::useLogFreq); @@ -696,10 +712,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->btnHelp->setUrl("https://wiki.analog.com/university/tools/m2k/scopy/spectrumanalyzer"); +#ifndef SPECTRAL_MSR // TODO: enable measurements ui->boxMeasure->setChecked(false); ui->boxMeasure->setVisible(false); ui->btnMeasure->setVisible(false); +#endif #ifdef __ANDROID__ ui->btnAddRef->setIconSize(QSize(24, 24)); @@ -727,9 +745,11 @@ SpectrumAnalyzer::~SpectrumAnalyzer() delete *it; } +#ifdef SPECTRAL_MSR for (auto it = d_measureObjs.begin(); it != d_measureObjs.end(); ++it) { delete *it; } +#endif if (iio) { bool started = isIioManagerStarted(); @@ -892,11 +912,18 @@ void SpectrumAnalyzer::toggleRightMenu(CustomPushButton *btn, bool checked) index = 3; } else if (btn == ui->btnAddRef) { index = 4; + +#ifdef SPECTRAL_MSR } else if (btn == ui->btnMeasure) { index = 5; } else if (btn == ui->btnCursors) { index = 6; } +#else + } else if (btn == ui->btnCursors) { + index = 5; + } +#endif } if (id != -1) { @@ -991,6 +1018,7 @@ void SpectrumAnalyzer::on_btnMarkers_toggled(bool checked) static_cast(QObject::sender()), checked); } +#ifdef SPECTRAL_MSR void SpectrumAnalyzer::on_btnMeasure_toggled(bool checked) { triggerRightMenuToggle(static_cast(QObject::sender()), checked); @@ -1394,6 +1422,7 @@ void SpectrumAnalyzer::on_boxMeasure_toggled(bool checked) { } measurePanel->setVisible(checked); } +#endif void SpectrumAnalyzer::on_btnAddRef_toggled(bool checked) { @@ -1619,9 +1648,11 @@ void SpectrumAnalyzer::add_ref_waveform(QVector xData, QVector y ui->btnAddRef->hide(); } +#ifdef SPECTRAL_MSR init_selected_measurements(curve_id, {0, 1, 4, 5}); computeMeasurementsForChannel(curve_id, sample_rate); +#endif } void SpectrumAnalyzer::add_ref_waveform(unsigned int chIdx) @@ -1637,6 +1668,7 @@ void SpectrumAnalyzer::add_ref_waveform(unsigned int chIdx) add_ref_waveform(xData, yData); } +#ifdef SPECTRAL_MSR void SpectrumAnalyzer::cleanUpMeasurementsBeforeChannelRemoval(int chnIdx) { Measure *measure = measureOfChannel(chnIdx); @@ -1650,6 +1682,7 @@ void SpectrumAnalyzer::cleanUpMeasurementsBeforeChannelRemoval(int chnIdx) delete measure; } } +#endif void SpectrumAnalyzer::onReferenceChannelDeleted() { @@ -1675,12 +1708,14 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() shouldDisable = false; } +#ifdef SPECTRAL_MSR if (shouldDisable) measure_settings->disableDisplayAll(); measure_settings->onChannelRemoved(channelWidget->id()); cleanUpMeasurementsBeforeChannelRemoval(channelWidget->id()); +#endif fft_plot->unregisterReferenceWaveform(qname); ui->channelsList->removeWidget(channelWidget); @@ -1704,7 +1739,11 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() if (channelWidget->id() < crt_channel_id) { crt_channel_id--; Q_EMIT selectedChannelChanged(crt_channel_id); + +#ifdef SPECTRAL_MSR update_measure_for_channel(crt_channel_id); +#endif + } else if (channelWidget->id() == crt_channel_id) { for (int i = 0; i < m_adc_nb_channels + nb_ref_channels; ++i) { auto cw = getChannelWidgetAt(i); @@ -1714,10 +1753,17 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() if (cw->enableButton()->isChecked()) { channelsEnabled = true; Q_EMIT selectedChannelChanged(0); + +#ifdef SPECTRAL_MSR update_measure_for_channel(0); +#endif + cw->nameButton()->setChecked(true); Q_EMIT selectedChannelChanged(cw->id()); +#ifdef SPECTRAL_MSR update_measure_for_channel(cw->id()); +#endif + break; } } @@ -1744,7 +1790,9 @@ void SpectrumAnalyzer::onReferenceChannelDeleted() menuOrder.removeAll(static_cast(channelWidget->menuButton())); } +#ifdef SPECTRAL_MSR onMeasurementSelectionListChanged(); +#endif delete channelWidget; } @@ -2163,9 +2211,11 @@ void SpectrumAnalyzer::onChannelSelected(bool en) static_cast(ui->btnMarkers), en); } +#ifdef SPECTRAL_MSR if (measurementsEnabled()) { update_measure_for_channel(chIdx); } +#endif } void SpectrumAnalyzer::updateMarkerMenu(unsigned int id) @@ -2224,8 +2274,11 @@ void SpectrumAnalyzer::onChannelEnabled(bool en) if (shouldActivate) { //nu trebe asta Q_EMIT selectedChannelChanged(cw->id()); + +#ifdef SPECTRAL_MSR update_measure_for_channel(cw->id()); measure_settings->activateDisplayAll(); +#endif } } else { @@ -2242,9 +2295,11 @@ void SpectrumAnalyzer::onChannelEnabled(bool en) } } +#ifdef SPECTRAL_MSR if (shouldDisable) { measure_settings->disableDisplayAll(); } +#endif if (allDisabled) { QSignalBlocker(ui->btnMarkers); @@ -2267,7 +2322,9 @@ void SpectrumAnalyzer::onChannelEnabled(bool en) } } +#ifdef SPECTRAL_MSR measureLabelsRearrange(); +#endif fft_plot->replot(); updateRunButton(en); diff --git a/src/spectrum_analyzer.hpp b/src/spectrum_analyzer.hpp index f75dc52dae..5d5a08bdc1 100644 --- a/src/spectrum_analyzer.hpp +++ b/src/spectrum_analyzer.hpp @@ -35,7 +35,10 @@ #include "gui/spinbox_a.hpp" #include "gui/customPushButton.hpp" #include "gui/startstoprangewidget.h" + +#ifdef SPECTRAL_MSR #include "gui/measure.h" +#endif #include #include @@ -57,7 +60,10 @@ namespace Ui { class SpectrumAnalyzer; class CursorReadouts; class CursorsSettings; + +#ifdef SPECTRAL_MSR class MeasurementsPanel; +#endif } namespace adiscope { @@ -65,8 +71,11 @@ class SpectrumChannel; class Filter; class ChannelWidget; class DbClickButtons; + +#ifdef SPECTRAL_MSR class MeasurementData; class MeasurementGui; +#endif } class QPushButton; @@ -77,7 +86,10 @@ namespace adiscope { class SpectrumAnalyzer_API; class SpectrumChannel_API; class SpectrumMarker_API; + +#ifdef SPECTRAL_MSR class MeasureSettings; +#endif class SpectrumAnalyzer: public Tool { @@ -119,7 +131,10 @@ public Q_SLOTS: void started(bool); void showTool(); void selectedChannelChanged(int); + +#ifdef SPECTRAL_MSR void measurementsAvailable(); +#endif private Q_SLOTS: void on_btnHistory_toggled(bool checked); @@ -129,6 +144,7 @@ private Q_SLOTS: void on_btnSweep_toggled(bool checked); void on_btnMarkers_toggled(bool checked); +#ifdef SPECTRAL_MSR void on_btnMeasure_toggled(bool); void on_boxMeasure_toggled(bool); @@ -140,6 +156,7 @@ private Q_SLOTS: void setMeasuremensEnabled(bool en); void onChannelAdded(int); void onNewDataReceived(); +#endif void on_boxCursors_toggled(bool on); void on_btnCursors_toggled(bool); @@ -226,6 +243,7 @@ private Q_SLOTS: libm2k::analog::GenericAnalogIn* m_generic_analogin; Ui::SpectrumAnalyzer *ui; +#ifdef SPECTRAL_MSR QWidget *measurePanel; Ui::MeasurementsPanel *measure_panel_ui; adiscope::MeasureSettings *measure_settings; @@ -233,6 +251,7 @@ private Q_SLOTS: QList> measurements_gui; QList d_measureObjs; bool d_measurementsEnabled; +#endif Ui::CursorReadouts *cursor_readouts_ui; QWidget *cursorReadouts; @@ -303,6 +322,7 @@ private Q_SLOTS: bool canSwitchAverageHistory(FftDisplayPlot::AverageType avg_type); +#ifdef SPECTRAL_MSR //din capture plot QList> measurements(int chnIdx); std::shared_ptr measurement(int id, int chnIdx); @@ -325,6 +345,7 @@ private Q_SLOTS: void measureUpdateValues(); void measureLabelsRearrange(); void measureCreateAndAppendGuiFrom(const MeasurementData&); +#endif }; class SpectrumChannel: public QObject From 50a80efbb0e5d78f6423c2386f96937e34dc3a6e Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 8 Nov 2021 12:41:05 +0200 Subject: [PATCH 062/125] tool_launcher: save session on android pause callback Signed-off-by: Adrian Suciu --- android/src/org/adi/scopy/ScopyActivity.java | 32 ++++++++++----- src/main.cpp | 6 +++ src/tool_launcher.cpp | 32 ++++++++++++++- src/tool_launcher.hpp | 7 ++++ src/tool_launcher_api.cpp | 41 ++++++++++++-------- src/tool_launcher_api.hpp | 2 + 6 files changed, 93 insertions(+), 27 deletions(-) diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java index b3e0aeccaa..fd658736a2 100644 --- a/android/src/org/adi/scopy/ScopyActivity.java +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -31,10 +31,12 @@ public class ScopyActivity extends QtActivity { + public static native void saveSessionJavaHelper(); + @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); //saveSessionJavaHelper(); } @Override @@ -46,17 +48,27 @@ protected void onStart() @Override protected void onStop() { - super.onStop(); + super.onStop(); } + protected void onPause(){ + saveSessionJavaHelper(); + super.onPause(); + } + + protected void onDestroy(){ + super.onPause(); + } + public void restart() { - System.out.println("-- ScopyActivity: Restarting "); - Context context = getApplicationContext(); - PackageManager packageManager = context.getPackageManager(); - Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName()); - ComponentName componentName = intent.getComponent(); - Intent mainIntent = Intent.makeRestartActivityTask(componentName); - context.startActivity(mainIntent); - Runtime.getRuntime().exit(0); + saveSessionJavaHelper(); + System.out.println("-- ScopyActivity: Restarting "); + Context context = getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName()); + ComponentName componentName = intent.getComponent(); + Intent mainIntent = Intent.makeRestartActivityTask(componentName); + context.startActivity(mainIntent); + Runtime.getRuntime().exit(0); } } diff --git a/src/main.cpp b/src/main.cpp index b7e01f9fa2..0f9d713b2b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,11 @@ using namespace adiscope; +ToolLauncher* tl_ptr; +ToolLauncher* adiscope::getToolLauncherInstance() { + return tl_ptr; +} + int main(int argc, char **argv) { #ifdef __ANDROID__ @@ -166,6 +171,7 @@ int main(int argc, char **argv) } ToolLauncher launcher(prevCrashDump); + tl_ptr = &launcher; launcher.getPrefPanel()->setColorEditor(colorEditor); bool nogui = parser.isSet("nogui"); diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 3e96baa444..b6d3c70f84 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -122,11 +122,16 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : ui->setupUi(this); -#ifdef __ANDROID__ + +#ifdef __ANDROID__ // LIBUSB WEAK_AUTHORITY libusb_set_option(NULL,LIBUSB_OPTION_ANDROID_JAVAVM,jnienv->javaVM()); libusb_set_option(NULL,LIBUSB_OPTION_WEAK_AUTHORITY,NULL); #endif +#ifdef __ANDROID__ // JNI hooks + registerNativeMethods(); +#endif + setWindowIcon(QIcon(":/icon.ico")); QApplication::setWindowIcon(QIcon(":/icon.ico")); @@ -487,6 +492,8 @@ void ToolLauncher::saveSession() QString fileName = QFileDialog::getSaveFileName(this, tr("Save session"), "", tr("Scopy-Files (*.ini)"), nullptr, (m_useNativeDialogs ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog)); + QFileInfo fi(fileName); + qDebug()<tl_api->save(fileName); } @@ -1992,3 +1999,26 @@ bool ToolLauncher::eventFilter(QObject *watched, QEvent *event) return QObject::eventFilter(watched, event); } + +#ifdef __ANDROID__ + +void ToolLauncher::saveSessionJavaHelper(JNIEnv *env, jobject /*thiz*/) { + qDebug()<<"-- Saving session"; + getToolLauncherInstance()->tl_api->sync(); + getToolLauncherInstance()->saveSettings(); +} + +void ToolLauncher::registerNativeMethods() +{ + JNINativeMethod methods[] = {{"saveSessionJavaHelper", "()V", reinterpret_cast(saveSessionJavaHelper) }}; + + QAndroidJniObject activity = QtAndroid::androidActivity(); + QAndroidJniEnvironment env; + jclass objectClass = env->GetObjectClass(activity.object()); + + env->RegisterNatives(objectClass, + methods, + sizeof(methods) / sizeof(methods[0])); + env->DeleteLocalRef(objectClass); +} +#endif diff --git a/src/tool_launcher.hpp b/src/tool_launcher.hpp index cb22fa8136..dc27a6fd42 100644 --- a/src/tool_launcher.hpp +++ b/src/tool_launcher.hpp @@ -77,6 +77,8 @@ class Debugger; class ManualCalibration; class UserNotes; +ToolLauncher* getToolLauncherInstance(); + class ToolLauncher : public QMainWindow { friend class ToolLauncher_API; @@ -194,6 +196,10 @@ private Q_SLOTS: DeviceWidget* getDevice(QString uri); void setupAddPage(); void allowExternalScript(bool); +#ifdef __ANDROID__ + void registerNativeMethods(); + static void saveSessionJavaHelper(JNIEnv *env, jobject /*thiz*/); +#endif private: Ui::ToolLauncher *ui; @@ -284,6 +290,7 @@ private Q_SLOTS: PhoneHome* m_phoneHome; SessionInfo m_sessionInfo; + }; } #endif // M2K_TOOL_LAUNCHER_H diff --git a/src/tool_launcher_api.cpp b/src/tool_launcher_api.cpp index af2647ae2d..3c65732cb0 100644 --- a/src/tool_launcher_api.cpp +++ b/src/tool_launcher_api.cpp @@ -244,35 +244,44 @@ bool ToolLauncher_API::reset() return did_reconnect; } -void ToolLauncher_API::save(const QString& file) -{ - QSettings settings(file, QSettings::IniFormat); +void ToolLauncher_API::sync() { + + QSettings settings; + save(&settings); +} - this->ApiObject::save(settings); - this->tl->m_sessionInfo.save(settings); +void ToolLauncher_API::save(QSettings *settings) { + this->ApiObject::save(*settings); + this->tl->m_sessionInfo.save(*settings); if (tl->notesPanel) - tl->notesPanel->api()->save(settings); + tl->notesPanel->api()->save(*settings); if (tl->oscilloscope) - tl->oscilloscope->getApi()->save(settings); + tl->oscilloscope->getApi()->save(*settings); if (tl->dmm) - tl->dmm->getApi()->save(settings); + tl->dmm->getApi()->save(*settings); if (tl->power_control) - tl->power_control->getApi()->save(settings); + tl->power_control->getApi()->save(*settings); if (tl->signal_generator) - tl->signal_generator->getApi()->save(settings); + tl->signal_generator->getApi()->save(*settings); if (tl->logic_analyzer) - tl->logic_analyzer->getApi()->save(settings); + tl->logic_analyzer->getApi()->save(*settings); if (tl->dio) - tl->dio->getApi()->save(settings); + tl->dio->getApi()->save(*settings); if (tl->pattern_generator) - tl->pattern_generator->getApi()->save(settings); + tl->pattern_generator->getApi()->save(*settings); if (tl->network_analyzer) - tl->network_analyzer->getApi()->save(settings); + tl->network_analyzer->getApi()->save(*settings); if (tl->spectrum_analyzer) - tl->spectrum_analyzer->getApi()->save(settings); + tl->spectrum_analyzer->getApi()->save(*settings); - ApiObjectManager::getInstance().save(settings); + ApiObjectManager::getInstance().save(*settings); +} + +void ToolLauncher_API::save(const QString& file) +{ + QSettings settings(file, QSettings::IniFormat); + save(&settings); } void ToolLauncher::addDebugWindow() diff --git a/src/tool_launcher_api.hpp b/src/tool_launcher_api.hpp index 16fc246c78..11eba1468b 100644 --- a/src/tool_launcher_api.hpp +++ b/src/tool_launcher_api.hpp @@ -105,6 +105,8 @@ class ToolLauncher_API: public ApiObject Q_INVOKABLE void load(const QString& file); Q_INVOKABLE void save(const QString& file); + Q_INVOKABLE void save(QSettings *settings); + Q_INVOKABLE void sync(); Q_INVOKABLE bool reset(); Q_INVOKABLE bool enableExtern(bool); Q_INVOKABLE bool enableCalibScript(bool); From cdef7aaedf7999d4a86ed2ddfd9591c8453e7a9e Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 14 Jan 2022 13:24:32 +0200 Subject: [PATCH 063/125] .gitignore: added autogenerated stylesheets Signed-off-by: Adrian Suciu --- .gitignore | 2 ++ resources/stylesheets/{default.qss => default-old.qss} | 0 resources/stylesheets/{light.qss => light-old.qss} | 0 3 files changed, 2 insertions(+) rename resources/stylesheets/{default.qss => default-old.qss} (100%) rename resources/stylesheets/{light.qss => light-old.qss} (100%) diff --git a/.gitignore b/.gitignore index 28b186e8c4..944a64cb8e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ deps scopy android/assets/libsigrokdecode/* android/assets/python3.*/* +resources/stylesheets/default.qss +resources/stylesheets/light.qss !android/src/org/adi/scopy *.swp html/ diff --git a/resources/stylesheets/default.qss b/resources/stylesheets/default-old.qss similarity index 100% rename from resources/stylesheets/default.qss rename to resources/stylesheets/default-old.qss diff --git a/resources/stylesheets/light.qss b/resources/stylesheets/light-old.qss similarity index 100% rename from resources/stylesheets/light.qss rename to resources/stylesheets/light-old.qss From cf5fb2dd05b00aa55f1f46ba583cb9d7357fe73a Mon Sep 17 00:00:00 2001 From: Alexandra Trifan Date: Fri, 3 Dec 2021 18:12:14 +0200 Subject: [PATCH 064/125] install_ubuntu_20.sh: Add missing packages for qwt and sigrokdecode. Signed-off-by: Alexandra Trifan --- CI/appveyor/install_qt_ubuntu_20.sh | 2 +- CI/appveyor/install_ubuntu_20_deps.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CI/appveyor/install_qt_ubuntu_20.sh b/CI/appveyor/install_qt_ubuntu_20.sh index c31e693a32..1a62f461d9 100755 --- a/CI/appveyor/install_qt_ubuntu_20.sh +++ b/CI/appveyor/install_qt_ubuntu_20.sh @@ -1,2 +1,2 @@ #!/bin/bash -sudo apt-get -y install vim git cmake qt5-default qtcreator qtdeclarative5-dev qtdeclarative5-dev-tools libqt5svg5 libqt5svg5-devqttools5-dev qttools5-dev-tools +sudo apt-get -y install vim git cmake qt5-default qtcreator qtdeclarative5-dev qtdeclarative5-dev-tools libqt5svg5 libqt5svg5-dev qttools5-dev qttools5-dev-tools libqt5opengl5 diff --git a/CI/appveyor/install_ubuntu_20_deps.sh b/CI/appveyor/install_ubuntu_20_deps.sh index f9b458cc93..7f64d8902b 100755 --- a/CI/appveyor/install_ubuntu_20_deps.sh +++ b/CI/appveyor/install_ubuntu_20_deps.sh @@ -30,7 +30,7 @@ WORKDIR=${PWD} install_apt() { - sudo apt-get -y install libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev g++ git cmake autoconf libzip5 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev libclang1-9 doxygen curl libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev gnuradio libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev + sudo apt-get -y install libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev g++ git cmake autoconf libzip5 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev libclang1-9 doxygen curl libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev gnuradio libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libtool } @@ -91,7 +91,7 @@ build_libm2k() { cmake ${CMAKE_OPTS} \ -DENABLE_PYTHON=OFF\ -DENABLE_CSHARP=OFF\ - -DENABLE_EXAMPLES=OFF\ + -DBUILD_EXAMPLES=OFF\ -DENABLE_TOOLS=OFF\ -DINSTALL_UDEV_RULES=OFF\ -DENABLE_LOG=ON\ From 8087d662fcf70a8815db0ac9c737629b017e3ada Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 23 Nov 2021 12:08:57 +0200 Subject: [PATCH 065/125] tool_launcher: create a getter for tool launcher global instance Signed-off-by: Adrian Suciu --- src/tool_launcher.cpp | 9 +++++++++ src/tool_launcher.hpp | 1 + 2 files changed, 10 insertions(+) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index b6d3c70f84..c5a4a4b75a 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -85,6 +85,11 @@ using namespace adiscope; using namespace libm2k::context; using namespace libm2k::digital; +ToolLauncher* adiscope::tl_ptr; +ToolLauncher* adiscope::getToolLauncherInstance() { + return tl_ptr; +} + ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : QMainWindow(parent), ui(new Ui::ToolLauncher), ctx(nullptr), @@ -350,6 +355,8 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : // f.open(QIODevice::ReadOnly); // qDebug()< Date: Tue, 23 Nov 2021 12:16:53 +0200 Subject: [PATCH 066/125] api: added returnToApplication method Previously scripts took full control of the application. The returnToApplication method gives back control to the user. Signed-off-by: Adrian Suciu --- src/qtjs.cpp | 15 +++++++++++++++ src/qtjs.hpp | 1 + 2 files changed, 16 insertions(+) diff --git a/src/qtjs.cpp b/src/qtjs.cpp index 4cd1adbacd..ecc8b77088 100644 --- a/src/qtjs.cpp +++ b/src/qtjs.cpp @@ -29,6 +29,8 @@ #include #include +#include + using std::cout; using namespace adiscope; @@ -54,6 +56,19 @@ void QtJs::exit() QApplication::closeAllWindows(); } +void QtJs::returnToApplication() +{ + bool done; + connect(getToolLauncherInstance(), &ToolLauncher::launcherClosed,[&done](){ + done=true; + }); + + while(!done) { + QCoreApplication::processEvents(); + QThread::msleep(1); + } +} + void QtJs::sleep(unsigned long s) { msleep(s * 1000); diff --git a/src/qtjs.hpp b/src/qtjs.hpp index 1e34cef6b7..c2737e5273 100644 --- a/src/qtjs.hpp +++ b/src/qtjs.hpp @@ -40,6 +40,7 @@ class QtJs : public QObject Q_INVOKABLE void msleep(unsigned long ms); Q_INVOKABLE void printToConsole(const QString& text); Q_INVOKABLE QString readFromConsole(const QString& text); + Q_INVOKABLE void returnToApplication(); private: QFutureWatcher watcher; From 50015ab111c8a272564ce9ae0ee4dfb9b9498534 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 23 Nov 2021 12:18:52 +0200 Subject: [PATCH 067/125] api: added focus instrument call Signed-off-by: Adrian Suciu --- src/tool_launcher.cpp | 4 ++++ src/tool_launcher.hpp | 2 ++ src/tool_launcher_api.cpp | 11 +++++++++++ src/tool_launcher_api.hpp | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index c5a4a4b75a..094c0ca1ef 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -1906,6 +1906,10 @@ void ToolLauncher::toolDetached(bool detached) tool->setMinimumSize(910, 490); } +enum tool ToolLauncher::getSelectedToolId() const { + return selectedToolId; +} + void ToolLauncher::closeEvent(QCloseEvent *event) diff --git a/src/tool_launcher.hpp b/src/tool_launcher.hpp index d891a14ac0..bccb30511d 100644 --- a/src/tool_launcher.hpp +++ b/src/tool_launcher.hpp @@ -104,6 +104,7 @@ class ToolLauncher : public QMainWindow void setNativeDialogs(bool nativeDialogs); PhoneHome *getPhoneHome() const; + enum tool getSelectedToolId() const; Q_SIGNALS: void connectionDone(bool success); @@ -291,6 +292,7 @@ private Q_SLOTS: PhoneHome* m_phoneHome; SessionInfo m_sessionInfo; + enum tool selectedToolId; }; } diff --git a/src/tool_launcher_api.cpp b/src/tool_launcher_api.cpp index 3c65732cb0..61caa31f83 100644 --- a/src/tool_launcher_api.cpp +++ b/src/tool_launcher_api.cpp @@ -89,6 +89,17 @@ bool ToolLauncher_API::calibration_skipped() return tl->skip_calibration; } +int ToolLauncher_API::focused_instrument() { + return static_cast(tl->getSelectedToolId()); +} + +void ToolLauncher_API::focus_instrument(int inst) { + if(inst >= 0 ) { + tl->_toolSelected(static_cast(inst)); + } + +} + QList ToolLauncher_API::usb_uri_list() { QList uri_list; diff --git a/src/tool_launcher_api.hpp b/src/tool_launcher_api.hpp index 11eba1468b..c031dab1cc 100644 --- a/src/tool_launcher_api.hpp +++ b/src/tool_launcher_api.hpp @@ -31,6 +31,8 @@ class ToolLauncher_API: public ApiObject Q_PROPERTY(bool menu_opened READ menu_opened WRITE open_menu STORED false); + Q_PROPERTY(int focused_instrument READ focused_instrument WRITE focus_instrument ); + Q_PROPERTY(bool hidden READ hidden WRITE hide STORED false); Q_PROPERTY(QString previous_ip READ getPreviousIp WRITE addIp @@ -64,6 +66,9 @@ class ToolLauncher_API: public ApiObject bool calibration_skipped(); void skip_calibration(bool); + int focused_instrument(); + void focus_instrument(int); + bool debugger_enabled(); void enable_debugger(bool); From 9f462d1dca903c9c9a42715f9e3706142b062534 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 23 Nov 2021 12:22:56 +0200 Subject: [PATCH 068/125] js: added startupscript.js This example shows how to connect and run instruments and then resume control to the application. Can be useful for development. Signed-off-by: Adrian Suciu --- js/startupscript.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 js/startupscript.js diff --git a/js/startupscript.js b/js/startupscript.js new file mode 100644 index 0000000000..b1acead1d9 --- /dev/null +++ b/js/startupscript.js @@ -0,0 +1,38 @@ +#!/usr/bin/scopy -s + +/* This is the script that starts scopy up and connects to the emulator */ +var host = "127.0.0.1" + + +function connect(host) { + print("Connecting to " + host + "...") + + var success = launcher.connect("ip:" + host) + + if (success) + print("Connected!") + else + print("Failed!") + + return success; +} + + +function main() { + /* hardcoded for now */ + + var connected = connect(host) + if (!connected) + return Error() + + /* Run signal generator with values saved in .ini file*/ + siggen.running=true + /* Run oscilloscope */ + osc.running=true + /* Focus oscilloscope */ + launcher.focused_instrument=0 + /* Resume control to the application*/ + returnToApplication(); +} + +main() From 38c8b203719b2d6369c9071800b4817bae921fa5 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 14 Jan 2022 14:56:14 +0200 Subject: [PATCH 069/125] ScopyActivity: fix crash after closing scopy on Android Signed-off-by: Adrian Suciu --- android/src/org/adi/scopy/ScopyActivity.java | 39 +++++++++++-------- .../src/org/adi/scopy/ScopyApplication.java | 2 + src/tool_launcher.cpp | 8 +++- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java index fd658736a2..91bfb53bb6 100644 --- a/android/src/org/adi/scopy/ScopyActivity.java +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -33,31 +33,36 @@ public class ScopyActivity extends QtActivity { public static native void saveSessionJavaHelper(); - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); //saveSessionJavaHelper(); - } + @Override + public void onCreate(Bundle savedInstanceState) + { + System.out.println("-- ScopyActivity: onCreate"); + super.onCreate(savedInstanceState); + } - @Override - protected void onStart() - { - super.onStart(); - } + @Override + protected void onStart() + { + System.out.println("-- ScopyActivity: onStart"); + super.onStart(); + } - @Override - protected void onStop() - { - super.onStop(); - } + @Override + protected void onStop() + { + System.out.println("-- ScopyActivity: onStop"); + super.onStop(); + } protected void onPause(){ - saveSessionJavaHelper(); + System.out.println("-- ScopyActivity: onPause - saving application state to ini file "); + saveSessionJavaHelper(); // actually save the data super.onPause(); } protected void onDestroy(){ - super.onPause(); + System.out.println("-- ScopyActivity: onDestroy "); + super.onDestroy(); } public void restart() { diff --git a/android/src/org/adi/scopy/ScopyApplication.java b/android/src/org/adi/scopy/ScopyApplication.java index 5a5e1a3e34..3679319e18 100644 --- a/android/src/org/adi/scopy/ScopyApplication.java +++ b/android/src/org/adi/scopy/ScopyApplication.java @@ -33,6 +33,7 @@ public class ScopyApplication extends QtApplication @Override public void onCreate() { + System.out.println("QtApplication started"); String apk = getApplicationInfo().sourceDir; String cache = getApplicationContext().getCacheDir().toString(); System.out.println("sourcedir: "+ getApplicationInfo().sourceDir); @@ -58,4 +59,5 @@ public void onCreate() super.onCreate(); } + } diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 094c0ca1ef..508bd92a24 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -2017,8 +2017,12 @@ bool ToolLauncher::eventFilter(QObject *watched, QEvent *event) void ToolLauncher::saveSessionJavaHelper(JNIEnv *env, jobject /*thiz*/) { qDebug()<<"-- Saving session"; - getToolLauncherInstance()->tl_api->sync(); - getToolLauncherInstance()->saveSettings(); + ToolLauncher* tl = getToolLauncherInstance(); + if(tl) + { + getToolLauncherInstance()->tl_api->sync(); + getToolLauncherInstance()->saveSettings(); + } } void ToolLauncher::registerNativeMethods() From 6800b02f8517189b165ba1e023e35c56d72f6bd1 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 14 Jan 2022 16:01:33 +0200 Subject: [PATCH 070/125] main.cpp: fix compilation error Signed-off-by: Adrian Suciu --- src/main.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0f9d713b2b..ee56b369b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,14 +40,8 @@ #include "scopy_color_editor.h" #include "application_restarter.h" - using namespace adiscope; -ToolLauncher* tl_ptr; -ToolLauncher* adiscope::getToolLauncherInstance() { - return tl_ptr; -} - int main(int argc, char **argv) { #ifdef __ANDROID__ @@ -171,7 +165,6 @@ int main(int argc, char **argv) } ToolLauncher launcher(prevCrashDump); - tl_ptr = &launcher; launcher.getPrefPanel()->setColorEditor(colorEditor); bool nogui = parser.isSet("nogui"); From 3a42fb978e8161762742207495a837c8a5b00e08 Mon Sep 17 00:00:00 2001 From: Teo Perisanu Date: Tue, 11 Jan 2022 17:28:19 +0200 Subject: [PATCH 071/125] iio-emu: Update emulator. Add support for get_trigger and set_trigger Signed-off-by: Teo Perisanu --- iio-emu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iio-emu b/iio-emu index 710386fda8..b8208485f5 160000 --- a/iio-emu +++ b/iio-emu @@ -1 +1 @@ -Subproject commit 710386fda8daa5bbb3ffb6d4f214f014aa260f57 +Subproject commit b8208485f50573a38263031ab05e9cd30712bf0c From b6539ebe6e1cb35ec911af28d834181279fba6d2 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 21 Jan 2022 17:56:54 +0200 Subject: [PATCH 072/125] cmake: Add C as project language C compiler preprocessor is used when generating stylesheets from templates Signed-off-by: ioanachelaru --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b265be624f..5b4c62a91f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ cmake_minimum_required(VERSION 3.5) -project(scopy LANGUAGES CXX VERSION 1.3.0) +project(scopy LANGUAGES C CXX VERSION 1.3.0) set(Python_ADDITIONAL_VERSIONS 3) FIND_PACKAGE(PythonInterp REQUIRED) From 2adc4ecd81b03e7b76d64034bc0645a17c39fd1e Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 28 Jan 2022 17:31:30 +0200 Subject: [PATCH 073/125] android: scale ui based on screen properties Signed-off-by: ioanachelaru --- android/src/org/adi/scopy/ScopyActivity.java | 17 ++++++++++++++++- src/main.cpp | 11 +++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java index 91bfb53bb6..25df24c0f0 100644 --- a/android/src/org/adi/scopy/ScopyActivity.java +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.ComponentName; import android.os.Bundle; +import android.util.DisplayMetrics; public class ScopyActivity extends QtActivity { @@ -75,5 +76,19 @@ public void restart() { Intent mainIntent = Intent.makeRestartActivityTask(componentName); context.startActivity(mainIntent); Runtime.getRuntime().exit(0); - } + } + + public String getScaleFactor() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + + double scaleFactor = ((double)displayMetrics.widthPixels/displayMetrics.heightPixels) + /displayMetrics.scaledDensity; + String formattedScaleFactor = String.format("%.02f", scaleFactor); + System.out.println("-- ScopyActivity: scale factor is: " + formattedScaleFactor); + + + return formattedScaleFactor.replace(",","."); + } + } } diff --git a/src/main.cpp b/src/main.cpp index ee56b369b2..74055db208 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,17 +40,24 @@ #include "scopy_color_editor.h" #include "application_restarter.h" +#ifdef __ANDROID__ + #include +#endif + using namespace adiscope; int main(int argc, char **argv) { #ifdef __ANDROID__ - qputenv("QT_SCALE_FACTOR", "1.17"); + QAndroidJniObject jniObject = QtAndroid::androidActivity().callObjectMethod("getScaleFactor", "()Ljava/lang/String;"); + QString scaleFactor = jniObject.toString(); + + qputenv("QT_SCALE_FACTOR", scaleFactor.toUtf8()); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif - ScopyApplication app(argc, argv); + #ifdef LIBM2K_ENABLE_LOG enableLogging(true); google::InitGoogleLogging(argv[0]); From 6d822d85f5639aac0c10d6d5af978e59bbc04ed7 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 28 Jan 2022 17:32:03 +0200 Subject: [PATCH 074/125] android: lock landscape mode Signed-off-by: ioanachelaru --- android/AndroidManifest.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 4b6537c56d..b3774e0b25 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + Remove the comment if you do not require these default permissions. --> @@ -12,12 +12,12 @@ + Remove the comment if you do not require these default features. --> - + From 648fd92d5b54d87768917c6f7be86bd3318b3634 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 31 Jan 2022 16:36:49 +0200 Subject: [PATCH 075/125] android: disable tool item move feature Signed-off-by: ioanachelaru --- src/toolmenuitem.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/toolmenuitem.cpp b/src/toolmenuitem.cpp index 24c937da93..6a39e8150c 100644 --- a/src/toolmenuitem.cpp +++ b/src/toolmenuitem.cpp @@ -52,6 +52,10 @@ ToolMenuItem::ToolMenuItem(QString name, QString iconPath, QWidget *parent): setDynamicProperty(this, "selected", on); } }); + +#ifdef __ANDROID__ + setDynamicProperty(this, "allowHover", false); +#endif } ToolMenuItem::~ToolMenuItem() @@ -144,32 +148,42 @@ void ToolMenuItem::setDisabled(bool disabled) void ToolMenuItem::mouseMoveEvent(QMouseEvent *event) { +#ifndef __ANDROID__ BaseMenuItem::mouseMoveEvent(event); setDynamicProperty(this, "allowHover", false); +#endif } void ToolMenuItem::enterEvent(QEvent *event) { +#ifndef __ANDROID__ setDynamicProperty(this, "allowHover", true); event->accept(); +#endif } void ToolMenuItem::leaveEvent(QEvent *event) { +#ifndef __ANDROID__ setDynamicProperty(this, "allowHover", false); event->accept(); +#endif } void ToolMenuItem::dragMoveEvent(QDragMoveEvent *event) { +#ifndef __ANDROID__ setDynamicProperty(this, "allowHover", false); BaseMenuItem::dragMoveEvent(event); +#endif } void ToolMenuItem::dragLeaveEvent(QDragLeaveEvent *event) { +#ifndef __ANDROID__ setDynamicProperty(this, "allowHover", true); BaseMenuItem::dragLeaveEvent(event); +#endif } void ToolMenuItem::_buildUI() From 944c9445bde97f94e844809fa573975598548daa Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 31 Jan 2022 16:38:06 +0200 Subject: [PATCH 076/125] gui: added scroll area for tools menu Signed-off-by: ioanachelaru --- ui/tool_launcher.ui | 56 +++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/ui/tool_launcher.ui b/ui/tool_launcher.ui index b141bf6f6b..8d86212e02 100644 --- a/ui/tool_launcher.ui +++ b/ui/tool_launcher.ui @@ -314,24 +314,43 @@ - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 633 + 416 + - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + @@ -1656,6 +1675,9 @@ border-width: 0px; + + + From 7ba13be34fe76db529e741c7d5590c3810ed3a63 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 1 Feb 2022 16:55:47 +0200 Subject: [PATCH 077/125] gui: fixed widgets positioning for voltmeter and power supply Signed-off-by: ioanachelaru --- ui/dmm.ui | 51 +++++++++----- ui/powercontrol.ui | 169 +++++++++++++++++++++++---------------------- 2 files changed, 120 insertions(+), 100 deletions(-) diff --git a/ui/dmm.ui b/ui/dmm.ui index a3991606e5..eee40ddc13 100644 --- a/ui/dmm.ui +++ b/ui/dmm.ui @@ -351,17 +351,20 @@ text-align:center; - + 0 0 - 130 + 0 0 + + font-size:35pt; + MAX @@ -370,17 +373,20 @@ text-align:center; - + 0 0 - 130 + 0 0 + + font-size:35pt; + MIN @@ -467,20 +473,21 @@ text-align:center; - + 0 0 - 130 - 105 + 0 + 0 QLabel[ac=false] { qproperty-text: "VDC"; } -QLabel[ac=true] { qproperty-text: "VRMS"; } +QLabel[ac=true] { qproperty-text: "VRMS"; } +QLabel{font-size:35pt;} VDC @@ -631,17 +638,20 @@ text-align:center; - + 0 0 - 130 + 0 0 + + font-size:35pt; + MIN @@ -719,17 +729,20 @@ text-align:center; - + 0 0 - 130 + 0 0 + + font-size:35pt; + MAX @@ -803,20 +816,21 @@ text-align:center; - + 0 0 - 130 - 105 + 0 + 0 QLabel[ac=false] { qproperty-text: "VDC"; } -QLabel[ac=true] { qproperty-text: "VRMS"; } +QLabel[ac=true] { qproperty-text: "VRMS"; } +QLabel{font-size:35pt;} VDC @@ -2020,6 +2034,9 @@ line-height: 14px; + + + @@ -2056,7 +2073,7 @@ line-height: 14px; - + diff --git a/ui/powercontrol.ui b/ui/powercontrol.ui index c4e5df3208..dd9fc66502 100644 --- a/ui/powercontrol.ui +++ b/ui/powercontrol.ui @@ -123,23 +123,33 @@ - - + + + + QLabel { color: #444444; qproperty-alignment: 'AlignBottom | AlignLeft'; font-size: 30pt; font-weight: bold; } + + + VDC + + + + + - + 0 0 - 220 + 0 0 - 220 + 16777215 16777215 @@ -154,55 +164,38 @@ QLabel { color: #444444; qproperty-alignment: 'AlignBottom | AlignLeft'; font-size: 30pt; font-weight: bold; } - Set + Measure - - - - Qt::Horizontal - - - - 42 - 20 - - - - - - + + - + 0 0 - 220 - 0 + 210 + 80 - - - 220 - 16777215 - + + QLCDNumber { +background-color: transparent; +color: #ff7200; +} - - - 30 - 75 - true - + + QFrame::NoFrame - - QLabel { color: #444444; qproperty-alignment: 'AlignBottom | AlignLeft'; font-size: 30pt; font-weight: bold; } + + 5 - - Measure + + QLCDNumber::Flat @@ -244,65 +237,72 @@ color: #ff7200; - - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + - + 0 0 - 210 - 80 + 0 + 0 - - QLCDNumber { -background-color: transparent; -color: #ff7200; -} - - - QFrame::NoFrame - - - 5 + + + 16777215 + 16777215 + - - QLCDNumber::Flat + + + 30 + 75 + true + - - - - - QLabel { color: #444444; qproperty-alignment: 'AlignBottom | AlignLeft'; font-size: 30pt; font-weight: bold; } + QLabel { color: #444444; qproperty-alignment: 'AlignBottom | AlignLeft'; font-size: 30pt; font-weight: bold; } - VDC + Set - - + + Qt::Horizontal - - QSizePolicy::Fixed - - 20 + 42 20 - - + + Qt::Horizontal @@ -335,25 +335,25 @@ color: #ff7200; - 7 + 6 - + 0 0 - 220 + 0 0 - 220 + 16777215 16777215 @@ -382,7 +382,7 @@ color: #ff7200; - 252 + 210 80 @@ -396,7 +396,7 @@ color: #9013fe; QFrame::NoFrame - 6 + 5 QLCDNumber::Flat @@ -406,20 +406,20 @@ color: #9013fe; - + 0 0 - 220 + 0 0 - 220 + 16777215 16777215 @@ -445,7 +445,7 @@ color: #9013fe; - 0 + 42 20 @@ -461,7 +461,7 @@ color: #9013fe; - 252 + 210 80 @@ -475,7 +475,7 @@ color: #9013fe; QFrame::NoFrame - 6 + 5 QLCDNumber::Flat @@ -1127,6 +1127,9 @@ QPushButton:disabled { + + + From 748d635d352b1e836343f451227ca04675d4f1c9 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Thu, 3 Feb 2022 12:17:10 +0200 Subject: [PATCH 078/125] gui: added toggle menu button for signal generator Signed-off-by: ioanachelaru --- src/signal_generator.cpp | 4 ++++ ui/signal_generator.ui | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 3f98deeef3..cee7af2877 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -637,6 +637,10 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); ui->plot->addWidget(ui->instrumentNotes, 3, 0); + + connect(ui->toggleMenuBtn, &QPushButton::toggled, [=](bool toggled){ + ui->rightMenu->toggleMenu(toggled); + }); } SignalGenerator::~SignalGenerator() diff --git a/ui/signal_generator.ui b/ui/signal_generator.ui index 04c0d173f1..01f343edb5 100644 --- a/ui/signal_generator.ui +++ b/ui/signal_generator.ui @@ -109,6 +109,25 @@ + + + + + + + true + + + true + + + true + + + true + + + From 9f84e689cceb449c174d987fd19dbbea83eaab23 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 1 Mar 2022 11:39:04 +0200 Subject: [PATCH 079/125] resources: add svg assets zip file Signed-off-by: Adrian Suciu --- resources/svg_sketch.zip | Bin 0 -> 113661 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/svg_sketch.zip diff --git a/resources/svg_sketch.zip b/resources/svg_sketch.zip new file mode 100644 index 0000000000000000000000000000000000000000..3c5400cbcbd2bf93e1f862eaaa5d20439e8a2d15 GIT binary patch literal 113661 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%0A-{H(Z4G25XuS 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 Date: Mon, 22 Nov 2021 14:56:18 +0200 Subject: [PATCH 080/125] plots: implemented basicplot BasicPlots provides benchmarking as well as general configuration for all plots in scopy Signed-off-by: Adrian Suciu --- src/BasicPlot.cpp | 149 +++++++++++++++++++++++++++ src/BasicPlot.h | 67 ++++++++++++ src/ConstellationDisplayPlot.cc | 2 +- src/FftDisplayPlot.cc | 6 +- src/HistogramDisplayPlot.cc | 2 +- src/TimeDomainDisplayPlot.cc | 2 +- src/dbgraph.cpp | 2 +- src/logging_categories.cpp | 1 + src/logging_categories.h | 2 + src/logicanalyzer/logic_analyzer.cpp | 1 + src/main.cpp | 4 +- src/movingaverage.cpp | 36 +++++++ src/movingaverage.h | 24 +++++ src/network_analyzer.cpp | 2 + src/oscilloscope.cpp | 5 +- src/printableplot.cpp | 50 ++++----- src/printableplot.h | 15 +-- src/spectrum_analyzer.cpp | 2 + 18 files changed, 333 insertions(+), 39 deletions(-) create mode 100644 src/BasicPlot.cpp create mode 100644 src/BasicPlot.h create mode 100644 src/movingaverage.cpp create mode 100644 src/movingaverage.h diff --git a/src/BasicPlot.cpp b/src/BasicPlot.cpp new file mode 100644 index 0000000000..da80888ddb --- /dev/null +++ b/src/BasicPlot.cpp @@ -0,0 +1,149 @@ +#include "BasicPlot.h" + +#include +#include + +#include +#include + +namespace adiscope { + +static int staticPlotId = 0; + +#define replotFrameDuration 1000.0/replotFrameRate + +BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotFrameRate(60) +{ + connect(&replotTimer,SIGNAL(timeout()),this,SLOT(replotNow())); + QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); + plotCanvas->setPaintAttribute(QwtPlotCanvas::BackingStore ); +#ifdef IMMEDIATE_PAINT + plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true); +#endif + qDebug(CAT_PLOT)< fpsLabelRefreshTime) { + QString rendering = (useOpenGlCanvas) ? "OpenGl rendering" : "Software rendering"; + fpsTxt.setText(rendering + "\n" + + "instrument: " + QString::number(1000.0 / instrumentCycleAvg) + " fps / " + QString::number(instrumentCycleAvg) + " ms" + "\n" +#ifdef IMMEDIATE_PAINT + + "plot: " + QString::number(replotTheoreticalFpsAvg) + "fps / " + QString::number(replotDurationAvg) + " ms" +#endif + ); + fpsLabel.setText(fpsTxt); + fpsLabelTimer.restart(); + } +} + + +void BasicPlot::replot() { +#ifdef IMMEDIATE_PAINT + if(!replotTimer.isActive()) { + //qDebug(CAT_PLOT)< +#include +#include +#include +#include +#include +#include + + +/*Qt includes*/ +#include +#include +#include + +/*Own includes */ +#include + +//#define IMMEDIATE_PAINT + +namespace adiscope { + +class BasicPlot : public QwtPlot +{ + Q_OBJECT +public: + BasicPlot(QWidget* parent); + +public Q_SLOTS: + void start(); + void startStop(bool en = true); + void stop(); + bool isStarted(); + + void setRefreshRate(double hz); + double getRefreshRate(); + + void replotNow(); + void setVisibleFpsLabel(bool vis); + void hideFpsLabel(); + void showFpsLabel(); + void replot() override; + +protected: + QTimer replotTimer; +private: + bool started; + bool debug; + double replotFrameRate; + int id = 0; + MovingAverage pfps, pms; + MovingAverage ifps, ims; + const int fpsHistoryCount = 60; + const int fpsLabelRefreshTime = 500; + QwtPlotTextLabel fpsLabel; + QwtText fpsTxt; + QElapsedTimer fpsTimer; + QElapsedTimer fpsLabelTimer; + +}; + +} + +#endif // BASICPLOT_H diff --git a/src/ConstellationDisplayPlot.cc b/src/ConstellationDisplayPlot.cc index f5e551e642..43a46fb0cd 100644 --- a/src/ConstellationDisplayPlot.cc +++ b/src/ConstellationDisplayPlot.cc @@ -192,7 +192,7 @@ ConstellationDisplayPlot::set_pen_size(int size) void ConstellationDisplayPlot::replot() { - QwtPlot::replot(); + BasicPlot::replot(); } diff --git a/src/FftDisplayPlot.cc b/src/FftDisplayPlot.cc index b8adaa0da8..b8b9feea25 100644 --- a/src/FftDisplayPlot.cc +++ b/src/FftDisplayPlot.cc @@ -88,6 +88,10 @@ FftDisplayPlot::FftDisplayPlot(int nplots, QWidget *parent) : for (unsigned int i = 0; i < nplots; i++) { auto plot = new QwtPlotCurve(QString("CH %1").arg(i + 1)); + plot->setPaintAttribute(QwtPlotCurve::ClipPolygons); + plot->setPaintAttribute(QwtPlotCurve::ImageBuffer); + plot->setPaintAttribute(QwtPlotCurve::FilterPoints); + plot->setPaintAttribute(QwtPlotCurve::FilterPointsAggressive); plot->setPen(QPen(d_CurveColors[i])); plot->attach(this); @@ -222,7 +226,7 @@ void FftDisplayPlot::replot() d_leftHandlesArea->repaint(); d_bottomHandlesArea->repaint(); - QwtPlot::replot(); + BasicPlot::replot(); } bool FftDisplayPlot::isReferenceWaveform(unsigned int chnIdx) diff --git a/src/HistogramDisplayPlot.cc b/src/HistogramDisplayPlot.cc index 4b5024f5aa..3556a03f4a 100644 --- a/src/HistogramDisplayPlot.cc +++ b/src/HistogramDisplayPlot.cc @@ -471,7 +471,7 @@ void HistogramDisplayPlot::setDataInterval(int min, int max) void HistogramDisplayPlot::replot() { - QwtPlot::replot(); + BasicPlot::replot(); } void diff --git a/src/TimeDomainDisplayPlot.cc b/src/TimeDomainDisplayPlot.cc index cd3a32986c..ad200dd60a 100644 --- a/src/TimeDomainDisplayPlot.cc +++ b/src/TimeDomainDisplayPlot.cc @@ -274,7 +274,7 @@ TimeDomainDisplayPlot::~TimeDomainDisplayPlot() void TimeDomainDisplayPlot::replot() { - QwtPlot::replot(); + BasicPlot::replot(); } void diff --git a/src/dbgraph.cpp b/src/dbgraph.cpp index f6a134ddd6..e3ad9a69d4 100644 --- a/src/dbgraph.cpp +++ b/src/dbgraph.cpp @@ -224,7 +224,7 @@ void dBgraph::replot() d_leftHandlesArea->repaint(); d_topHandlesArea->repaint(); - QwtPlot::replot(); + BasicPlot::replot(); } void dBgraph::enableXaxisLabels() diff --git a/src/logging_categories.cpp b/src/logging_categories.cpp index c802193858..78ea67262d 100644 --- a/src/logging_categories.cpp +++ b/src/logging_categories.cpp @@ -33,4 +33,5 @@ Q_LOGGING_CATEGORY(CAT_PATTERN_GENERATOR, "patternGenerator") Q_LOGGING_CATEGORY(CAT_CALIBRATION, "calibration") Q_LOGGING_CATEGORY(CAT_CALIBRATION_MANUAL, "calibration.manual") Q_LOGGING_CATEGORY(CAT_IIO_MANAGER, "iioManager") +Q_LOGGING_CATEGORY(CAT_PLOT, "plot") #endif diff --git a/src/logging_categories.h b/src/logging_categories.h index edabca2186..588a19dcb9 100644 --- a/src/logging_categories.h +++ b/src/logging_categories.h @@ -36,6 +36,7 @@ Q_DECLARE_LOGGING_CATEGORY(CAT_PATTERN_GENERATOR) Q_DECLARE_LOGGING_CATEGORY(CAT_CALIBRATION) Q_DECLARE_LOGGING_CATEGORY(CAT_CALIBRATION_MANUAL) Q_DECLARE_LOGGING_CATEGORY(CAT_IIO_MANAGER) +Q_DECLARE_LOGGING_CATEGORY(CAT_PLOT) #else #define CAT_TOOL_LAUNCHER #define CAT_OSCILLOSCOPE @@ -50,6 +51,7 @@ Q_DECLARE_LOGGING_CATEGORY(CAT_IIO_MANAGER) #define CAT_CALIBRATION #define CAT_CALIBRATION_MANUAL #define CAT_IIO_MANAGER +#define CAT_PLOT #endif #endif // LOGGING_CATEGORIES_H diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index fdfa480e04..7d71952f5e 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1728,6 +1728,7 @@ void LogicAnalyzer::startStop(bool start) } m_started = start; + m_plot.startStop(start); m_triggerUpdater->setEnabled(start); if (start) { diff --git a/src/main.cpp b/src/main.cpp index 74055db208..022ec6d1dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,10 +54,12 @@ int main(int argc, char **argv) qputenv("QT_SCALE_FACTOR", scaleFactor.toUtf8()); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents, true); + QApplication::setAttribute(Qt::AA_CompressTabletEvents, true); #endif ScopyApplication app(argc, argv); - #ifdef LIBM2K_ENABLE_LOG enableLogging(true); google::InitGoogleLogging(argv[0]); diff --git a/src/movingaverage.cpp b/src/movingaverage.cpp new file mode 100644 index 0000000000..ed0be4f4f8 --- /dev/null +++ b/src/movingaverage.cpp @@ -0,0 +1,36 @@ +#include "movingaverage.h" + +MovingAverage::MovingAverage() +{ + sum = 0; +} + +int MovingAverage::getCapacity() const +{ + return capacity; +} + +void MovingAverage::setCapacity(int newCapacity) +{ + capacity = newCapacity; +} + +const QVector &MovingAverage::getHistory() const +{ + return history; +} + +double MovingAverage::pushValueReturnAverage(double newVal){ + sum+=newVal; + if(history.count() == capacity && capacity > 0) { + sum-=history.front(); + history.pop_front(); + } + history.push_back(newVal); + return (sum/(double)history.count()); +} + +void MovingAverage::clearHistory() { + history.clear(); + sum = 0; +} diff --git a/src/movingaverage.h b/src/movingaverage.h new file mode 100644 index 0000000000..6df37c2f32 --- /dev/null +++ b/src/movingaverage.h @@ -0,0 +1,24 @@ +#ifndef MOVINGAVERAGE_H +#define MOVINGAVERAGE_H + +#include + +class MovingAverage +{ +public: + MovingAverage(); + double pushValueReturnAverage(double newVal); + + int getCapacity() const; + void setCapacity(int newCapacity); + void clearHistory(); + const QVector &getHistory() const; + +private: + QVector history; + int capacity; + double sum; + +}; + +#endif // MOVINGAVERAGE_H diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 0c43c0ed86..66f2322ae4 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -1665,6 +1665,8 @@ void NetworkAnalyzer::startStop(bool pressed) } m_running = pressed; + m_dBgraph.startStop(pressed); + m_phaseGraph.startStop(pressed); bool shouldClear = false; if (QObject::sender() == ui->runSingleWidget) { diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index 2301f35431..51c44fd07a 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -932,7 +932,8 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, #ifdef __ANDROID__ ui->btnAddMath->setIconSize(QSize(24, 24)); #endif - + plot.replot(); + plot.zoomBaseUpdate(true); } int Oscilloscope::binSearchPointOnXaxis(double time) @@ -2883,6 +2884,8 @@ void Oscilloscope::runStopToggled(bool checked) // Update trigger status m_running = checked; + plot.startStop(checked); + triggerUpdater->setEnabled(checked); } diff --git a/src/printableplot.cpp b/src/printableplot.cpp index 7e00299a0c..f47325cecb 100644 --- a/src/printableplot.cpp +++ b/src/printableplot.cpp @@ -26,16 +26,16 @@ using namespace adiscope; PrintablePlot::PrintablePlot(QWidget *parent) : - QwtPlot(parent), + BasicPlot(parent), d_plotRenderer(new QwtPlotRenderer(this)), d_useNativeDialog(true) { - dropBackground(true); + dropBackground(true); } void PrintablePlot::dropBackground(bool drop) { - d_plotRenderer.setDiscardFlag(QwtPlotRenderer::DiscardBackground, drop); + d_plotRenderer.setDiscardFlag(QwtPlotRenderer::DiscardBackground, drop); d_plotRenderer.setDiscardFlag(QwtPlotRenderer::DiscardCanvasBackground, drop); } @@ -46,15 +46,15 @@ void PrintablePlot::setUseNativeDialog(bool nativeDialog) void PrintablePlot::printPlot(const QString& toolName) { - legendDisplay = new QwtLegend(this); - legendDisplay->setDefaultItemMode(QwtLegendData::ReadOnly); - insertLegend(legendDisplay, QwtPlot::TopLegend); + legendDisplay = new QwtLegend(this); + legendDisplay->setDefaultItemMode(QwtLegendData::ReadOnly); + insertLegend(legendDisplay, QwtPlot::TopLegend); - updateLegend(); + updateLegend(); - QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"); + QString date = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"); - QString fileName = "Scopy-" + toolName + "-" + date; + QString fileName = "Scopy-" + toolName + "-" + date; // https://github.com/osakared/qwt/blob/qwt-6.1-multiaxes/src/qwt_plot_renderer.cpp#L1023 // QwtPlotRenderer does not expose an option to select which file dialog to use @@ -62,33 +62,33 @@ void PrintablePlot::printPlot(const QString& toolName) // call of QFileDialog::getSaveFileName(...) where we take into account the d_useNativeDialog // boolean const QList imageFormats = - QImageWriter::supportedImageFormats(); + QImageWriter::supportedImageFormats(); QStringList filter; filter += QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)"; filter += QString( "SVG " ) + tr( "Documents" ) + " (*.svg)"; filter += QString( "Postscript " ) + tr( "Documents" ) + " (*.ps)"; if ( imageFormats.size() > 0 ) { - for ( int i = 0; i < imageFormats.size(); i++ ) { - filter += (imageFormats[i].toUpper() + " " + for ( int i = 0; i < imageFormats.size(); i++ ) { + filter += (imageFormats[i].toUpper() + " " + tr("Image") + " (*." + imageFormats[i] + ")"); - } + } } - QString selectedFilter = QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)"; + QString selectedFilter = QString( "PDF " ) + tr( "Documents" ) + " (*.pdf)"; fileName = QFileDialog::getSaveFileName( - nullptr, tr( "Export File Name" ), fileName, - filter.join( ";;" ), &selectedFilter, - (d_useNativeDialog ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog)); - - if (fileName.split(".").size() <= 1) { - // file name w/o extension. Let's append it - QString ext = selectedFilter.split(".")[1].split(")")[0]; - fileName += "." + ext; - } + nullptr, tr( "Export File Name" ), fileName, + filter.join( ";;" ), &selectedFilter, + (d_useNativeDialog ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog)); + + if (fileName.split(".").size() <= 1) { + // file name w/o extension. Let's append it + QString ext = selectedFilter.split(".")[1].split(")")[0]; + fileName += "." + ext; + } - d_plotRenderer.renderDocument(this, fileName, QSizeF( 300, 200 )); + d_plotRenderer.renderDocument(this, fileName, QSizeF( 300, 200 )); - insertLegend(nullptr); + insertLegend(nullptr); } diff --git a/src/printableplot.h b/src/printableplot.h index 2938999495..9adc390335 100644 --- a/src/printableplot.h +++ b/src/printableplot.h @@ -28,23 +28,24 @@ /*Qt includes*/ #include +#include namespace adiscope { -class PrintablePlot : public QwtPlot +class PrintablePlot : public BasicPlot { - Q_OBJECT + Q_OBJECT public: - PrintablePlot(QWidget *parent); + PrintablePlot(QWidget *parent); - void dropBackground(bool drop); + void dropBackground(bool drop); void setUseNativeDialog(bool nativeDialog); public Q_SLOTS: - void printPlot(const QString& toolName = ""); + void printPlot(const QString& toolName = ""); private: - QwtPlotRenderer d_plotRenderer; - QwtLegend *legendDisplay; + QwtPlotRenderer d_plotRenderer; + QwtLegend *legendDisplay; bool d_useNativeDialog; }; } diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 623451f3f6..33ceccd3bb 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -1834,7 +1834,9 @@ void SpectrumAnalyzer::runStopToggled(bool checked) if (!checked) { fft_plot->resetAverageHistory(); } + fft_plot->startStop(checked); m_running = checked; + fft_plot->startStop(checked); } void SpectrumAnalyzer::build_gnuradio_block_chain() From bc531ac38de15be96a13749df3a8f8d4d07a4af0 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 20 Jan 2022 14:23:58 +0200 Subject: [PATCH 081/125] plots: remove 10fps cap of the plots Signed-off-by: Adrian Suciu --- src/histogram_sink_f_impl.cc | 5 ++--- src/logicanalyzer/logic_analyzer.cpp | 2 +- src/mixed_signal_sink.h | 1 + src/mixed_signal_sink_impl.cpp | 10 ++++++++++ src/mixed_signal_sink_impl.h | 1 + src/scope_sink_f_impl.cc | 4 ++-- src/signal_generator.cpp | 2 -- src/xy_sink_c_impl.cc | 3 +-- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/histogram_sink_f_impl.cc b/src/histogram_sink_f_impl.cc index 2e23798c6d..9ce422827b 100644 --- a/src/histogram_sink_f_impl.cc +++ b/src/histogram_sink_f_impl.cc @@ -94,7 +94,7 @@ namespace adiscope { set_alignment(std::max(1,alignment_multiple)); this->plot = (HistogramDisplayPlot*)plot; - initialize(); + initialize(); } histogram_sink_f_impl::~histogram_sink_f_impl() @@ -117,12 +117,11 @@ namespace adiscope { if(qApp != NULL) { d_qApplication = qApp; } + set_update_time(1/60.0); plot->setNumBins(d_bins); plot->setXaxis(d_xmin, d_xmax); - // initialize update time to 10 times a second - set_update_time(0.1); } void diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 7d71952f5e..c791c7006a 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1960,7 +1960,7 @@ void LogicAnalyzer::startStop(bool start) totalSamples = bufferSizeAdjusted; absIndex = 0; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(1000/60)); } } while (totalSamples && !m_stopRequested); diff --git a/src/mixed_signal_sink.h b/src/mixed_signal_sink.h index 16c471859a..64cca446de 100644 --- a/src/mixed_signal_sink.h +++ b/src/mixed_signal_sink.h @@ -38,6 +38,7 @@ class mixed_signal_sink : virtual public gr::sync_block virtual void clean_buffers() = 0; virtual void set_nsamps(int newsize) = 0; virtual void set_displayOneBuffer(bool display) = 0; + virtual void set_update_time(double t) = 0; }; #endif // MIXED_SIGNAL_SINK_H diff --git a/src/mixed_signal_sink_impl.cpp b/src/mixed_signal_sink_impl.cpp index ff200f181d..7440b8fee6 100644 --- a/src/mixed_signal_sink_impl.cpp +++ b/src/mixed_signal_sink_impl.cpp @@ -66,6 +66,8 @@ mixed_signal_sink_impl::mixed_signal_sink_impl(adiscope::logic::LogicAnalyzer *l static_cast(volk_malloc(d_buffer_size * sizeof(double), volk_get_alignment()))); memset(d_analog_plot_buffers[i], 0, d_buffer_size * sizeof(double)); } + + set_update_time(1/60.0); } int mixed_signal_sink_impl::work(int noutput_items, @@ -191,6 +193,14 @@ void mixed_signal_sink_impl::set_nsamps(int newsize) } } +void mixed_signal_sink_impl::set_update_time(double t) +{ + //convert update time to ticks + gr::high_res_timer_type tps = gr::high_res_timer_tps(); + d_update_time = t * tps; + d_last_time = 0; +} + void mixed_signal_sink_impl::set_displayOneBuffer(bool display) { if (d_display_one_buffer != display) { diff --git a/src/mixed_signal_sink_impl.h b/src/mixed_signal_sink_impl.h index fd6d70f84c..085ebf622e 100644 --- a/src/mixed_signal_sink_impl.h +++ b/src/mixed_signal_sink_impl.h @@ -36,6 +36,7 @@ class mixed_signal_sink_impl : public mixed_signal_sink void clean_buffers() override; void set_nsamps(int newsize) override; void set_displayOneBuffer(bool display) override; + void set_update_time(double t) override; private: void _adjust_tags(int adj); diff --git a/src/scope_sink_f_impl.cc b/src/scope_sink_f_impl.cc index e961923a7c..6ab3b2f5ff 100644 --- a/src/scope_sink_f_impl.cc +++ b/src/scope_sink_f_impl.cc @@ -136,8 +136,8 @@ namespace adiscope { d_qApplication = qApp; } - // initialize update time to 10 times a second - set_update_time(0.1); + // initialize update time to 60 times a second + set_update_time(1/60.0); } void diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index cee7af2877..12664a5847 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -584,8 +584,6 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, connect(ui->refreshBtn, SIGNAL(clicked()), this, SLOT(loadFileCurrentChannelData())); - time_block_data->time_block->set_update_time(0.001); - m_plot->addZoomer(0); resetZoom(); diff --git a/src/xy_sink_c_impl.cc b/src/xy_sink_c_impl.cc index 9e47b2c1d0..2be3875b4e 100644 --- a/src/xy_sink_c_impl.cc +++ b/src/xy_sink_c_impl.cc @@ -118,8 +118,7 @@ namespace adiscope { d_qApplication = qApp; } - // initialize update time to 10 times a second - set_update_time(0.1); + set_update_time(1/60.0); } void From 4d27df84a9182242758edfb6a053f2c34021b585 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 22 Nov 2021 14:56:35 +0200 Subject: [PATCH 082/125] measurements: only measure when measurements are enabled Signed-off-by: Adrian Suciu --- src/oscilloscope_plot.cpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index b0dcf35e3c..7617627681 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -1857,28 +1857,30 @@ int CapturePlot::activeMeasurementsCount(int chnIdx) void CapturePlot::onNewDataReceived() { int ref_idx = 0; - for (int i = 0; i < d_measureObjs.size(); i++) { - Measure *measure = d_measureObjs[i]; - int chn = measure->channel(); - if (isReferenceWaveform(Curve(chn))) { - measure->setDataSource(d_ref_ydata[ref_idx], - Curve(chn)->data()->size()); - ref_idx++; - } else { - int count = countReferenceWaveform(chn); - measure->setDataSource(d_ydata[chn - count], - Curve(chn)->data()->size()); - } + if(d_measurementsEnabled) { + for (int i = 0; i < d_measureObjs.size(); i++) { + Measure *measure = d_measureObjs[i]; + int chn = measure->channel(); + if (isReferenceWaveform(Curve(chn))) { + measure->setDataSource(d_ref_ydata[ref_idx], + Curve(chn)->data()->size()); + ref_idx++; + } else { + int count = countReferenceWaveform(chn); + measure->setDataSource(d_ydata[chn - count], + Curve(chn)->data()->size()); + } - if (isMathWaveform(Curve(chn))) { - measure->setAdcBitCount(0); + if (isMathWaveform(Curve(chn))) { + measure->setAdcBitCount(0); + } + + measure->setSampleRate(this->sampleRate()); + measure->measure(); } - measure->setSampleRate(this->sampleRate()); - measure->measure(); + Q_EMIT measurementsAvailable(); } - - Q_EMIT measurementsAvailable(); } QList> CapturePlot::measurements(int chnIdx) From 435d43486a8a2f8439f48e20b434089be8ac53d5 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Wed, 24 Nov 2021 14:33:17 +0200 Subject: [PATCH 083/125] oscilloscope: a series of optimizations related to plotting and zooming Replot and zoomBaseUpdate was used excessively leading to stuttering. Enhanced replot and zoomBaseUpdate logic Signed-off-by: Adrian Suciu --- src/DisplayPlot.cc | 5 +++-- src/DisplayPlot.h | 2 +- src/HistogramDisplayPlot.cc | 6 ------ src/extendingplotzoomer.cpp | 1 - src/handles_area.cpp | 1 + src/limitedplotzoomer.cpp | 14 +++++++++++++- src/limitedplotzoomer.h | 2 ++ src/oscilloscope.cpp | 17 +++-------------- src/oscilloscope_plot.cpp | 3 --- src/patterngenerator/pattern_generator.cpp | 1 - src/signal_generator.cpp | 2 +- src/spectrum_analyzer.cpp | 1 - 12 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/DisplayPlot.cc b/src/DisplayPlot.cc index d2759bbd32..1cd3a28aa2 100644 --- a/src/DisplayPlot.cc +++ b/src/DisplayPlot.cc @@ -1661,10 +1661,11 @@ DisplayPlot::onPickerPointSelected6(const QPointF & p) Q_EMIT plotPointSelected(point); } -void DisplayPlot::zoomBaseUpdate() +void DisplayPlot::zoomBaseUpdate(bool force) { + qDebug()<<"ZoomBaseUpdate"; for (unsigned int i = 0; i < d_zoomer.size(); ++i) - d_zoomer[i]->setZoomBase(true); + d_zoomer[i]->setZoomBase(force); } void DisplayPlot::AddAxisOffset(int axisPos, int axisIdx, double offset) diff --git a/src/DisplayPlot.h b/src/DisplayPlot.h index a9fc666aa2..bdc98e406e 100644 --- a/src/DisplayPlot.h +++ b/src/DisplayPlot.h @@ -370,7 +370,7 @@ class DisplayPlot:public PrintablePlot QwtPlotCurve *Curve(unsigned int curveIdx); - void zoomBaseUpdate(); + void zoomBaseUpdate(bool force = false); void setMinXaxisDivision(double minDivison); double minXaxisDivision(); diff --git a/src/HistogramDisplayPlot.cc b/src/HistogramDisplayPlot.cc index 3556a03f4a..67543b30c6 100644 --- a/src/HistogramDisplayPlot.cc +++ b/src/HistogramDisplayPlot.cc @@ -437,18 +437,13 @@ void HistogramDisplayPlot::setYaxisSpan(unsigned int chIdx, double bot, double t return; } - _resetZoom(); if (d_orientation == Qt::Vertical) { setAxisScale(QwtAxisId(QwtPlot::xBottom, chIdx), bot, top); - replot(); - zoomBaseUpdate(); return; } setAxisScale(QwtAxisId(QwtPlot::yLeft, chIdx), bot, top); - replot(); - zoomBaseUpdate(); } void HistogramDisplayPlot::setXaxisSpan(double start, double stop) @@ -705,7 +700,6 @@ void HistogramDisplayPlot::_orientationChanged() } } - zoomBaseUpdate(); for (int i = 0; i < d_zoomer.size(); ++i) { d_zoomer[i]->setEnabled(d_orientation == Qt::Horizontal ? false : true); diff --git a/src/extendingplotzoomer.cpp b/src/extendingplotzoomer.cpp index ece6d8f5bc..ef325abea5 100644 --- a/src/extendingplotzoomer.cpp +++ b/src/extendingplotzoomer.cpp @@ -77,7 +77,6 @@ QPolygon ExtendingPlotZoomer::adjustedPoints(const QPolygon &points) const cornerMarkers[1]->detach(); cornerMarkers[2]->detach(); cornerMarkers[3]->detach(); - static_cast((QwtPlot*)plot())->replot(); return points; } diff --git a/src/handles_area.cpp b/src/handles_area.cpp index f4e8407489..fd515016af 100644 --- a/src/handles_area.cpp +++ b/src/handles_area.cpp @@ -53,6 +53,7 @@ void HandlesArea::mousePressEvent(QMouseEvent *event) hotspot = event->pos() - child->pos(); child->setGrabbed(true); } + event->accept(); } void HandlesArea::mouseReleaseEvent(QMouseEvent *) diff --git a/src/limitedplotzoomer.cpp b/src/limitedplotzoomer.cpp index bc975c21cf..1946f0b584 100644 --- a/src/limitedplotzoomer.cpp +++ b/src/limitedplotzoomer.cpp @@ -19,12 +19,13 @@ */ #include "limitedplotzoomer.h" #include +#include using namespace adiscope; LimitedPlotZoomer::LimitedPlotZoomer(QWidget *parent, bool doReplot): QwtPlotZoomer(parent, doReplot), - m_boundVertical(false) + m_boundVertical(false), m_updateBaseNextZoom(true) { setMaxStackDepth(5); } @@ -32,6 +33,7 @@ LimitedPlotZoomer::LimitedPlotZoomer(QWidget *parent, bool doReplot): void LimitedPlotZoomer::resetZoom() { QwtPlotZoomer::zoom(0); + m_updateBaseNextZoom = true; } void LimitedPlotZoomer::popZoom() @@ -46,6 +48,11 @@ void LimitedPlotZoomer::setBoundVertical(bool bound) void LimitedPlotZoomer::zoom(const QRectF &rect) { + + if(m_updateBaseNextZoom) { + setZoomBase(); + m_updateBaseNextZoom = false; + } QRectF boundedRect = rect & zoomBase(); if (m_boundVertical) { @@ -57,6 +64,11 @@ void LimitedPlotZoomer::zoom(const QRectF &rect) QwtPlotZoomer::zoom(boundedRect); } + + +void LimitedPlotZoomer::axesChanged() { + qDebug()<<"Axes changed"; +} QSizeF LimitedPlotZoomer::minZoomSize() const { const double eps = 10e12; diff --git a/src/limitedplotzoomer.h b/src/limitedplotzoomer.h index 046bfa4b7e..0016087fed 100644 --- a/src/limitedplotzoomer.h +++ b/src/limitedplotzoomer.h @@ -34,10 +34,12 @@ class LimitedPlotZoomer : public QwtPlotZoomer protected: virtual void zoom(const QRectF &); + virtual void axesChanged() override; virtual QSizeF minZoomSize() const override; private: bool m_boundVertical; + bool m_updateBaseNextZoom; }; } diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index 51c44fd07a..de7907490e 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -932,6 +932,7 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, #ifdef __ANDROID__ ui->btnAddMath->setIconSize(QSize(24, 24)); #endif + plot.replot(); plot.zoomBaseUpdate(true); } @@ -1127,7 +1128,6 @@ void Oscilloscope::add_ref_waveform(QString name, QVector xData, QVector plot.showYAxisWidget(curve_id, true); plot.setVertUnitsPerDiv(1, curve_id); // force v/div to 1 - plot.zoomBaseUpdate(); init_selected_measurements(curve_id, {0, 1, 4, 5}); plot.computeMeasurementsForChannel(curve_id, sampleRate); @@ -1246,7 +1246,6 @@ void Oscilloscope::add_ref_waveform(unsigned int chIdx) plot.showYAxisWidget(curve_id, true); plot.setVertUnitsPerDiv(1, curve_id); // force v/div to 1 - plot.zoomBaseUpdate(); init_selected_measurements(curve_id, {0, 1, 4, 5}); } @@ -2885,6 +2884,7 @@ void Oscilloscope::runStopToggled(bool checked) // Update trigger status m_running = checked; plot.startStop(checked); + hist_plot.startStop(checked); triggerUpdater->setEnabled(checked); } @@ -3393,8 +3393,6 @@ void adiscope::Oscilloscope::onVertScaleValueChanged(double value) cancelZoom(); if (value != plot.VertUnitsPerDiv(current_ch_widget)) { plot.setVertUnitsPerDiv(value, current_ch_widget); - plot.replot(); - plot.zoomBaseUpdate(); } voltsPosition->setStep(value / 10); @@ -3408,7 +3406,6 @@ void adiscope::Oscilloscope::onVertScaleValueChanged(double value) if (current_ch_widget == index_y) { xy_plot.setVertUnitsPerDiv(value, QwtPlot::yLeft); } - xy_plot.replot(); xy_plot.zoomBaseUpdate(); if (current_ch_widget < nb_channels) { @@ -3473,12 +3470,11 @@ void Oscilloscope::onCmbMemoryDepthChanged(QString value) setSinksDisplayOneBuffer(false); } - plot.replot(); plot.setXAxisNumPoints(bufferSize); plot.setHorizOffset(params.timePos); plot.setDataStartingPoint(active_trig_sample_count); plot.resetXaxisOnNextReceivedData(); - plot.zoomBaseUpdate(); + plot.cancelZoom(); if (zoom_level == 0) { noZoomXAxisWidth = plot.axisInterval(QwtPlot::xBottom).width(); @@ -3571,10 +3567,8 @@ void adiscope::Oscilloscope::onHorizScaleValueChanged(double value) plot.replot(); plot.setDataStartingPoint(active_trig_sample_count); plot.resetXaxisOnNextReceivedData(); - plot.zoomBaseUpdate(); plot.setXAxisNumPoints(0); - hist_plot.zoomBaseUpdate(); hist_plot.replot(); ch_ui->cmbMemoryDepth->setCurrentIndex(0); @@ -3701,12 +3695,9 @@ void adiscope::Oscilloscope::onVertOffsetValueChanged(double value) if (value != -plot.VertOffset(current_ch_widget)) { plot.setVertOffset(-value, current_ch_widget); - plot.replot(); } - plot.zoomBaseUpdate(); scaleHistogramPlot(); - updateXyPlotScales(); // Switch between high and low gain modes only for the M2K channels @@ -3762,8 +3753,6 @@ void adiscope::Oscilloscope::onTimePositionChanged(double value) plot.setDataStartingPoint(active_trig_sample_count); plot.resetXaxisOnNextReceivedData(); - if (zoom_level == 0) - plot.zoomBaseUpdate(); if (started) { trigger_settings.setTriggerDelay(active_trig_sample_count); diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index 7617627681..d5866889ba 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -1040,9 +1040,6 @@ bool CapturePlot::eventFilter(QObject *object, QEvent *event) d_hCursorHandle2->triggerMove(); d_vCursorHandle1->triggerMove(); d_vCursorHandle2->triggerMove(); - d_timeTriggerHandle->triggerMove(); - d_levelTriggerAHandle->triggerMove(); - d_levelTriggerBHandle->triggerMove(); /* update the size of the gates when the plot canvas is resized */ updateGateMargins(); diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index aa12d55922..14f0c3076d 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -822,7 +822,6 @@ void PatternGenerator::generateBuffer() m_plot.setHorizOffset(1.0 / m_sampleRate * m_bufferSize / 2.0); m_plot.cancelZoom(); m_plot.zoomBaseUpdate(); - m_plot.replot(); for (QPair, PatternUI *> &pattern : m_enabledPatterns) { pattern.second->get_pattern()->generate_pattern(sr, bufferSize, pattern.first.size()); diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 12664a5847..3e80245a54 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -488,7 +488,7 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, ((double) sample_rate * 10.0)); m_plot->setHorizOffset((double) nb_points / ((double) sample_rate * 2.0)); - m_plot->zoomBaseUpdate(); + m_plot->zoomBaseUpdate(true); //ui->plot->insertWidget(0,m_plot, 0, 0); connect(ui->btnAppearanceCollapse, SIGNAL(toggled(bool)),ui->wAppearance, SLOT(setVisible(bool))); diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 33ceccd3bb..4fa3a0b3bd 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -1836,7 +1836,6 @@ void SpectrumAnalyzer::runStopToggled(bool checked) } fft_plot->startStop(checked); m_running = checked; - fft_plot->startStop(checked); } void SpectrumAnalyzer::build_gnuradio_block_chain() From 537c244a8755fc8f307687d98e20b7be001feaac Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 20 Dec 2021 14:49:01 +0200 Subject: [PATCH 084/125] oscilloscope: run hw queries in a threadpool Signed-off-by: Adrian Suciu --- src/oscilloscope.cpp | 30 ++++++++++++++++++++---------- src/oscilloscope.hpp | 5 +++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index de7907490e..3d33c70bbf 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include /* libm2k includes */ #include @@ -3233,7 +3235,7 @@ void adiscope::Oscilloscope::onChannelWidgetEnabled(bool en) hist_plot.enableChannel(id, en); if (id < nb_channels) { - m_m2k_analogin->enableChannel(id, en); + runInHwThreadPool( m_m2k_analogin->enableChannel(id, en);); } if (en) { @@ -4672,9 +4674,11 @@ void Oscilloscope::setupAutosetFreqSweep() qt_time_block->reset(); qt_time_block->clean_buffers(); if (m_m2k_analogin) { + runInHwThreadPool({ high_gain_modes[autosetChannel] = libm2k::analog::PLUS_MINUS_25V; trigger_settings.autoTriggerDisable(); trigger_settings.setTriggerEnable(false); + }); } autosetSampleRateCnt--; active_sample_rate = m_m2k_analogin->getAvailableSampleRates()[autosetSampleRateCnt]; @@ -5102,7 +5106,8 @@ void Oscilloscope::setGainMode(uint chnIdx, libm2k::analog::M2K_RANGE gain_mode) if (ui->runSingleWidget->runButtonChecked()) { try { libm2k::analog::ANALOG_IN_CHANNEL chn = static_cast(chnIdx); - m_m2k_analogin->setRange(chn, gain_mode); + runInHwThreadPool( m_m2k_analogin->setRange(chn, gain_mode) ); + } catch (libm2k::m2k_exception &e) { HANDLE_EXCEPTION(e) qDebug(CAT_OSCILLOSCOPE) << e.what(); @@ -5114,7 +5119,7 @@ void Oscilloscope::setGainMode(uint chnIdx, libm2k::analog::M2K_RANGE gain_mode) iio->freq_comp_filt[chnIdx][0]->set_high_gain(gain_mode); iio->freq_comp_filt[chnIdx][1]->set_high_gain(gain_mode); update_chn_settings_panel(chnIdx); - trigger_settings.updateHwVoltLevels(chnIdx); + runInHwThreadPool(trigger_settings.updateHwVoltLevels(chnIdx);); } void Oscilloscope::setChannelHwOffset(uint chnIdx, double offset) @@ -5123,7 +5128,8 @@ void Oscilloscope::setChannelHwOffset(uint chnIdx, double offset) if (ui->runSingleWidget->runButtonChecked()) { try { libm2k::analog::ANALOG_IN_CHANNEL chn = static_cast(chnIdx); - m_m2k_analogin->setVerticalOffset(chn, offset); + runInHwThreadPool(m_m2k_analogin->setVerticalOffset(chn, offset); ); + } catch (libm2k::m2k_exception &e) { HANDLE_EXCEPTION(e) qDebug(CAT_OSCILLOSCOPE) << e.what(); @@ -5163,11 +5169,13 @@ void Oscilloscope::writeAllSettingsToHardware() if (m_m2k_analogin) { for (uint i = 0; i < nb_channels; i++) { try { - libm2k::analog::M2K_RANGE mode = high_gain_modes[i] ? - libm2k::analog::PLUS_MINUS_2_5V : libm2k::analog::PLUS_MINUS_25V; - libm2k::analog::ANALOG_IN_CHANNEL chn = static_cast(i); - m_m2k_analogin->setRange(chn, mode); - m_m2k_analogin->setVerticalOffset(chn, channel_offset[i]); + runInHwThreadPool( { + libm2k::analog::M2K_RANGE mode = high_gain_modes[i] ? + libm2k::analog::PLUS_MINUS_2_5V : libm2k::analog::PLUS_MINUS_25V; + libm2k::analog::ANALOG_IN_CHANNEL chn = static_cast(i); + m_m2k_analogin->setRange(chn, mode); + m_m2k_analogin->setVerticalOffset(chn, channel_offset[i]); + } ); } catch (libm2k::m2k_exception &e) { HANDLE_EXCEPTION(e) qDebug(CAT_OSCILLOSCOPE) << e.what(); @@ -5182,7 +5190,7 @@ void Oscilloscope::writeAllSettingsToHardware() } // Writes all trigger settings to hardware - trigger_settings.setAdcRunningState(true); + runInHwThreadPool(trigger_settings.setAdcRunningState(true);); } void Oscilloscope::on_xyPlotLineType_toggled(bool checked) @@ -5269,6 +5277,7 @@ void Oscilloscope::setSampleRate(double sample_rate) if (!m_m2k_analogin) { return; } + runInHwThreadPool( { try { auto maxSampleRate = m_m2k_analogin->getMaximumSamplerate(); if (m_filtering_enabled == false) { @@ -5282,6 +5291,7 @@ void Oscilloscope::setSampleRate(double sample_rate) HANDLE_EXCEPTION(e) qDebug(CAT_OSCILLOSCOPE) << e.what(); } + }); } diff --git a/src/oscilloscope.hpp b/src/oscilloscope.hpp index 4811f40965..9122d6c950 100644 --- a/src/oscilloscope.hpp +++ b/src/oscilloscope.hpp @@ -44,6 +44,7 @@ #include #include #include +#include /* Local includes */ #include "apiObject.hpp" @@ -497,6 +498,10 @@ namespace adiscope { gr::m2k::mixed_signal_source::sptr mixed_source; mixed_signal_sink::sptr mixed_sink; QMetaObject::Connection showLogicAnalyzerTriggerConnection; + + QThreadPool hwSettingsThreadPool; + #define runInHwThreadPool(x) QtConcurrent::run(&hwSettingsThreadPool, std::bind([=]() { x; } )) + }; } #endif /* M2K_OSCILLOSCOPE_H */ From a7cf7d6501575f9ee969e885e7563823c1a40bed Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 18 Jan 2022 14:56:21 +0200 Subject: [PATCH 085/125] preferences: added show fps to debug preferences Signed-off-by: Adrian Suciu --- src/logicanalyzer/logic_analyzer.cpp | 4 +- src/network_analyzer.cpp | 3 + src/oscilloscope.cpp | 5 + src/patterngenerator/pattern_generator.cpp | 2 + src/preferences.cpp | 29 +- src/preferences.h | 8 + src/signal_generator.cpp | 2 + src/spectrum_analyzer.cpp | 2 + ui/preferences.ui | 1593 ++++++++++---------- 9 files changed, 860 insertions(+), 788 deletions(-) diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index c791c7006a..94cd0ff329 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -2447,7 +2447,9 @@ void LogicAnalyzer::restoreTriggerState() void LogicAnalyzer::readPreferences() { - qDebug() << "reading preferences!!!!"; + bool showFps = prefPanel->getShow_plot_fps(); + m_plot.setVisibleFpsLabel(showFps); + for (GenericLogicPlotCurve *curve : qAsConst(m_plotCurves)) { if (curve->getType() == LogicPlotCurveType::Data) { LogicDataCurve *ldc = dynamic_cast(curve); diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 66f2322ae4..20de208763 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -1812,6 +1812,9 @@ void NetworkAnalyzer::toggleCursors(bool en) void NetworkAnalyzer::readPreferences() { + bool showFps = prefPanel->getShow_plot_fps(); + m_dBgraph.setVisibleFpsLabel(showFps); + m_phaseGraph.setVisibleFpsLabel(showFps); m_dBgraph.setShowZero(prefPanel->getNa_show_zero()); m_phaseGraph.setShowZero(prefPanel->getNa_show_zero()); ui->instrumentNotes->setVisible(prefPanel->getInstrumentNotesActive()); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index 3d33c70bbf..fae0596ba0 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -1349,6 +1349,11 @@ void Oscilloscope::settingsLoaded() void Oscilloscope::readPreferences() { + bool showFps = prefPanel->getShow_plot_fps(); + plot.setVisibleFpsLabel(showFps); + xy_plot.setVisibleFpsLabel(showFps); + fft_plot.setVisibleFpsLabel(showFps); + hist_plot.setVisibleFpsLabel(showFps); plot.setGraticuleEnabled(prefPanel->getOsc_graticule_enabled()); ui->instrumentNotes->setVisible(prefPanel->getInstrumentNotesActive()); diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 14f0c3076d..7cae0fbbd6 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -156,6 +156,8 @@ PatternGenerator::PatternGenerator(struct iio_context *ctx, Filter *filt, void PatternGenerator::readPreferences() { + bool showFps = prefPanel->getShow_plot_fps(); + m_plot.setVisibleFpsLabel(showFps); m_ui->instrumentNotes->setVisible(prefPanel->getInstrumentNotesActive()); } diff --git a/src/preferences.cpp b/src/preferences.cpp index 8e2934f84e..02e4717d91 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -70,7 +70,8 @@ Preferences::Preferences(QWidget *parent) : first_application_run(true), check_updates_url("http://swdownloads.analog.com/cse/sw_versions.json"), m_colorEditor(nullptr), - m_logging_enabled(false) + m_logging_enabled(false), + m_show_plot_fps(false) { ui->setupUi(this); @@ -214,6 +215,11 @@ Preferences::Preferences(QWidget *parent) : Q_EMIT notify(); }); + connect(ui->showPlotFps, &QCheckBox::stateChanged, [=](int state) { + m_show_plot_fps = state; + Q_EMIT notify(); + }); + QString preference_ini_file = getPreferenceIniFile(); QSettings settings(preference_ini_file, QSettings::IniFormat); @@ -394,6 +400,7 @@ void Preferences::showEvent(QShowEvent *event) ui->debugInstrumentCheckbox->setChecked(debugger_enabled); ui->tempLutCalibCheckbox->setChecked(m_attemptTempLutCalib); ui->skipCalCheckbox->setChecked(m_skipCalIfCalibrated); + ui->showPlotFps->setChecked(m_show_plot_fps); // by this point the preferences menu is initialized m_initialized = true; ui->autoUpdatesCheckBox->setChecked(automatical_version_checking_enabled); @@ -431,6 +438,16 @@ void Preferences::resetScopy() } } +bool Preferences::getShow_plot_fps() const +{ + return m_show_plot_fps; +} + +void Preferences::setShow_plot_fps(bool newShow_plot_fps) +{ + m_show_plot_fps = newShow_plot_fps; +} + bool Preferences::getDigital_decoders_enabled() const { return digital_decoders_enabled; @@ -681,7 +698,6 @@ void Preferences::setFirst_application_run(bool value) first_application_run = value; } - void Preferences::setColorEditor(ScopyColorEditor *colorEditor) { m_colorEditor = colorEditor; @@ -995,3 +1011,12 @@ void Preferences_API::setFirstApplicationRun(const bool &first) { preferencePanel->first_application_run = first; } + + +bool Preferences_API::getShowPlotFps() const { + return preferencePanel->m_show_plot_fps; +} +void Preferences_API::setShowPlotFps(const bool& fps) { + preferencePanel->m_show_plot_fps = fps; + +} diff --git a/src/preferences.h b/src/preferences.h index eedfda6d80..ec509ef17f 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -141,6 +141,9 @@ class Preferences : public QWidget bool getLogging_enabled() const; void setLogging_enabled(bool value); + bool getShow_plot_fps() const; + void setShow_plot_fps(bool newShow_plot_fps); + Q_SIGNALS: void notify(); @@ -190,6 +193,7 @@ private Q_SLOTS: bool m_attemptTempLutCalib; bool m_skipCalIfCalibrated; bool m_logging_enabled; + bool m_show_plot_fps; Preferences_API *pref_api; @@ -228,6 +232,7 @@ class Preferences_API : public ApiObject Q_PROPERTY(bool first_application_run READ getFirstApplicationRun WRITE setFirstApplicationRun) Q_PROPERTY(QString currentStylesheet READ getCurrentStylesheet WRITE setCurrentStylesheet) Q_PROPERTY(QStringList userStylesheets READ getUserStylesheets WRITE setUserStylesheets) + Q_PROPERTY(bool showPlotFps READ getShowPlotFps WRITE setShowPlotFps) public: @@ -311,6 +316,9 @@ class Preferences_API : public ApiObject bool getFirstApplicationRun() const; void setFirstApplicationRun(const bool& first); + bool getShowPlotFps() const; + void setShowPlotFps(const bool& first); + QString getCurrentStylesheet() const; void setCurrentStylesheet(const QString ¤tStylesheet); diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 3e80245a54..4d01ad0ffa 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -665,6 +665,8 @@ void SignalGenerator::settingsLoaded() void SignalGenerator::readPreferences() { + bool showFps = prefPanel->getShow_plot_fps(); + m_plot->setVisibleFpsLabel(showFps); double prefered_periods_nr = prefPanel->getSig_gen_periods_nr(); if (nr_of_periods != prefered_periods_nr) { nr_of_periods = prefered_periods_nr; diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 4fa3a0b3bd..56910df4fe 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -779,6 +779,8 @@ void SpectrumAnalyzer::setNativeDialogs(bool nativeDialogs) } void SpectrumAnalyzer::readPreferences() { + bool showFps = prefPanel->getShow_plot_fps(); + fft_plot->setVisibleFpsLabel(showFps); fft_plot->setVisiblePeakSearch(prefPanel->getSpectrum_visible_peak_search()); ui->instrumentNotes->setVisible(prefPanel->getInstrumentNotesActive()); } diff --git a/ui/preferences.ui b/ui/preferences.ui index 6fe641a81e..bb42fb74c2 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -73,8 +73,8 @@ 0 0 - 1082 - 752 + 1068 + 792 @@ -225,6 +225,187 @@ 10 + + + + + + + + + true + + + + + + + Enable animations + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + NETWORK ANALYZER + + + true + + + + + + + + 200 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + true + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + + + + 16777215 + 16777215 + + + + + + + + + + + + 0 + 0 + + + + Always display 0db value on graph + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + @@ -327,62 +508,6 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - Save session when closing Scopy - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -438,22 +563,6 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - @@ -488,243 +597,9 @@ - - - - 6 - - - 0 - - - 0 - - - - - 6 - - - 0 - - - - - - 0 - 0 - - - - SPECTRUM ANALYZER - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - - - - - - - - - - true - - - - - - - - 0 - 0 - - - - Only search marker peaks in visible domain - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 0 - - - - - - - - - - - - Enable automatic update checking - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Enable all instrument notes - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Show advanced device information - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Enable user notes in main page - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - + + + 0 @@ -734,244 +609,45 @@ 0 - - 0 - - - 0 - - - 10 - - - - - - - - - - - - - - Enable graticule - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Enable sample rate filters - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + 0 + + + + + + + - - - - - - - - - - - - - Enable mini histogram - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + Double click to detach a tool + + + 0 + + - - + + - Qt::Vertical + Qt::Horizontal - 0 - 0 + 40 + 20 - - - - - - - - - Show ADC digital filter config - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Enable labels on the plot - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - OSCILLOSCOPE - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - - - - - - 0 - 0 - - - + + + 0 @@ -988,26 +664,31 @@ 0 - - - - - - - - + + + 0 + + + 10 + + + 10 + - + - Run external scripts (Experimental) + Language (requires app restart) - + Qt::Horizontal + + QSizePolicy::Fixed + 40 @@ -1016,32 +697,48 @@ + + + + + 0 + 0 + + + + - - - - 0 - + + - + + + + 0 + 0 + + + + true + - + - Skip calibration if already calibrated (needs FW >= 0.26) + Save session when closing Scopy - + Qt::Horizontal @@ -1055,9 +752,9 @@ - - - + + + 6 @@ -1074,7 +771,7 @@ 0 - + 6 @@ -1082,7 +779,7 @@ 0 - + 0 @@ -1090,7 +787,7 @@ - NETWORK ANALYZER + SIGNAL GENERATOR true @@ -1098,10 +795,10 @@ - + - 200 + 0 1 @@ -1122,7 +819,7 @@ - + Qt::Vertical @@ -1132,44 +829,46 @@ 20 - 15 + 5 - + - - - - 16777215 - 16777215 - - + - + Number of displayed periods - + - + 0 0 - - Always display 0db value on graph + + +QLineEdit[invalid=true] { +border-color: red; +color: red; +} +QLineEdit[valid=true] { +border-color: grey; +color: white; +} - - 0 + + 1 - + Qt::Horizontal @@ -1184,17 +883,17 @@ - + Qt::Vertical - QSizePolicy::Fixed + QSizePolicy::Expanding - 20 - 5 + 0 + 0 @@ -1202,34 +901,270 @@ - - + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + Run external scripts (Experimental) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + + + + Enable user notes in main page + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 6 + + + 0 + + + 0 + + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + SPECTRUM ANALYZER + + + true + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + true + + + + + + + + + + + + + + true + + + + + + + + 0 + 0 + + + + Only search marker peaks in visible domain + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + + + + + + + + + + Show advanced device information + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 - - - - - + - - true - - + - Enable animations + Enable automatic update checking - + Qt::Horizontal @@ -1243,12 +1178,9 @@ - - - - - 6 - + + + 0 @@ -1261,81 +1193,145 @@ 0 - - - - 6 - - - 0 - + + 0 + + + 10 + + + - - - - 0 - 0 - - + - SIGNAL GENERATOR + - - true + + + + + + Enable graticule - - + + + Qt::Horizontal + + - 0 - 1 + 40 + 20 - + + + + + + + + + + + + + Enable sample rate filters + + + + + + + Qt::Horizontal + + - 16777215 - 1 + 40 + 20 + + + + + + + + + + + + + + + + + Enable mini histogram + + + + + Qt::Horizontal - - true + + + 40 + 20 + - + - - + + Qt::Vertical - - QSizePolicy::Fixed - - 20 - 5 + 0 + 0 - - + + - + + + + - Number of displayed periods + Show ADC digital filter config - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 @@ -1343,73 +1339,107 @@ - -QLineEdit[invalid=true] { -border-color: red; -color: red; -} -QLineEdit[valid=true] { -border-color: grey; -color: white; -} + + + + + + + + + + + Enable labels on the plot + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + + + + 0 + 0 + - 1 + OSCILLOSCOPE + + + true - - - Qt::Horizontal + + + + 0 + 1 + - + - 40 - 20 + 16777215 + 1 - + + Qt::Horizontal + + + true + + - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - + + + + 0 + - + - + - Attempt temperature-based calibration (EXPERIMENTAL) + Skip calibration if already calibrated (needs FW >= 0.26) - + Qt::Horizontal @@ -1423,72 +1453,36 @@ color: white; - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - 10 - - - 10 - - - - - Language (requires app restart) - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - - - - + + + + + + + + + + + + + Enable all instrument notes + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -1557,6 +1551,40 @@ color: white; + + + + 0 + + + + + + + + + + + + Show plot FPS + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -1672,41 +1700,36 @@ color: white; - - - - - - - - - - - - - - Double click to detach a tool - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + + + + + + + + + + + + Attempt temperature-based calibration (EXPERIMENTAL) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + From 677ceb118336cbb72ed9d00c71d5adf07b060083 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 7 Jan 2022 14:56:09 +0200 Subject: [PATCH 086/125] general: port qwt to latest version of multiaxes Signed-off-by: Adrian Suciu --- src/ConstellationDisplayPlot.cc | 8 +- src/DisplayPlot.cc | 98 ++++++++-------- src/FftDisplayPlot.cc | 44 ++++---- src/HistogramDisplayPlot.cc | 112 +++++++++---------- src/TimeDomainDisplayPlot.cc | 44 ++++---- src/dbgraph.cpp | 98 ++++++++-------- src/extendingplotzoomer.cpp | 6 +- src/gui/cursor_readouts.cpp | 4 +- src/handlesareaextension.cpp | 18 +-- src/logicanalyzer/annotationcurve.cpp | 6 +- src/logicanalyzer/logic_analyzer.cpp | 8 +- src/logicanalyzer/logicdatacurve.cpp | 6 +- src/marker_controller.cpp | 4 +- src/network_analyzer.cpp | 4 +- src/networkanalyzerbufferviewer.cpp | 6 +- src/nyquistGraph.cpp | 12 +- src/osc_scale_zoomer.cpp | 4 +- src/oscilloscope.cpp | 60 +++++----- src/oscilloscope_plot.cpp | 124 ++++++++++----------- src/patterngenerator/pattern_generator.cpp | 4 +- src/signal_generator.cpp | 2 +- src/sismograph.cpp | 22 ++-- src/spectrum_analyzer.cpp | 24 ++-- src/spectrum_marker.cpp | 4 +- src/symbol.cpp | 6 +- src/x_axis_scale_zoomer.cpp | 4 +- 26 files changed, 366 insertions(+), 366 deletions(-) diff --git a/src/ConstellationDisplayPlot.cc b/src/ConstellationDisplayPlot.cc index 43a46fb0cd..eb19cb1acb 100644 --- a/src/ConstellationDisplayPlot.cc +++ b/src/ConstellationDisplayPlot.cc @@ -108,13 +108,13 @@ ConstellationDisplayPlot::ConstellationDisplayPlot(int nplots, QWidget* parent) d_zoomer[0]->setRubberBandPen(c); d_zoomer[0]->setTrackerPen(c); -// setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); +// setAxisScaleEngine(QwtAxis::XBottom, new QwtLinearScaleEngine); // set_xaxis(-2.0, 2.0); -// setAxisTitle(QwtPlot::xBottom, "In-phase"); +// setAxisTitle(QwtAxis::XBottom, "In-phase"); -// setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); +// setAxisScaleEngine(QwtAxis::YLeft, new QwtLinearScaleEngine); // set_yaxis(-2.0, 2.0); -// setAxisTitle(QwtPlot::yLeft, "Quadrature"); +// setAxisTitle(QwtAxis::YLeft, "Quadrature"); updateAxes(); // Setup dataPoints and plot vectors diff --git a/src/DisplayPlot.cc b/src/DisplayPlot.cc index 1cd3a28aa2..11fd48b090 100644 --- a/src/DisplayPlot.cc +++ b/src/DisplayPlot.cc @@ -58,6 +58,7 @@ #include #include #include +#include using namespace adiscope; @@ -396,11 +397,11 @@ PlotAxisConfiguration::PlotAxisConfiguration(int axisPos, int axisIdx, DisplayPl Qt::CursorShape shape; switch (axisPos) { - case QwtPlot::yLeft: + case QwtAxis::YLeft: shape = Qt::CursorShape::SizeVerCursor; break; - case QwtPlot::xBottom: + case QwtAxis::XBottom: shape = Qt::CursorShape::SizeHorCursor; break; @@ -413,7 +414,7 @@ PlotAxisConfiguration::PlotAxisConfiguration(int axisPos, int axisIdx, DisplayPl // Avoid jumping when labels with more/less digits // appear/disappear when scrolling vertically - if (axisPos == QwtPlot::yLeft) { + if (axisPos == QwtAxis::YLeft) { const QFontMetrics fm(d_plot->axisWidget(d_axis)->font()); QwtScaleDraw *scaleDraw = d_plot->axisScaleDraw(d_axis); scaleDraw->setMinimumExtent( fm.width("100.00") ); @@ -429,7 +430,7 @@ PlotAxisConfiguration::PlotAxisConfiguration(int axisPos, int axisIdx, DisplayPl d_ptsPerDiv = 1.0; d_offset = 0.0; - if (axisPos == QwtPlot::yLeft) { + if (axisPos == QwtAxis::YLeft) { d_mouseGestures = new VertMouseGestures(d_plot->axisWidget(d_axis), d_axis); d_mouseGestures->setEnabled(false); @@ -442,7 +443,7 @@ PlotAxisConfiguration::PlotAxisConfiguration(int axisPos, int axisIdx, DisplayPl d_plot, SLOT(onVertAxisOffsetDecrease())); QObject::connect(this->d_mouseGestures, SIGNAL(downMovement(double)), d_plot, SLOT(onVertAxisOffsetIncrease())); - } else if (axisPos == QwtPlot::xBottom) { + } else if (axisPos == QwtAxis::XBottom) { d_mouseGestures = new HorizMouseGestures(d_plot->axisWidget(d_axis), d_axis); d_mouseGestures->setEnabled(false); @@ -508,7 +509,6 @@ void PlotAxisConfiguration::setMouseGesturesEnabled(bool en) /* * DisplayPlot class */ - DisplayPlot::DisplayPlot(int nplots, QWidget* parent, bool isdBgraph, unsigned int xNumDivs, unsigned int yNumDivs) : PrintablePlot(parent), d_nplots(nplots), d_stop(false), @@ -559,7 +559,7 @@ DisplayPlot::DisplayPlot(int nplots, QWidget* parent, bool isdBgraph, setPaletteColor(default_palette_color); d_panner = new QwtPlotPanner(canvas()); - d_panner->setAxisEnabled(QwtPlot::yRight, false); + d_panner->setAxisEnabled(QwtAxis::YRight, false); d_panner->setMouseButton(Qt::MidButton, Qt::ControlModifier); // emit the position of clicks on widget @@ -592,10 +592,10 @@ DisplayPlot::DisplayPlot(int nplots, QWidget* parent, bool isdBgraph, plotLayout()->setAlignCanvasToScales(true); - this->plotLayout()->setCanvasMargin(0, QwtPlot::yLeft); - this->plotLayout()->setCanvasMargin(0, QwtPlot::yRight); - this->plotLayout()->setCanvasMargin(0, QwtPlot::xTop); - this->plotLayout()->setCanvasMargin(0, QwtPlot::xBottom); + this->plotLayout()->setCanvasMargin(0, QwtAxis::YLeft); + this->plotLayout()->setCanvasMargin(0, QwtAxis::YRight); + this->plotLayout()->setCanvasMargin(0, QwtAxis::XTop); + this->plotLayout()->setCanvasMargin(0, QwtAxis::XBottom); ((QFrame*) canvas())->setLineWidth(0); @@ -664,7 +664,7 @@ void DisplayPlot::setupDisplayPlotDiv(bool isdBgraph) { scaleItem->scaleDraw()->setAlignment(scale); scaleItem->scaleDraw()->enableComponent(QwtAbstractScaleDraw::Backbone, false); scaleItem->scaleDraw()->enableComponent(QwtAbstractScaleDraw::Labels, false); - scaleItem->setFont(this->axisWidget(QwtPlot::yLeft)->font()); + scaleItem->setFont(this->axisWidget(QwtAxis::YLeft)->font()); QPalette palette = scaleItem->palette(); palette.setBrush(QPalette::Foreground, QColor("#6E6E6F")); @@ -807,7 +807,7 @@ void DisplayPlot::setupReadouts() { d_cursorReadoutsVisible = false; d_cursorReadouts = new CursorReadouts(this); - d_cursorReadouts->setAxis(QwtPlot::xTop,QwtPlot::yLeft); + d_cursorReadouts->setAxis(QwtAxis::XTop,QwtAxis::YLeft); d_cursorReadouts->setTopLeftStartingPoint(QPoint(8, 8)); d_cursorReadouts->setTimeReadoutVisible(false); d_cursorReadouts->setVoltageReadoutVisible(false); @@ -1171,17 +1171,17 @@ void DisplayPlot::displayIntersection() attachmk2 = false; } - bool value = isAxisValid(QwtAxisId(QwtPlot::yLeft, d_selected_channel)); + bool value = isAxisValid(QwtAxisId(QwtAxis::YLeft, d_selected_channel)); if(value) { - markerIntersection1->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, d_selected_channel)); - markerIntersection2->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, d_selected_channel)); + markerIntersection1->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, d_selected_channel)); + markerIntersection2->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, d_selected_channel)); } else { - markerIntersection1->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, 0)); - markerIntersection2->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, 0)); + markerIntersection1->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, 0)); + markerIntersection2->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, 0)); } markerIntersection1->setValue(d_vBar1->plotCoord().x(), intersectionCursor1); @@ -1276,7 +1276,7 @@ void DisplayPlot::setAllYAxis(double min, double max) { for (unsigned int i = 0; i < vertAxes.size(); ++i) { - setAxisScale(QwtAxisId(QwtPlot::yLeft, i), min, max); + setAxisScale(QwtAxisId(QwtAxis::YLeft, i), min, max); } if (!d_autoscale_state) { @@ -1288,7 +1288,7 @@ DisplayPlot::setAllYAxis(double min, double max) void DisplayPlot::setYaxis(double min, double max) { - setAxisScale(QwtPlot::yLeft, min, max); + setAxisScale(QwtAxis::YLeft, min, max); if(!d_autoscale_state) { for (unsigned int i = 0; i < d_zoomer.size(); ++i) d_zoomer[i]->setZoomBase(); @@ -1298,7 +1298,7 @@ DisplayPlot::setYaxis(double min, double max) void DisplayPlot::setXaxis(double min, double max) { - setAxisScale(QwtPlot::xBottom, min, max); + setAxisScale(QwtAxis::XBottom, min, max); for (unsigned int i = 0; i < d_zoomer.size(); ++i) d_zoomer[i]->setZoomBase(); } @@ -1422,13 +1422,13 @@ DisplayPlot::getAxisLabelFontSize(int axisId) const { void DisplayPlot::setYaxisLabelFontSize(int fs) { - setAxisLabelFontSize(QwtPlot::yLeft, fs); + setAxisLabelFontSize(QwtAxis::YLeft, fs); } void DisplayPlot::printWithNoBackground(const QString& toolName, bool editScaleDraw) { - OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtAxisId(QwtPlot::yLeft, d_activeVertAxis))); + OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtAxisId(QwtAxis::YLeft, d_activeVertAxis))); QStack colors; for (int i = 0; i < d_plot_curve.size(); ++i) { colors.push_back(getLineColor(i)); @@ -1453,32 +1453,32 @@ DisplayPlot::printWithNoBackground(const QString& toolName, bool editScaleDraw) int DisplayPlot::getYaxisLabelFontSize() const { - int fs = getAxisLabelFontSize(QwtPlot::yLeft); + int fs = getAxisLabelFontSize(QwtAxis::YLeft); return fs; } void DisplayPlot::setXaxisLabelFontSize(int fs) { - setAxisLabelFontSize(QwtPlot::xBottom, fs); + setAxisLabelFontSize(QwtAxis::XBottom, fs); } int DisplayPlot::getXaxisLabelFontSize() const { - int fs = getAxisLabelFontSize(QwtPlot::xBottom); + int fs = getAxisLabelFontSize(QwtAxis::XBottom); return fs; } void DisplayPlot::setAxesLabelFontSize(int fs) { - setAxisLabelFontSize(QwtPlot::yLeft, fs); - setAxisLabelFontSize(QwtPlot::xBottom, fs); + setAxisLabelFontSize(QwtAxis::YLeft, fs); + setAxisLabelFontSize(QwtAxis::XBottom, fs); } int DisplayPlot::getAxesLabelFontSize() const { // Returns 0 if all axes do not have the same font size. - int fs = getAxisLabelFontSize(QwtPlot::yLeft); - if (getAxisLabelFontSize(QwtPlot::xBottom) != fs) + int fs = getAxisLabelFontSize(QwtAxis::YLeft); + if (getAxisLabelFontSize(QwtAxis::XBottom) != fs) return 0; return fs; } @@ -1675,12 +1675,12 @@ void DisplayPlot::AddAxisOffset(int axisPos, int axisIdx, double offset) double ptsPerDiv = 1; switch (axisPos) { - case QwtPlot::yLeft: + case QwtAxis::YLeft: ptsPerDiv = vertAxes[axisIdx]->ptsPerDiv(); min = d_yAxisMin * ptsPerDiv; max = d_yAxisMax * ptsPerDiv; break; - case QwtPlot::xBottom: + case QwtAxis::XBottom: ptsPerDiv = horizAxis->ptsPerDiv(); min = d_xAxisMin * ptsPerDiv; max = d_xAxisMax * ptsPerDiv; @@ -1695,7 +1695,7 @@ void DisplayPlot::AddAxisOffset(int axisPos, int axisIdx, double offset) void DisplayPlot::setVertOffset(double offset, int axisIdx) { - AddAxisOffset(QwtPlot::yLeft, axisIdx, offset); + AddAxisOffset(QwtAxis::YLeft, axisIdx, offset); vertAxes[axisIdx]->setOffset(offset); } @@ -1708,7 +1708,7 @@ double DisplayPlot::VertOffset(int axisIdx) void DisplayPlot::setHorizOffset(double offset) { - AddAxisOffset(QwtPlot::xBottom, 0, offset); + AddAxisOffset(QwtAxis::XBottom, 0, offset); horizAxis->setOffset(offset); } @@ -1727,7 +1727,7 @@ void DisplayPlot::setVertUnitsPerDiv(double upd, int axisIdx) vertAxes[axisIdx]->setPtsPerDiv(upd); min = (d_yAxisMin * upd) + offset; max = (d_yAxisMax * upd) + offset; - setAxisScale(QwtAxisId(QwtPlot::yLeft, axisIdx), min, max, upd); + setAxisScale(QwtAxisId(QwtAxis::YLeft, axisIdx), min, max, upd); Q_EMIT vertScaleDivisionChanged(upd); } } @@ -1747,7 +1747,7 @@ void DisplayPlot::setHorizUnitsPerDiv(double upd) horizAxis->setPtsPerDiv(upd); min = (d_xAxisMin * upd) + offset; max = (d_xAxisMax * upd) + offset; - setAxisScale(QwtPlot::xBottom, min, max, upd); + setAxisScale(QwtAxis::XBottom, min, max, upd); Q_EMIT horizScaleDivisionChanged(upd); } } @@ -1766,15 +1766,15 @@ void DisplayPlot::setDisplayScale(double value) { d_displayScale = value; OscScaleDraw *osd = nullptr; - osd = static_cast(axisWidget(QwtAxisId(QwtPlot::yLeft, d_activeVertAxis))->scaleDraw()); + osd = static_cast(axisWidget(QwtAxisId(QwtAxis::YLeft, d_activeVertAxis))->scaleDraw()); osd->setDisplayScale(d_displayScale); osd->invalidateCache(); - axisWidget(QwtAxisId(QwtPlot::yLeft, d_activeVertAxis))->update(); + axisWidget(QwtAxisId(QwtAxis::YLeft, d_activeVertAxis))->update(); } void DisplayPlot::setActiveVertAxis(unsigned int axisIdx, bool selected) { - int numAxes = this->axesCount(QwtPlot::yLeft); + int numAxes = this->axesCount(QwtAxis::YLeft); if (axisIdx >= numAxes) return; @@ -1783,13 +1783,13 @@ void DisplayPlot::setActiveVertAxis(unsigned int axisIdx, bool selected) if (d_usingLeftAxisScales && selected) { for (int i = 0; i < numAxes; i++) { - this->setAxisVisible(QwtAxisId(QwtPlot::yLeft, i), + this->setAxisVisible(QwtAxisId(QwtAxis::YLeft, i), (i == axisIdx)); } } if (d_coloredLabels && selected) { - OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtAxisId(QwtPlot::yLeft, axisIdx))); + OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtAxisId(QwtAxis::YLeft, axisIdx))); scaleDraw->setColor(getLineColor(axisIdx)); scaleDraw->invalidateCache(); } @@ -2065,7 +2065,7 @@ void DisplayPlot::removeLeftVertAxis(unsigned int axis) if (axis >= numAxis) return; - setAxesCount(QwtPlot::yLeft, numAxis - 1); + setAxesCount(QwtAxis::YLeft, numAxis - 1); for (unsigned int i = axis + 1; i < numAxis; i++) vertAxes[i]->axis().id = i - 1; @@ -2077,7 +2077,7 @@ void DisplayPlot::removeLeftVertAxis(unsigned int axis) double ptsPerDiv = vertAxes[i]->ptsPerDiv(); double offset = vertAxes[i]->offset(); - setAxisScale(QwtAxisId(QwtPlot::yLeft, i), + setAxisScale(QwtAxisId(QwtAxis::YLeft, i), (d_yAxisMin * ptsPerDiv) + offset, (d_yAxisMax * ptsPerDiv) + offset, ptsPerDiv); @@ -2086,7 +2086,7 @@ void DisplayPlot::removeLeftVertAxis(unsigned int axis) void DisplayPlot::setLeftVertAxesCount(int count) { - setAxesCount(QwtPlot::yLeft, count); + setAxesCount(QwtAxis::YLeft, count); const int numAxis = vertAxes.size(); @@ -2096,9 +2096,9 @@ void DisplayPlot::setLeftVertAxesCount(int count) vertAxes.resize(count); for (int i = numAxis; i < count; i++) { - vertAxes[i] = new PlotAxisConfiguration(QwtPlot::yLeft, i, this); - configureAxis(QwtPlot::yLeft, i); - this->setAxisVisible(QwtAxisId(QwtPlot::yLeft, i), + vertAxes[i] = new PlotAxisConfiguration(QwtAxis::YLeft, i, this); + configureAxis(QwtAxis::YLeft, i); + this->setAxisVisible(QwtAxisId(QwtAxis::YLeft, i), d_usingLeftAxisScales); connect(axisWidget(vertAxes[i]->axis()), SIGNAL(scaleDivChanged()), this, SLOT(_onYleftAxisWidgetScaleDivChanged())); @@ -2142,9 +2142,9 @@ void DisplayPlot::resizeEvent(QResizeEvent *event) void DisplayPlot::bottomHorizAxisInit() { - horizAxis = new PlotAxisConfiguration(QwtPlot::xBottom, 0, this); + horizAxis = new PlotAxisConfiguration(QwtAxis::XBottom, 0, this); horizAxis->setMouseGesturesEnabled(d_mouseGesturesEnabled); - configureAxis(QwtPlot::xBottom, 0); + configureAxis(QwtAxis::XBottom, 0); connect(axisWidget(horizAxis->axis()), SIGNAL(scaleDivChanged()), this, SLOT(_onXbottomAxisWidgetScaleDivChanged())); } diff --git a/src/FftDisplayPlot.cc b/src/FftDisplayPlot.cc index b8b9feea25..0a72364a92 100644 --- a/src/FftDisplayPlot.cc +++ b/src/FftDisplayPlot.cc @@ -128,11 +128,11 @@ FftDisplayPlot::FftDisplayPlot(int nplots, QWidget *parent) : freqFormatter.setTwoDecimalMode(true); OscScaleDraw *yScaleDraw = new OscScaleDraw(&dBFormatter, ""); - setAxisScaleDraw(QwtPlot::yLeft, yScaleDraw); + setAxisScaleDraw(QwtAxis::YLeft, yScaleDraw); yScaleDraw->setFloatPrecision(2); OscScaleDraw *xScaleDraw = new OscScaleDraw(&freqFormatter, ""); - setAxisScaleDraw(QwtPlot::xBottom, xScaleDraw); + setAxisScaleDraw(QwtAxis::XBottom, xScaleDraw); xScaleDraw->setFloatPrecision(2); _resetXAxisPoints(); @@ -175,8 +175,8 @@ FftDisplayPlot::FftDisplayPlot(int nplots, QWidget *parent) : d_leftHandlesArea->setBottomPadding(55); d_leftHandlesArea->setMinimumHeight(this->minimumHeight()); - enableAxis(QwtPlot::xBottom, false); - enableAxis(QwtPlot::yLeft, false); + setAxisVisible(QwtAxis::XBottom, false); + setAxisVisible(QwtAxis::YLeft, false); d_formatter = static_cast(new MetricPrefixFormatter); setupReadouts(); @@ -423,8 +423,8 @@ void FftDisplayPlot::setSelectedChannel(int id) void FftDisplayPlot::setZoomerEnabled() { - enableAxis(QwtPlot::xBottom, true); - enableAxis(QwtPlot::yLeft, true); + setAxisVisible(QwtAxis::XBottom, true); + setAxisVisible(QwtAxis::YLeft, true); if(!d_zoomer[0]) { d_zoomer[0] = new FftDisplayZoomer(canvas()); @@ -561,23 +561,23 @@ void FftDisplayPlot::useLogScaleY(bool log_scale) if (log_scale) { setPlotYLogaritmic(true); QwtLogScaleEngine *scaleEngine = new QwtLogScaleEngine(); - setAxisScaleEngine(QwtPlot::yLeft, (QwtScaleEngine *)scaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, (QwtScaleEngine *)scaleEngine); // OscScaleDraw *yScaleDraw = new OscScaleDraw(&dBFormatter, "V/√Hz"); // yScaleDraw->enableComponent(QwtAbstractScaleDraw::Ticks, true); // yScaleDraw->setFloatPrecision(2); -// setAxisScaleDraw(QwtPlot::yLeft, yScaleDraw); +// setAxisScaleDraw(QwtAxis::YLeft, yScaleDraw); replot(); - auto div = axisScaleDiv(QwtPlot::yLeft); + auto div = axisScaleDiv(QwtAxis::YLeft); setYaxisMajorTicksPos(div.ticks(2)); } else { setPlotYLogaritmic(false); OscScaleEngine *scaleEngine = new OscScaleEngine(); - this->setAxisScaleEngine(QwtPlot::yLeft, (QwtScaleEngine *)scaleEngine); + this->setAxisScaleEngine(QwtAxis::YLeft, (QwtScaleEngine *)scaleEngine); // OscScaleDraw *yScaleDraw = new OscScaleDraw(&dBFormatter, ""); // yScaleDraw->setFloatPrecision(2); -// setAxisScaleDraw(QwtPlot::yLeft, yScaleDraw); +// setAxisScaleDraw(QwtAxis::YLeft, yScaleDraw); replot(); - auto div = axisScaleDiv(QwtPlot::yLeft); + auto div = axisScaleDiv(QwtAxis::YLeft); setYaxisNumDiv((div.ticks(2)).size()); } @@ -588,16 +588,16 @@ void FftDisplayPlot::useLogFreq(bool use_log_freq) { if (use_log_freq) { setPlotLogaritmic(true); - setAxisScaleEngine(QwtPlot::xBottom, new QwtLogScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLogScaleEngine); replot(); - auto div = axisScaleDiv(QwtPlot::xBottom); + auto div = axisScaleDiv(QwtAxis::XBottom); setXaxisMajorTicksPos(div.ticks(2)); } else { setPlotLogaritmic(false); OscScaleEngine *scaleEngine = new OscScaleEngine(); - this->setAxisScaleEngine(QwtPlot::xBottom, (QwtScaleEngine *)scaleEngine); + this->setAxisScaleEngine(QwtAxis::XBottom, (QwtScaleEngine *)scaleEngine); replot(); - auto div = axisScaleDiv(QwtPlot::xBottom); + auto div = axisScaleDiv(QwtAxis::XBottom); setXaxisNumDiv((div.ticks(2)).size() - 1); } d_logScaleEnabled = use_log_freq; @@ -1043,7 +1043,7 @@ void FftDisplayPlot::setLeftVertAxisUnit(const QString& unit) d_yAxisUnit = unit; auto scale_draw = dynamic_cast( - axisScaleDraw(QwtPlot::yLeft)); + axisScaleDraw(QwtAxis::YLeft)); if (scale_draw) { scale_draw->setUnitType(unit); } @@ -1057,7 +1057,7 @@ void FftDisplayPlot::setBtmHorAxisUnit(const QString &unit) if (d_xAxisUnit != unit) { d_xAxisUnit = unit; - auto scale_draw = dynamic_cast(axisScaleDraw(QwtPlot::xBottom)); + auto scale_draw = dynamic_cast(axisScaleDraw(QwtAxis::XBottom)); if (scale_draw) scale_draw->setUnitType(unit); } @@ -1355,7 +1355,7 @@ void FftDisplayPlot:: setMarkerEnabled(uint chIdx, uint mkIdx, bool en) if (en) { auto data_sp = std::make_shared(); data_sp->x = 0; - data_sp->y = axisScaleDiv(QwtPlot::yLeft).lowerBound(); + data_sp->y = axisScaleDiv(QwtAxis::YLeft).lowerBound(); data_sp->bin = 0; data_sp->update_ui = true; @@ -1443,7 +1443,7 @@ void FftDisplayPlot::setMarkerAtFreq(uint chIdx, uint mkIdx, double freq) if (ydata) { y = ydata[pos]; } else { - y = axisScaleDiv(QwtPlot::yLeft).lowerBound(); + y = axisScaleDiv(QwtAxis::YLeft).lowerBound(); } if (d_markers[chIdx][mkIdx].data->type != 0) { @@ -1533,7 +1533,7 @@ void FftDisplayPlot::setStartStop(double start, double stop) { m_sweepStart = start; m_sweepStop = stop; - auto div = axisScaleDiv(QwtPlot::xBottom); + auto div = axisScaleDiv(QwtAxis::XBottom); setXaxisNumDiv((div.ticks(2)).size() - 1); setXaxisMajorTicksPos(div.ticks(2)); } @@ -1640,7 +1640,7 @@ void FftDisplayPlot::onMrkCtrlMarkerPosChanged(std::shared_ptr & y = ydata[bin]; } else { qDebug() << "problem"; - y = axisScaleDiv(QwtPlot::yLeft).upperBound(); + y = axisScaleDiv(QwtAxis::YLeft).upperBound(); } marker_data->type = 0; // Fixed marker diff --git a/src/HistogramDisplayPlot.cc b/src/HistogramDisplayPlot.cc index 67543b30c6..9aaec608c9 100644 --- a/src/HistogramDisplayPlot.cc +++ b/src/HistogramDisplayPlot.cc @@ -242,18 +242,18 @@ HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) d_autoscale_state = false; d_autoscalex_state = false; - setAxesCount(QwtPlot::xBottom, 2); + setAxesCount(QwtAxis::XBottom, 2); horizAxes.resize(2); d_pf = new MetricPrefixFormatter(); d_pf->setTwoDecimalMode(true); for (int i = 0; i < 2; ++i) { - horizAxes[i] = new PlotAxisConfiguration(QwtPlot::xBottom, i, this); - configureAxis(QwtPlot::xBottom, i); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, i), + horizAxes[i] = new PlotAxisConfiguration(QwtAxis::XBottom, i, this); + configureAxis(QwtAxis::XBottom, i); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, i), d_usingLeftAxisScales); connect(axisWidget(horizAxes[i]->axis()), SIGNAL(scaleDivChanged()), this, SLOT(_onXbottomAxisWidgetScaleDivChanged())); - QwtScaleWidget *scaleWidget = axisWidget(QwtAxisId(QwtPlot::xBottom, i)); + QwtScaleWidget *scaleWidget = axisWidget(QwtAxisId(QwtAxis::XBottom, i)); OscScaleDraw* osd = dynamic_cast(scaleWidget->scaleDraw()); osd->setFormatter(d_pf); osd->setUnitType("V"); @@ -265,7 +265,7 @@ HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) for (int i = 0; i < 2; ++i) { HistogramScaleDraw *hsd = new HistogramScaleDraw(); hsd->setColor(d_CurveColors[i]); - setAxisScaleDraw(QwtAxisId(QwtPlot::yLeft, i), hsd); + setAxisScaleDraw(QwtAxisId(QwtAxis::YLeft, i), hsd); } this->_updateXScales(100); @@ -277,8 +277,8 @@ HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) memset(d_ydata[i], 0, d_bins*sizeof(double)); d_histograms.push_back(new Histogram(QString("Data %1").arg(i), d_CurveColors[i])); - d_histograms[i]->setXAxis(QwtAxisId(QwtPlot::xBottom, i)); - d_histograms[i]->setYAxis(QwtAxisId(QwtPlot::yLeft, i)); + d_histograms[i]->setXAxis(QwtAxisId(QwtAxis::XBottom, i)); + d_histograms[i]->setYAxis(QwtAxisId(QwtAxis::YLeft, i)); d_histograms[i]->setOrientation(Qt::Horizontal); d_histograms[i]->attach(this); @@ -309,8 +309,8 @@ HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) for (unsigned int i = 0; i < d_histograms.size(); ++i) { d_zoomer.push_back(new HistogramDisplayZoomer(canvas(), 0, getLineColor(i))); - d_zoomer[i]->setAxes(QwtAxisId(QwtPlot::xBottom, i), - QwtAxisId(QwtPlot::yLeft, i)); + d_zoomer[i]->setAxes(QwtAxisId(QwtAxis::XBottom, i), + QwtAxisId(QwtAxis::YLeft, i)); connect(d_zoomer[i], SIGNAL(zoomed(QRectF)), this, SLOT(_onZoom(QRectF))); @@ -320,14 +320,14 @@ HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) _resetXAxisPoints(-1, 1); for (unsigned int i = 0; i < d_histograms.size(); ++i) { - setAxisScale(QwtAxisId(QwtPlot::xBottom, i), 0, 100); - setAxisScale(QwtAxisId(QwtPlot::yLeft, i), -10, 10); + setAxisScale(QwtAxisId(QwtAxis::XBottom, i), 0, 100); + setAxisScale(QwtAxisId(QwtAxis::YLeft, i), -10, 10); } - setAxisVisible(QwtAxisId(QwtPlot::yLeft, 0), false); - setAxisVisible(QwtAxisId(QwtPlot::yLeft, 1), false); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, 0), false); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, 1), false); + setAxisVisible(QwtAxisId(QwtAxis::YLeft, 0), false); + setAxisVisible(QwtAxisId(QwtAxis::YLeft, 1), false); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, 0), false); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, 1), false); insertLegend(nullptr); @@ -363,29 +363,29 @@ void HistogramDisplayPlot::setOrientation(Qt::Orientation orientation) } } - QwtInterval xInt = axisInterval(QwtPlot::xBottom); - QwtInterval yInt = axisInterval(QwtPlot::yLeft); - QwtInterval xInt2 = axisInterval(QwtAxisId(QwtPlot::xBottom, 1)); - QwtInterval yInt2 = axisInterval(QwtAxisId(QwtPlot::yLeft, 1)); + QwtInterval xInt = axisInterval(QwtAxis::XBottom); + QwtInterval yInt = axisInterval(QwtAxis::YLeft); + QwtInterval xInt2 = axisInterval(QwtAxisId(QwtAxis::XBottom, 1)); + QwtInterval yInt2 = axisInterval(QwtAxisId(QwtAxis::YLeft, 1)); if (orientation == Qt::Vertical) { - setAxisScale(QwtAxisId(QwtPlot::xBottom, 0), yInt.minValue(), yInt.maxValue()); - setAxisScale(QwtAxisId(QwtPlot::yLeft, 0), xInt.maxValue(), -xInt.minValue()); - setAxisScale(QwtAxisId(QwtPlot::xBottom, 1), yInt2.minValue(), yInt2.maxValue()); - setAxisScale(QwtAxisId(QwtPlot::yLeft, 1), xInt2.maxValue(), -xInt2.minValue()); + setAxisScale(QwtAxisId(QwtAxis::XBottom, 0), yInt.minValue(), yInt.maxValue()); + setAxisScale(QwtAxisId(QwtAxis::YLeft, 0), xInt.maxValue(), -xInt.minValue()); + setAxisScale(QwtAxisId(QwtAxis::XBottom, 1), yInt2.minValue(), yInt2.maxValue()); + setAxisScale(QwtAxisId(QwtAxis::YLeft, 1), xInt2.maxValue(), -xInt2.minValue()); - setAxisVisible(QwtAxisId(QwtPlot::yLeft, d_selected_channel), true); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, d_selected_channel), true); + setAxisVisible(QwtAxisId(QwtAxis::YLeft, d_selected_channel), true); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, d_selected_channel), true); } else { - setAxisScale(QwtPlot::xBottom, 0, yInt.maxValue()); - setAxisScale(QwtAxisId(QwtPlot::yLeft, 0), xInt.minValue(), xInt.maxValue()); - setAxisScale(QwtAxisId(QwtPlot::yLeft, 1), xInt.minValue(), xInt.maxValue()); - - setAxisVisible(QwtAxisId(QwtPlot::yLeft, 0), false); - setAxisVisible(QwtAxisId(QwtPlot::yLeft, 1), false); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, 0), false); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, 1), false); + setAxisScale(QwtAxis::XBottom, 0, yInt.maxValue()); + setAxisScale(QwtAxisId(QwtAxis::YLeft, 0), xInt.minValue(), xInt.maxValue()); + setAxisScale(QwtAxisId(QwtAxis::YLeft, 1), xInt.minValue(), xInt.maxValue()); + + setAxisVisible(QwtAxisId(QwtAxis::YLeft, 0), false); + setAxisVisible(QwtAxisId(QwtAxis::YLeft, 1), false); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, 0), false); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, 1), false); } _orientationChanged(); @@ -438,11 +438,11 @@ void HistogramDisplayPlot::setYaxisSpan(unsigned int chIdx, double bot, double t } if (d_orientation == Qt::Vertical) { - setAxisScale(QwtAxisId(QwtPlot::xBottom, chIdx), bot, top); + setAxisScale(QwtAxisId(QwtAxis::XBottom, chIdx), bot, top); return; } - setAxisScale(QwtAxisId(QwtPlot::yLeft, chIdx), bot, top); + setAxisScale(QwtAxisId(QwtAxis::YLeft, chIdx), bot, top); } @@ -453,7 +453,7 @@ void HistogramDisplayPlot::setXaxisSpan(double start, double stop) } for (int i = 0; i < d_histograms.size(); ++i) { - setAxisScale(QwtAxisId(QwtPlot::xBottom, i), start, stop); + setAxisScale(QwtAxisId(QwtAxis::XBottom, i), start, stop); } } @@ -555,8 +555,8 @@ HistogramDisplayPlot::plotNewData(const std::vector dataPoints, } double pr = h * 0.15; - if (abs(h - axisInterval(QwtAxisId(QwtPlot::yLeft, i)).maxValue()) > pr) { - setAxisScale(QwtAxisId(QwtPlot::yLeft, i), 0, h); + if (abs(h - axisInterval(QwtAxisId(QwtAxis::YLeft, i)).maxValue()) > pr) { + setAxisScale(QwtAxisId(QwtAxis::YLeft, i), 0, h); } } } @@ -624,10 +624,10 @@ HistogramDisplayPlot::_resetXAxisPoints(double left, double right) d_xdata[loc] = d_left + loc*d_width; } #if QWT_VERSION < 0x060100 - axisScaleDiv(QwtPlot::xBottom)->setInterval(d_left, d_right); + axisScaleDiv(QwtAxis::XBottom)->setInterval(d_left, d_right); #else /* QWT_VERSION < 0x060100 */ QwtScaleDiv scalediv(d_left, d_right); - setAxisScaleDiv(QwtPlot::xBottom, scalediv); + setAxisScaleDiv(QwtAxis::XBottom, scalediv); #endif /* QWT_VERSION < 0x060100 */ // Set up zoomer base for maximum unzoom x-axis @@ -635,11 +635,11 @@ HistogramDisplayPlot::_resetXAxisPoints(double left, double right) QRectF zbase = d_zoomer[0]->zoomBase(); if(d_semilogx) { - setAxisScale(QwtPlot::xBottom, 1e-1, d_right); + setAxisScale(QwtAxis::XBottom, 1e-1, d_right); zbase.setLeft(1e-1); } else { - setAxisScale(QwtPlot::xBottom, d_left, d_right); + setAxisScale(QwtAxis::XBottom, d_left, d_right); zbase.setLeft(d_left); } @@ -672,7 +672,7 @@ void HistogramDisplayPlot::_updateXScales(unsigned int totalSamples) { for (int i = 0; i < d_histograms.size(); ++i) { HistogramScaleDraw *hsd = dynamic_cast( - axisWidget(QwtAxisId(QwtPlot::yLeft, i))->scaleDraw()); + axisWidget(QwtAxisId(QwtAxis::YLeft, i))->scaleDraw()); if (hsd) { hsd->setTotalSamples(totalSamples); hsd->invalidateCache(); @@ -688,7 +688,7 @@ void HistogramDisplayPlot::_orientationChanged() min_h = std::min(min_h, d_histograms[i]->getMaxHeight()); } for (int i = 0; i < d_histograms.size(); ++i) { - setAxisScale(QwtAxisId(QwtPlot::xBottom, i), -min_h, 0); + setAxisScale(QwtAxisId(QwtAxis::XBottom, i), -min_h, 0); } } else { @@ -696,7 +696,7 @@ void HistogramDisplayPlot::_orientationChanged() for (int i = 0; i < d_histograms.size(); ++i) { int h = d_histograms[i]->getMaxHeight(); h += h * 0.2; - setAxisScale(QwtAxisId(QwtPlot::yLeft, i), 0, h); + setAxisScale(QwtAxisId(QwtAxis::YLeft, i), 0, h); } } @@ -734,8 +734,8 @@ void HistogramDisplayPlot::setSelectedChannel(unsigned int value) } for (int i = 0; i < d_histograms.size(); ++i) { - setAxisVisible(QwtAxisId(QwtPlot::yLeft, i), value == i); - setAxisVisible(QwtAxisId(QwtPlot::xBottom, i), value == i); + setAxisVisible(QwtAxisId(QwtAxis::YLeft, i), value == i); + setAxisVisible(QwtAxisId(QwtAxis::XBottom, i), value == i); } } @@ -758,13 +758,13 @@ HistogramDisplayPlot::setSemilogx(bool en) { d_semilogx = en; if(!d_semilogx) { - setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLinearScaleEngine); } else { #if QWT_VERSION < 0x060100 - setAxisScaleEngine(QwtPlot::xBottom, new QwtLog10ScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLog10ScaleEngine); #else /* QWT_VERSION < 0x060100 */ - setAxisScaleEngine(QwtPlot::xBottom, new QwtLogScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLogScaleEngine); #endif /* QWT_VERSION < 0x060100 */ } } @@ -776,20 +776,20 @@ HistogramDisplayPlot::setSemilogy(bool en) d_semilogy = en; #if QWT_VERSION < 0x060100 - double max = axisScaleDiv(QwtPlot::yLeft)->upperBound(); + double max = axisScaleDiv(QwtAxis::YLeft)->upperBound(); #else /* QWT_VERSION < 0x060100 */ - double max = axisScaleDiv(QwtPlot::yLeft).upperBound(); + double max = axisScaleDiv(QwtAxis::YLeft).upperBound(); #endif /* QWT_VERSION < 0x060100 */ if(!d_semilogy) { - setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLinearScaleEngine); setYaxis(-pow(10.0, max/10.0), pow(10.0, max/10.0)); } else { #if QWT_VERSION < 0x060100 - setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLog10ScaleEngine); #else /* QWT_VERSION < 0x060100 */ - setAxisScaleEngine(QwtPlot::yLeft, new QwtLogScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLogScaleEngine); #endif /* QWT_VERSION < 0x060100 */ setYaxis(1e-10, 10.0*log10(100*max)); } diff --git a/src/TimeDomainDisplayPlot.cc b/src/TimeDomainDisplayPlot.cc index ad200dd60a..ffcf9afed6 100644 --- a/src/TimeDomainDisplayPlot.cc +++ b/src/TimeDomainDisplayPlot.cc @@ -189,8 +189,8 @@ TimeDomainDisplayPlot::TimeDomainDisplayPlot(QWidget* parent, bool isdBgraph, un d_autoscale_state = false; // Reconfigure the bottom horizontal axis that was created by the base class - configureAxis(QwtPlot::xBottom, 0, pfXaxis); - configureAxis(QwtPlot::yLeft, 0, pfYaxis); + configureAxis(QwtAxis::XBottom, 0, pfXaxis); + configureAxis(QwtAxis::YLeft, 0, pfYaxis); d_xAxisUnit = ""; d_yAxisUnit = ""; @@ -558,11 +558,11 @@ TimeDomainDisplayPlot::_resetXAxisPoints(double*& xAxis, unsigned long long numP // QRectF zbase = d_zoomer->zoomBase(); // if(d_semilogx) { -// setAxisScale(QwtPlot::xBottom, 1e-1, d_numPoints*delt); +// setAxisScale(QwtAxis::XBottom, 1e-1, d_numPoints*delt); // zbase.setLeft(1e-1); // } // else { -// setAxisScale(QwtPlot::xBottom, 0, d_numPoints*delt); +// setAxisScale(QwtAxis::XBottom, 0, d_numPoints*delt); // zbase.setLeft(0); // } @@ -621,7 +621,7 @@ TimeDomainDisplayPlot::addZoomer(unsigned int zoomerIdx) d_zoomer[zoomerIdx]->setTrackerPen(getLineColor(zoomerIdx)); d_zoomer[zoomerIdx]->setEnabled(true); - d_zoomer[zoomerIdx]->setAxes(QwtAxisId(QwtPlot::xBottom, 0), QwtAxisId(QwtPlot::yLeft, zoomerIdx)); + d_zoomer[zoomerIdx]->setAxes(QwtAxisId(QwtAxis::XBottom, 0), QwtAxisId(QwtAxis::YLeft, zoomerIdx)); } void @@ -646,7 +646,7 @@ TimeDomainDisplayPlot::removeZoomer(unsigned int zoomerIdx) for (int i = 0; i < d_zoomer.size(); ++i) { if (d_zoomer[i]->isEnabled()) { - d_zoomer[i]->setAxes(QwtAxisId(QwtPlot::xBottom, 0), QwtAxisId(QwtPlot::yLeft, i)); + d_zoomer[i]->setAxes(QwtAxisId(QwtAxis::XBottom, 0), QwtAxisId(QwtAxis::YLeft, i)); d_zoomer[i]->setTrackerPen(getLineColor(i)); } } @@ -719,7 +719,7 @@ void TimeDomainDisplayPlot::setYaxisUnit(QString unitType) if (d_yAxisUnit != unitType) { d_yAxisUnit = unitType; - OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtPlot::yLeft)); + OscScaleDraw *scaleDraw = static_cast(this->axisScaleDraw(QwtAxis::YLeft)); if (scaleDraw) scaleDraw->setUnitType(d_yAxisUnit); } @@ -735,7 +735,7 @@ void TimeDomainDisplayPlot::setXaxisUnit(QString unitType) if (d_xAxisUnit != unitType) { d_xAxisUnit = unitType; - OscScaleDraw* scaleDraw = static_cast(this->axisScaleDraw(QwtPlot::xBottom)); + OscScaleDraw* scaleDraw = static_cast(this->axisScaleDraw(QwtAxis::XBottom)); if (scaleDraw) scaleDraw->setUnitType(d_xAxisUnit); } @@ -830,13 +830,13 @@ TimeDomainDisplayPlot::setSemilogx(bool en) { d_semilogx = en; if(!d_semilogx) { - setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLinearScaleEngine); } else { #if QWT_VERSION < 0x060100 - setAxisScaleEngine(QwtPlot::xBottom, new QwtLog10ScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLog10ScaleEngine); #else /* QWT_VERSION < 0x060100 */ - setAxisScaleEngine(QwtPlot::xBottom, new QwtLogScaleEngine); + setAxisScaleEngine(QwtAxis::XBottom, new QwtLogScaleEngine); #endif /*QWT_VERSION < 0x060100 */ } for (unsigned int i = 0; i < d_sinkManager.sinkListLength(); i++) @@ -850,20 +850,20 @@ TimeDomainDisplayPlot::setSemilogy(bool en) d_semilogy = en; #if QWT_VERSION < 0x060100 - double max = axisScaleDiv(QwtPlot::yLeft)->upperBound(); + double max = axisScaleDiv(QwtAxis::YLeft)->upperBound(); #else /* QWT_VERSION < 0x060100 */ - double max = axisScaleDiv(QwtPlot::yLeft).upperBound(); + double max = axisScaleDiv(QwtAxis::YLeft).upperBound(); #endif /* QWT_VERSION < 0x060100 */ if(!d_semilogy) { - setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLinearScaleEngine); setYaxis(-pow(10.0, max/10.0), pow(10.0, max/10.0)); } else { #if QWT_VERSION < 0x060100 - setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLog10ScaleEngine); #else /* QWT_VERSION < 0x060100 */ - setAxisScaleEngine(QwtPlot::yLeft, new QwtLogScaleEngine); + setAxisScaleEngine(QwtAxis::YLeft, new QwtLogScaleEngine); #endif /*QWT_VERSION < 0x060100 */ setYaxis(1e-10, 10.0*log10(max)); } @@ -930,7 +930,7 @@ bool TimeDomainDisplayPlot::isZoomerEnabled() void TimeDomainDisplayPlot::setZoomerVertAxis(int index) { - if (index < -1 || index >= axesCount(QwtPlot::yLeft)) + if (index < -1 || index >= axesCount(QwtAxis::YLeft)) return; for (unsigned int i = 0; i < d_zoomer.size(); ++i) @@ -946,7 +946,7 @@ QString TimeDomainDisplayPlot::timeScaleValueFormat(double value, int precision) QString TimeDomainDisplayPlot::timeScaleValueFormat(double value) { - OscScaleDraw *scale = static_cast(this->axisScaleDraw(QwtPlot::xBottom)); + OscScaleDraw *scale = static_cast(this->axisScaleDraw(QwtAxis::XBottom)); return d_yAxisFormatter->format(value, "", scale->getFloatPrecison()); } @@ -959,7 +959,7 @@ QString TimeDomainDisplayPlot::yAxisScaleValueFormat(double value, int precision QString TimeDomainDisplayPlot::yAxisScaleValueFormat(double value) { - OscScaleDraw *scale = static_cast(this->axisScaleDraw(QwtPlot::yLeft)); + OscScaleDraw *scale = static_cast(this->axisScaleDraw(QwtAxis::YLeft)); return d_xAxisFormatter->format(value, d_yAxisUnit, scale->getFloatPrecison()); } @@ -971,7 +971,7 @@ TimeDomainDisplayPlot::setYLabel(const std::string &label, std::string l = label; if(unit.length() > 0) l += " (" + unit + ")"; - setAxisTitle(QwtPlot::yLeft, QString(l.c_str())); + setAxisTitle(QwtAxis::YLeft, QString(l.c_str())); } void @@ -1380,7 +1380,7 @@ void TimeDomainDisplayPlot::configureAxis(int axisPos, int axisIdx, PrefixFormat unsigned int floatPrecision; unsigned int numDivs; - if (axisPos == QwtPlot::yLeft) { + if (axisPos == QwtAxis::YLeft) { d_xAxisFormatter = prefixFormatter; unit = d_yAxisUnit; floatPrecision = 2; @@ -1401,7 +1401,7 @@ void TimeDomainDisplayPlot::configureAxis(int axisPos, int axisIdx, PrefixFormat OscScaleDraw *scaleDraw = new OscScaleDraw(prefixFormatter, unit); scaleDraw->setFloatPrecision(floatPrecision); this->setAxisScaleDraw(axis, scaleDraw); - if (axisPos == QwtPlot::yLeft) { + if (axisPos == QwtAxis::YLeft) { //yLeft 0 has a different position than the rest, so we //give it a bigger minimum extent in order to align it with //the other yLeft axes. diff --git a/src/dbgraph.cpp b/src/dbgraph.cpp index e3ad9a69d4..9f7f4cd9d2 100644 --- a/src/dbgraph.cpp +++ b/src/dbgraph.cpp @@ -43,17 +43,17 @@ void dBgraph::setupVerticalBars() d_frequencyBar->setPen(frequencyLinePen); d_frequencyBar->setVisible(true); - d_frequencyBar->setMobileAxis(QwtPlot::xTop); + d_frequencyBar->setMobileAxis(QwtAxis::XTop); d_frequencyBar->setPixelPosition(0); d_plotBar->setPen(plotLinePen); - d_plotBar->setMobileAxis(QwtPlot::xTop); + d_plotBar->setMobileAxis(QwtAxis::XTop); connect(d_frequencyBar, &VertBar::pixelPositionChanged, this, &dBgraph::frequencyBarPositionChanged); - d_vBar1->setMobileAxis(QwtPlot::xTop); - d_vBar2->setMobileAxis(QwtPlot::xTop); + d_vBar1->setMobileAxis(QwtAxis::XTop); + d_vBar2->setMobileAxis(QwtAxis::XTop); } void dBgraph::setupReadouts() @@ -85,11 +85,11 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) delta_label(false), d_plotBarEnabled(true) { - enableAxis(QwtPlot::xBottom, false); - enableAxis(QwtPlot::xTop, true); + setAxisVisible(QwtAxis::XBottom, false); + setAxisVisible(QwtAxis::XTop, true); - setAxisAutoScale(QwtPlot::yLeft, false); - setAxisAutoScale(QwtPlot::xTop, false); + setAxisAutoScale(QwtAxis::YLeft, false); + setAxisAutoScale(QwtAxis::XTop, false); QColor plotColor; if (QIcon::themeName() == "scopy-default") { @@ -101,19 +101,19 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) EdgelessPlotGrid *grid = new EdgelessPlotGrid; grid->setMajorPen(plotColor, 1.0, Qt::DashLine); - grid->setXAxis(QwtPlot::xTop); + grid->setXAxis(QwtAxis::XTop); grid->attach(this); plotLayout()->setAlignCanvasToScales(true); curve.attach(this); curve.setRenderHint(QwtPlotItem::RenderAntialiased); - curve.setXAxis(QwtPlot::xTop); - curve.setYAxis(QwtPlot::yLeft); + curve.setXAxis(QwtAxis::XTop); + curve.setYAxis(QwtAxis::YLeft); reference.setRenderHint(QwtPlotItem::RenderAntialiased); - reference.setXAxis(QwtPlot::xTop); - reference.setYAxis(QwtPlot::yLeft); + reference.setXAxis(QwtAxis::XTop); + reference.setYAxis(QwtAxis::YLeft); reference.setPen(Qt::red, 1.5); thickness = 1; @@ -123,7 +123,7 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) OscScaleEngine *scaleLeft = new OscScaleEngine; setYaxisNumDiv(7); scaleLeft->setMajorTicksCount(7); - this->setAxisScaleEngine(QwtPlot::yLeft, + this->setAxisScaleEngine(QwtAxis::YLeft, static_cast(scaleLeft)); /* draw_x / draw_y: Outmost X / Y scales. Only draw the labels */ @@ -131,14 +131,14 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) draw_x->setFloatPrecision(2); draw_x->enableComponent(QwtAbstractScaleDraw::Ticks, false); draw_x->enableComponent(QwtAbstractScaleDraw::Backbone, false); - setAxisScaleDraw(QwtPlot::xTop, draw_x); + setAxisScaleDraw(QwtAxis::XTop, draw_x); draw_y = new OscScaleDraw("dB"); draw_y->setFloatPrecision(2); draw_y->enableComponent(QwtAbstractScaleDraw::Ticks, false); draw_y->enableComponent(QwtAbstractScaleDraw::Backbone, false); draw_y->setMinimumExtent(50); - setAxisScaleDraw(QwtPlot::yLeft, draw_y); + setAxisScaleDraw(QwtAxis::YLeft, draw_y); d_leftHandlesArea->setMinimumWidth(60); d_leftHandlesArea->setTopPadding(10); @@ -162,9 +162,9 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) /* Top/bottom scales must be sync'd to xTop; left/right scales * must be sync'd to yLeft */ if (i < 2) { - scaleItem->setXAxis(QwtPlot::xTop); + scaleItem->setXAxis(QwtAxis::XTop); } else { - scaleItem->setYAxis(QwtPlot::yLeft); + scaleItem->setYAxis(QwtAxis::YLeft); } scaleItem->scaleDraw()->enableComponent( @@ -194,18 +194,18 @@ dBgraph::dBgraph(QWidget *parent, bool isdBgraph) margins.setBottom(0); setContentsMargins(margins); - enableAxis(QwtPlot::yLeft, false); - enableAxis(QwtPlot::xTop, false); + setAxisVisible(QwtAxis::YLeft, false); + setAxisVisible(QwtAxis::XTop, false); - QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xTop); + QwtScaleWidget *scaleWidget = axisWidget(QwtAxis::XTop); const int fmw = QFontMetrics(scaleWidget->font()).width("-XXXX.XX XX"); scaleWidget->setMinBorderDist(fmw / 2, fmw / 2); setupVerticalBars(); setupReadouts(); - markerIntersection1->setAxes(QwtPlot::xTop, QwtPlot::yLeft); - markerIntersection2->setAxes(QwtPlot::xTop, QwtPlot::yLeft); + markerIntersection1->setAxes(QwtAxis::XTop, QwtAxis::YLeft); + markerIntersection2->setAxes(QwtAxis::XTop, QwtAxis::YLeft); } dBgraph::~dBgraph() @@ -249,8 +249,8 @@ void dBgraph::parametersOverrange(bool enable) void dBgraph::setAxesScales(double xmin, double xmax, double ymin, double ymax) { - setAxisScale(QwtPlot::xTop, xmin, xmax); - setAxisScale(QwtPlot::yLeft, ymin, ymax); + setAxisScale(QwtAxis::XTop, xmin, xmax); + setAxisScale(QwtAxis::YLeft, ymin, ymax); } void dBgraph::setAxesTitles(const QString& x, const QString& y) @@ -258,13 +258,13 @@ void dBgraph::setAxesTitles(const QString& x, const QString& y) QwtText xTitle(x); QwtText yTitle(y); - QFont font = axisTitle(QwtPlot::xTop).font(); + QFont font = axisTitle(QwtAxis::XTop).font(); font.setWeight(QFont::Normal); xTitle.setFont(font); yTitle.setFont(font); - setAxisTitle(QwtPlot::xTop, xTitle); - setAxisTitle(QwtPlot::yLeft, yTitle); + setAxisTitle(QwtAxis::XTop, xTitle); + setAxisTitle(QwtAxis::YLeft, yTitle); } void dBgraph::plot(double x, double y) @@ -347,7 +347,7 @@ void dBgraph::setShowZero(bool en) scaleLeft->setMajorTicksCount(7); scaleLeft->showZero(en); - this->setAxisScaleEngine(QwtPlot::yLeft, + this->setAxisScaleEngine(QwtAxis::YLeft, static_cast(scaleLeft)); replot(); @@ -404,32 +404,32 @@ double dBgraph::getThickness() QString dBgraph::xTitle() const { - return axisTitle(QwtPlot::xTop).text(); + return axisTitle(QwtAxis::XTop).text(); } QString dBgraph::yTitle() const { - return axisTitle(QwtPlot::yLeft).text(); + return axisTitle(QwtAxis::YLeft).text(); } void dBgraph::setXTitle(const QString& title) { QwtText xTitle(title); - QFont font = axisTitle(QwtPlot::xTop).font(); + QFont font = axisTitle(QwtAxis::XTop).font(); font.setWeight(QFont::Normal); xTitle.setFont(font); - setAxisTitle(QwtPlot::xTop, xTitle); + setAxisTitle(QwtAxis::XTop, xTitle); } void dBgraph::setYTitle(const QString& title) { QwtText yTitle(title); - QFont font = axisTitle(QwtPlot::xTop).font(); + QFont font = axisTitle(QwtAxis::XTop).font(); font.setWeight(QFont::Normal); yTitle.setFont(font); - setAxisTitle(QwtPlot::yLeft, yTitle); + setAxisTitle(QwtAxis::YLeft, yTitle); d_cursorReadouts->setVoltageCursor1LabelText(title.mid(0,3)+"1 = "); d_cursorReadouts->setVoltageCursor2LabelText(title.mid(0,3)+"2 = "); d_cursorReadouts->setDeltaVoltageLabelText("Δ"+title.mid(0,3)+" = "); @@ -438,13 +438,13 @@ void dBgraph::setYTitle(const QString& title) void dBgraph::setXMin(double val) { zoomer->resetZoom(); - setAxisScale(QwtPlot::xTop, val, xmax); + setAxisScale(QwtAxis::XTop, val, xmax); xmin = val; draw_x->invalidateCache(); zoomer->setZoomBase(); replot(); - auto div = axisScaleDiv(QwtPlot::xTop); + auto div = axisScaleDiv(QwtAxis::XTop); setXaxisNumDiv((div.ticks(2)).size() - 1); setXaxisMajorTicksPos(div.ticks(2)); } @@ -452,20 +452,20 @@ void dBgraph::setXMin(double val) void dBgraph::setXMax(double val) { zoomer->resetZoom(); - setAxisScale(QwtPlot::xTop, xmin, val); + setAxisScale(QwtAxis::XTop, xmin, val); xmax = val; draw_x->invalidateCache(); zoomer->setZoomBase(); replot(); - auto div = axisScaleDiv(QwtPlot::xTop); + auto div = axisScaleDiv(QwtAxis::XTop); setXaxisNumDiv((div.ticks(2)).size() - 1); setXaxisMajorTicksPos(div.ticks(2)); } void dBgraph::setYMin(double val) { - setAxisScale(QwtPlot::yLeft, val, ymax); + setAxisScale(QwtAxis::YLeft, val, ymax); ymin = val; replot(); d_leftHandlesArea->repaint(); @@ -478,7 +478,7 @@ void dBgraph::setYMin(double val) void dBgraph::setYMax(double val) { - setAxisScale(QwtPlot::yLeft, ymin, val); + setAxisScale(QwtAxis::YLeft, ymin, val); ymax = val; replot(); @@ -511,9 +511,9 @@ void dBgraph::useLogFreq(bool use_log_freq) { if (use_log_freq) { setPlotLogaritmic(true); - this->setAxisScaleEngine(QwtPlot::xTop, new QwtLogScaleEngine); + this->setAxisScaleEngine(QwtAxis::XTop, new QwtLogScaleEngine); replot(); - auto div = axisScaleDiv(QwtPlot::xTop); + auto div = axisScaleDiv(QwtAxis::XTop); setXaxisNumDiv((div.ticks(2)).size() - 1); setXaxisMajorTicksPos(div.ticks(2)); } else { @@ -521,7 +521,7 @@ void dBgraph::useLogFreq(bool use_log_freq) auto scaleTop = new OscScaleEngine; scaleTop->setMajorTicksCount(9); setXaxisNumDiv(8); - this->setAxisScaleEngine(QwtPlot::xTop, scaleTop); + this->setAxisScaleEngine(QwtAxis::XTop, scaleTop); } this->log_freq = use_log_freq; @@ -533,7 +533,7 @@ void dBgraph::useLogFreq(bool use_log_freq) // Use delta only when log scale is disabled and delta // label mode is enabled - auto sw = axisWidget(QwtPlot::xTop); + auto sw = axisWidget(QwtAxis::XTop); auto *sd = dynamic_cast(sw->scaleDraw()); sd->enableDeltaLabel(delta_label & (!use_log_freq)); sw->repaint(); @@ -547,7 +547,7 @@ void dBgraph::useDeltaLabel(bool use_delta) delta_label = use_delta; if (!log_freq) { - auto sw = axisWidget(QwtPlot::xTop); + auto sw = axisWidget(QwtAxis::XTop); auto *sd = dynamic_cast(sw->scaleDraw()); sd->enableDeltaLabel(use_delta); sw->repaint(); @@ -752,8 +752,8 @@ void dBgraph::setYAxisInterval(double min, double max, double correction) void dBgraph::scaleDivChanged() { QwtPlot *plt = static_cast((sender())->parent()); - QwtInterval intv = plt->axisInterval(QwtPlot::xTop); - this->setAxisScale(QwtPlot::xTop, intv.minValue(), intv.maxValue()); + QwtInterval intv = plt->axisInterval(QwtAxis::XTop); + this->setAxisScale(QwtAxis::XTop, intv.minValue(), intv.maxValue()); this->replot(); onVCursor1Moved(d_vBar1->plotCoord().x()); @@ -788,7 +788,7 @@ void dBgraph::showEvent(QShowEvent *event) { d_hCursorHandle1->updatePosition(); d_hCursorHandle2->updatePosition(); - auto sw = axisWidget(QwtPlot::xTop); + auto sw = axisWidget(QwtAxis::XTop); sw->scaleDraw()->invalidateCache(); sw->repaint(); } diff --git a/src/extendingplotzoomer.cpp b/src/extendingplotzoomer.cpp index ef325abea5..fa56c91acc 100644 --- a/src/extendingplotzoomer.cpp +++ b/src/extendingplotzoomer.cpp @@ -113,7 +113,7 @@ QPolygon ExtendingPlotZoomer::adjustedPoints(const QPolygon &points) const adjusted += topLeft; adjusted += bottomRight; - if (yAxis() == QwtAxisId(QwtPlot::yLeft, 0)) { + if (yAxis() == QwtAxisId(QwtAxis::YLeft, 0)) { cornerMarkers[0]->detach(); cornerMarkers[1]->detach(); cornerMarkers[2]->detach(); @@ -157,7 +157,7 @@ QPolygon ExtendingPlotZoomer::adjustedPoints(const QPolygon &points) const adjusted += topLeft; adjusted += bottomRight; - if (yAxis() == QwtAxisId(QwtPlot::yLeft, 0)) { + if (yAxis() == QwtAxisId(QwtAxis::YLeft, 0)) { cornerMarkers[0]->detach(); cornerMarkers[1]->detach(); cornerMarkers[2]->detach(); @@ -196,7 +196,7 @@ QPolygon ExtendingPlotZoomer::adjustedPoints(const QPolygon &points) const } - if (yAxis() == QwtAxisId(QwtPlot::yLeft, 0)) { + if (yAxis() == QwtAxisId(QwtAxis::YLeft, 0)) { extendMarkers[0]->detach(); extendMarkers[1]->detach(); diff --git a/src/gui/cursor_readouts.cpp b/src/gui/cursor_readouts.cpp index e9945679b7..352b59608d 100644 --- a/src/gui/cursor_readouts.cpp +++ b/src/gui/cursor_readouts.cpp @@ -39,8 +39,8 @@ CursorReadouts::CursorReadouts(QwtPlot *plot): freq_delta_visible(true), d_topLeft(QPoint(0, 0)), currentPosition(CustomPlotPositionButton::topLeft), - hAxis(QwtPlot::xBottom), - vAxis(QwtPlot::yLeft) + hAxis(QwtAxis::XBottom), + vAxis(QwtAxis::YLeft) { ui->setupUi(this); ui->TimeCursors->setParent(plot->canvas()); diff --git a/src/handlesareaextension.cpp b/src/handlesareaextension.cpp index 29797112cb..406aceb36f 100644 --- a/src/handlesareaextension.cpp +++ b/src/handlesareaextension.cpp @@ -41,7 +41,7 @@ bool XBottomRuller::draw(QPainter *painter, QWidget *owner) for (int i = 0; i < majorTicks.size(); ++i) { currentValue = majorTicks.at(i); - pointVal = plot->transform(QwtPlot::xBottom, currentValue) + leftP; + pointVal = plot->transform(QwtAxis::XBottom, currentValue) + leftP; const QString text = plot->formatXValue(currentValue, 2); @@ -75,7 +75,7 @@ bool XBottomRuller::draw(QPainter *painter, QWidget *owner) // PainterSaveRestore psr(painter); - const QwtInterval interval = plot->axisInterval(QwtPlot::xBottom); + const QwtInterval interval = plot->axisInterval(QwtAxis::XBottom); const int labels = plot->xAxisNumDiv() + 1; @@ -110,7 +110,7 @@ bool XBottomRuller::draw(QPainter *painter, QWidget *owner) } // get nr of major ticks - const int nrMajorTicks = plot->axisScaleDiv(QwtPlot::xBottom).ticks(QwtScaleDiv::MajorTick).size(); + const int nrMajorTicks = plot->axisScaleDiv(QwtAxis::XBottom).ticks(QwtScaleDiv::MajorTick).size(); const int midLabelTick = nrMajorTicks / 2; if (allLabelsTheSame) { @@ -240,7 +240,7 @@ bool XTopRuller::draw(QPainter *painter, QWidget *owner) for (int i = 0; i < majorTicks.size(); ++i) { currentValue = majorTicks.at(i); - pointVal = plot->transform(QwtPlot::xTop, currentValue) + leftP; + pointVal = plot->transform(QwtAxis::XTop, currentValue) + leftP; const QString text = plot->formatXValue(currentValue, 2); @@ -276,7 +276,7 @@ bool XTopRuller::draw(QPainter *painter, QWidget *owner) // PainterSaveRestore psr(painter); - const QwtInterval interval = plot->axisInterval(QwtPlot::xTop); + const QwtInterval interval = plot->axisInterval(QwtAxis::XTop); const int labels = plot->xAxisNumDiv() + 1; @@ -310,7 +310,7 @@ bool XTopRuller::draw(QPainter *painter, QWidget *owner) } // get nr of major ticks - const int nrMajorTicks = plot->axisScaleDiv(QwtPlot::xTop).ticks(QwtScaleDiv::MajorTick).size(); + const int nrMajorTicks = plot->axisScaleDiv(QwtAxis::XTop).ticks(QwtScaleDiv::MajorTick).size(); const int midLabelTick = nrMajorTicks / 2; if (allLabelsTheSame) { @@ -444,7 +444,7 @@ bool YLeftRuller::draw(QPainter *painter, QWidget *owner) for (int i = 0; i < majorTicks.size(); ++i) { currentValue = majorTicks.at(i); - pointVal = plot->transform(QwtPlot::yLeft, currentValue) + topP; + pointVal = plot->transform(QwtAxis::YLeft, currentValue) + topP; const QString text = plot->formatYValue(currentValue, 2); @@ -475,7 +475,7 @@ bool YLeftRuller::draw(QPainter *painter, QWidget *owner) else { - const QwtInterval interval = plot->axisInterval(QwtPlot::yLeft); + const QwtInterval interval = plot->axisInterval(QwtAxis::YLeft); const int labels = plot->yAxisNumDiv(); @@ -511,7 +511,7 @@ bool YLeftRuller::draw(QPainter *painter, QWidget *owner) } // get nr of major ticks - const int nrMajorTicks = plot->axisScaleDiv(QwtPlot::yLeft).ticks(QwtScaleDiv::MajorTick).size(); + const int nrMajorTicks = plot->axisScaleDiv(QwtAxis::YLeft).ticks(QwtScaleDiv::MajorTick).size(); const int midLabelTick = nrMajorTicks / 2; if (allLabelsTheSame) { diff --git a/src/logicanalyzer/annotationcurve.cpp b/src/logicanalyzer/annotationcurve.cpp index c490f9e9a9..ce5ea7ac7c 100644 --- a/src/logicanalyzer/annotationcurve.cpp +++ b/src/logicanalyzer/annotationcurve.cpp @@ -325,7 +325,7 @@ void AnnotationCurve::drawLines(QPainter *painter, const QwtScaleMap &xMap, cons mapper.setFlag( QwtPointMapper::RoundPoints, QwtPainter::roundingAlignment( painter ) ); mapper.setBoundingRect(canvasRect); - auto interval = plot()->axisInterval(QwtAxis::xBottom); + auto interval = plot()->axisInterval(QwtAxis::XBottom); QStringList titles; @@ -612,7 +612,7 @@ void AnnotationCurve::drawTwoSampleAnnotation(int row, const Annotation &ann, QP // END DRAW MULTI POINT annotation // DRAW LABEL - QwtInterval interval = plot()->axisInterval(QwtAxis::xBottom); + QwtInterval interval = plot()->axisInterval(QwtAxis::XBottom); const double bonus = xMap.invTransform(titleSize.width() + 5) - xMap.invTransform(0); @@ -690,7 +690,7 @@ void AnnotationCurve::drawOneSampleAnnotation(int row, const Annotation &ann, QP painter->save(); painter->setPen(QPen(QBrush(Qt::black), 20)); - QwtInterval interval = plot()->axisInterval(QwtAxis::xBottom); + QwtInterval interval = plot()->axisInterval(QwtAxis::XBottom); const double bonus = xMap.invTransform(titleSize.width() + 5) - xMap.invTransform(0); const double WidthInPoints = xMap.invTransform(m_traceHeight) - xMap.invTransform(0); diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 94cd0ff329..288c9275c4 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1350,8 +1350,8 @@ void LogicAnalyzer::setupUi() m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - m_plot.enableAxis(QwtPlot::yLeft, false); - m_plot.enableAxis(QwtPlot::xBottom, false); + m_plot.setAxisVisible(QwtAxis::YLeft, false); + m_plot.setAxisVisible(QwtAxis::XBottom, false); m_plot.setUsingLeftAxisScales(false); m_plot.enableLabels(false); @@ -1646,7 +1646,7 @@ void LogicAnalyzer::settingsPanelUpdate(int id) void LogicAnalyzer::updateBufferPreviewer(int64_t min, int64_t max) { // Time interval within the plot canvas - QwtInterval plotInterval = m_plot.axisInterval(QwtPlot::xBottom); + QwtInterval plotInterval = m_plot.axisInterval(QwtAxis::XBottom); // Time interval that represents the captured data QwtInterval dataInterval(0.0, 0.0); @@ -1696,7 +1696,7 @@ void LogicAnalyzer::initBufferScrolling() connect(m_bufferPreviewer, &BufferPreviewer::bufferMovedBy, [=](int value) { m_resetHorizAxisOffset = false; double moveTo = 0.0; - auto interval = m_plot.axisInterval(QwtPlot::xBottom); + auto interval = m_plot.axisInterval(QwtAxis::XBottom); double min = interval.minValue(); double max = interval.maxValue(); int width = m_bufferPreviewer->width(); diff --git a/src/logicanalyzer/logicdatacurve.cpp b/src/logicanalyzer/logicdatacurve.cpp index 8e94da8b00..60889ae296 100644 --- a/src/logicanalyzer/logicdatacurve.cpp +++ b/src/logicanalyzer/logicdatacurve.cpp @@ -169,7 +169,7 @@ void LogicDataCurve::drawLines(QPainter *painter, const QwtScaleMap &xMap, } if (edges.back().first + 1 == m_endSample - 1) { - displayedData += QPointF(plot()->axisInterval(QwtAxis::xBottom).maxValue(), displayedData.back().y()); + displayedData += QPointF(plot()->axisInterval(QwtAxis::XBottom).maxValue(), displayedData.back().y()); } painter->save(); @@ -193,7 +193,7 @@ void LogicDataCurve::drawLines(QPainter *painter, const QwtScaleMap &xMap, return; } - QwtInterval interval = plot()->axisInterval(QwtAxis::xBottom); + QwtInterval interval = plot()->axisInterval(QwtAxis::XBottom); int start = fromTimeToSample(interval.minValue()); int end = fromTimeToSample(interval.maxValue()); @@ -229,7 +229,7 @@ void LogicDataCurve::getSubsampledEdges(std::vector> & double dist = xMap.transform(fromSampleToTime(1)) - xMap.transform(fromSampleToTime(0)); - QwtInterval interval = plot()->axisInterval(QwtAxis::xBottom); + QwtInterval interval = plot()->axisInterval(QwtAxis::XBottom); uint64_t firstEdge = edgeAtX(fromTimeToSample(interval.minValue()), m_edges); uint64_t lastEdge = edgeAtX(fromTimeToSample(interval.maxValue()), m_edges); diff --git a/src/marker_controller.cpp b/src/marker_controller.cpp index 3d20650a52..30b6064b2e 100644 --- a/src/marker_controller.cpp +++ b/src/marker_controller.cpp @@ -118,9 +118,9 @@ void MarkerController::onPickerMoved(QPoint p) if (d_picked_mrk) { QPointF val = d_picked_mrk->plotPixelsToValue(p); QwtInterval xItv = plot()->axisScaleDiv( - QwtPlot::xBottom).interval(); + QwtAxis::XBottom).interval(); QwtInterval yItv = plot()->axisScaleDiv( - QwtPlot::yLeft).interval(); + QwtAxis::YLeft).interval(); // Make sure the marker does not leave the plot canvas if (val.x() < xItv.minValue()) { diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 20de208763..96554455de 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -581,9 +581,9 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, api->load(*settings); api->js_register(engine); - connect((m_dBgraph.getAxisWidget(QwtPlot::xTop)), SIGNAL(scaleDivChanged()), + connect((m_dBgraph.getAxisWidget(QwtAxis::XTop)), SIGNAL(scaleDivChanged()), &m_phaseGraph, SLOT(scaleDivChanged())); - connect((m_phaseGraph.getAxisWidget(QwtPlot::xTop)), SIGNAL(scaleDivChanged()), + connect((m_phaseGraph.getAxisWidget(QwtAxis::XTop)), SIGNAL(scaleDivChanged()), &m_dBgraph, SLOT(scaleDivChanged())); connect(&m_dBgraph,SIGNAL(resetZoom()),&m_phaseGraph,SLOT(onResetZoom())); diff --git a/src/networkanalyzerbufferviewer.cpp b/src/networkanalyzerbufferviewer.cpp index cf104f817a..b8c7e2cd72 100644 --- a/src/networkanalyzerbufferviewer.cpp +++ b/src/networkanalyzerbufferviewer.cpp @@ -220,12 +220,12 @@ void NetworkAnalyzerBufferViewer::_setupPlot() d_plot->insertLegend(nullptr); d_plot->setUsingLeftAxisScales(false); - int nrAxes = d_plot->axesCount(QwtPlot::yLeft); + int nrAxes = d_plot->axesCount(QwtAxis::YLeft); for (int i = 0; i < nrAxes; ++i) { - d_plot->setAxisVisible(QwtAxisId(QwtPlot::yLeft, i), + d_plot->setAxisVisible(QwtAxisId(QwtAxis::YLeft, i), false); } - d_plot->setAxisVisible(QwtAxisId(QwtPlot::xBottom, 0), + d_plot->setAxisVisible(QwtAxisId(QwtAxis::XBottom, 0), false); d_plot->plotLayout()->setAlignCanvasToScales(true); diff --git a/src/nyquistGraph.cpp b/src/nyquistGraph.cpp index a97068a67e..6023c65254 100644 --- a/src/nyquistGraph.cpp +++ b/src/nyquistGraph.cpp @@ -43,14 +43,14 @@ namespace adiscope { NyquistSamplesArray() : QwtArraySeriesData() {} void addSample(const QwtPointPolar& point) { - d_samples.push_back(point); + m_samples.push_back(point); } - void clear() { d_samples.clear(); } - void reserve(unsigned int nb) { d_samples.reserve(nb); } + void clear() { m_samples.clear(); } + void reserve(unsigned int nb) { m_samples.reserve(nb); } QRectF boundingRect() const; QwtPointPolar sample(size_t index) const { - return d_samples.at(index); + return m_samples.at(index); } }; } @@ -65,8 +65,8 @@ QRectF NyquistSamplesArray::boundingRect() const { double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0; - for (auto it = d_samples.begin(); - it != d_samples.end(); ++it) { + for (auto it = m_samples.begin(); + it != m_samples.end(); ++it) { double point_x = it->radius() * cos(it->azimuth()); double point_y = it->radius() * sin(it->azimuth()); diff --git a/src/osc_scale_zoomer.cpp b/src/osc_scale_zoomer.cpp index aeab9d2b84..0a169f4720 100644 --- a/src/osc_scale_zoomer.cpp +++ b/src/osc_scale_zoomer.cpp @@ -49,9 +49,9 @@ OscScaleZoomer::~OscScaleZoomer() QwtText OscScaleZoomer::trackerText(const QPoint& pos) const { const OscScaleDraw *draw_x = static_cast( - plot()->axisScaleDraw(QwtPlot::xTop)); + plot()->axisScaleDraw(QwtAxis::XTop)); const OscScaleDraw *draw_y = static_cast( - plot()->axisScaleDraw(QwtPlot::yLeft)); + plot()->axisScaleDraw(QwtAxis::YLeft)); QPointF dp = QwtPlotZoomer::invTransform(pos); QString text; diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index fae0596ba0..cee03c58d8 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -403,8 +403,8 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, for (unsigned int i = 0; i < nb_channels; i++) { plot.Curve(i)->setAxes( - QwtAxisId(QwtPlot::xBottom, 0), - QwtAxisId(QwtPlot::yLeft, i)); + QwtAxisId(QwtAxis::XBottom, 0), + QwtAxisId(QwtAxis::YLeft, i)); plot.addZoomer(i); probe_attenuation.push_back(1); auto multiply = gr::blocks::multiply_const_ff::make(1); @@ -420,13 +420,13 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, iio->unlock(); - plot.levelTriggerA()->setMobileAxis(QwtAxisId(QwtPlot::yLeft, 0)); + plot.levelTriggerA()->setMobileAxis(QwtAxisId(QwtAxis::YLeft, 0)); plot.setTriggerAEnabled(trigger_settings.analogEnabled()); plot.levelTriggerA()->setPosition(trigger_settings.level()); // TO DO: Give user the option to make these axes visible - plot.enableAxis(QwtPlot::yLeft, false); - plot.enableAxis(QwtPlot::xBottom, false); + plot.setAxisVisible(QwtAxis::YLeft, false); + plot.setAxisVisible(QwtAxis::XBottom, false); plot.setUsingLeftAxisScales(false); fft_plot.setMinimumHeight(250); @@ -967,7 +967,7 @@ int Oscilloscope::binSearchPointOnXaxis(double time) void Oscilloscope::resetHistogramDataPoints() { - QwtInterval xBot = plot.axisInterval(QwtPlot::xBottom); + QwtInterval xBot = plot.axisInterval(QwtAxis::XBottom); double zoomMinTime = xBot.minValue(); double zoomMaxTime = xBot.maxValue(); @@ -1109,8 +1109,8 @@ void Oscilloscope::add_ref_waveform(QString name, QVector xData, QVector probe_attenuation.push_back(1); plot.Curve(curve_id)->setAxes( - QwtAxisId(QwtPlot::xBottom, 0), - QwtAxisId(QwtPlot::yLeft, curve_id)); + QwtAxisId(QwtAxis::XBottom, 0), + QwtAxisId(QwtAxis::YLeft, curve_id)); plot.Curve(curve_id)->setTitle("REF " + QString::number(nb_ref_channels + 1)); plot.addZoomer(curve_id); plot.replot(); @@ -1227,8 +1227,8 @@ void Oscilloscope::add_ref_waveform(unsigned int chIdx) probe_attenuation.push_back(1); plot.Curve(curve_id)->setAxes( - QwtAxisId(QwtPlot::xBottom, 0), - QwtAxisId(QwtPlot::yLeft, curve_id)); + QwtAxisId(QwtAxis::XBottom, 0), + QwtAxisId(QwtAxis::YLeft, curve_id)); plot.Curve(curve_id)->setTitle("REF " + QString::number(nb_ref_channels + 1)); plot.addZoomer(curve_id); plot.replot(); @@ -2514,8 +2514,8 @@ void Oscilloscope::add_math_channel(const std::string& function) ui->chn_scales->addWidget(label); plot.Curve(curve_id)->setAxes( - QwtAxisId(QwtPlot::xBottom, 0), - QwtAxisId(QwtPlot::yLeft, curve_id)); + QwtAxisId(QwtAxis::XBottom, 0), + QwtAxisId(QwtAxis::YLeft, curve_id)); plot.Curve(curve_id)->setTitle("M " + QString::number(curve_number + 1)); plot.addZoomer(curve_id); plot.replot(); @@ -2739,8 +2739,8 @@ void Oscilloscope::onChannelWidgetDeleteClicked() i < nb_channels + nb_math_channels + nb_ref_channels; i++) { /* Update the curve-to-axis map */ plot.Curve(i)->setAxes( - QwtAxisId(QwtPlot::xBottom, 0), - QwtAxisId(QwtPlot::yLeft, i)); + QwtAxisId(QwtAxis::XBottom, 0), + QwtAxisId(QwtAxis::YLeft, i)); } onMeasurementSelectionListChanged(); @@ -2902,8 +2902,8 @@ void Oscilloscope::setFFT_params(bool force) fft_plot.setSampleRate(getSampleRate(), 1, ""); double start = 0; double stop = getSampleRate() / 2; - fft_plot.setAxisScale(QwtPlot::xBottom, start, stop); - fft_plot.setAxisScale(QwtPlot::yLeft, -200, 0, 10); + fft_plot.setAxisScale(QwtAxis::XBottom, start, stop); + fft_plot.setAxisScale(QwtAxis::YLeft, -200, 0, 10); fft_plot.zoomBaseUpdate(); } } @@ -3180,7 +3180,7 @@ void adiscope::Oscilloscope::on_boxMeasure_toggled(bool on) void Oscilloscope::onTriggerSourceChanged(int chnIdx) { - plot.levelTriggerA()->setMobileAxis(QwtAxisId(QwtPlot::yLeft, chnIdx)); + plot.levelTriggerA()->setMobileAxis(QwtAxisId(QwtAxis::YLeft, chnIdx)); trigger_settings.setChannelAttenuation(probe_attenuation[chnIdx]); if (chnAcCoupled.at(chnIdx)) { deactivateAcCouplingTrigger(); @@ -3411,7 +3411,7 @@ void adiscope::Oscilloscope::onVertScaleValueChanged(double value) xy_plot.setHorizUnitsPerDiv(value); } if (current_ch_widget == index_y) { - xy_plot.setVertUnitsPerDiv(value, QwtPlot::yLeft); + xy_plot.setVertUnitsPerDiv(value, QwtAxis::YLeft); } xy_plot.zoomBaseUpdate(); @@ -3484,7 +3484,7 @@ void Oscilloscope::onCmbMemoryDepthChanged(QString value) plot.cancelZoom(); if (zoom_level == 0) { - noZoomXAxisWidth = plot.axisInterval(QwtPlot::xBottom).width(); + noZoomXAxisWidth = plot.axisInterval(QwtAxis::XBottom).width(); } setFFT_params(true); @@ -3595,7 +3595,7 @@ void adiscope::Oscilloscope::onHorizScaleValueChanged(double value) } if (zoom_level == 0) { - noZoomXAxisWidth = plot.axisInterval(QwtPlot::xBottom).width(); + noZoomXAxisWidth = plot.axisInterval(QwtAxis::XBottom).width(); } /* Reconfigure the GNU Radio block to receive a different number of samples */ @@ -3669,7 +3669,7 @@ bool adiscope::Oscilloscope::gainUpdateNeeded() double offset = plot.VertOffset(current_ch_widget); QwtInterval hw_input_itv(-2.5 + offset, 2.5 + offset); QwtInterval plot_vert_itv = plot.axisScaleDiv( - QwtAxisId(QwtPlot::yLeft, current_ch_widget)).interval(); + QwtAxisId(QwtAxis::YLeft, current_ch_widget)).interval(); if (plot_vert_itv.minValue() < hw_input_itv.minValue() || plot_vert_itv.maxValue() > hw_input_itv.maxValue() || @@ -3690,8 +3690,8 @@ void Oscilloscope::updateXyPlotScales() { int x = gsettings_ui->cmb_x_channel->currentIndex(); int y = gsettings_ui->cmb_y_channel->currentIndex(); - auto xInterval = plot.axisInterval(QwtAxisId(QwtPlot::yLeft, x)); - auto yInterval = plot.axisInterval(QwtAxisId(QwtPlot::yLeft, y)); + auto xInterval = plot.axisInterval(QwtAxisId(QwtAxis::YLeft, x)); + auto yInterval = plot.axisInterval(QwtAxisId(QwtAxis::YLeft, y)); xy_plot.set_axis(xInterval.minValue(), xInterval.maxValue(), yInterval.minValue(), yInterval.maxValue()); } @@ -4879,8 +4879,8 @@ void Oscilloscope::scaleHistogramPlot(bool newData) } for (int i = 0; i < nb_channels; i++) { - double min = plot.axisInterval(QwtAxisId(QwtPlot::yLeft, i)).minValue(); - double max = plot.axisInterval(QwtAxisId(QwtPlot::yLeft, i)).maxValue(); + double min = plot.axisInterval(QwtAxisId(QwtAxis::YLeft, i)).minValue(); + double max = plot.axisInterval(QwtAxisId(QwtAxis::YLeft, i)).maxValue(); hist_plot.setYaxisSpan(i, min, max); } @@ -4994,7 +4994,7 @@ void Oscilloscope::onTriggerModeChanged(int mode) void Oscilloscope::updateBufferPreviewer() { // Time interval within the plot canvas - QwtInterval plotInterval = plot.axisInterval(QwtPlot::xBottom); + QwtInterval plotInterval = plot.axisInterval(QwtAxis::XBottom); // Time interval that represents the captured data QwtInterval dataInterval(0.0, 0.0); @@ -5060,7 +5060,7 @@ void Oscilloscope::updateGainMode() double offset = plot.VertOffset(current_ch_widget); QwtInterval hw_input_itv(-2.5 + offset, 2.5 + offset); QwtInterval plot_vert_itv = plot.axisScaleDiv( - QwtAxisId(QwtPlot::yLeft, current_ch_widget)).interval(); + QwtAxisId(QwtAxis::YLeft, current_ch_widget)).interval(); auto chn = static_cast(current_ch_widget); // If max signal span that can be captured is smaller than the plot @@ -5218,10 +5218,10 @@ void Oscilloscope::setup_xy_channels() { int x = gsettings_ui->cmb_x_channel->currentIndex(); int y = gsettings_ui->cmb_y_channel->currentIndex(); - QWidget *xsw = xy_plot.axisWidget(QwtPlot::xBottom); + QWidget *xsw = xy_plot.axisWidget(QwtAxis::XBottom); xsw->setStyleSheet( QString("color: %1").arg(plot.getLineColor(x).name())); - QWidget *ysw = xy_plot.axisWidget(QwtPlot::yLeft); + QWidget *ysw = xy_plot.axisWidget(QwtAxis::YLeft); ysw->setStyleSheet( QString("color: %1").arg(plot.getLineColor(y).name())); @@ -5230,7 +5230,7 @@ void Oscilloscope::setup_xy_channels() onXY_view_toggled(true); xy_plot.setHorizUnitsPerDiv(plot.VertUnitsPerDiv(x)); - xy_plot.setVertUnitsPerDiv(plot.VertUnitsPerDiv(y), QwtPlot::yLeft); + xy_plot.setVertUnitsPerDiv(plot.VertUnitsPerDiv(y), QwtAxis::YLeft); updateXyPlotScales(); } diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index d5866889ba..b7922fe179 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Analog Devices Inc. + * CopQwtAxis::YRight (c) 2019 Analog Devices Inc. * * This file is part of Scopy * (see http://www.github.com/analogdevicesinc/scopy). @@ -189,7 +189,7 @@ CapturePlot::CapturePlot(QWidget *parent, bool isdBgraph, unsigned int xNumDivs [=]() { double pos = d_timeTriggerHandle->position(); - QwtScaleMap xMap = this->canvasMap(QwtAxisId(QwtPlot::xBottom, 0)); + QwtScaleMap xMap = this->canvasMap(QwtAxisId(QwtAxis::XBottom, 0)); double min = -(xAxisNumDiv() / 2.0) * HorizUnitsPerDiv(); double max = (xAxisNumDiv() / 2.0) * HorizUnitsPerDiv(); @@ -361,7 +361,7 @@ CapturePlot::CapturePlot(QWidget *parent, bool isdBgraph, unsigned int xNumDivs this, &CapturePlot::onDigitalChannelAdded); installEventFilter(this); - QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xBottom); + QwtScaleWidget *scaleWidget = axisWidget(QwtAxis::XBottom); const int fmw = QFontMetrics(scaleWidget->font()).width("-XXX.XXX XX"); scaleWidget->setMinBorderDist(fmw / 2 + 30, fmw / 2 + 30); @@ -375,20 +375,20 @@ CapturePlot::CapturePlot(QWidget *parent, bool isdBgraph, unsigned int xNumDivs /* configure the measurement gates */ leftGate = new QwtPlotShapeItem(); - leftGate->setAxes(QwtPlot::xBottom,QwtPlot::yRight); - leftGateRect.setTop(axisScaleDiv(yRight).upperBound()); - leftGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); - leftGateRect.setLeft(axisScaleDiv(xBottom).lowerBound()); + leftGate->setAxes(QwtAxis::XBottom,QwtAxis::YRight); + leftGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + leftGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); + leftGateRect.setLeft(axisScaleDiv(QwtAxis::XBottom).lowerBound()); leftGateRect.setRight(d_gateBar1->plotCoord().x()); leftGate->setRect(leftGateRect); leftGate->setBrush(gateBrush); rightGate = new QwtPlotShapeItem(); - rightGate->setAxes(QwtPlot::xBottom,QwtPlot::yRight); - rightGateRect.setTop(axisScaleDiv(yRight).upperBound()); - rightGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); + rightGate->setAxes(QwtAxis::XBottom,QwtAxis::YRight); + rightGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + rightGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); rightGateRect.setLeft(d_gateBar2->plotCoord().x()); - rightGateRect.setRight(axisScaleDiv(xBottom).upperBound()); + rightGateRect.setRight(axisScaleDiv(QwtAxis::XBottom).upperBound()); rightGate->setRect(rightGateRect); rightGate->setBrush(gateBrush); @@ -431,7 +431,7 @@ void CapturePlot::replot() return; } - const QwtInterval interval = axisInterval(QwtPlot::xBottom); + const QwtInterval interval = axisInterval(QwtAxis::XBottom); if (interval.minValue() != d_xAxisInterval.first || interval.maxValue() != d_xAxisInterval.second) { @@ -599,9 +599,9 @@ void CapturePlot::onGateBar1Moved(double value) } //update gate handle - leftGateRect.setTop(axisScaleDiv(yRight).upperBound()); - leftGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); - leftGateRect.setLeft(axisScaleDiv(xBottom).lowerBound()); + leftGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + leftGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); + leftGateRect.setLeft(axisScaleDiv(QwtAxis::XBottom).lowerBound()); leftGateRect.setRight(value); leftGate->setRect(leftGateRect); @@ -611,8 +611,8 @@ void CapturePlot::onGateBar1Moved(double value) double minTime = 0; if (n == 0) { - maxTime = axisScaleDiv(xBottom).upperBound(); - minTime = axisScaleDiv(xBottom).lowerBound(); + maxTime = axisScaleDiv(QwtAxis::XBottom).upperBound(); + minTime = axisScaleDiv(QwtAxis::XBottom).lowerBound(); } else { maxTime = Curve(d_selected_channel)->data()->sample(n-1).x(); minTime = Curve(d_selected_channel)->data()->sample(0).x(); @@ -628,7 +628,7 @@ void CapturePlot::onGateBar1Moved(double value) value_gateLeft = value; //find the percentage of the gate in relation with plot width - double width = (value - axisScaleDiv(xBottom).lowerBound()) / (axisScaleDiv(xBottom).upperBound() - axisScaleDiv(xBottom).lowerBound()); + double width = (value - axisScaleDiv(QwtAxis::XBottom).lowerBound()) / (axisScaleDiv(QwtAxis::XBottom).upperBound() - axisScaleDiv(QwtAxis::XBottom).lowerBound()); Q_EMIT leftGateChanged(width); d_hGatingHandle1->setTimeValue(d_gateBar1->plotCoord().x()); @@ -643,10 +643,10 @@ void CapturePlot::onGateBar2Moved(double value) } //update gate handle - rightGateRect.setTop(axisScaleDiv(yRight).upperBound()); - rightGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); + rightGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + rightGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); rightGateRect.setLeft(value); - rightGateRect.setRight(axisScaleDiv(xBottom).upperBound()); + rightGateRect.setRight(axisScaleDiv(QwtAxis::XBottom).upperBound()); rightGate->setRect(rightGateRect); int n = Curve(d_selected_channel)->data()->size(); @@ -655,8 +655,8 @@ void CapturePlot::onGateBar2Moved(double value) double minTime = 0; if (n == 0) { - maxTime = axisScaleDiv(xBottom).upperBound(); - minTime = axisScaleDiv(xBottom).lowerBound(); + maxTime = axisScaleDiv(QwtAxis::XBottom).upperBound(); + minTime = axisScaleDiv(QwtAxis::XBottom).lowerBound(); } else { maxTime = Curve(d_selected_channel)->data()->sample(n-1).x(); minTime = Curve(d_selected_channel)->data()->sample(0).x(); @@ -672,7 +672,7 @@ void CapturePlot::onGateBar2Moved(double value) value_gateRight = value; //find the percentage of the gate in relation with plot width - double width = (axisScaleDiv(xBottom).upperBound() - value) / (axisScaleDiv(xBottom).upperBound() - axisScaleDiv(xBottom).lowerBound()); + double width = (axisScaleDiv(QwtAxis::XBottom).upperBound() - value) / (axisScaleDiv(QwtAxis::XBottom).upperBound() - axisScaleDiv(QwtAxis::XBottom).lowerBound()); Q_EMIT rightGateChanged(width); d_hGatingHandle2->setTimeValue(d_gateBar2->plotCoord().x()); @@ -747,7 +747,7 @@ bool CapturePlot::measurementsEnabled() void CapturePlot::onTimeTriggerHandlePosChanged(int pos) { - QwtScaleMap xMap = this->canvasMap(QwtAxisId(QwtPlot::xBottom, 0)); + QwtScaleMap xMap = this->canvasMap(QwtAxisId(QwtAxis::XBottom, 0)); double min = -(xAxisNumDiv() / 2.0) * HorizUnitsPerDiv(); double max = (xAxisNumDiv() / 2.0) * HorizUnitsPerDiv(); @@ -793,12 +793,12 @@ void CapturePlot::showEvent(QShowEvent *event) void CapturePlot::printWithNoBackground(const QString& toolName, bool editScaleDraw) { QwtPlotMarker detailsMarker; - detailsMarker.setAxes(QwtPlot::xBottom, QwtPlot::yLeft); + detailsMarker.setAxes(QwtAxis::XBottom, QwtAxis::YLeft); detailsMarker.attach(this); - double xMarker = axisInterval(QwtPlot::xBottom).maxValue(); - double length = axisInterval(QwtPlot::xBottom).maxValue() - axisInterval(QwtPlot::xBottom).minValue(); + double xMarker = axisInterval(QwtAxis::XBottom).maxValue(); + double length = axisInterval(QwtAxis::XBottom).maxValue() - axisInterval(QwtAxis::XBottom).minValue(); xMarker -= (0.2 * length); - double yMarker = axisInterval(QwtPlot::yLeft).maxValue(); + double yMarker = axisInterval(QwtAxis::YLeft).maxValue(); yMarker -= (0.1 * yMarker); detailsMarker.setValue(xMarker, yMarker); QwtText text(d_timeBaseLabel->text() + " " + d_sampleRateLabel->text()); @@ -849,11 +849,11 @@ void CapturePlot::enableXaxisLabels() void CapturePlot::enableAxisLabels(bool enabled) { - enableAxis(QwtPlot::xBottom, enabled); + setAxisVisible(QwtAxis::XBottom, enabled); if (!enabled) { - int nrAxes = axesCount(QwtPlot::yLeft); + int nrAxes = axesCount(QwtAxis::YLeft); for (int i = 0; i < nrAxes; ++i) { - setAxisVisible(QwtAxisId(QwtPlot::yLeft, i), + setAxisVisible(QwtAxisId(QwtAxis::YLeft, i), enabled); } } @@ -935,7 +935,7 @@ void CapturePlot::setActiveVertAxis(unsigned int axisIdx, bool selected) DisplayPlot::setActiveVertAxis(axisIdx, selected); updateHandleAreaPadding(d_labelsEnabled); if (d_labelsEnabled) { - enableAxis(QwtPlot::xBottom, true); + setAxisVisible(QwtAxis::XBottom, true); } } @@ -944,21 +944,21 @@ void CapturePlot::showYAxisWidget(unsigned int axisIdx, bool en) if (!d_labelsEnabled) return; - setAxisVisible(QwtAxisId(QwtPlot::yLeft, axisIdx), + setAxisVisible(QwtAxisId(QwtAxis::YLeft, axisIdx), en); - int nrAxes = axesCount(QwtPlot::yLeft); + int nrAxes = axesCount(QwtAxis::YLeft); bool allAxisDisabled = true; for (int i = 0; i < nrAxes; ++i) - if (isAxisVisible(QwtAxisId(QwtPlot::yLeft, i))) + if (isAxisVisible(QwtAxisId(QwtAxis::YLeft, i))) allAxisDisabled = false; if (allAxisDisabled) { - setAxisVisible(QwtPlot::xBottom, false); + setAxisVisible(QwtAxis::XBottom, false); updateHandleAreaPadding(false); } if (en) { - setAxisVisible(QwtPlot::xBottom, true); + setAxisVisible(QwtAxis::XBottom, true); } } @@ -966,16 +966,16 @@ void CapturePlot::updateHandleAreaPadding(bool enabled) { double xAxisBonusWidth = 0.0; - if (axisEnabled(QwtPlot::xBottom)) { - if (!axisEnabled(QwtPlot::yLeft)) { + if (isAxisVisible(QwtAxis::XBottom)) { + if (!isAxisVisible(QwtAxis::YLeft)) { xAxisBonusWidth = 65.0; } } if (enabled) { - d_bottomHandlesArea->setLeftPadding(50 + axisWidget(QwtAxisId(QwtPlot::yLeft, d_activeVertAxis))->width()); - d_topGateHandlesArea->setLeftPadding(90 + axisWidget(QwtAxisId(QwtPlot::yLeft, d_activeVertAxis))->width()); - QwtScaleWidget *scaleWidget = axisWidget(QwtPlot::xBottom); + d_bottomHandlesArea->setLeftPadding(50 + axisWidget(QwtAxisId(QwtAxis::YLeft, d_activeVertAxis))->width()); + d_topGateHandlesArea->setLeftPadding(90 + axisWidget(QwtAxisId(QwtAxis::YLeft, d_activeVertAxis))->width()); + QwtScaleWidget *scaleWidget = axisWidget(QwtAxis::XBottom); const int fmw = QFontMetrics(scaleWidget->font()).width("-XX.XX XX"); const int fmh = QFontMetrics(scaleWidget->font()).height(); d_bottomHandlesArea->setRightPadding(50 + fmw/2 + d_bonusWidth); @@ -1018,12 +1018,12 @@ void CapturePlot::updateHandleAreaPadding(bool enabled) void CapturePlot::updateGateMargins(){ /* update the size of the gates */ - leftGateRect.setTop(axisScaleDiv(yRight).upperBound()); - leftGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); + leftGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + leftGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); leftGate->setRect(leftGateRect); - rightGateRect.setTop(axisScaleDiv(yRight).upperBound()); - rightGateRect.setBottom(axisScaleDiv(yRight).lowerBound()); + rightGateRect.setTop(axisScaleDiv(QwtAxis::YRight).upperBound()); + rightGateRect.setBottom(axisScaleDiv(QwtAxis::YRight).lowerBound()); rightGate->setRect(rightGateRect); replot(); @@ -1108,7 +1108,7 @@ void CapturePlot::addToGroup(int currentGroup, int toAdd) void CapturePlot::onDigitalChannelAdded(int chnIdx) { setLeftVertAxesCount(d_ydata.size() + d_ref_ydata.size() + chnIdx + 1); - setAxisScale( QwtAxisId(QwtPlot::yLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx), -5, 5); + setAxisScale( QwtAxisId(QwtAxis::YLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx), -5, 5); replot(); QColor chnColor; @@ -1120,14 +1120,14 @@ void CapturePlot::onDigitalChannelAdded(int chnIdx) QwtPlotCurve *curve = getDigitalPlotCurve(chnIdx); GenericLogicPlotCurve *logicCurve = dynamic_cast(curve); - curve->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx)); + curve->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx)); /* Channel offset widget */ HorizBar *chOffsetBar = new HorizBar(this); d_symbolCtrl->attachSymbol(chOffsetBar); chOffsetBar->setCanLeavePlot(true); chOffsetBar->setVisible(false); - chOffsetBar->setMobileAxis(QwtAxisId(QwtPlot::yLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx)); + chOffsetBar->setMobileAxis(QwtAxisId(QwtAxis::YLeft, d_ydata.size() + d_ref_ydata.size() + chnIdx)); d_offsetBars.push_back(chOffsetBar); RoundedHandleV *chOffsetHdl = new RoundedHandleV( @@ -1189,9 +1189,9 @@ void CapturePlot::onDigitalChannelAdded(int chnIdx) // qDebug() << pos; - QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtPlot::yLeft, chn_id)); + QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtAxis::YLeft, chn_id)); - auto y = axisInterval(QwtAxisId(QwtPlot::yLeft, chn_id)); + auto y = axisInterval(QwtAxisId(QwtAxis::YLeft, chn_id)); // double min = -(yAxisNumDiv() / 2.0) * VertUnitsPerDiv(0); // double max = (yAxisNumDiv() / 2.0) * VertUnitsPerDiv(0); @@ -1355,8 +1355,8 @@ bool CapturePlot::endGroupSelection(bool moveAnnotationCurvesLast) for (const auto &group : qAsConst(d_groupHandles)) { // Add group marker - QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtPlot::yLeft, 0)); - const QwtInterval y = axisInterval(QwtAxisId(QwtPlot::yLeft, 0)); + QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtAxis::YLeft, 0)); + const QwtInterval y = axisInterval(QwtAxisId(QwtAxis::YLeft, 0)); const double min = y.minValue(); const double max = y.maxValue(); yMap.setScaleInterval(min, max); @@ -1368,7 +1368,7 @@ bool CapturePlot::endGroupSelection(bool moveAnnotationCurvesLast) QwtPlotZoneItem *groupMarker = new QwtPlotZoneItem(); d_groupMarkers.push_back(groupMarker); - groupMarker->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, 0)); + groupMarker->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, 0)); groupMarker->setPen(QColor(74, 100, 255, 30), 2.0); groupMarker->setBrush(QBrush(QColor(74, 100, 255, 10))); groupMarker->setInterval(y2, y1); @@ -1606,8 +1606,8 @@ void CapturePlot::handleInGroupChangedPosition(int position) } // update plot marker - QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtPlot::yLeft, 0)); - const QwtInterval y = axisInterval(QwtAxisId(QwtPlot::yLeft, 0)); + QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtAxis::YLeft, 0)); + const QwtInterval y = axisInterval(QwtAxisId(QwtAxis::YLeft, 0)); const double min = y.minValue(); const double max = y.maxValue(); yMap.setScaleInterval(min, max); @@ -1645,11 +1645,11 @@ void adiscope::CapturePlot::pushBackNewOffsetWidgets(RoundedHandleV *chOffsetHdl d_offsetHandles.insert(indexOfNewChannel, chOffsetHdl); for (int i = 0; i < d_offsetBars.size(); ++i) { - d_offsetBars[i]->setMobileAxis(QwtAxisId(QwtPlot::yLeft, i)); + d_offsetBars[i]->setMobileAxis(QwtAxisId(QwtAxis::YLeft, i)); } for (int i = 0; i < d_logic_curves.size(); ++i) { - d_logic_curves[i]->setAxes(QwtPlot::xBottom, QwtAxisId(QwtPlot::yLeft, d_ydata.size() + d_ref_ydata.size() + i)); + d_logic_curves[i]->setAxes(QwtAxis::XBottom, QwtAxisId(QwtAxis::YLeft, d_ydata.size() + d_ref_ydata.size() + i)); } } @@ -1663,7 +1663,7 @@ void CapturePlot::onChannelAdded(int chnIdx) d_symbolCtrl->attachSymbol(chOffsetBar); chOffsetBar->setCanLeavePlot(true); chOffsetBar->setVisible(false); - chOffsetBar->setMobileAxis(QwtAxisId(QwtPlot::yLeft, chnIdx)); + chOffsetBar->setMobileAxis(QwtAxisId(QwtAxis::YLeft, chnIdx)); RoundedHandleV *chOffsetHdl = new RoundedHandleV( QPixmap(":/icons/handle_right_arrow.svg"), @@ -1681,7 +1681,7 @@ void CapturePlot::onChannelAdded(int chnIdx) if (chn_id < 0) return; - QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtPlot::yLeft, chn_id)); + QwtScaleMap yMap = this->canvasMap(QwtAxisId(QwtAxis::YLeft, chn_id)); double min = -(yAxisNumDiv() / 2.0) * VertUnitsPerDiv(chn_id); double max = (yAxisNumDiv() / 2.0) * VertUnitsPerDiv(chn_id); @@ -1823,7 +1823,7 @@ void CapturePlot::removeOffsetWidgets(int chnIdx) return; HorizBar *bar = d_offsetBars.takeAt(chnIdx); - bar->setMobileAxis(QwtAxisId(QwtPlot::yLeft, 0)); + bar->setMobileAxis(QwtAxisId(QwtAxis::YLeft, 0)); d_symbolCtrl->detachSymbol(bar); delete bar; delete(d_offsetHandles.takeAt(chnIdx)); diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 7cae0fbbd6..33dd8c689a 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -274,8 +274,8 @@ void PatternGenerator::setupUi() m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - m_plot.enableAxis(QwtPlot::yLeft, false); - m_plot.enableAxis(QwtPlot::xBottom, false); + m_plot.setAxisVisible(QwtAxis::YLeft, false); + m_plot.setAxisVisible(QwtAxis::XBottom, false); m_plot.setUsingLeftAxisScales(false); m_plot.enableLabels(false); diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 4d01ad0ffa..ae18c16a44 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -596,7 +596,7 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, m_plot->setOffsetHandleVisible(0,false); m_plot->setOffsetHandleVisible(1,false); - m_plot->enableAxis(QwtPlot::yLeft, false); + m_plot->setAxisVisible(QwtAxis::YLeft, false); m_plot->setActiveVertAxis(0); m_plot->enableTimeTrigger(false); diff --git a/src/sismograph.cpp b/src/sismograph.cpp index 131261eef7..14acac855a 100644 --- a/src/sismograph.cpp +++ b/src/sismograph.cpp @@ -29,19 +29,19 @@ using namespace adiscope; Sismograph::Sismograph(QWidget *parent) : QwtPlot(parent), curve("data"), sampleRate(10.0) { - enableAxis(QwtPlot::xBottom, false); - enableAxis(QwtPlot::xTop, true); + setAxisVisible(QwtAxis::XBottom, false); + setAxisVisible(QwtAxis::XTop, true); - setAxisTitle(QwtPlot::xTop, tr("Voltage (V)")); - setAxisTitle(QwtPlot::yLeft, tr("Time (s)")); + setAxisTitle(QwtAxis::XTop, tr("Voltage (V)")); + setAxisTitle(QwtAxis::YLeft, tr("Time (s)")); - setAxisAutoScale(QwtPlot::yLeft, false); + setAxisAutoScale(QwtAxis::YLeft, false); - setAxisAutoScale(QwtPlot::xTop, false); - setAxisScale(QwtPlot::xTop, -0.1, +0.1); + setAxisAutoScale(QwtAxis::XTop, false); + setAxisScale(QwtAxis::XTop, -0.1, +0.1); QVector divs; - QwtScaleEngine *engine = axisScaleEngine(QwtPlot::xTop); + QwtScaleEngine *engine = axisScaleEngine(QwtAxis::XTop); divs.push_back(engine->divideScale(-0.1, +0.1, 5, 5)); divs.push_back(engine->divideScale(-1.0, +1.0, 5, 5)); divs.push_back(engine->divideScale(-5.0, +5.0, 10, 2)); @@ -57,7 +57,7 @@ Sismograph::Sismograph(QWidget *parent) : QwtPlot(parent), plotLayout()->setAlignCanvasToScales(true); curve.attach(this); - curve.setXAxis(QwtPlot::xTop); + curve.setXAxis(QwtAxis::XTop); } Sismograph::~Sismograph() @@ -91,7 +91,7 @@ void Sismograph::setNumSamples(int num) ydata.resize(numSamples + 1); xdata.reserve(numSamples + 1); - setAxisScale(QwtPlot::yLeft, (double) numSamples / sampleRate, 0.0); + setAxisScale(QwtAxis::YLeft, (double) numSamples / sampleRate, 0.0); setSampleRate(sampleRate); replot(); @@ -125,7 +125,7 @@ void Sismograph::setColor(const QColor& color) void Sismograph::updateScale(const QwtScaleDiv div) { - setAxisScale(QwtPlot::xTop, div.lowerBound(), div.upperBound()); + setAxisScale(QwtAxis::XTop, div.lowerBound(), div.upperBound()); } void Sismograph::setLineWidth(qreal width) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 56910df4fe..758e0e18b8 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -366,7 +366,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, startStopRange = new StartStopRangeWidget(0); connect(startStopRange, &StartStopRangeWidget::rangeChanged, [=](double start, double stop){ fft_plot->setStartStop(start, stop); - fft_plot->setAxisScale(QwtPlot::xBottom, start, stop); + fft_plot->setAxisScale(QwtAxis::XBottom, start, stop); fft_plot->replot(); fft_plot->bottomHandlesArea()->repaint(); @@ -2864,10 +2864,10 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) ui->divisionWidget->setCurrentIndex(stackedWidgetCurrentIdx); top_scale->setValue(2.5E1); bottom_scale->setValue(1E-12); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_scale->value(), top_scale->value()); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom_scale->value(), top_scale->value()); fft_plot->replot(); - fft_plot->setYaxisMajorTicksPos(fft_plot->axisScaleDiv(QwtPlot::yLeft).ticks(2)); + fft_plot->setYaxisMajorTicksPos(fft_plot->axisScaleDiv(QwtAxis::YLeft).ticks(2)); fft_plot->leftHandlesArea()->repaint(); break; @@ -2899,17 +2899,17 @@ void SpectrumAnalyzer::on_cmb_units_currentIndexChanged(const QString& unit) case FftDisplayPlot::VRMS: bottom->setValue(-10); unit_per_div->setValue(2); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom->value(), top->value()); break; case FftDisplayPlot::VROOTHZ: top_scale->setValue(2.5E1); bottom_scale->setValue(1E-12); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_scale->value(), top_scale->value()); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom_scale->value(), top_scale->value()); break; default: top->setValue(0); bottom->setValue(-200); - fft_plot->setAxisScale(QwtPlot::yLeft, bottom->value(), top->value()); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom->value(), top->value()); break; } }); @@ -2989,10 +2989,10 @@ void SpectrumAnalyzer::onTopValueChanged(double top_value) bottom_value = bottom_scale->value(); } - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_value, top_value); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom_value, top_value); fft_plot->replot(); - auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); + auto div = fft_plot->axisScaleDiv(QwtAxis::YLeft); fft_plot->setYaxisMajorTicksPos(div.ticks(2)); fft_plot->leftHandlesArea()->repaint(); } @@ -3023,10 +3023,10 @@ void SpectrumAnalyzer::onScalePerDivValueChanged(double perDiv) top->blockSignals(false); } - fft_plot->setAxisScale(QwtPlot::yLeft, bottomValue, topValue); + fft_plot->setAxisScale(QwtAxis::YLeft, bottomValue, topValue); fft_plot->replot(); - auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); + auto div = fft_plot->axisScaleDiv(QwtAxis::YLeft); fft_plot->setYaxisMajorTicksPos(div.ticks(2)); fft_plot->leftHandlesArea()->repaint(); } @@ -3047,10 +3047,10 @@ void SpectrumAnalyzer::onBottomValueChanged(double bottom_value) top_value = top_scale->value(); } - fft_plot->setAxisScale(QwtPlot::yLeft, bottom_value, top_value); + fft_plot->setAxisScale(QwtAxis::YLeft, bottom_value, top_value); fft_plot->replot(); - auto div = fft_plot->axisScaleDiv(QwtPlot::yLeft); + auto div = fft_plot->axisScaleDiv(QwtAxis::YLeft); fft_plot->setYaxisMajorTicksPos(div.ticks(2)); fft_plot->leftHandlesArea()->repaint(); } diff --git a/src/spectrum_marker.cpp b/src/spectrum_marker.cpp index d97175b29a..d67f17d952 100644 --- a/src/spectrum_marker.cpp +++ b/src/spectrum_marker.cpp @@ -27,8 +27,8 @@ using namespace adiscope; SpectrumMarker::SpectrumMarker(const QString &title, bool movable) : - QwtPlotMarker(title), m_xAxis(QwtPlot::xBottom), - m_yAxis(QwtPlot::yLeft), m_movable(movable), m_selected(false) + QwtPlotMarker(title), m_xAxis(QwtAxis::XBottom), + m_yAxis(QwtAxis::YLeft), m_movable(movable), m_selected(false) { } diff --git a/src/symbol.cpp b/src/symbol.cpp index 0fb36f36e4..dd4c3e9a71 100644 --- a/src/symbol.cpp +++ b/src/symbol.cpp @@ -225,7 +225,7 @@ void Symbol::updateSurfacePos() QwtInterval interval = plot()->axisInterval(d_mobileAxis); if (d_within_plot) { - if (d_mobileAxis.pos == QwtPlot::yLeft) { + if (d_mobileAxis.pos == QwtAxis::YLeft) { if (plotCoord.y() < interval.minValue()) plotCoord.setY(interval.minValue()); else if (plotCoord.y() > interval.maxValue()) @@ -298,7 +298,7 @@ void Symbol::onMobileScaleChanged() VertDebugSymbol::VertDebugSymbol(QObject *parent, const QSize& size, bool opposedToFixed, bool floats): - Symbol(parent, size, QwtPlot::xBottom, QwtPlot::yLeft, opposedToFixed, + Symbol(parent, size, QwtAxis::XBottom, QwtAxis::YLeft, opposedToFixed, floats) { int x = opposedToFixed ? surface().width() : 0; @@ -390,7 +390,7 @@ void VertDebugSymbol::onBasePixelPositionChanged(int x, int y) HorizDebugSymbol::HorizDebugSymbol(QObject *parent, const QSize& size, bool opposedToFixed, bool floats): - Symbol(parent, size, QwtPlot::yLeft, QwtPlot::xBottom, opposedToFixed, + Symbol(parent, size, QwtAxis::YLeft, QwtAxis::XBottom, opposedToFixed, floats) { int y = opposedToFixed ? 0 : surface().height(); diff --git a/src/x_axis_scale_zoomer.cpp b/src/x_axis_scale_zoomer.cpp index 86582c087c..608adb5446 100644 --- a/src/x_axis_scale_zoomer.cpp +++ b/src/x_axis_scale_zoomer.cpp @@ -51,7 +51,7 @@ QwtText XAxisScaleZoomer::trackerText(const QPoint &p) const QPointF dp = QwtPlotZoomer::invTransform(p); const dBgraph *plt = dynamic_cast(plot()); - t.setText(plt->getScaleValueFormat(dp.x(), QwtPlot::xTop, 4) + ", " + - plt->getScaleValueFormat(dp.y(), QwtPlot::yLeft)); + t.setText(plt->getScaleValueFormat(dp.x(), QwtAxis::XTop, 4) + ", " + + plt->getScaleValueFormat(dp.y(), QwtAxis::YLeft)); return t; } From 26e91d1a4d2b966080db76fc9eb3eb2c25d8ec2b Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 18 Jan 2022 16:51:14 +0200 Subject: [PATCH 087/125] plots: added opengl rendering option Signed-off-by: Adrian Suciu --- src/BasicPlot.cpp | 27 ++++++++++++++++++++++++--- src/BasicPlot.h | 1 + src/logicanalyzer/logic_analyzer.cpp | 4 ---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/BasicPlot.cpp b/src/BasicPlot.cpp index da80888ddb..28dba3a5af 100644 --- a/src/BasicPlot.cpp +++ b/src/BasicPlot.cpp @@ -1,6 +1,7 @@ #include "BasicPlot.h" #include +#include #include #include @@ -12,14 +13,34 @@ static int staticPlotId = 0; #define replotFrameDuration 1000.0/replotFrameRate +bool useOpenGlCanvas = 0; BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotFrameRate(60) { + auto openGLEnvVar = qgetenv("SCOPY_USE_OPENGL"); + useOpenGlCanvas = (bool)openGLEnvVar.toInt(); + connect(&replotTimer,SIGNAL(timeout()),this,SLOT(replotNow())); - QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); - plotCanvas->setPaintAttribute(QwtPlotCanvas::BackingStore ); + + if(useOpenGlCanvas) { + QwtPlotOpenGLCanvas* plotCanvas = qobject_cast< QwtPlotOpenGLCanvas* >( canvas() ); + if ( plotCanvas == NULL ) + { + plotCanvas = new QwtPlotOpenGLCanvas(this); + plotCanvas->setPaintAttribute(QwtPlotAbstractGLCanvas::BackingStore ); +#ifdef IMMEDIATE_PAINT + plotCanvas->setPaintAttribute(QwtPlotAbstractGLCanvas::ImmediatePaint, true); +#endif + setCanvas( plotCanvas ); + } else { + ; + } + } else { + QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); #ifdef IMMEDIATE_PAINT - plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true); + plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true); #endif + } + qDebug(CAT_PLOT)< Date: Thu, 20 Jan 2022 14:39:43 +0200 Subject: [PATCH 088/125] preferences: added show fps and opengl rendering to preferences Signed-off-by: Adrian Suciu --- src/main.cpp | 23 +++++++++++++++++++---- src/preferences.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- src/preferences.h | 9 +++++++++ ui/preferences.ui | 38 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 022ec6d1dc..a180493532 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -124,10 +124,12 @@ int main(int argc, char **argv) parser.addVersionOption(); parser.addOptions({ - { {"s", "script"}, "Run given script.", "script" }, - { {"n", "nogui"}, "Run Scopy without GUI" }, - { {"d", "nodecoders"}, "Run Scopy without digital decoders"}, - { {"nd", "nonativedialog"}, "Run Scopy without native file dialogs"} + { {"s", "script"}, "Run given script.", "script" }, + { {"n", "nogui"}, "Run Scopy without GUI" }, + { {"d", "nodecoders"}, "Run Scopy without digital decoders"}, + { {"nd", "nonativedialog"}, "Run Scopy without native file dialogs"}, + { {"og", "opengl"}, "Force use OpenGL for plots"} , + { {"nog", "noopengl"}, "Force software rendering for plots"} , }); parser.process(app); @@ -173,6 +175,19 @@ int main(int argc, char **argv) app.setStyleSheet(colorEditor->getStyleSheet()); } + bool openGl = parser.isSet("opengl"); + bool noOpenGl = parser.isSet("noopengl"); + if(openGl && noOpenGl) { + qDebug()<<"Ambigous openGL parameters"; + return -1; + } + if(openGl) { + qputenv("SCOPY_USE_OPENGL","1"); + } + if(noOpenGl) { + qputenv("SCOPY_USE_OPENGL","0"); + } + ToolLauncher launcher(prevCrashDump); launcher.getPrefPanel()->setColorEditor(colorEditor); diff --git a/src/preferences.cpp b/src/preferences.cpp index 02e4717d91..b0464871e0 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -31,6 +31,7 @@ #include #include #include "application_restarter.h" +#include using namespace adiscope; @@ -71,7 +72,8 @@ Preferences::Preferences(QWidget *parent) : check_updates_url("http://swdownloads.analog.com/cse/sw_versions.json"), m_colorEditor(nullptr), m_logging_enabled(false), - m_show_plot_fps(false) + m_show_plot_fps(false), + m_use_open_gl(false) { ui->setupUi(this); @@ -261,6 +263,16 @@ Preferences::Preferences(QWidget *parent) : Q_EMIT notify(); }); + connect(ui->useOpenGl, &QCheckBox::stateChanged, [=](int state){ + m_use_open_gl = state; + + qputenv("SCOPY_USE_OPENGL",QByteArray::number(state)); + if (m_initialized) { + requestRestart(); + } + Q_EMIT notify(); + }); + ui->comboBoxTheme->addItem("default"); ui->comboBoxTheme->addItem("light"); ui->comboBoxTheme->addItem("browse"); @@ -297,6 +309,8 @@ Preferences::Preferences(QWidget *parent) : }); } + + void Preferences::requestRestart() { QMessageBox msgBox; @@ -401,6 +415,7 @@ void Preferences::showEvent(QShowEvent *event) ui->tempLutCalibCheckbox->setChecked(m_attemptTempLutCalib); ui->skipCalCheckbox->setChecked(m_skipCalIfCalibrated); ui->showPlotFps->setChecked(m_show_plot_fps); + ui->useOpenGl->setChecked(m_use_open_gl); // by this point the preferences menu is initialized m_initialized = true; ui->autoUpdatesCheckBox->setChecked(automatical_version_checking_enabled); @@ -438,6 +453,16 @@ void Preferences::resetScopy() } } +bool Preferences::getUse_open_gl() const +{ + return m_use_open_gl; +} + +void Preferences::setUse_open_gl(bool newUse_open_gl) +{ + m_use_open_gl = newUse_open_gl; +} + bool Preferences::getShow_plot_fps() const { return m_show_plot_fps; @@ -1012,11 +1037,23 @@ void Preferences_API::setFirstApplicationRun(const bool &first) preferencePanel->first_application_run = first; } - -bool Preferences_API::getShowPlotFps() const { +bool Preferences_API::getShowPlotFps() const +{ return preferencePanel->m_show_plot_fps; } -void Preferences_API::setShowPlotFps(const bool& fps) { + +void Preferences_API::setShowPlotFps(const bool& fps) +{ preferencePanel->m_show_plot_fps = fps; +} + +bool Preferences_API::getUseOpenGl() const +{ + return preferencePanel->m_use_open_gl; +} +void Preferences_API::setUseOpenGl(const bool& val) +{ + preferencePanel->m_use_open_gl = val; + qputenv("SCOPY_USE_OPENGL",QByteArray::number(val)); } diff --git a/src/preferences.h b/src/preferences.h index ec509ef17f..32def60e33 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -144,6 +144,9 @@ class Preferences : public QWidget bool getShow_plot_fps() const; void setShow_plot_fps(bool newShow_plot_fps); + bool getUse_open_gl() const; + void setUse_open_gl(bool newUse_open_gl); + Q_SIGNALS: void notify(); @@ -194,6 +197,7 @@ private Q_SLOTS: bool m_skipCalIfCalibrated; bool m_logging_enabled; bool m_show_plot_fps; + bool m_use_open_gl; Preferences_API *pref_api; @@ -233,6 +237,7 @@ class Preferences_API : public ApiObject Q_PROPERTY(QString currentStylesheet READ getCurrentStylesheet WRITE setCurrentStylesheet) Q_PROPERTY(QStringList userStylesheets READ getUserStylesheets WRITE setUserStylesheets) Q_PROPERTY(bool showPlotFps READ getShowPlotFps WRITE setShowPlotFps) + Q_PROPERTY(bool useOpenGl READ getUseOpenGl WRITE setUseOpenGl) public: @@ -319,6 +324,10 @@ class Preferences_API : public ApiObject bool getShowPlotFps() const; void setShowPlotFps(const bool& first); + bool getUseOpenGl() const; + void setUseOpenGl(const bool& first); + + QString getCurrentStylesheet() const; void setCurrentStylesheet(const QString ¤tStylesheet); diff --git a/ui/preferences.ui b/ui/preferences.ui index bb42fb74c2..97a04114d1 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -72,9 +72,9 @@ 0 - 0 + -72 1068 - 792 + 824 @@ -1698,6 +1698,40 @@ color: white; + + + + 0 + + + + + + + + + + + + Use hardware accelerated plotting - OpenGL (EXPERIMENTAL) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + From 8f6e05a4d38bfc443b24df8ce031633ce3c6aa69 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 20 Jan 2022 16:39:37 +0200 Subject: [PATCH 089/125] plots: fix OpenGL on Android Signed-off-by: Adrian Suciu --- src/BasicPlot.cpp | 19 +++++++++---------- src/BasicPlot.h | 2 ++ src/main.cpp | 1 + src/preferences.cpp | 24 ++++++++---------------- src/preferences.h | 1 + src/tool_launcher.cpp | 34 +++++++++++++++++++++++++++++----- src/tool_launcher.hpp | 7 ++++++- 7 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/BasicPlot.cpp b/src/BasicPlot.cpp index 28dba3a5af..4e8086b592 100644 --- a/src/BasicPlot.cpp +++ b/src/BasicPlot.cpp @@ -3,22 +3,17 @@ #include #include #include +#include #include #include namespace adiscope { -static int staticPlotId = 0; - -#define replotFrameDuration 1000.0/replotFrameRate - -bool useOpenGlCanvas = 0; +int BasicPlot::staticPlotId = 0; BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotFrameRate(60) { - auto openGLEnvVar = qgetenv("SCOPY_USE_OPENGL"); - useOpenGlCanvas = (bool)openGLEnvVar.toInt(); - + useOpenGlCanvas = getToolLauncherInstance()->isOpenGlLoaded(); connect(&replotTimer,SIGNAL(timeout()),this,SLOT(replotNow())); if(useOpenGlCanvas) { @@ -36,6 +31,7 @@ BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotF } } else { QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); + plotCanvas->setPaintAttribute(QwtPlotCanvas::BackingStore, true); #ifdef IMMEDIATE_PAINT plotCanvas->setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true); #endif @@ -45,8 +41,11 @@ BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotF id = staticPlotId; staticPlotId++; // for debug pfps.setCapacity(fpsHistoryCount); + pms.setCapacity(fpsHistoryCount); ifps.setCapacity(fpsHistoryCount); + ims.setCapacity(fpsHistoryCount); fpsLabel.attach(this); + fpsLabel.hide(); QFont font; font.setBold( true ); fpsTxt.setFont( font ); @@ -92,12 +91,14 @@ void BasicPlot::setRefreshRate(double hz) { void BasicPlot::setVisibleFpsLabel(bool vis) { fpsLabel.setVisible(vis); + debug = vis; } void BasicPlot::hideFpsLabel() { fpsLabel.hide(); debug = false; } + void BasicPlot::showFpsLabel() { fpsLabel.show(); debug = true; @@ -150,11 +151,9 @@ void BasicPlot::replotNow() { } } - void BasicPlot::replot() { #ifdef IMMEDIATE_PAINT if(!replotTimer.isActive()) { - //qDebug(CAT_PLOT)<useOpenGl, &QCheckBox::stateChanged, [=](int state){ m_use_open_gl = state; - qputenv("SCOPY_USE_OPENGL",QByteArray::number(state)); + if (m_initialized) { requestRestart(); } @@ -290,26 +290,18 @@ Preferences::Preferences(QWidget *parent) : m_colorEditor->setUserStylesheets({filePath}); m_colorEditor->setCurrentStylesheet(filePath); - - requestRestart(); - - } else { m_colorEditor->setCurrentStylesheet(stylesheet); - - // force saving of the ini file as the new Scopy process - // when restarted will start before scopy closes. A race condition - // will appear on who gets to read/write to the .ini file first - QString preference_ini_file = getPreferenceIniFile(); - QSettings settings(preference_ini_file, QSettings::IniFormat); - pref_api->save(settings); - - requestRestart(); } + requestRestart(); }); } - +void Preferences::save() { + QString preference_ini_file = getPreferenceIniFile(); + QSettings settings(preference_ini_file, QSettings::IniFormat); + pref_api->save(settings); +} void Preferences::requestRestart() { @@ -324,7 +316,7 @@ void Preferences::requestRestart() int ret = msgBox.exec(); if (ret == QMessageBox::Ok) { - // restart: + save(); // save before restarting adiscope::ApplicationRestarter::triggerRestart(); } } diff --git a/src/preferences.h b/src/preferences.h index 32def60e33..432443722a 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -155,6 +155,7 @@ class Preferences : public QWidget public Q_SLOTS: + void save(); QString loadLanguage(); diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index 508bd92a24..e35b3e2bc9 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #if __ANDROID__ #include #include @@ -117,7 +118,8 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : skip_calibration_if_already_calibrated(true), m_adc_tools_failed(false), m_dac_tools_failed(false), - about(nullptr) + about(nullptr), + openGlLoaded(false) #ifdef __ANDROID__ ,jnienv(new QAndroidJniEnvironment()) #endif @@ -126,8 +128,6 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : notifier.setEnabled(false); ui->setupUi(this); - - #ifdef __ANDROID__ // LIBUSB WEAK_AUTHORITY libusb_set_option(NULL,LIBUSB_OPTION_ANDROID_JAVAVM,jnienv->javaVM()); libusb_set_option(NULL,LIBUSB_OPTION_WEAK_AUTHORITY,NULL); @@ -293,6 +293,11 @@ ToolLauncher::ToolLauncher(QString prevCrashDump, QWidget *parent) : readPreferences(); this->installEventFilter(this); ui->btnConnect->hide(); + // Read preferences and then decide if we load OpenGL + auto openGLEnvVar = qgetenv("SCOPY_USE_OPENGL"); + if( (bool)openGLEnvVar.toInt()) { + loadOpenGL(); + } _setupToolMenu(); @@ -1206,7 +1211,7 @@ void adiscope::ToolLauncher::ping() int ret = iio_device_get_trigger(dev, &test_device); - if (ret < 0 && ret != -ENOENT) { + if (ret < 0 && ret != -ENOENT && ret!=-19) { disconnect(); } } @@ -1441,6 +1446,7 @@ void adiscope::ToolLauncher::stopToolsBeforeCalibration() for(Tool* tool : qAsConst(calibration_saved_tools)) tool->stop(); } + void adiscope::ToolLauncher::restartToolsAfterCalibration() { menu->getToolMenuItemFor(TOOL_DMM)->setCalibrating(false); @@ -1701,7 +1707,6 @@ bool adiscope::ToolLauncher::switchContext(const QString& uri) || filter->compatible(TOOL_DIGITALIO)) { dioManager = new DIOManager(ctx, filter); } - if (filter->compatible(TOOL_LOGIC_ANALYZER) || filter->compatible(TOOL_PATTERN_GENERATOR)) { @@ -2039,3 +2044,22 @@ void ToolLauncher::registerNativeMethods() env->DeleteLocalRef(objectClass); } #endif + +bool ToolLauncher::isOpenGlLoaded() const { + return openGlLoaded; +} + +void ToolLauncher::loadOpenGL() { + // set surfaceFormat as in Qt example: HelloGL2 - https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/hellogl2/main.cpp?h=5.15#n81 + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + QSurfaceFormat::setDefaultFormat(fmt); + + // This acts as a loader for the OpenGL context, our plots load and draw in the OpenGL context + // at the same time which causes some race condition and causes the app to hang + // with this workaround, the app loads the OpenGL context before any plots are created + // Probably there's a better way to do this + auto a = new QOpenGLWidget(this); + delete a; + openGlLoaded = true; +} diff --git a/src/tool_launcher.hpp b/src/tool_launcher.hpp index bccb30511d..36b7e9bc9a 100644 --- a/src/tool_launcher.hpp +++ b/src/tool_launcher.hpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #ifdef __ANDROID__ @@ -56,6 +55,7 @@ #include "gui/detachedWindow.hpp" #include "preferences.h" #include "gui/info_page.hpp" +#include #include "device_widget.hpp" #include "gui/connectDialog.hpp" #include "toolmenu.h" @@ -106,6 +106,8 @@ class ToolLauncher : public QMainWindow PhoneHome *getPhoneHome() const; enum tool getSelectedToolId() const; + bool isOpenGlLoaded() const; + Q_SIGNALS: void connectionDone(bool success); void adcCalibrationDone(); @@ -198,6 +200,7 @@ private Q_SLOTS: DeviceWidget* getDevice(QString uri); void setupAddPage(); void allowExternalScript(bool); + void loadOpenGL(); #ifdef __ANDROID__ void registerNativeMethods(); static void saveSessionJavaHelper(JNIEnv *env, jobject /*thiz*/); @@ -294,6 +297,8 @@ private Q_SLOTS: SessionInfo m_sessionInfo; enum tool selectedToolId; + bool openGlLoaded; + }; } #endif // M2K_TOOL_LAUNCHER_H From d898b421f328cf4373d872ed112b411363e1a2e8 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Fri, 21 Jan 2022 18:14:21 +0200 Subject: [PATCH 090/125] general: use snappier animations Signed-off-by: Adrian Suciu --- src/gui/menu_anim.cpp | 25 ++++++++++++++++++++----- src/gui/menu_anim.hpp | 4 ++++ src/gui/stacked_homepage.cpp | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/gui/menu_anim.cpp b/src/gui/menu_anim.cpp index 27b5b603f2..b64b15e41e 100644 --- a/src/gui/menu_anim.cpp +++ b/src/gui/menu_anim.cpp @@ -31,18 +31,19 @@ MenuAnim::MenuAnim(QWidget *parent) : ColoredQWidget(parent), close_anim_max(this, "maximumWidth"), close_anim_min(this, "minimumWidth"), min_width(-1), - animInProg(false) + animInProg(false), + animationDuration(200) { - open_anim_max.setDuration(500); + open_anim_max.setDuration(animationDuration); open_anim_max.setEasingCurve(QEasingCurve::InOutExpo); - open_anim_min.setDuration(500); + open_anim_min.setDuration(animationDuration); open_anim_min.setEasingCurve(QEasingCurve::InOutExpo); - close_anim_max.setDuration(500); + close_anim_max.setDuration(animationDuration); close_anim_max.setEasingCurve(QEasingCurve::InOutExpo); - close_anim_min.setDuration(500); + close_anim_min.setDuration(animationDuration); close_anim_min.setEasingCurve(QEasingCurve::InOutExpo); connect(&open_anim_max, SIGNAL(finished()), @@ -116,3 +117,17 @@ void MenuAnim::openAnimFinished() animInProg = false; Q_EMIT finished(true); } + +int MenuAnim::getAnimationDuration() const +{ + return animationDuration; +} + +void MenuAnim::setAnimationDuration(int newAnimationDuration) +{ + animationDuration = newAnimationDuration; + open_anim_max.setDuration(animationDuration); + open_anim_min.setDuration(animationDuration); + close_anim_max.setDuration(animationDuration); + close_anim_min.setDuration(animationDuration); +} diff --git a/src/gui/menu_anim.hpp b/src/gui/menu_anim.hpp index 0c5b6a4c40..c4c72941be 100644 --- a/src/gui/menu_anim.hpp +++ b/src/gui/menu_anim.hpp @@ -39,6 +39,9 @@ namespace adiscope { void setMinimumSize(QSize size); bool animInProgress() const; + int getAnimationDuration() const; + void setAnimationDuration(int newAnimationDuration); + Q_SIGNALS: void finished(bool opened); @@ -53,6 +56,7 @@ namespace adiscope { CustomAnimation close_anim_max, close_anim_min, open_anim_max, open_anim_min; int min_width; + int animationDuration; bool animInProg; }; } diff --git a/src/gui/stacked_homepage.cpp b/src/gui/stacked_homepage.cpp index c25e27057a..b8a86b0e2c 100644 --- a/src/gui/stacked_homepage.cpp +++ b/src/gui/stacked_homepage.cpp @@ -36,7 +36,7 @@ StackedHomepage::StackedHomepage(QWidget *parent) : connect(s_hc, &HomepageControls::goRight, this, &StackedHomepage::moveRight); connect(s_hc, &HomepageControls::openFile, this, &StackedHomepage::openFile); - s_speed = 500; + s_speed = 200; s_animationType = QEasingCurve::InOutCubic; s_wrap = false; s_active = false; From b295632572a88236884b716e8830053da8bacb53 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Sat, 22 Jan 2022 17:28:51 +0200 Subject: [PATCH 091/125] spectrum_analyzer: reenable channel thickness while running Revert commit 6bec931dddcd4f5e9319e552572efa0fe76c6b15 OpenGL rendering provides good performance when channel thickness is greater than 1 Signed-off-by: Adrian Suciu --- src/spectrum_analyzer.cpp | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 758e0e18b8..c6bb27eea2 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -1810,19 +1810,11 @@ void SpectrumAnalyzer::stop() void SpectrumAnalyzer::runStopToggled(bool checked) { - ui->comboBox_line_thickness->setEnabled(!checked); - ui->comboBox_line_thickness->setCurrentIndex(1); - if (checked) { if (iio) { writeAllSettingsToHardware(); } - for(int i = 0;i < channels.size();i++) - { - fft_plot->setLineWidth(i, 1); - } - fft_plot->presetSampleRate(sample_rate); fft_sink->set_samp_rate(sample_rate); m_time_start = std::chrono::system_clock::now(); @@ -2017,22 +2009,20 @@ void SpectrumAnalyzer::on_comboBox_window_currentIndexChanged(const QString& s) void SpectrumAnalyzer::on_comboBox_line_thickness_currentIndexChanged(int index) { - int crt_channel = channelIdOfOpenedSettings(); + int crt_channel = channelIdOfOpenedSettings(); - if (crt_channel < 0) { - qDebug(CAT_SPECTRUM_ANALYZER) << "invalid channel ID for the opened Settings menu"; - return; - } + if (crt_channel < 0) { + qDebug(CAT_SPECTRUM_ANALYZER) << "invalid channel ID for the opened Settings menu"; + return; + } - qreal width = 0.5 * (index + 1); + qreal width = 0.5 * (index + 1); - if (width != channels[crt_channel]->lineWidth()) { - channels[crt_channel]->setLinewidth(width); - if(!isRunning()) { + if (width != channels[crt_channel]->lineWidth()) { + channels[crt_channel]->setLinewidth(width); fft_plot->setLineWidth(crt_channel, width); fft_plot->replot(); } - } } void SpectrumAnalyzer::on_spinBox_averaging_valueChanged(int n) From b1dee9dae5edbdd4a102e248d1206d908ab12194 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 1 Feb 2022 15:39:04 +0200 Subject: [PATCH 092/125] plots: use ShareOpenGLContexts application attribute If opengl is used, make sure to share context across opengl instances This ensures that reparenting of the openGL windows during docking will not break openGL context requiring an extra update Signed-off-by: Adrian Suciu --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index d57eb02c25..734cb4e8ca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,7 @@ int main(int argc, char **argv) QApplication::setAttribute(Qt::AA_CompressTabletEvents, true); qputenv("SCOPY_USE_OPEN_GL", "1"); #endif + QApplication::setAttribute(Qt::AA_ShareOpenGLContexts,true); ScopyApplication app(argc, argv); #ifdef LIBM2K_ENABLE_LOG From 92776343512627d92a7db64c9590156f37fae934 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 10 Feb 2022 17:32:57 +0200 Subject: [PATCH 093/125] ToolLauncher: use queued connection when loading tools to prevent hanging Signed-off-by: Adrian Suciu --- src/tool_launcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool_launcher.cpp b/src/tool_launcher.cpp index e35b3e2bc9..e7ada7db1e 100644 --- a/src/tool_launcher.cpp +++ b/src/tool_launcher.cpp @@ -849,7 +849,7 @@ QPushButton *ToolLauncher::addContext(const QString& uri) if (connectBtn) { connect(connectBtn, SIGNAL(clicked(bool)), - this, SLOT(connectBtn_clicked(bool))); + this, SLOT(connectBtn_clicked(bool)), Qt::QueuedConnection); } connect(deviceWidget, SIGNAL(forgetDevice(QString)), From e4968790099dfb1a5d467e32f263fe89a2f25399 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Wed, 9 Feb 2022 16:44:15 +0200 Subject: [PATCH 094/125] iio_manager: set data_rate to the gr-m2k blocks Requires gr-m2k at least version: https://github.com/analogdevicesinc/gr-m2k/commit/7cc48f59a77d177adc19de8127d342accd7c0195 Signed-off-by: Adrian Suciu --- src/iio_manager.cpp | 19 +++---------------- src/oscilloscope.cpp | 2 +- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/iio_manager.cpp b/src/iio_manager.cpp index d5a4fcee39..1d4dc80710 100644 --- a/src/iio_manager.cpp +++ b/src/iio_manager.cpp @@ -54,22 +54,9 @@ iio_manager::iio_manager(unsigned int block_id, nb_channels = iio_device_get_channels_count(dev); - iio_block = gr::m2k::analog_in_source::make_from(m_context, - _buffer_size, - {1, 1}, - {0, 0}, - 10000, - 1, - KERNEL_BUFFERS_DEFAULT, - false, - false, - {0, 0}, - {0, 0}, - 0, - 0, - {0, 0}, - false, - false); + iio_block = gr::m2k::analog_in_source::make_from(m_context, _buffer_size, {1, 1}, {0, 0}, 10000, 1, + KERNEL_BUFFERS_DEFAULT, false, false, {0, 0}, {0, 0}, 0, 0, {0, 0}, + false, false, 60.0); /* Avoid unconnected channel errors by connecting a dummy sink */ auto dummy_copy = blocks::copy::make(sizeof(short)); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index cee03c58d8..a65cd88952 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -1420,7 +1420,7 @@ void Oscilloscope::enableMixedSignalView(ChannelWidget *cw) mixed_sink = mixed_signal_sink::make(m_logicAnalyzer, &this->plot, active_sample_count); - mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count); + mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count, 60.0); if (iioStarted) { // enable the mixed_source in the iio_manager From 6934bd58ff37bfec9959483ddb65e80f51246c2f Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Wed, 9 Feb 2022 16:44:48 +0200 Subject: [PATCH 095/125] Preferences: add fps parameter Signed-off-by: Adrian Suciu --- src/BasicPlot.h | 2 +- src/iio_manager.cpp | 9 +- src/iio_manager.hpp | 1 + src/logicanalyzer/logic_analyzer.cpp | 3 +- src/oscilloscope.cpp | 19 +- src/preferences.cpp | 34 +- src/preferences.h | 11 + src/spectrum_analyzer.cpp | 7 + ui/preferences.ui | 1790 +++++++++++++------------- 9 files changed, 996 insertions(+), 880 deletions(-) diff --git a/src/BasicPlot.h b/src/BasicPlot.h index 0b826a6847..63d26949c9 100644 --- a/src/BasicPlot.h +++ b/src/BasicPlot.h @@ -55,7 +55,7 @@ public Q_SLOTS: int id = 0; MovingAverage pfps, pms; MovingAverage ifps, ims; - const int fpsHistoryCount = 60; + const int fpsHistoryCount = 30; const int fpsLabelRefreshTime = 500; QwtPlotTextLabel fpsLabel; QwtText fpsTxt; diff --git a/src/iio_manager.cpp b/src/iio_manager.cpp index 1d4dc80710..c17e1ec854 100644 --- a/src/iio_manager.cpp +++ b/src/iio_manager.cpp @@ -28,6 +28,7 @@ #include #include +#include using namespace adiscope; using namespace gr; @@ -54,9 +55,11 @@ iio_manager::iio_manager(unsigned int block_id, nb_channels = iio_device_get_channels_count(dev); + // get target fps from preferences + double targetFps = getScopyPreferences()->getTarget_fps(); iio_block = gr::m2k::analog_in_source::make_from(m_context, _buffer_size, {1, 1}, {0, 0}, 10000, 1, KERNEL_BUFFERS_DEFAULT, false, false, {0, 0}, {0, 0}, 0, 0, {0, 0}, - false, false, 60.0); + false, false, targetFps); /* Avoid unconnected channel errors by connecting a dummy sink */ auto dummy_copy = blocks::copy::make(sizeof(short)); @@ -358,6 +361,10 @@ void iio_manager::set_device_timeout(unsigned int mseconds) } } +void iio_manager::set_data_rate(double rate) { + iio_block->set_data_rate(rate); +} + void iio_manager::enableMixedSignal(m2k::mixed_signal_source::sptr mixed_source) { for (int i = 0; i < nb_channels; ++i) { diff --git a/src/iio_manager.hpp b/src/iio_manager.hpp index 1d8fdf6082..9dd1aefa04 100644 --- a/src/iio_manager.hpp +++ b/src/iio_manager.hpp @@ -105,6 +105,7 @@ namespace adiscope { * Warning: the flowgraph needs to be locked first! */ void set_buffer_size(port_id id, unsigned long size); void set_filter_parameters(int channel, int index, bool enable, float TC, float gain, float sample_rate ); + void set_data_rate(double rate); /* VERY ugly hack. The reconfiguration that happens after * locking/unlocking the flowgraph is sort of broken; the tags diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index c7eab113e3..c348c38524 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1956,7 +1956,8 @@ void LogicAnalyzer::startStop(bool start) totalSamples = bufferSizeAdjusted; absIndex = 0; - std::this_thread::sleep_for(std::chrono::milliseconds(1000/60)); + int ms = (int)(1000.0 / getScopyPreferences()->getTarget_fps()); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } } while (totalSamples && !m_stopRequested); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index a65cd88952..ceb6655ddf 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -67,6 +67,7 @@ #include "scopyExceptionHandler.h" #include "oscilloscope_api.hpp" #include "mixed_signal_sink.h" +#include #include "gui/runsinglewidget.h" @@ -196,6 +197,13 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, this->qt_time_block->set_trigger_mode(TRIG_MODE_TAG, 0, "buffer_start"); + // get target fps from preferences + double targetFps = getScopyPreferences()->getTarget_fps(); + qt_time_block->set_update_time(1.0/targetFps); + qt_fft_block->set_update_time(1.0/targetFps); + qt_hist_block->set_update_time(1.0/targetFps); + qt_xy_block->set_update_time(1.0/targetFps); + // Prevent the application from hanging while waiting for a trigger condition m_m2k_context->setTimeout(UINT_MAX); @@ -1382,6 +1390,12 @@ void Oscilloscope::readPreferences() update_chn_settings_panel(current_ch_widget); setFilteringEnabled(prefPanel->getOsc_filtering_enabled()); + double fps = prefPanel->getTarget_fps(); + iio->set_data_rate(std::max(fps, 15.0)); // minimum 15 buffers/second + qt_time_block->set_update_time(1.0/fps); + qt_fft_block->set_update_time(1.0/fps);; + qt_xy_block->set_update_time(1.0/fps);; + qt_hist_block->set_update_time(1.0/fps);; } @@ -1420,7 +1434,8 @@ void Oscilloscope::enableMixedSignalView(ChannelWidget *cw) mixed_sink = mixed_signal_sink::make(m_logicAnalyzer, &this->plot, active_sample_count); - mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count, 60.0); + double targetFps = getScopyPreferences()->getTarget_fps(); + mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count, targetFps); if (iioStarted) { // enable the mixed_source in the iio_manager @@ -2440,6 +2455,8 @@ void Oscilloscope::add_math_channel(const std::string& function) noZoomXAxisWidth * getSampleRate() / m_m2k_analogin->getOversamplingRatio(), getSampleRate() / m_m2k_analogin->getOversamplingRatio(), name, 1, (QObject *)&plot); + double targetFps = getScopyPreferences()->getTarget_fps(); + math_sink->set_update_time(1.0/targetFps); /* Add the math block and the math scope sink into a container, so that * we can disconnect them when removing the math channel later */ auto math_pair = QPair( diff --git a/src/preferences.cpp b/src/preferences.cpp index 3e1564b397..15a5f5a855 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -36,6 +36,10 @@ using namespace adiscope; +Preferences* adiscope::pref_ptr; +Preferences* adiscope::getScopyPreferences() { + return pref_ptr; +} Preferences::Preferences(QWidget *parent) : QWidget(parent), @@ -73,7 +77,8 @@ Preferences::Preferences(QWidget *parent) : m_colorEditor(nullptr), m_logging_enabled(false), m_show_plot_fps(false), - m_use_open_gl(false) + m_use_open_gl(false), + m_target_fps(60) { ui->setupUi(this); @@ -273,6 +278,11 @@ Preferences::Preferences(QWidget *parent) : Q_EMIT notify(); }); + connect(ui->cmbPlotTargetFps, &QComboBox::currentTextChanged, [=](QString fps){ + m_target_fps = fps.toDouble(); + Q_EMIT notify(); + }); + ui->comboBoxTheme->addItem("default"); ui->comboBoxTheme->addItem("light"); ui->comboBoxTheme->addItem("browse"); @@ -295,6 +305,7 @@ Preferences::Preferences(QWidget *parent) : } requestRestart(); }); + pref_ptr = this; } void Preferences::save() { @@ -408,6 +419,7 @@ void Preferences::showEvent(QShowEvent *event) ui->skipCalCheckbox->setChecked(m_skipCalIfCalibrated); ui->showPlotFps->setChecked(m_show_plot_fps); ui->useOpenGl->setChecked(m_use_open_gl); + ui->cmbPlotTargetFps->setCurrentText(QString::number(m_target_fps)); // by this point the preferences menu is initialized m_initialized = true; ui->autoUpdatesCheckBox->setChecked(automatical_version_checking_enabled); @@ -445,6 +457,16 @@ void Preferences::resetScopy() } } +double Preferences::getTarget_fps() const +{ + return m_target_fps; +} + +void Preferences::setTarget_fps(double newTarget_fps) +{ + m_target_fps = newTarget_fps; +} + bool Preferences::getUse_open_gl() const { return m_use_open_gl; @@ -1049,3 +1071,13 @@ void Preferences_API::setUseOpenGl(const bool& val) preferencePanel->m_use_open_gl = val; qputenv("SCOPY_USE_OPENGL",QByteArray::number(val)); } + +double Preferences_API::getTargetFps() const +{ + return preferencePanel->m_target_fps; +} + +void Preferences_API::setTargetFps(const double &val) +{ + preferencePanel->m_target_fps = val; +} diff --git a/src/preferences.h b/src/preferences.h index 432443722a..e4db56c739 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -147,6 +147,9 @@ class Preferences : public QWidget bool getUse_open_gl() const; void setUse_open_gl(bool newUse_open_gl); + double getTarget_fps() const; + void setTarget_fps(double newTarget_fps); + Q_SIGNALS: void notify(); @@ -199,6 +202,7 @@ private Q_SLOTS: bool m_logging_enabled; bool m_show_plot_fps; bool m_use_open_gl; + int m_target_fps; Preferences_API *pref_api; @@ -239,6 +243,7 @@ class Preferences_API : public ApiObject Q_PROPERTY(QStringList userStylesheets READ getUserStylesheets WRITE setUserStylesheets) Q_PROPERTY(bool showPlotFps READ getShowPlotFps WRITE setShowPlotFps) Q_PROPERTY(bool useOpenGl READ getUseOpenGl WRITE setUseOpenGl) + Q_PROPERTY(double targetFps READ getTargetFps WRITE setTargetFps) public: @@ -328,6 +333,8 @@ class Preferences_API : public ApiObject bool getUseOpenGl() const; void setUseOpenGl(const bool& first); + double getTargetFps() const; + void setTargetFps(const double& val); QString getCurrentStylesheet() const; void setCurrentStylesheet(const QString ¤tStylesheet); @@ -339,6 +346,10 @@ class Preferences_API : public ApiObject Preferences *preferencePanel; }; + +extern Preferences* pref_ptr; +Preferences* getScopyPreferences(); + } #endif // PREFERENCE_PANEL_H diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index c6bb27eea2..65583ce634 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -50,6 +50,7 @@ #include "filemanager.h" #include "spectrum_analyzer_api.hpp" #include "stream_to_vector_overlap.h" +#include "tool_launcher.hpp" #ifdef SPECTRAL_MSR #include "gui/measure.h" @@ -1839,6 +1840,9 @@ void SpectrumAnalyzer::build_gnuradio_block_chain() (QObject *)fft_plot); fft_sink->set_trigger_mode(TRIG_MODE_TAG, 0, "buffer_start"); + double targetFps = getScopyPreferences()->getTarget_fps(); + fft_sink->set_update_time(1.0/targetFps); + bool started = isIioManagerStarted(); if (started) { @@ -1881,6 +1885,9 @@ void SpectrumAnalyzer::build_gnuradio_block_chain_no_ctx() "Osc Frequency", m_adc_nb_channels, (QObject *)fft_plot); + double targetFps = getScopyPreferences()->getTarget_fps(); + fft_sink->set_update_time(1.0/targetFps); + top_block = gr::make_top_block("spectrum_analyzer"); for (int i = 0; i < m_adc_nb_channels; i++) { diff --git a/ui/preferences.ui b/ui/preferences.ui index 97a04114d1..02aebae879 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -72,9 +72,9 @@ 0 - -72 + -60 1068 - 824 + 829 @@ -225,46 +225,9 @@ 10 - - - - - - - - - true - - - - - - - Enable animations - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 6 - + + + 0 @@ -277,103 +240,168 @@ 0 - - - - 6 - - - 0 - + + 0 + + + 10 + + + - - - - 0 - 0 - - + - NETWORK ANALYZER + - - true + + + + + + Enable graticule - - + + + Qt::Horizontal + + - 200 - 1 + 40 + 20 - + + + + + + + + + + + + + Enable sample rate filters + + + + + + + Qt::Horizontal + + - 16777215 - 1 + 40 + 20 + + + + + + + + + + + + + + + + + Enable mini histogram + + + + + Qt::Horizontal - - true + + + 40 + 20 + - + - - + + Qt::Vertical - - QSizePolicy::Fixed - - 20 - 15 + 0 + 0 - - + + - - - - 16777215 - 16777215 - - + + + + - + Show ADC digital filter config - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + - + 0 0 + + + - Always display 0db value on graph + - - 0 + + + + + + Enable labels on the plot - + Qt::Horizontal @@ -387,51 +415,80 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed + + + + 0 - - - 20 - 5 - + + 0 - - - - - - - - - 6 - - - 0 - - - 0 - - - - - 0 - - - - - - 0 - 0 - + + + + + 0 + 0 + + + + OSCILLOSCOPE + + + true + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + true + + + + + + + + + + + + 10 + + + 0 + + + 0 + + + + + + + + 0 + 0 + - LOGIC ANALYZER + DEBUG true @@ -439,7 +496,13 @@ - + + + + 0 + 1 + + 16777215 @@ -457,26 +520,26 @@ - + + + 0 + - + - + - Display sampling points when zoomed - - - 0 + Show plot FPS - + Qt::Horizontal @@ -491,101 +554,174 @@ - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 15 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - Scriptable manual calibration - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - true - - + + + + + + + + + + + + + + Enable Session Logging (Only for Debugging, Bugreporting) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + color:red + + + Currently unavailable: libm2k logging system is disabled + + + + - - - Enable digital decoders + + + 0 - + + + + + + + + + + + Enable IIO Debug Instrument (Requires Scopy restart) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - Qt::Horizontal + + + 0 + + + + + + + + + + + + Enable Debug Messages (Only for Debugging, Bugreporting) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + + + + + + + + Use hardware accelerated plotting - OpenGL (EXPERIMENTAL) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + + + Enable all instrument notes + + + + + + + Qt::Horizontal @@ -597,57 +733,9 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - Double click to detach a tool - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - + + + 0 @@ -664,31 +752,29 @@ 0 - - - 0 - - - 10 - - - 10 - + - + - Language (requires app restart) + - + + + Scriptable manual calibration + + + 0 + + + + + Qt::Horizontal - - QSizePolicy::Fixed - 40 @@ -697,16 +783,6 @@ - - - - - 0 - 0 - - - - @@ -901,127 +977,32 @@ color: white; - - - - - 0 - 0 - + + + + 6 + + + 0 + + + 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - Run external scripts (Experimental) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - - - Enable user notes in main page - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 6 - - - 0 - - - 0 - - - - - 6 - - - 0 + + + 0 - + - + 0 0 - SPECTRUM ANALYZER + LOGIC ANALYZER true @@ -1029,13 +1010,7 @@ color: white; - - - - 0 - 1 - - + 16777215 @@ -1053,27 +1028,18 @@ color: white; - + - + - - true - - - - - 0 - 0 - - + - Only search marker peaks in visible domain + Display sampling points when zoomed 0 @@ -1081,7 +1047,7 @@ color: white; - + Qt::Horizontal @@ -1095,92 +1061,30 @@ color: white; - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - - - - - - - - - - Show advanced device information - - - - + - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - - - - - - - - - - Enable automatic update checking + Qt::Vertical - - - - - - Qt::Horizontal + + QSizePolicy::Expanding - 40 - 20 + 20 + 15 - - - + + + + + 6 + 0 @@ -1193,88 +1097,103 @@ color: white; 0 - - 0 - - - 10 - - - + + + + 6 + + + 0 + - - - + + + + 0 + 0 + - - - - - Enable graticule + NETWORK ANALYZER + + + true - - - Qt::Horizontal + + + + 200 + 1 + - + - 40 - 20 + 16777215 + 1 - + + Qt::Horizontal + + + true + + - - - - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + - + + + + 16777215 + 16777215 + + - Enable sample rate filters + - - - Qt::Horizontal - - - - 40 - 20 - + + + + 0 + 0 + - - - - - - - - - + Always display 0db value on graph - - - - - - Enable mini histogram + + 0 - + Qt::Horizontal @@ -1288,73 +1207,67 @@ color: white; - - + + Qt::Vertical + + QSizePolicy::Fixed + - 0 - 0 + 20 + 5 - - - - - - - - - Show ADC digital filter config - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + - - - - 0 - 0 - - - - - + - + - Enable labels on the plot + Run external scripts (Experimental) - + Qt::Horizontal @@ -1368,78 +1281,30 @@ color: white; - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - OSCILLOSCOPE - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - - - - - 0 - + + - + + + true + - + - Skip calibration if already calibrated (needs FW >= 0.26) + Enable digital decoders - + Qt::Horizontal @@ -1453,24 +1318,27 @@ color: white; - - + + - + + + true + - + - Enable all instrument notes + Enable animations - + Qt::Horizontal @@ -1484,207 +1352,143 @@ color: white; - - - - - - Theme - - - - - - - - - - - - 10 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - DEBUG - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Double click to detach a tool + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 6 + + + 0 + + + 0 + - + + + 6 + 0 - - - + + + + 0 + 0 + - - - - - Show plot FPS + SPECTRUM ANALYZER + + + true - - - Qt::Horizontal + + + + 0 + 1 + - + - 40 - 20 + 16777215 + 1 - - - - - - - - - - - - - - - - - - - Enable Session Logging (Only for Debugging, Bugreporting) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - color:red + + Qt::Horizontal - - Currently unavailable: libm2k logging system is disabled + + true - - - 0 - + - + - - - - - - Enable IIO Debug Instrument (Requires Scopy restart) + + true - - - Qt::Horizontal - - - - 40 - 20 - + + + + 0 + 0 + - - - - - - - - 0 - - - - + Only search marker peaks in visible domain - - - - - - Enable Debug Messages (Only for Debugging, Bugreporting) + + 0 - + Qt::Horizontal @@ -1698,39 +1502,19 @@ color: white; + + + + - - - 0 + + + Theme - - - - - - - - - - - Use hardware accelerated plotting - OpenGL (EXPERIMENTAL) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + @@ -1765,6 +1549,262 @@ color: white; + + + + + + + + + + + + + Show advanced device information + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 10 + + + 10 + + + + + Language (requires app restart) + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + + + + + + + + + + + + Enable automatic update checking + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + Enable user notes in main page + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + + + + + + + + Skip calibration if already calibrated (needs FW >= 0.26) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + + + Plotting refresh rate + + + + + + + + 60 + + + + + 30 + + + + + 15 + + + + + 10 + + + + + 5 + + + + + + From d2aa2a11285bd986d3cac7376d631cce4f574452 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 15 Feb 2022 13:17:48 +0200 Subject: [PATCH 096/125] SpectrumAnalyzer: workaround stuttering when huge number of points is plotted Signed-off-by: Adrian Suciu --- src/spectrum_analyzer.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 65583ce634..7a282ee163 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -2439,6 +2439,19 @@ void SpectrumAnalyzer::on_btnMaxPeak_clicked() void SpectrumAnalyzer::on_cmb_rbw_currentIndexChanged(int index) { + double update_time = 1.0/getScopyPreferences()->getTarget_fps(); + switch(bin_sizes[index]) { + case 1<<17: + fft_sink->set_update_time(update_time * 2); + break; + case 1<<18: + fft_sink->set_update_time(update_time * 4); + break; + default: + fft_sink->set_update_time(update_time); + break; + } + uint new_fft_size = bin_sizes[index]; if (new_fft_size != fft_size) { From 016fa0f71bd1fd134c77569ba0a9f59f745d1819 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 15 Feb 2022 13:33:47 +0200 Subject: [PATCH 097/125] iio_manager: only use one kernel buffer There is no need for multiple kernel buffers when displaying only one buffer on the screen Signed-off-by: Adrian Suciu --- src/iio_manager.cpp | 2 +- src/oscilloscope.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/iio_manager.cpp b/src/iio_manager.cpp index c17e1ec854..5deb6ea7d9 100644 --- a/src/iio_manager.cpp +++ b/src/iio_manager.cpp @@ -32,7 +32,7 @@ using namespace adiscope; using namespace gr; -static const int KERNEL_BUFFERS_DEFAULT = 4; +static const int KERNEL_BUFFERS_DEFAULT = 1; std::map iio_manager::dev_map; unsigned iio_manager::_id = 0; diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index ceb6655ddf..a2d505dfeb 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -1435,7 +1435,11 @@ void Oscilloscope::enableMixedSignalView(ChannelWidget *cw) mixed_sink = mixed_signal_sink::make(m_logicAnalyzer, &this->plot, active_sample_count); double targetFps = getScopyPreferences()->getTarget_fps(); - mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count, targetFps); + + // this was buggy from the beginning - when using autotriggering, mixed signal acquisition restarts + // with wrong trigger settings because autotriggering switches inbetween always and actual trigger mode + // workaround consists in using 2 kernel buffers before restarting the acquisition + mixed_source = gr::m2k::mixed_signal_source::make_from(m_m2k_context, active_sample_count, targetFps, 2); if (iioStarted) { // enable the mixed_source in the iio_manager From 53c36b9712116b94fdbea2e6549116bf631fdaae Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 15 Feb 2022 17:32:44 +0200 Subject: [PATCH 098/125] ci: update ci with latest library versions Signed-off-by: Adrian Suciu --- .github/workflows/linuxflatpakbuild.yml | 4 ++-- CI/appveyor/data | 1 - CI/appveyor/install_macos_deps.sh | 15 ++++++++++++--- CI/appveyor/install_ubuntu_18_deps.sh | 6 +++--- CI/appveyor/install_ubuntu_20_deps.sh | 6 +++--- 5 files changed, 20 insertions(+), 12 deletions(-) delete mode 100644 CI/appveyor/data diff --git a/.github/workflows/linuxflatpakbuild.yml b/.github/workflows/linuxflatpakbuild.yml index d442724d91..cdef2225ce 100644 --- a/.github/workflows/linuxflatpakbuild.yml +++ b/.github/workflows/linuxflatpakbuild.yml @@ -10,13 +10,13 @@ jobs: steps: - uses: actions/checkout@v2 - name: Pull the Docker Image - run: docker pull adisuciu/scopy-flatpak-ubuntu20:latest + run: docker pull analogdevices/scopy-build:flatpak - name: Run Docker Image run: | docker run --privileged \ -v `pwd`:$GITHUB_WORKSPACE:rw \ -e "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ - adisuciu/scopy-flatpak-ubuntu20:latest /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/inside_flatpak_docker.sh + analogdevices/scopy-build:flatpak /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/inside_flatpak_docker.sh - uses: actions/upload-artifact@v2 with: name: Scopy.flatpak diff --git a/CI/appveyor/data b/CI/appveyor/data deleted file mode 100644 index 2c37c2a94b..0000000000 --- a/CI/appveyor/data +++ /dev/null @@ -1 +0,0 @@ -vineri 26 martie 2021, 13:48:33 +0200 diff --git a/CI/appveyor/install_macos_deps.sh b/CI/appveyor/install_macos_deps.sh index 716385f24b..815949ba0f 100755 --- a/CI/appveyor/install_macos_deps.sh +++ b/CI/appveyor/install_macos_deps.sh @@ -1,14 +1,14 @@ #!/bin/bash -LIBIIO_VERSION=v0.21 +LIBIIO_VERSION=0ed18cd8f6b2fac5204a99e38922bea73f1f778c LIBAD9361_BRANCH=master LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=scopy +GNURADIO_BRANCH=scopy-android-2 GRSCOPY_BRANCH=master GRM2K_BRANCH=master -QWT_BRANCH=qwt-6.1-multiaxes +QWT_BRANCH=qwt-multiaxes LIBSIGROK_BRANCH=master LIBSIGROKDECODE_BRANCH=master BOOST_VERSION_FILE=1_73_0 @@ -78,6 +78,7 @@ build_libiio() { -DWITH_TESTS:BOOL=OFF \ -DWITH_DOC:BOOL=OFF \ -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DENABLE_DNS_SD:BOOL=OFF\ -DCSHARP_BINDINGS:BOOL=OFF \ -DPYTHON_BINDINGS:BOOL=OFF \ -DOSX_PACKAGE:BOOL=OFF \ @@ -168,6 +169,14 @@ build_gnuradio() { -DENABLE_SPHINX:BOOL=OFF \ -DENABLE_DOXYGEN:BOOL=OFF \ -DENABLE_INTERNAL_VOLK=ON \ + -DENABLE_PYTHON=OFF \ + -DENABLE_TESTING=OFF \ + -DENABLE_GR_CHANNELS=OFF \ + -DENABLE_GR_VOCODER=OFF \ + -DENABLE_GR_TRELLIS=OFF \ + -DENABLE_GR_WAVELET=OFF \ + -DENABLE_GR_CTRLPORT=OFF \ + -DENABLE_CTRLPORT_THRIFT=OFF \ -DCMAKE_C_FLAGS=-fno-asynchronous-unwind-tables \ ${WORKDIR}/gnuradio make $JOBS diff --git a/CI/appveyor/install_ubuntu_18_deps.sh b/CI/appveyor/install_ubuntu_18_deps.sh index 85e2c66a82..0561dd9fd7 100755 --- a/CI/appveyor/install_ubuntu_18_deps.sh +++ b/CI/appveyor/install_ubuntu_18_deps.sh @@ -1,15 +1,15 @@ #!/bin/bash -LIBIIO_VERSION=v0.21 +LIBIIO_VERSION=0ed18cd8f6b2fac5204a99e38922bea73f1f778c LIBAD9361_BRANCH=master GLOG_BRANCH=v0.4.0 LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=ming-3.8-clean +GNURADIO_BRANCH=scopy-android-2 GRSCOPY_BRANCH=master GRM2K_BRANCH=master -QWT_BRANCH=qwt-6.1-multiaxes +QWT_BRANCH=qwt-multiaxes LIBSIGROK_BRANCH=master LIBSIGROKDECODE_BRANCH=master LIBTINYIIOD_BRANCH=master diff --git a/CI/appveyor/install_ubuntu_20_deps.sh b/CI/appveyor/install_ubuntu_20_deps.sh index 7f64d8902b..59638d48b2 100755 --- a/CI/appveyor/install_ubuntu_20_deps.sh +++ b/CI/appveyor/install_ubuntu_20_deps.sh @@ -1,15 +1,15 @@ #!/bin/bash -LIBIIO_VERSION=v0.21 +LIBIIO_VERSION=0ed18cd8f6b2fac5204a99e38922bea73f1f778c LIBAD9361_BRANCH=master GLOG_BRANCH=v0.4.0 LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=ming-3.8-clean +GNURADIO_BRANCH=scopy-android-2 GRSCOPY_BRANCH=master GRM2K_BRANCH=master -QWT_BRANCH=qwt-6.1-multiaxes +QWT_BRANCH=qwt-multiaxes LIBSIGROK_BRANCH=master LIBSIGROKDECODE_BRANCH=master LIBTINYIIOD_BRANCH=master From 85fb83908ae68deb2f4c2574dc85ce5c36c5457e Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 21 Feb 2022 16:04:55 +0200 Subject: [PATCH 099/125] ci: disable win32 build and switched win image to windows-2019 https://github.com/actions/virtual-environments/issues/4856 Signed-off-by: Adrian Suciu --- .github/workflows/mingwbuild.yml | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/mingwbuild.yml b/.github/workflows/mingwbuild.yml index d8e052325d..3787b8c2ca 100644 --- a/.github/workflows/mingwbuild.yml +++ b/.github/workflows/mingwbuild.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: make-exe-64: - runs-on: windows-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v2 @@ -30,28 +30,28 @@ jobs: name: scopy-x86_64-setup.exe path: ${{ github.workspace }}\artifacts\scopy-64-setup.exe - make-exe-32: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v2 - - name: Pull the Docker Image - run: docker pull analogdevices/scopy-build:mingw32 - - - name: Run Docker Image - shell: cmd - run: | - mkdir %GITHUB_WORKSPACE%\artifacts & echo %GITHUB_WORKSPACE% & docker run -v %cd%:C:\msys64\home\docker\scopy:rw -v %GITHUB_WORKSPACE%\artifacts:C:\msys64\home\docker\artifact_i686:rw -e GITHUB_WORKSPACE=%GITHUB_WORKSPACE% analogdevices/scopy-build:mingw32 C:\msys64\usr\bin\bash.exe -c '/home/docker/scopy/CI/appveyor/inside_mingw_docker.sh' - - uses: actions/upload-artifact@v2 - with: - name: scopy-i686.zip - path: ${{ github.workspace }}\artifacts\scopy-i686.zip - - - uses: actions/upload-artifact@v2 - with: - name: debug-i686.zip - path: ${{ github.workspace }}\artifacts\debug-i686.zip - - uses: actions/upload-artifact@v2 - with: - name: scopy-i686-setup.exe - path: ${{ github.workspace }}\artifacts\scopy-32-setup.exe +# make-exe-32: +# runs-on: windows-latest +# +# steps: +## - uses: actions/checkout@v2 +# - name: Pull the Docker Image +# run: docker pull analogdevices/scopy-build:mingw32 +# +# - name: Run Docker Image +# shell: cmd +# run: | +# mkdir %GITHUB_WORKSPACE%\artifacts & echo %GITHUB_WORKSPACE% & #docker run -v %cd%:C:\msys64\home\docker\scopy:rw -v #%GITHUB_WORKSPACE%\artifacts:C:\msys64\home\docker\artifact_i686:rw #-e GITHUB_WORKSPACE=%GITHUB_WORKSPACE% analogdevices/#scopy-build:mingw32 C:\msys64\usr\bin\bash.exe -c '/home/docker/#scopy/CI/appveyor/inside_mingw_docker.sh' +# - uses: actions/upload-artifact@v2 +# with: +# name: scopy-i686.zip +# path: ${{ github.workspace }}\artifacts\scopy-i686.zip +# +# - uses: actions/upload-artifact@v2 +# with: +# name: debug-i686.zip +# path: ${{ github.workspace }}\artifacts\debug-i686.zip +# - uses: actions/upload-artifact@v2 +# with: +# name: scopy-i686-setup.exe +# path: ${{ github.workspace }}\artifacts\scopy-32-setup.exe From 47f29c50120e8b3731275f4428215fff878c0cf8 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 21 Feb 2022 16:30:38 +0200 Subject: [PATCH 100/125] ci: fix scopy apk target build folder Signed-off-by: Adrian Suciu --- .github/workflows/androidbuild.yml | 2 +- CI/appveyor/build_scopy_apk.sh | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/androidbuild.yml b/.github/workflows/androidbuild.yml index 1f3dcee429..b2b9d67f77 100644 --- a/.github/workflows/androidbuild.yml +++ b/.github/workflows/androidbuild.yml @@ -17,7 +17,7 @@ jobs: -v `pwd`:$GITHUB_WORKSPACE:rw \ -e "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" \ -e "BRANCH=${GITHUB_REF#refs/heads/}" \ - analogdevices/scopy-build:android /bin/bash -xe /home/runner/src/scopy-android-deps/clone_and_build.sh + analogdevices/scopy-build:android /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/build_scopy_apk.sh - uses: actions/upload-artifact@v2 with: name: scopy-android diff --git a/CI/appveyor/build_scopy_apk.sh b/CI/appveyor/build_scopy_apk.sh index bfc0e93fe7..93db40bf9c 100755 --- a/CI/appveyor/build_scopy_apk.sh +++ b/CI/appveyor/build_scopy_apk.sh @@ -3,6 +3,7 @@ set -xe source ./android_toolchain.sh $1 $2 ARTIFACT_LOCATION=$GITHUB_WORKSPACE +BUILD_FOLDER=build_${ABI}_${BUILD_TYPE} build_scopy() { @@ -20,12 +21,12 @@ build_scopy() { ./android_cmake.sh . - cd build_$ABI + cd $BUILD_FOLDER make -j$JOBS cd .. ./android_deploy_qt.sh - cd build_$ABI + cd $BUILD_FOLDER make apk make aab cd .. @@ -35,9 +36,9 @@ build_scopy() { move_artifact() { pushd scopy - sudo cp ./build_$ABI/android-build/build/outputs/apk/debug/android-build-debug.apk $ARTIFACT_LOCATION/ - sudo cp ./build_$ABI/android-build/build/outputs/bundle/debug/android-build-debug.aab $ARTIFACT_LOCATION/ - sudo cp ./build_$ABI/android-build/build/outputs/bundle/release/android-build-release.aab $ARTIFACT_LOCATION/ + sudo cp ./$BUILD_FOLDER/android-build/build/outputs/apk/debug/android-build-debug.apk $ARTIFACT_LOCATION/ + sudo cp ./$BUILD_FOLDER/android-build/build/outputs/bundle/debug/android-build-debug.aab $ARTIFACT_LOCATION/ + sudo cp ./$BUILD_FOLDER/android-build/build/outputs/bundle/release/android-build-release.aab $ARTIFACT_LOCATION/ pushd $ARTIFACT_LOCATION sudo chmod 644 ./android-build-debug.apk sudo chmod 644 ./android-build-debug.aab From ff19aaa8e570ed990d2f817085a2eeafa5a71ac5 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 21 Feb 2022 17:25:26 +0200 Subject: [PATCH 101/125] preferences: use 30 fps and openGL by default on some platforms Signed-off-by: Adrian Suciu --- src/preferences.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/preferences.cpp b/src/preferences.cpp index 15a5f5a855..875f849d4f 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -77,8 +77,15 @@ Preferences::Preferences(QWidget *parent) : m_colorEditor(nullptr), m_logging_enabled(false), m_show_plot_fps(false), - m_use_open_gl(false), - m_target_fps(60) + m_use_open_gl( +// Android/macOS/Windows use OpenGL by default +#if defined __ANDROID__ || defined __APPLE__ || defined __MINGW32__ + true +#else + false +#endif + ), + m_target_fps(30) { ui->setupUi(this); From a0981a139fb0feb7de4e54ef4e0bf54c9cba4692 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 22 Feb 2022 10:26:47 +0200 Subject: [PATCH 102/125] ci: fix macOs and ubuntu build errors Signed-off-by: Adrian Suciu --- CI/appveyor/before_install_lib.sh | 97 --------------------------- CI/appveyor/install_macos_deps.sh | 8 +-- CI/appveyor/install_ubuntu_18_deps.sh | 3 +- CI/appveyor/install_ubuntu_20_deps.sh | 3 +- 4 files changed, 8 insertions(+), 103 deletions(-) diff --git a/CI/appveyor/before_install_lib.sh b/CI/appveyor/before_install_lib.sh index 5cbe2000ee..9af9c92962 100755 --- a/CI/appveyor/before_install_lib.sh +++ b/CI/appveyor/before_install_lib.sh @@ -161,103 +161,6 @@ qmake_build_git() { qmake_build_local() { local dir="$1" local qtarget="$2" - local patchfunc="$3" - - [ ! -d "$WORKDIR/$dir" ] || { - [ -z "$patchfunc" ] || { - pushd $WORKDIR/$dir - $patchfunc - popd - } - } __build_common "$dir" "__qmake" } -patch_qwt() { - patch -p1 <<-EOF ---- a/qwtconfig.pri -+++ b/qwtconfig.pri -@@ -19,7 +19,7 @@ - QWT_INSTALL_PREFIX = \$\$[QT_INSTALL_PREFIX] - - unix { -- QWT_INSTALL_PREFIX = /usr/local/qwt-\$\$QWT_VERSION-svn -+ QWT_INSTALL_PREFIX = $STAGINGDIR - # QWT_INSTALL_PREFIX = /usr/local/qwt-\$\$QWT_VERSION-svn-qt-\$\$QT_VERSION - } - -@@ -107,7 +107,7 @@ - # Otherwise you have to build it from the designer directory. - ###################################################################### - --QWT_CONFIG += QwtDesigner -+#QWT_CONFIG += QwtDesigner - - ###################################################################### - # Compile all Qwt classes into the designer plugin instead -@@ -148,7 +148,7 @@ - # Otherwise you have to build them from the tests directory. - ###################################################################### - --QWT_CONFIG += QwtTests -+#QWT_CONFIG += QwtTests - - ###################################################################### - # When Qt has been built as framework qmake wants -@@ -157,7 +157,7 @@ - - macx:!static:CONFIG(qt_framework, qt_framework|qt_no_framework) { - -- QWT_CONFIG += QwtFramework -+# QWT_CONFIG += QwtFramework - } - - ###################################################################### ---- a/src/src.pro -+++ b/src/src.pro -@@ -30,7 +30,8 @@ contains(QWT_CONFIG, QwtDll) { - - # we increase the SONAME for every minor number - -- QWT_SONAME=libqwt.so.\$\${VER_MAJ}.\$\${VER_MIN} -+ !macx: QWT_SONAME=libqwt.so.\$\${VER_MAJ}.\$\${VER_MIN} -+ macx: QWT_SONAME=\$\${QWT_INSTALL_LIBS}/libqwt.dylib - QMAKE_LFLAGS *= \$\${QMAKE_LFLAGS_SONAME}\$\${QWT_SONAME} - QMAKE_LFLAGS_SONAME= - } -EOF -} - -patch_qwtpolar() { - patch -p1 < ${WORKDIR}/projects/scopy/CI/appveyor/patches/qwtpolar-qwt-qt-compat.patch - - patch -p1 <<-EOF ---- a/qwtpolarconfig.pri -+++ b/qwtpolarconfig.pri -@@ -70,14 +72,14 @@ QWT_POLAR_INSTALL_FEATURES = \$\${QWT_POLAR_INSTALL_PREFIX}/features - # Otherwise you have to build it from the designer directory. - ###################################################################### - --QWT_POLAR_CONFIG += QwtPolarDesigner -+#QWT_POLAR_CONFIG += QwtPolarDesigner - - ###################################################################### - # If you want to auto build the examples, enable the line below - # Otherwise you have to build them from the examples directory. - ###################################################################### - --QWT_POLAR_CONFIG += QwtPolarExamples -+#QWT_POLAR_CONFIG += QwtPolarExamples - - ###################################################################### - # When Qt has been built as framework qmake wants -@@ -86,6 +88,6 @@ QWT_POLAR_CONFIG += QwtPolarExamples - - macx:CONFIG(qt_framework, qt_framework|qt_no_framework) { - -- QWT_POLAR_CONFIG += QwtPolarFramework -+ #QWT_POLAR_CONFIG += QwtPolarFramework - } - -EOF -} diff --git a/CI/appveyor/install_macos_deps.sh b/CI/appveyor/install_macos_deps.sh index 815949ba0f..cd9c8af209 100755 --- a/CI/appveyor/install_macos_deps.sh +++ b/CI/appveyor/install_macos_deps.sh @@ -5,7 +5,7 @@ LIBAD9361_BRANCH=master LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=scopy-android-2 +GNURADIO_BRANCH=scopy GRSCOPY_BRANCH=master GRM2K_BRANCH=master QWT_BRANCH=qwt-multiaxes @@ -261,9 +261,9 @@ build_libsigrokdecode() { } build_qwt() { - echo "### Building qwt - branch qwt-6.1-multiaxes" - svn checkout https://svn.code.sf.net/p/qwt/code/branches/$QWT_BRANCH ${WORKDIR}/qwt - qmake_build_local "qwt" "qwt.pro" "patch_qwt" + echo "### Building qwt - branch $QWT_BRANCH" + git clone --depth 1 https://github.com/cseci/qwt --branch $QWT_BRANCH $WORKDIR/qwt + qmake_build_local "qwt" "qwt.pro" } build_libtinyiiod() { diff --git a/CI/appveyor/install_ubuntu_18_deps.sh b/CI/appveyor/install_ubuntu_18_deps.sh index 0561dd9fd7..dca215fa99 100755 --- a/CI/appveyor/install_ubuntu_18_deps.sh +++ b/CI/appveyor/install_ubuntu_18_deps.sh @@ -6,7 +6,7 @@ GLOG_BRANCH=v0.4.0 LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=scopy-android-2 +GNURADIO_BRANCH=scopy GRSCOPY_BRANCH=master GRM2K_BRANCH=master QWT_BRANCH=qwt-multiaxes @@ -58,6 +58,7 @@ build_libiio() { cmake ${CMAKE_OPTS} \ -DWITH_TESTS:BOOL=OFF \ -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=OFF\ -DWITH_MATLAB_BINDINGS:BOOL=OFF \ -DCSHARP_BINDINGS:BOOL=OFF \ -DPYTHON_BINDINGS:BOOL=OFF \ diff --git a/CI/appveyor/install_ubuntu_20_deps.sh b/CI/appveyor/install_ubuntu_20_deps.sh index 59638d48b2..e9e62ddad9 100755 --- a/CI/appveyor/install_ubuntu_20_deps.sh +++ b/CI/appveyor/install_ubuntu_20_deps.sh @@ -6,7 +6,7 @@ GLOG_BRANCH=v0.4.0 LIBM2K_BRANCH=master GRIIO_BRANCH=upgrade-3.8 GNURADIO_FORK=analogdevicesinc -GNURADIO_BRANCH=scopy-android-2 +GNURADIO_BRANCH=scopy GRSCOPY_BRANCH=master GRM2K_BRANCH=master QWT_BRANCH=qwt-multiaxes @@ -49,6 +49,7 @@ build_libiio() { cmake ${CMAKE_OPTS} \ -DWITH_TESTS:BOOL=OFF \ -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=OFF\ -DWITH_MATLAB_BINDINGS:BOOL=OFF \ -DCSHARP_BINDINGS:BOOL=OFF \ -DPYTHON_BINDINGS:BOOL=OFF \ From a14e0a18f942bb452a1a6e69971edf42db15a9df Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 22 Feb 2022 16:18:04 +0200 Subject: [PATCH 103/125] ci: added dependency information --- CI/README.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 CI/README.md diff --git a/CI/README.md b/CI/README.md new file mode 100644 index 0000000000..098e43fe49 --- /dev/null +++ b/CI/README.md @@ -0,0 +1,89 @@ + +This file contains dependency information for different platforms + +**Windows (x86_64)** - +https://github.com/analogdevicesinc/scopy-mingw-build-deps +builds the docker image - `docker pull analogdevices/scopy-build:mingw64` +Github Actions workflow: +https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/mingwbuild.yml + +**Linux (flatpak - x86_64)** +https://github.com/analogdevicesinc/scopy-flatpak +docker pull analogdevices/scopy-build:flatpak + + ARCH=x86_64 make + +Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/linuxflatpakbuild.yml + +**Linux (flatpak - arm)** +https://github.com/analogdevicesinc/scopy-flatpak +Run locally on arm machine (raspberry pi) + + ARCH=arm make +Not build in CI + +**Linux (ubuntu - x86_64) - development** +Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml +**macOS (x86_64)** +Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml + +**Android (aarch64)** +https://github.com/analogdevicesinc/scopy-android-deps - `docker pull analogdevices/scopy-build:android` +Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/androidbuild.yml + +Dependency versions (links used in source builds) + + - Qt: 5.15.12 + - Qwt - https://github.com/cseci/qwt - qwt-multiaxes + - libiio - https://github.com/analogdevicesinc/libiio - https://github.com/analogdevicesinc/libiio/tree/cad83146837971acdac28beaeb8156b9da33ba6b - v0.24 + - libxml2 - https://github.com/GNOME/libxml2 + - iconv (only on Android from src) + - libffi (only on Android from src) + - libgettext (only on Android from src) + - libusb - https://downloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.24/libusb-1.0.24.tar.bz2 + - Android specific libusb: https://github.com/xloem/libusb/tree/d1856aa8c246f9e56cf00a0765462b67fc5a4871 + - libm2k- https://github.com/analogdevicesinc/libm2k - master + - glog - https://github.com/google/glog + - boost - + - gnuradio - https://github.com/analogdevicesinc/gnuradio - scopy / scopy-android-2(for Android) + - volk + - log4cpp - https://github.com/cseci/log4cpp + - fftw3 + - libgmp + - gr-iio https://github.com/analogdevicesinc/gr-iio - upgrade3.8 + - libad9361 - https://github.com/analogdevicesinc/ad9361 + - gr-m2k - https://github.com/analogdevicesinc/gr-m2k - master + - gr-scopy - https://github.com/analogdevicesinc/gr-scopy - master + - libsigrokdecode - https://github.com/sigrokproject/libsigrokdecode - master + - glib + - glibmm + - sigcpp + - python + - libtinyiiod - https://github.com/analogdevicesinc/libtinyiiod - master + +How to install Qt from qt.io : https://github.com/analogdevicesinc/scopy-android-deps/blob/master/docker/Dockerfile#L43-L49 + + +| Dependency | Windows | Linux Flatpak | Linux Ubuntu(development) | Linux ARM | macOS | Android | +| --- | --- | --- | --- | --- | --- | --- | +| Qt | pacman | org.kde.Sdk (v5.15)| Qt.io |org.kde.Sdk (v5.14) | brew | Qt.io | +| qwt | src | src | src | src | src | src | +| libxml2 | pacman | src | apt | src | brew | src | +| libusb | pacman | src | apt | src | brew | src - android branch/commit | +| libiio | src | src | src | src | src | src| +| glog | src | src | src | src | brew | src | +| libm2k | src | src | src | src | src | src | +| volk | src | with GR | with GR | src | with GR | src | +| fftw3 | pacman| src | apt | src | brew| src| +| libgmp | pacman | src | apt | src | brew | src | +| boost | 1.75 | 1.72 | apt/src | 1.72 | brew | Boost-for-Android | +| gnuradio | src | src | apt/src | src | src | src | +| gr-iio | src | src | src | src | src | src | +| gr-m2k | src | src | src | src | src | src | +| gr-scopy | src | src | src | src | src | src | +| glib | pacman | src | apt | src | brew | src | +| glibmm | pacman | src | apt | src | src | src | +| sigcpp | pacman | src | apt | src | src | src | +| python | pacman | src | apt | src | brew | src | +| libsigrokdecode | src | src | src | src | src | src | +| libtinyiiod | src | src | src | src | src | src| From 35fd528545aad9fc9b7a3dedea33c272d9594406 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Mon, 28 Feb 2022 12:13:28 +0200 Subject: [PATCH 104/125] ci: fix ubuntu build Signed-off-by: Adrian Suciu --- CI/appveyor/install_ubuntu_18_deps.sh | 12 ++---------- CI/appveyor/install_ubuntu_20_deps.sh | 12 ++---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/CI/appveyor/install_ubuntu_18_deps.sh b/CI/appveyor/install_ubuntu_18_deps.sh index dca215fa99..ecbcefa2aa 100755 --- a/CI/appveyor/install_ubuntu_18_deps.sh +++ b/CI/appveyor/install_ubuntu_18_deps.sh @@ -37,7 +37,7 @@ install_apt() { sudo cp -a doxygen-1.8.17/bin/doxy* /usr/local/bin doxygen --version - sudo apt-get -y install build-essential libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev libboost1.65-dev libboost1.65 g++ git cmake autoconf libzip4 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev curl libvolk1-bin libvolk1-dev libvolk1.3 libgmp-dev libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev + sudo apt-get -y install build-essential libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev libboost1.65-dev libboost1.65 g++ git cmake autoconf libzip4 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev curl libvolk1-bin libvolk1-dev libvolk1.3 libgmp-dev libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libaio-dev sudo apt-get -y update sudo apt-get -y install gnuradio @@ -213,17 +213,9 @@ build_libsigrokdecode() { build_qwt() { echo "### Building qwt - branch $QWT_BRANCH" - svn checkout https://svn.code.sf.net/p/qwt/code/branches/$QWT_BRANCH ${WORKDIR}/qwt + git clone https://github.com/cseci/qwt --branch $QWT_BRANCH ${WORKDIR}/qwt cd ${WORKDIR}/qwt - # Disable components that we won't build - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtTests$/#/g" qwtconfig.pri - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtDesigner$/#/g" qwtconfig.pri - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtExamples$/#/g" qwtconfig.pri - - # Fix prefix - sed -i 's/\/usr\/local\/qwt-$$QWT_VERSION-svn/\/usr\/local/g' qwtconfig.pri - $QMAKE qwt.pro make $JOBS sudo make install diff --git a/CI/appveyor/install_ubuntu_20_deps.sh b/CI/appveyor/install_ubuntu_20_deps.sh index e9e62ddad9..dd96442b40 100755 --- a/CI/appveyor/install_ubuntu_20_deps.sh +++ b/CI/appveyor/install_ubuntu_20_deps.sh @@ -30,7 +30,7 @@ WORKDIR=${PWD} install_apt() { - sudo apt-get -y install libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev g++ git cmake autoconf libzip5 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev libclang1-9 doxygen curl libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev gnuradio libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libtool + sudo apt-get -y install libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev g++ git cmake autoconf libzip5 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev libclang1-9 doxygen curl libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev gnuradio libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libtool libaio-dev } @@ -204,17 +204,9 @@ build_libsigrokdecode() { build_qwt() { echo "### Building qwt - branch $QWT_BRANCH" - svn checkout https://svn.code.sf.net/p/qwt/code/branches/$QWT_BRANCH ${WORKDIR}/qwt + git clone https://github.com/cseci/qwt --branch $QWT_BRANCH ${WORKDIR}/qwt cd ${WORKDIR}/qwt - # Disable components that we won't build - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtTests$/#/g" qwtconfig.pri - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtDesigner$/#/g" qwtconfig.pri - sed -i "s/^QWT_CONFIG\\s*+=\\s*QwtExamples$/#/g" qwtconfig.pri - - # Fix prefix - sed -i 's/\/usr\/local\/qwt-$$QWT_VERSION-svn/\/usr\/local/g' qwtconfig.pri - $QMAKE qwt.pro make $JOBS sudo make install From dad694f96a63341db51afb6308e3fad90f22e7e1 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Tue, 1 Mar 2022 16:14:39 +0200 Subject: [PATCH 105/125] cmake: remove reference to qwtpolar as this library has been included in qwt and the old library is no longer compatible with Scopy Signed-off-by: Adrian Suciu --- CMakeLists.txt | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b4c62a91f..8fd4b138d0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,19 +206,6 @@ if(ANDROID) find_library(QWT_LIBRARIES REQUIRED NAMES qwt_${ANDROID_ABI}) else() find_library(QWT_LIBRARIES REQUIRED NAMES qwt) - find_library(QWTPOLAR_LIBRARIES qwtpolar) - find_path(QWTPOLAR_INCLUDE_DIRS qwt_polar_plot.h PATH_SUFFIXES qwt) - if (QWTPOLAR_LIBRARIES) - message(STATUS "QwtPolar libraries found - ${QWTPOLAR_LIBRARIES}") - else() - message(STATUS "QwtPolar libraries not found - assuming they are built in Qwt") - set(QWTPOLAR_LIBRARIES "") - endif() - if (NOT QWTPOLAR_INCLUDE_DIRS) - message(SEND_ERROR "QwtPolar includes not found") - else() - message(STATUS "Found QwtPolar include files - ${QWTPOLAR_INCLUDE_DIRS}/qwt_polar_plot.h") - endif() endif() find_package(PkgConfig) @@ -238,7 +225,6 @@ include_directories( ${Qt5Qml_INCLUDE_DIRS} ${Qt5UiTools_INCLUDE_DIRS} ${QWT_INCLUDE_DIRS} - ${QWTPOLAR_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${SCOPY_INCLUDE_DIRS} @@ -457,7 +443,6 @@ target_link_libraries(${PROJECT_NAME} LINK_PRIVATE gnuradio::gnuradio-m2k ${Boost_LIBRARIES} ${QWT_LIBRARIES} - ${QWTPOLAR_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Xml_LIBRARIES} ${MATIO_LIBRARIES} From 36afaa1e3da6a7f9d194b4092a07a240eb5dba16 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 3 Mar 2022 11:37:04 +0200 Subject: [PATCH 106/125] ScopyActivity.java: fix syntax error Signed-off-by: Adrian Suciu --- android/src/org/adi/scopy/ScopyActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/org/adi/scopy/ScopyActivity.java b/android/src/org/adi/scopy/ScopyActivity.java index 25df24c0f0..5f8570d762 100644 --- a/android/src/org/adi/scopy/ScopyActivity.java +++ b/android/src/org/adi/scopy/ScopyActivity.java @@ -90,5 +90,4 @@ public String getScaleFactor() { return formattedScaleFactor.replace(",","."); } - } } From 574b8250ee526e86f7c25c827c2f247589b24621 Mon Sep 17 00:00:00 2001 From: IMuthi Date: Tue, 23 Nov 2021 13:44:32 +0200 Subject: [PATCH 107/125] osc: probe attenuation custom value Signed-off-by: Ionut Muthi --- src/oscilloscope.cpp | 18 ++++++------------ src/oscilloscope_api.cpp | 11 +---------- ui/channel_settings.ui | 29 ++--------------------------- 3 files changed, 9 insertions(+), 49 deletions(-) diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index a2d505dfeb..db30f6e213 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -804,10 +804,9 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, resetHistogramDataPoints(); }); - connect(ch_ui->probe_attenuation, QOverload::of(&QComboBox::currentIndexChanged), - [=](int index){ - double value = 0.1 * (std::pow(10, index)); + connect(ch_ui->probe_attenuation_value, &QLineEdit::textChanged, this, [=](){ + double value = ch_ui->probe_attenuation_value->text().toDouble(); probe_attenuation[current_ch_widget] = value; if (current_channel == current_ch_widget) { plot.setDisplayScale(probe_attenuation[current_ch_widget]); @@ -1629,6 +1628,7 @@ void Oscilloscope::init_channel_settings() }); } + void Oscilloscope::activateAcCoupling(int i) { double alpha, beta; @@ -4025,21 +4025,15 @@ void Oscilloscope::update_chn_settings_panel(int id) } if (chn_widget->isMathChannel() || chn_widget->isReferenceChannel()) { - ch_ui->probe_attenuation->setVisible(false); + ch_ui->probe_attenuation_value->setVisible(false); ch_ui->probe_label->setVisible(false); ch_ui->label_3->setVisible(false); ch_ui->cmbMemoryDepth->setVisible(false); ch_ui->btnAutoset->setVisible(false); } else { - int index = 0; - double value = probe_attenuation[id]; - while (value > 0.1) { - value /= 10; - index++; - } - ch_ui->probe_attenuation->setCurrentIndex(index); - ch_ui->probe_attenuation->setVisible(true); + ch_ui->probe_attenuation_value->setText(QString::number(probe_attenuation[current_ch_widget])); + ch_ui->probe_attenuation_value->setVisible(true); ch_ui->probe_label->setVisible(true); ch_ui->label_3->setVisible(true); ch_ui->cmbMemoryDepth->setVisible(true); diff --git a/src/oscilloscope_api.cpp b/src/oscilloscope_api.cpp index ee9937e850..fd900a27b9 100644 --- a/src/oscilloscope_api.cpp +++ b/src/oscilloscope_api.cpp @@ -799,17 +799,8 @@ double Channel_API::getProbeAttenuation() const void Channel_API::setProbeAttenuation(double val) { int index = osc->channels_api.indexOf(this); - - int idx = std::log10(val / 0.1); - if (idx >= osc->ch_ui->probe_attenuation->count()) { - idx = osc->ch_ui->probe_attenuation->count() - 1; - } else if (idx <= 0) { - idx = 0; - } - val = std::pow(10, idx) * 0.1; - if (index == osc->current_ch_widget) { - osc->ch_ui->probe_attenuation->setCurrentIndex(idx); + osc->ch_ui->probe_attenuation_value->setText(QString::number(val)); } else { osc->probe_attenuation[index] = val; } diff --git a/ui/channel_settings.ui b/ui/channel_settings.ui index 9f74674596..83db850463 100644 --- a/ui/channel_settings.ui +++ b/ui/channel_settings.ui @@ -815,37 +815,12 @@ - Probe -Attenuation + Probe Attenuation - - - 1 - - - - 0.1X - - - - - 1X - - - - - 10X - - - - - 100X - - - + From d1130a91c91e7852cb126e72d016e59be5001739 Mon Sep 17 00:00:00 2001 From: AlexandraTrifan Date: Fri, 4 Mar 2022 14:47:16 +0200 Subject: [PATCH 108/125] azure-pipelines.yml: Move MacOS builds to Azure Pipelines. Signed-off-by: AlexandraTrifan --- CI/appveyor/before_install_lib.sh | 93 ++++++++++++++------- CI/appveyor/build_appveyor_macos.sh | 24 +++--- CI/appveyor/install_macos_deps.sh | 123 ++++++++++------------------ CI/appveyor/package_darwin.sh | 34 ++++---- appveyor.yml | 22 ----- azure-pipelines.yml | 74 +++++++++++++++++ 6 files changed, 206 insertions(+), 164 deletions(-) create mode 100644 azure-pipelines.yml diff --git a/CI/appveyor/before_install_lib.sh b/CI/appveyor/before_install_lib.sh index 9af9c92962..28d4a042f1 100755 --- a/CI/appveyor/before_install_lib.sh +++ b/CI/appveyor/before_install_lib.sh @@ -16,45 +16,26 @@ __cmake() { mkdir -p build pushd build # build - if [ "$APPVEYOR" == "true" ] ; then - cmake $args .. - make -j${NUM_JOBS} - sudo make install - else - cmake -DCMAKE_PREFIX_PATH="$STAGINGDIR" -DCMAKE_INSTALL_PREFIX="$STAGINGDIR" \ - -DCMAKE_EXE_LINKER_FLAGS="-L${STAGINGDIR}/lib" \ - $args .. $SILENCED - CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib make -j${NUM_JOBS} $SILENCED - make install - fi - + cmake -DCMAKE_PREFIX_PATH="$STAGINGDIR" -DCMAKE_INSTALL_PREFIX="$STAGINGDIR" \ + -DCMAKE_EXE_LINKER_FLAGS="-L${STAGINGDIR}/lib" \ + $args .. $SILENCED + CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib make -j${NUM_JOBS} $SILENCED + make -j${NUM_JOBS} + sudo make install popd } __make() { $preconfigure - if [ "$APPVEYOR" == "true" ] ; then - $configure - $make -j${NUM_JOBS} - sudo $make install - else - $configure --prefix="$STAGINGDIR" $SILENCED - CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib $make -j${NUM_JOBS} $SILENCED - $SUDO $make install - fi + $configure --prefix="$STAGINGDIR" + $make -j${NUM_JOBS} + sudo $make install } __qmake() { - if [ "$APPVEYOR" == "true" ] ; then - $QMAKE $qtarget - make -j${NUM_JOBS} - sudo make install - else - $QMAKE "$qtarget" $SILENCED - QMAKE=$QMAKE CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib \ - make -j${NUM_JOBS} $SILENCED - $SUDO make install - fi + $QMAKE $qtarget + make -j${NUM_JOBS} + sudo make install } __build_common() { @@ -161,6 +142,56 @@ qmake_build_git() { qmake_build_local() { local dir="$1" local qtarget="$2" + local patchfunc="$3" + [ ! -d "$WORKDIR/$dir" ] || { + [ -z "$patchfunc" ] || { + pushd $WORKDIR/$dir + $patchfunc + popd + } + } __build_common "$dir" "__qmake" } +patch_qwt() { + patch -p1 <<-EOF +--- a/qwtconfig.pri ++++ b/qwtconfig.pri +@@ -19,7 +19,7 @@ + QWT_INSTALL_PREFIX = \$\$[QT_INSTALL_PREFIX] + + unix { +- QWT_INSTALL_PREFIX = /usr/local ++ QWT_INSTALL_PREFIX = $STAGINGDIR + # QWT_INSTALL_PREFIX = /usr/local/qwt-\$\$QWT_VERSION-ma-qt-\$\$QT_VERSION + } + +@@ -42,7 +42,7 @@ QWT_INSTALL_LIBS = \$\${QWT_INSTALL_PREFIX}/lib + # runtime environment of designer/creator. + ###################################################################### + +-QWT_INSTALL_PLUGINS = \$\${QWT_INSTALL_PREFIX}/plugins/designer ++#QWT_INSTALL_PLUGINS = \$\${QWT_INSTALL_PREFIX}/plugins/designer + + # linux distributors often organize the Qt installation + # their way and QT_INSTALL_PREFIX doesn't offer a good +@@ -163,7 +163,7 @@ QWT_CONFIG += QwtOpenGL + + macx:!static:CONFIG(qt_framework, qt_framework|qt_no_framework) { + +- QWT_CONFIG += QwtFramework ++# QWT_CONFIG += QwtFramework + } + + ###################################################################### +--- a/src/src.pro ++++ b/src/src.pro +@@ -36,6 +36,7 @@ contains(QWT_CONFIG, QwtDll) { + QMAKE_LFLAGS_SONAME= + } + } ++ macx: QWT_SONAME=\$\${QWT_INSTALL_LIBS}/libqwt.dylib + } +EOF +} + diff --git a/CI/appveyor/build_appveyor_macos.sh b/CI/appveyor/build_appveyor_macos.sh index 09f7a3c71f..eaea792395 100755 --- a/CI/appveyor/build_appveyor_macos.sh +++ b/CI/appveyor/build_appveyor_macos.sh @@ -1,5 +1,6 @@ #!/bin/bash set -e +STAGINGDIR="${PWD}/staging" # Use the /usr/local/bin/pkg-config instead of /Mono/../pkg-config export PATH="/usr/local/bin:$PATH" @@ -15,22 +16,17 @@ if command -v brew ; then fi NUM_JOBS=4 - mkdir -p build - cd build -if [ "$APPVEYOR" == "true" ] ; then - MACOS_VERSION=$(/usr/libexec/PlistBuddy -c "Print:ProductVersion" /System/Library/CoreServices/SystemVersion.plist) - if [[ "$MACOS_VERSION" == "10.14."* ]] ; then - export MACOSX_DEPLOYMENT_TARGET=10.13 - fi - cmake .. - make -j${NUM_JOBS} - otool -l ./Scopy.app/Contents/MacOS/Scopy # for debugging -else - cmake -DCMAKE_PREFIX_PATH="$STAGINGDIR;${QT_PATH}/lib/cmake" -DCMAKE_INSTALL_PREFIX="$STAGINGDIR" \ - -DCMAKE_EXE_LINKER_FLAGS="-L${STAGINGDIR}/lib" .. - CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib make -j${NUM_JOBS} +MACOS_VERSION=$(/usr/libexec/PlistBuddy -c "Print:ProductVersion" /System/Library/CoreServices/SystemVersion.plist) +if [[ "$MACOS_VERSION" == "10.14."* ]] ; then + export MACOSX_DEPLOYMENT_TARGET=10.13 fi +cmake -DCMAKE_STAGING_PREFIX="${STAGINGDIR}" \ + -DCMAKE_PREFIX_PATH="${STAGINGDIR};${STAGINGDIR}/lib/cmake;${STAGINGDIR}/lib/pkgconfig;${STAGINGDIR}/lib/cmake/gnuradio;${STAGINGDIR}/lib/cmake/iio;${QT_PATH}/lib/cmake" \ + -DCMAKE_INSTALL_PREFIX="${STAGINGDIR}" \ + -DCMAKE_EXE_LINKER_FLAGS="-L${STAGINGDIR}/lib" .. +CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib make -j${NUM_JOBS} +otool -l ./Scopy.app/Contents/MacOS/Scopy \ No newline at end of file diff --git a/CI/appveyor/install_macos_deps.sh b/CI/appveyor/install_macos_deps.sh index cd9c8af209..68c87f78a4 100755 --- a/CI/appveyor/install_macos_deps.sh +++ b/CI/appveyor/install_macos_deps.sh @@ -9,37 +9,20 @@ GNURADIO_BRANCH=scopy GRSCOPY_BRANCH=master GRM2K_BRANCH=master QWT_BRANCH=qwt-multiaxes -LIBSIGROK_BRANCH=master LIBSIGROKDECODE_BRANCH=master -BOOST_VERSION_FILE=1_73_0 -BOOST_VERSION=1.73.0 LIBTINYIIOD_BRANCH=master PYTHON="python3" -PACKAGES=" ${QT_FORMULAE} pkg-config cmake fftw bison gettext autoconf automake libtool libzip glib libusb glog $PYTHON" +PACKAGES=" ${QT_FORMULAE} boost pkg-config cmake fftw bison gettext autoconf automake libtool libzip glib libusb glog $PYTHON" PACKAGES="$PACKAGES doxygen wget gnu-sed libmatio dylibbundler libxml2 ghr" set -e -cd ~ +REPO_SRC=$BUILD_REPOSITORY_LOCALPATH WORKDIR=${PWD} -NUM_JOBS=4 +JOBS=4 -brew update brew search ${QT_FORMULAE} -brew_install_or_upgrade() { - brew install $1 || \ - brew upgrade $1 || \ - brew ls --versions $1 # check if installed last-ly -} - -brew_install() { - brew install $1 || \ - brew ls --versions $1 -} - -for pak in $PACKAGES ; do - brew_install $pak -done +brew install $PACKAGES for pkg in gcc bison gettext cmake python; do brew link --overwrite --force $pkg @@ -48,7 +31,7 @@ done pip3 install mako six pwd -source ./projects/scopy/CI/appveyor/before_install_lib.sh +source ${REPO_SRC}/CI/appveyor/before_install_lib.sh QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" @@ -57,6 +40,7 @@ export PATH="/usr/local/opt/bison/bin:$PATH" export PATH="${QT_PATH}:$PATH" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libzip/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libffi/lib/pkgconfig" +export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$STAGINGDIR/lib/pkgconfig" echo $PKG_CONFIG_PATH QMAKE="$(command -v qmake)" @@ -66,7 +50,6 @@ CMAKE_OPTS="-DCMAKE_PREFIX_PATH=$STAGINGDIR -DCMAKE_INSTALL_PREFIX=$STAGINGDIR" build_libiio() { echo "### Building libiio - version $LIBIIO_VERSION" - cd ~ git clone https://github.com/analogdevicesinc/libiio.git ${WORKDIR}/libiio cd ${WORKDIR}/libiio git checkout $LIBIIO_VERSION @@ -84,15 +67,14 @@ build_libiio() { -DOSX_PACKAGE:BOOL=OFF \ ${WORKDIR}/libiio - make $JOBS - sudo make ${JOBS} install + make -j $JOBS + sudo make -j ${JOBS} install } build_libm2k() { echo "### Building libm2k - branch $LIBM2K_BRANCH" - cd ~ git clone --depth 1 https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH ${WORKDIR}/libm2k mkdir ${WORKDIR}/libm2k/build-${ARCH} @@ -107,14 +89,13 @@ build_libm2k() { -DENABLE_LOG=ON\ ${WORKDIR}/libm2k - make $JOBS - sudo make ${JOBS} install + make -j $JOBS + sudo make -j ${JOBS} install } build_libad9361() { echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - cd ~ git clone --depth 1 https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH ${WORKDIR}/libad9361 mkdir ${WORKDIR}/libad9361/build-${ARCH} @@ -123,42 +104,29 @@ build_libad9361() { cmake ${CMAKE_OPTS} \ ${WORKDIR}/libad9361 - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install + make -j $JOBS + sudo make -j $JOBS install } build_log4cpp() { + cd ${WORKDIR} wget https://sourceforge.net/projects/log4cpp/files/latest/log4cpp-1.1.3.tar.gz tar xvzf log4cpp-1.1.3.tar.gz cd log4cpp ./configure --prefix=/usr/local/ - make $JOBS - sudo make install -} - -build_boost() { - echo "### Building boost - version $BOOST_VERSION_FILE" - - cd ~ - wget https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_$BOOST_VERSION_FILE.tar.gz - tar -xzf boost_$BOOST_VERSION_FILE.tar.gz - cd boost_$BOOST_VERSION_FILE - patch -p1 < ${WORKDIR}/projects/scopy/CI/appveyor/patches/boost-darwin.patch - ./bootstrap.sh --with-libraries=atomic,date_time,filesystem,program_options,system,chrono,thread,regex,test - ./b2 - ./b2 install + make -j $JOBS + sudo make -j ${JOBS} install } build_gnuradio() { echo "### Building gnuradio - branch $GNURADIO_BRANCH" - cd ~ - git clone --recurse-submodules https://github.com/$GNURADIO_FORK/gnuradio -b $GNURADIO_BRANCH + git clone --recurse-submodules https://github.com/$GNURADIO_FORK/gnuradio -b $GNURADIO_BRANCH ${WORKDIR}/gnuradio mkdir ${WORKDIR}/gnuradio/build-${ARCH} cd ${WORKDIR}/gnuradio/build-${ARCH} - cmake -DENABLE_GR_DIGITAL:BOOL=OFF \ + cmake ${CMAKE_OPTS} \ + -DENABLE_GR_DIGITAL:BOOL=OFF \ -DENABLE_GR_DTV:BOOL=OFF \ -DENABLE_GR_AUDIO:BOOL=OFF \ -DENABLE_GR_CHANNELS:BOOL=OFF \ @@ -179,73 +147,74 @@ build_gnuradio() { -DENABLE_CTRLPORT_THRIFT=OFF \ -DCMAKE_C_FLAGS=-fno-asynchronous-unwind-tables \ ${WORKDIR}/gnuradio - make $JOBS - sudo make install + make -j $JOBS + sudo make -j $JOBS install } build_griio() { echo "### Building gr-iio - branch $GRIIO_BRANCH" - cd ~ git clone --depth 1 https://github.com/analogdevicesinc/gr-iio.git -b $GRIIO_BRANCH ${WORKDIR}/gr-iio mkdir ${WORKDIR}/gr-iio/build-${ARCH} cd ${WORKDIR}/gr-iio/build-${ARCH} cmake ${CMAKE_OPTS} \ + -DWITH_PYTHON=OFF \ ${WORKDIR}/gr-iio - make $JOBS - sudo make $JOBS install + make -j $JOBS + sudo make -j $JOBS install } build_grm2k() { echo "### Building gr-m2k - branch $GRM2K_BRANCH" - cd ~ git clone --depth 1 https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH ${WORKDIR}/gr-m2k mkdir ${WORKDIR}/gr-m2k/build-${ARCH} cd ${WORKDIR}/gr-m2k/build-${ARCH} cmake ${CMAKE_OPTS} \ + -DWITH_PYTHON=OFF \ ${WORKDIR}/gr-m2k - make $JOBS - sudo make $JOBS install + make -j $JOBS + sudo make -j $JOBS install } build_grscopy() { echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - cd ~ + git clone --depth 1 https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH ${WORKDIR}/gr-scopy mkdir ${WORKDIR}/gr-scopy/build-${ARCH} cd ${WORKDIR}/gr-scopy/build-${ARCH} cmake ${CMAKE_OPTS} \ + -DWITH_PYTHON=OFF \ ${WORKDIR}/gr-scopy - make $JOBS - sudo make $JOBS install + make -j $JOBS + sudo make -j $JOBS install } build_glibmm() { echo "### Building glibmm - 2.64.0" - cd ~ + cd ${WORKDIR} wget http://ftp.acc.umu.se/pub/gnome/sources/glibmm/2.64/glibmm-2.64.0.tar.xz tar xzvf glibmm-2.64.0.tar.xz cd glibmm-2.64.0 - ./configure - make $JOBS - sudo make $JOBS install + ./configure --prefix=$STAGINGDIR + make -j $JOBS + sudo make -j $JOBS install } build_sigcpp() { echo "### Building libsigc++ -2.10.0" - cd ~ + cd ${WORKDIR} wget http://ftp.acc.umu.se/pub/GNOME/sources/libsigc++/2.10/libsigc++-2.10.0.tar.xz tar xvzf libsigc++-2.10.0.tar.xz cd libsigc++-2.10.0 - ./configure - make $JOBS - sudo make $JOBS install + ./configure --prefix=$STAGINGDIR + make -j $JOBS + sudo make -j $JOBS install } build_libsigrokdecode() { @@ -255,21 +224,20 @@ build_libsigrokdecode() { cd ${WORKDIR}/libsigrokdecode ./autogen.sh - ./configure + ./configure --prefix=$STAGINGDIR - sudo make $JOBS install + sudo make -j $JOBS install } build_qwt() { - echo "### Building qwt - branch $QWT_BRANCH" - git clone --depth 1 https://github.com/cseci/qwt --branch $QWT_BRANCH $WORKDIR/qwt - qmake_build_local "qwt" "qwt.pro" + echo "### Building qwt - branch qwt-multiaxes" + git clone --depth 1 https://github.com/cseci/qwt -b $QWT_BRANCH ${WORKDIR}/qwt + qmake_build_local "qwt" "qwt.pro" "patch_qwt" } build_libtinyiiod() { echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - cd ~ git clone --depth 1 https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH ${WORKDIR}/libtinyiiod mkdir ${WORKDIR}/libtinyiiod/build-${ARCH} cd ${WORKDIR}/libtinyiiod/build-${ARCH} @@ -278,8 +246,8 @@ build_libtinyiiod() { -DBUILD_EXAMPLES=OFF \ ${WORKDIR}/libtinyiiod - make $JOBS - sudo make $JOBS install + make -j $JOBS + sudo make -j $JOBS install } build_sigcpp @@ -288,7 +256,6 @@ build_libiio build_libad9361 build_libm2k build_log4cpp -build_boost build_gnuradio build_griio build_grscopy diff --git a/CI/appveyor/package_darwin.sh b/CI/appveyor/package_darwin.sh index 4b2b724416..1ad85ee169 100755 --- a/CI/appveyor/package_darwin.sh +++ b/CI/appveyor/package_darwin.sh @@ -1,19 +1,26 @@ #!/bin/bash set -e +STAGINGDIR="${PWD}/staging" cd build mkdir -p ./Scopy.app/Contents/Frameworks ## Handle libm2k paths -m2kpath=/usr/local/lib/libm2k.dylib +m2kpath=${STAGINGDIR}/lib/libm2k.dylib m2krpath="$(otool -D ${m2kpath} | grep @rpath)" m2kid=${m2krpath#"@rpath/"} -sudo cp /usr/local/lib/libm2k.* ./Scopy.app/Contents/Frameworks +sudo cp ${STAGINGDIR}/lib/libm2k.* ./Scopy.app/Contents/Frameworks sudo install_name_tool -id @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/${m2kid} sudo install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/MacOS/Scopy -export DYLD_FALLBACK_LIBRARY_PATH=/usr/local/lib +export DYLD_FALLBACK_LIBRARY_PATH=${STAGINGDIR}/lib + +# Copy the iio and ad9361 to the stagingdir path +sudo cp -R /Library/Frameworks/iio.framework ${STAGINGDIR}/lib +sudo cp -R /Library/Frameworks/ad9361.framework ${STAGINGDIR}/lib + ## Bundle some known dependencies -sudo echo "/usr/local/lib" | dylibbundler -od -b -x ./Scopy.app/Contents/MacOS/Scopy -d ./Scopy.app/Contents/Frameworks/ -p @executable_path/../Frameworks/ >/dev/null +# -ns == no signing +sudo echo "${STAGINGDIR}/lib" | dylibbundler -ns -od -b -x ./Scopy.app/Contents/MacOS/Scopy -d ./Scopy.app/Contents/Frameworks/ -p @executable_path/../Frameworks/ >/dev/null ## Copy the frameworks dylibbundler failed to copy sudo cp -R /usr/local/opt/python/Frameworks/Python.framework Scopy.app/Contents/Frameworks/ @@ -82,10 +89,10 @@ fi ## Handle iio-emu + libtinyiiod sudo cp ./iio-emu/iio-emu ./Scopy.app/Contents/MacOS/ -tinypath=/usr/local/lib/tinyiiod.dylib +tinypath=${STAGINGDIR}/lib/tinyiiod.dylib tinyrpath="$(otool -D ${tinypath} | grep @rpath)" tinyid=${tinyrpath#"@rpath/"} -sudo cp /usr/local/lib/tinyiiod.* ./Scopy.app/Contents/Frameworks +sudo cp ${STAGINGDIR}/lib/tinyiiod.* ./Scopy.app/Contents/Frameworks sudo install_name_tool -id @executable_path/../Frameworks/${tinyid} ./Scopy.app/Contents/Frameworks/${tinyid} sudo install_name_tool -change ${tinyrpath} @executable_path/../Frameworks/${tinyid} ./Scopy.app/Contents/MacOS/iio-emu @@ -93,20 +100,9 @@ sudo install_name_tool -change ${tinyrpath} @executable_path/../Frameworks/${tin sudo macdeployqt Scopy.app curl -o /tmp/macdeployqtfix.py https://raw.githubusercontent.com/aurelien-rainone/macdeployqtfix/master/macdeployqtfix.py -sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/Scopy /usr/local/opt/qt/ -sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/iio-emu /usr/local/opt/qt/ +sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/Scopy ${QT_PATH} +sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/iio-emu ${QT_PATH} sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/Scopy ./Scopy.app/Contents/Frameworks/ sudo macdeployqt Scopy.app -dmg -ls - -UPLOAD_TOOL=ghr - -if [ "$APPVEYOR_REPO_BRANCH" = "master" ]; then - echo Identified master branch - if [ -z "$APPVEYOR_PULL_REQUEST_NUMBER" ]; then - echo Not a pull request - $UPLOAD_TOOL -u $APPVEYOR_ACCOUNT_NAME -r $APPVEYOR_PROJECT_NAME -name "Continuous build" -b "Latest succesful master build " -prerelease -debug -replace continous $DEPLOY_FILE - fi -fi diff --git a/appveyor.yml b/appveyor.yml index c92cba5406..b9d40beb32 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,28 +9,6 @@ environment: BRANCH: $(APPVEYOR_REPO_BRANCH) REPO: $(APPVEYOR_REPO_NAME) matrix: - - ARCH: x86_64 - ARCH_BIT: 64 - APPVEYOR_BUILD_WORKER_IMAGE: macos-bigsur - BUILD_DEPS_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/install_macos_deps.sh" - BUILD_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/build_appveyor_macos.sh" - PACKAGE_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/package_darwin.sh" - DEPLOY_FILE: "/Users/appveyor/projects/scopy/build/Scopy.dmg" - C_COMPILER: -DCMAKE_C_COMPILER=/usr/bin/gcc - CXX_COMPILER: -DCMAKE_CXX_COMPILER=/usr/bin/g++ - QT_FORMULAE: "qt5" - - - ARCH: x86_64 - ARCH_BIT: 64 - APPVEYOR_BUILD_WORKER_IMAGE: macos - BUILD_DEPS_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/install_macos_deps.sh" - BUILD_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/build_appveyor_macos.sh" - PACKAGE_CMD: "/Users/appveyor/projects/scopy/CI/appveyor/package_darwin.sh" - DEPLOY_FILE: "/Users/appveyor/projects/scopy/build/Scopy.dmg" - C_COMPILER: -DCMAKE_C_COMPILER=/usr/bin/gcc - CXX_COMPILER: -DCMAKE_CXX_COMPILER=/usr/bin/g++ - QT_FORMULAE: "qt5" - - ARCH: x86_64 ARCH_BIT: 64 APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu1804 diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..e7343254b3 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,74 @@ +variables: + QT_FORMULAE: qt@5 + REPO_SLUG: $(Build.Repository.Name) + CURRENT_COMMIT: $(Build.SourceVersion) +trigger: + branches: + include: + - main + - master + - staging/* + - ci-* + tags: + include: + - v* + +pr: + branches: + include: + - main + - master + - 20* + +stages: +- stage: Builds + jobs: + - job: macOSBuilds + strategy: + matrix: + macOS_10_15: + vmImage: 'macOS-10.15' + artifactName: 'macOS-10.15' + macOS_11: + vmImage: 'macOS-11' + artifactName: 'macOS-11' + pool: + vmImage: $[ variables['vmImage'] ] + steps: + - checkout: self + fetchDepth: 1 + clean: true + - script: ./CI/appveyor/install_macos_deps.sh + displayName: 'Dependencies' + workingDirectory: $(Build.Repository.LocalPath) + - script: ./CI/appveyor/build_appveyor_macos.sh + displayName: 'Build' + workingDirectory: $(Build.Repository.LocalPath) + - script: ./CI/appveyor/package_darwin.sh + displayName: 'Create Scopy.dmg' + workingDirectory: $(Build.Repository.LocalPath) + - script: cp -R staging ${BUILD_ARTIFACTSTAGINGDIRECTORY} + displayName: 'Copy staging dir' + workingDirectory: $(Build.Repository.LocalPath) + - script: cp build/Scopy.dmg ${BUILD_ARTIFACTSTAGINGDIRECTORY} + displayName: 'Copy Scopy.dmg' + workingDirectory: $(Build.Repository.LocalPath) + - script: | + ACCOUNT_NAME=`echo $REPO_SLUG | awk -F "/" '{print $1}'` + PROJECT_NAME=`echo $REPO_SLUG | awk -F "/" '{print $2}'` + echo "ACCOUNT_NAME = " ${ACCOUNT_NAME} + echo "PROJECT_NAME = " ${PROJECT_NAME} + MACOS_VERSION=$(/usr/libexec/PlistBuddy -c "Print:ProductVersion" /System/Library/CoreServices/SystemVersion.plist) + DEPLOY_FILE=Scopy-macos${MACOS_VERSION}-${CURRENT_COMMIT:0:7}.dmg + cp build/Scopy.dmg ${DEPLOY_FILE} + ls -la + # TODO: Add GH_TOKEN for github cont release upload + #ghr -u $ACCOUNT_NAME -r $PROJECT_NAME -name "Continuous build" -b "Latest succesful master build " -prerelease -debug -replace continous ${DEPLOY_FILE} + displayName: 'Push to github continuous release' + workingDirectory: $(Build.Repository.LocalPath) + condition: and(succeeded(), and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))) + - task: PublishPipelineArtifact@1 + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)' + artifactName: '$(artifactName)' From c01f57785ec344eedaccedc715ad372e095e01c2 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Fri, 3 Dec 2021 17:47:43 +0200 Subject: [PATCH 109/125] pattern_generator: hide the buffer previewer layout Signed-off-by: ioanachelaru --- src/patterngenerator/pattern_generator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 33dd8c689a..6bb2721706 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -273,6 +273,8 @@ void PatternGenerator::setupUi() m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + // TODO: do we want the buffer previewer in this tool? + m_ui->hLayoutBufferPreview->hide(); m_plot.setAxisVisible(QwtAxis::YLeft, false); m_plot.setAxisVisible(QwtAxis::XBottom, false); From 0d51bf8444cdfd54cdd4b1f16ba57c9d9c0e8b9b Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 13 Dec 2021 14:06:07 +0200 Subject: [PATCH 110/125] capture plot: re-added the min sizes Signed-off-by: ioanachelaru --- src/oscilloscope_plot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index b7922fe179..c486d1b8d8 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -78,8 +78,8 @@ CapturePlot::CapturePlot(QWidget *parent, bool isdBgraph, unsigned int xNumDivs d_currentHandleInitPx(30), d_maxBufferError(nullptr) { -// setMinimumHeight(200); -// setMinimumWidth(450); + setMinimumHeight(200); + setMinimumWidth(450); /* Initial colors scheme */ d_trigAactiveLinePen = QPen(QColor(255, 255, 255), 2, Qt::SolidLine); From e63e9937ccfaf7df7bf5a398826f2bf132b4f350 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 21 Dec 2021 15:02:14 +0200 Subject: [PATCH 111/125] gui: fix bad ui rendering after docking operation Signed-off-by: ioanachelaru --- src/tool.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tool.cpp b/src/tool.cpp index 5df86adf30..e430a11f16 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -62,6 +62,10 @@ Tool::Tool(struct iio_context *ctx, ToolMenuItem *toolMenuItem, #endif connect(this, &Tool::detachedState, toolMenuItem, &ToolMenuItem::setDetached); + + // fixes bad ui rendering when dock->minimize->maximize + // TODO: may be removed after ToolLauncher refactoring + this->setVisible(false); } Tool::~Tool() From 6aa1f3cd1c24137b5854d856aebcebc97b91cbb4 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 13 Dec 2021 14:05:39 +0200 Subject: [PATCH 112/125] gui: re-added the non-dockable implementation Signed-off-by: ioanachelaru --- src/logicanalyzer/logic_analyzer.cpp | 27 +- src/network_analyzer.cpp | 35 +- src/oscilloscope.cpp | 201 +-- src/oscilloscope.hpp | 6 +- src/patterngenerator/pattern_generator.cpp | 32 +- src/preferences.cpp | 45 +- src/preferences.h | 10 + src/qtgui_util.cc | 10 + src/signal_generator.cpp | 51 +- src/spectrum_analyzer.cpp | 28 +- src/utils.h | 2 + ui/oscilloscope.ui | 394 ++++- ui/preferences.ui | 1605 ++++++++++---------- 13 files changed, 1438 insertions(+), 1008 deletions(-) diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index c348c38524..2f22f19973 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -1306,7 +1306,7 @@ void LogicAnalyzer::setupUi() QGridLayout* gridLayout = new QGridLayout(plotWidget); gridLayout->setVerticalSpacing(0); gridLayout->setHorizontalSpacing(0); - gridLayout->setContentsMargins(25, 0, 25, 0); + gridLayout->setContentsMargins(15, 0, 15, 0); plotWidget->setLayout(gridLayout); QSpacerItem *plotSpacer = new QSpacerItem(0, 5, @@ -1327,23 +1327,24 @@ void LogicAnalyzer::setupUi() vLayout->addWidget(plotWidget); centralWidget->setLayout(vLayout); - // Add dockable plot + if(prefPanel->getCurrent_docking_enabled()) { - QMainWindow* m_centralMainWindow = new QMainWindow(this); - m_centralMainWindow->setCentralWidget(0); - m_centralMainWindow->setWindowFlags(Qt::Widget); - ui->gridLayoutPlot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + // main window for dock widget + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + ui->gridLayoutPlot->addWidget(mainWindow, 1, 0, 1, 1); - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::AllDockWidgetAreas); - docker->setWidget(centralWidget); + QDockWidget* docker = DockerUtils::createDockWidget(mainWindow, centralWidget); + + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); + DockerUtils::configureTopBar(docker); #endif - - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + } else { + ui->gridLayoutPlot->addWidget(centralWidget, 1, 0, 1, 1); + } m_plot.setAxisVisible(QwtAxis::YLeft, false); diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 96554455de..5dd378a098 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -423,12 +423,9 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, m_phaseGraph.setVertCursorsHandleEnabled(false); - // Add dockable plot - - QWidget* widget = new QWidget(this); - QGridLayout* gridLayout = new QGridLayout(widget); - gridLayout->setVerticalSpacing(0); - gridLayout->setHorizontalSpacing(10); + // plot widget + QWidget* centralWidget = new QWidget(this); + QGridLayout* gridLayout = new QGridLayout(centralWidget); gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->addWidget(bufferPreviewer, 0, 1, 1, 1); @@ -444,24 +441,26 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, gridLayout->addWidget(m_dBgraph.bottomHandlesArea(), 6, 0, 1, 3); - widget->setLayout(gridLayout); + centralWidget->setLayout(gridLayout); + if(prefPanel->getCurrent_docking_enabled()) { + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + ui->gridLayout_plots->addWidget(mainWindow, 0, 0); - QMainWindow* m_centralMainWindow = new QMainWindow(this); - m_centralMainWindow->setCentralWidget(0); - m_centralMainWindow->setWindowFlags(Qt::Widget); - ui->gridLayout_plots->addWidget(m_centralMainWindow, 0, 0); + QDockWidget* dockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget); - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::AllDockWidgetAreas); - docker->setWidget(widget); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); + DockerUtils::configureTopBar(dockWidget); #endif - - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + } else { + gridLayout->setHorizontalSpacing(0); + gridLayout->setVerticalSpacing(6); + ui->gridLayout_plots->addWidget(centralWidget); + } m_phaseGraph.enableXaxisLabels(); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index db30f6e213..c923c5d6e7 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -368,39 +368,84 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, ui->plot_and_scales->removeWidget(ui->scalesWidget); vLayout->addWidget(ui->scalesWidget); + // hide fixed containers + ui->container_fft_plot->hide(); + ui->xy_plot_container->hide(); - // Create main window - QMainWindow* centralWindow = new QMainWindow(this); - centralWindow->setCentralWidget(0); - centralWindow->setWindowFlags(Qt::Widget); - ui->gridLayoutPlot->addWidget(centralWindow, 1, 0, 1, 1); + // add space for histogram + plot.setBonusWidthForHistogram(25); - // Create default plot docker - QDockWidget* plotDocker = new QDockWidget(centralWindow); - plotDocker->setFeatures(plotDocker->features() & ~QDockWidget::DockWidgetClosable); - plotDocker->setAllowedAreas(Qt::AllDockWidgetAreas); - plotDocker->setWidget(centralWidget); - plotDocker->setWindowTitle("TimeDomain"); + if(prefPanel->getCurrent_docking_enabled()) { + ui->histogram_container->hide(); + + // main window for all dock widgets + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + ui->gridLayoutPlot->addWidget(mainWindow, 1, 0, 1, 1); + + // time domain plot dock + QDockWidget* plotDockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget, "TimeDomain"); + + + // histogram widget + histWidget = new QWidget(this); + QVBoxLayout* histLayout = new QVBoxLayout(histWidget); + histWidget->setLayout(histLayout); + + // histogram dock + histDockWidget = DockerUtils::createDockWidget(mainWindow, histWidget, "Histogram"); + histDockWidget->hide(); - plot.setBonusWidthForHistogram(25); + // fft dock + fftDockWidget = DockerUtils::createDockWidget(mainWindow, &fft_plot, "FFT"); + fftDockWidget->hide(); - // setup docker for histogram plot - histDocker = new QDockWidget(centralWindow); - histDocker->setFeatures(plotDocker->features() & ~QDockWidget::DockWidgetClosable); - histDocker->setAllowedAreas(Qt::AllDockWidgetAreas); - histDocker->setWindowTitle("Histogram"); + // xy widget + QWidget *xyWidget = new QWidget(this); + QGridLayout *gridL = new QGridLayout(xyWidget); + gridL->setVerticalSpacing(0); + gridL->setHorizontalSpacing(0); + gridL->setContentsMargins(0, 0, 0, 0); - // build histogram widget - histWidget = new QWidget(histDocker); - QVBoxLayout* histLayout = new QVBoxLayout(histWidget); - histWidget->setLayout(histLayout); - histDocker->setWidget(histWidget); + gridL->addWidget(&xy_plot, 1, 0); + gridL->addWidget(gridL->itemAtPosition(1, 0)->widget(), 2, 0); + xyWidget->setLayout(gridL); - histDocker->hide(); + // xy dock + xyDockWidget = DockerUtils::createDockWidget(mainWindow, xyWidget, "XY"); + xyDockWidget->hide(); + // arange all dockers + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, fftDockWidget); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, histDockWidget); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, plotDockWidget); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, xyDockWidget); + + mainWindow->tabifyDockWidget(plotDockWidget, histDockWidget); + mainWindow->tabifyDockWidget(plotDockWidget, fftDockWidget); + mainWindow->tabifyDockWidget(plotDockWidget, xyDockWidget); + +#ifdef PLOT_MENU_BAR_ENABLED + DockerUtils::configureTopBar(plotDockWidget); + DockerUtils::configureTopBar(histDockWidget); + DockerUtils::configureTopBar(fftDockWidget); + DockerUtils::configureTopBar(xyDockWidget); +#endif + + } else { + ui->gridLayoutPlot->addWidget(centralWidget, 1, 0, 1, 1); + ui->hlayout_fft->addWidget(&fft_plot); + + QGridLayout *gridL = static_cast(ui->xy_plot_container->layout()); + QWidget *w = gridL->itemAtPosition(1, 0)->widget(); + gridL->addWidget(&xy_plot, 1, 0); + gridL->addWidget(w, 2, 0); + } + /* Default plot settings */ plot.setSampleRate(active_sample_rate, 1, ""); plot.setActiveVertAxis(0); @@ -462,52 +507,6 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, xy_plot.setLineColor(0, QColor("#4a64ff")); - - // Add dockable fft plot - fftDocker = new QDockWidget(centralWindow); - fftDocker->setFeatures(fftDocker->features() & ~QDockWidget::DockWidgetClosable); - fftDocker->setAllowedAreas(Qt::AllDockWidgetAreas); - fftDocker->setWindowTitle("FFT"); - fftDocker->setWidget(&fft_plot); - - fftDocker->hide(); - - - // setup XYPlot docker - QWidget *xyWidget = new QWidget(this); - QGridLayout *gridL = new QGridLayout(xyWidget); - gridL->setVerticalSpacing(0); - gridL->setHorizontalSpacing(0); - gridL->setContentsMargins(0, 0, 0, 0); - - gridL->addWidget(&xy_plot, 1, 0); - gridL->addWidget(gridL->itemAtPosition(1, 0)->widget(), 2, 0); - xyWidget->setLayout(gridL); - - xyDocker = new QDockWidget(centralWindow); - xyDocker->setFeatures(xyDocker->features() & ~QDockWidget::DockWidgetClosable); - xyDocker->setAllowedAreas(Qt::AllDockWidgetAreas); - xyDocker->setWindowTitle("XY"); - xyDocker->setWidget(xyWidget); - - xyDocker->hide(); - - // arange all dockers - centralWindow->addDockWidget(Qt::LeftDockWidgetArea, fftDocker); - centralWindow->addDockWidget(Qt::LeftDockWidgetArea, histDocker); - centralWindow->addDockWidget(Qt::LeftDockWidgetArea, plotDocker); - centralWindow->addDockWidget(Qt::RightDockWidgetArea, xyDocker); - - centralWindow->tabifyDockWidget(plotDocker, fftDocker); - centralWindow->tabifyDockWidget(plotDocker, histDocker); - -#ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(plotDocker); - DockerUtils::configureTopBar(histDocker); - DockerUtils::configureTopBar(fftDocker); - DockerUtils::configureTopBar(xyDocker); -#endif - ui->rightMenu->setMaximumWidth(0); // Controls for scale/division and position controls (Horizontal & Vertical) @@ -3005,10 +3004,19 @@ void Oscilloscope::onFFT_view_toggled(bool visible) iio->connect(ctm_blocks.at(i), 0, qt_fft_block, i); } - fftDocker->show(); + if(prefPanel->getCurrent_docking_enabled()) { + fftDockWidget->show(); + } else { + ui->container_fft_plot->show(); + } } else { - fftDocker->setFloating(false); - fftDocker->hide(); + + if(prefPanel->getCurrent_docking_enabled()) { + fftDockWidget->setFloating(false); + fftDockWidget->hide(); + } else { + ui->container_fft_plot->hide(); + } if (fft_is_visible && (fft_channels.size() > 0)) { for (unsigned int i = 0; i < nb_channels; i++) { @@ -3033,30 +3041,41 @@ void Oscilloscope::onHistogram_view_toggled(bool visible) { cancelZoom(); - if(!visible){ - histDocker->setFloating(false); - histDocker->hide(); + if(!visible) { + if(prefPanel->getCurrent_docking_enabled()) { + histDockWidget->setFloating(false); + histDockWidget->hide(); + histWidget->layout()->removeWidget(&hist_plot); + } else { + ui->hist_layout->removeWidget(&hist_plot); + } + + gridPlot->addWidget(&hist_plot, 3, 2, 1, 1); + gridPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); hist_plot.setOrientation(Qt::Horizontal); hist_plot.setMinimumWidth(25); hist_plot.setMaximumWidth(25); hist_plot.setMaximumHeight(1000); - histWidget->layout()->removeWidget(&hist_plot); - gridPlot->addWidget(&hist_plot, 3, 2, 1, 1); - gridPlot->addWidget(plot.rightHandlesArea(), 1, 3, 4, 1); plot.setBonusWidthForHistogram(25); scaleHistogramPlot(); toggleMiniHistogramPlotVisible(miniHistogram); } else { - histDocker->show(); + gridPlot->removeWidget(&hist_plot); + gridPlot->addWidget(plot.rightHandlesArea(), 1, 2, 3, 1); + + if(prefPanel->getCurrent_docking_enabled()) { + histWidget->layout()->addWidget(&hist_plot); + histDockWidget->show(); + } else { + ui->hist_layout->addWidget(&hist_plot); + } + hist_plot.setOrientation(Qt::Vertical); hist_plot.setMinimumWidth(plot.width()); hist_plot.setMaximumHeight(300); hist_plot.setMaximumWidth(2000); - gridPlot->removeWidget(&hist_plot); - gridPlot->addWidget(plot.rightHandlesArea(), 1, 2, 4, 1); - histWidget->layout()->addWidget(&hist_plot); plot.setBonusWidthForHistogram(0); scaleHistogramPlot(); hist_plot.setVisible(true); @@ -3069,7 +3088,9 @@ void Oscilloscope::onXY_view_toggled(bool visible) if (visible) { gsettings_ui->xySettings->show(); } else { - xyDocker->setFloating(false); + if(prefPanel->getCurrent_docking_enabled()) { + xyDockWidget->setFloating(false); + } gsettings_ui->xySettings->hide(); } @@ -3133,9 +3154,19 @@ void Oscilloscope::onXY_view_toggled(bool visible) if(!xy_is_visible) iio->connect(ftc, 0, this->qt_xy_block, 0); - xyDocker->show(); + if(prefPanel->getCurrent_docking_enabled()) { + xyDockWidget->show(); + } else { + ui->xy_plot_container->show(); + } } else { - xyDocker->hide(); + if(prefPanel->getCurrent_docking_enabled()) { + xyDockWidget->setFloating(false); + xyDockWidget->hide(); + } else { + ui->xy_plot_container->hide(); + } + // Disconnect the XY section from the running flowgraph iio->disconnect(xy_channels.at(index_x).first, xy_channels.at(index_x).second, diff --git a/src/oscilloscope.hpp b/src/oscilloscope.hpp index 9122d6c950..cebeef61d1 100644 --- a/src/oscilloscope.hpp +++ b/src/oscilloscope.hpp @@ -314,9 +314,9 @@ namespace adiscope { CustomPlotPositionButton *cursorsPositionButton; QGridLayout* gridPlot; - QDockWidget* fftDocker; - QDockWidget* xyDocker; - QDockWidget* histDocker; + QDockWidget* fftDockWidget; + QDockWidget* xyDockWidget; + QDockWidget* histDockWidget; QWidget* histWidget; MouseWheelWidgetGuard *wheelEventGuard; diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index 6bb2721706..ce4b1ca682 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -231,14 +231,14 @@ void PatternGenerator::setupUi() // set default menu width to 0 m_ui->rightMenu->setMaximumWidth(0); - // Add docking plot - QWidget* widget = new QWidget(this); - QGridLayout* gridLayout = new QGridLayout(widget); + // plot widget + QWidget* centralWidget = new QWidget(this); + QGridLayout* gridLayout = new QGridLayout(centralWidget); gridLayout->setVerticalSpacing(0); gridLayout->setHorizontalSpacing(0); gridLayout->setContentsMargins(0, 0, 0, 0); - widget->setLayout(gridLayout); + centralWidget->setLayout(gridLayout); QSpacerItem *plotSpacer = new QSpacerItem(0, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -255,24 +255,26 @@ void PatternGenerator::setupUi() gridLayout->addWidget(m_plot.bottomHandlesArea(), 3, 0, 1, 4); gridLayout->addItem(plotSpacer, 4, 0, 1, 4); + if(prefPanel->getCurrent_docking_enabled()) { - QMainWindow* m_centralMainWindow = new QMainWindow(this); - m_centralMainWindow->setCentralWidget(0); - m_centralMainWindow->setWindowFlags(Qt::Widget); - m_ui->gridLayoutPlot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + // main window for dock widget + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + m_ui->gridLayoutPlot->addWidget(mainWindow, 1, 0, 1, 1); - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::AllDockWidgetAreas); - docker->setWidget(widget); + QDockWidget* dockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget); + + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); + DockerUtils::configureTopBar(dockWidget); #endif + } else { + m_ui->gridLayoutPlot->addWidget(centralWidget, 0, 0, 1, 1); + } - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - // TODO: do we want the buffer previewer in this tool? m_ui->hLayoutBufferPreview->hide(); diff --git a/src/preferences.cpp b/src/preferences.cpp index 875f849d4f..a648d60c92 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -31,7 +31,7 @@ #include #include #include "application_restarter.h" -#include +#include "utils.h" using namespace adiscope; @@ -85,7 +85,8 @@ Preferences::Preferences(QWidget *parent) : false #endif ), - m_target_fps(30) + m_target_fps(30), + m_docking_enabled(false) { ui->setupUi(this); @@ -234,6 +235,12 @@ Preferences::Preferences(QWidget *parent) : Q_EMIT notify(); }); + connect(ui->enableDockableWidgetsCheckBox, &QCheckBox::stateChanged, [=](int state){ + m_docking_enabled = (!state ? false : true); + + requestRestart(); + }); + QString preference_ini_file = getPreferenceIniFile(); QSettings settings(preference_ini_file, QSettings::IniFormat); @@ -427,6 +434,12 @@ void Preferences::showEvent(QShowEvent *event) ui->showPlotFps->setChecked(m_show_plot_fps); ui->useOpenGl->setChecked(m_use_open_gl); ui->cmbPlotTargetFps->setCurrentText(QString::number(m_target_fps)); + + // requires restart after stateChanged, we avoid that here + ui->enableDockableWidgetsCheckBox->blockSignals(true); + ui->enableDockableWidgetsCheckBox->setChecked(m_docking_enabled); + ui->enableDockableWidgetsCheckBox->blockSignals(false); + // by this point the preferences menu is initialized m_initialized = true; ui->autoUpdatesCheckBox->setChecked(automatical_version_checking_enabled); @@ -765,6 +778,21 @@ void Preferences::setLogging_enabled(bool value) m_logging_enabled = value; } +bool Preferences::getDocking_enabled() const +{ + return m_docking_enabled; +} + +void Preferences::setDocking_enabled(bool value) +{ + m_docking_enabled = value; +} + +bool Preferences::getCurrent_docking_enabled() const +{ + return m_current_docking_state; +} + bool Preferences_API::getAnimationsEnabled() const { return preferencePanel->animations_enabled; @@ -976,7 +1004,18 @@ QStringList Preferences_API::getUserStylesheets() const void Preferences_API::setUserStylesheets(const QStringList &userStylesheets) { -// preferencePanel->m_colorEditor->setUserStylesheets(userStylesheets); + // preferencePanel->m_colorEditor->setUserStylesheets(userStylesheets); +} + +bool Preferences_API::getDockingEnabled() const +{ + return preferencePanel->m_docking_enabled; +} + +void Preferences_API::setDockingEnabled(const bool &first) +{ + preferencePanel->m_docking_enabled = first; + preferencePanel->m_current_docking_state = first; } bool Preferences::hasNativeDialogs() const diff --git a/src/preferences.h b/src/preferences.h index e4db56c739..79f145990b 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -150,6 +150,10 @@ class Preferences : public QWidget double getTarget_fps() const; void setTarget_fps(double newTarget_fps); + bool getDocking_enabled() const; + void setDocking_enabled(bool value); + bool getCurrent_docking_enabled() const; + Q_SIGNALS: void notify(); @@ -203,6 +207,8 @@ private Q_SLOTS: bool m_show_plot_fps; bool m_use_open_gl; int m_target_fps; + bool m_docking_enabled; + bool m_current_docking_state; Preferences_API *pref_api; @@ -244,6 +250,7 @@ class Preferences_API : public ApiObject Q_PROPERTY(bool showPlotFps READ getShowPlotFps WRITE setShowPlotFps) Q_PROPERTY(bool useOpenGl READ getUseOpenGl WRITE setUseOpenGl) Q_PROPERTY(double targetFps READ getTargetFps WRITE setTargetFps) + Q_PROPERTY(bool docking_enabled READ getDockingEnabled WRITE setDockingEnabled) public: @@ -342,6 +349,9 @@ class Preferences_API : public ApiObject QStringList getUserStylesheets() const; void setUserStylesheets(const QStringList &userStylesheets); + bool getDockingEnabled() const; + void setDockingEnabled(const bool& first); + private: Preferences *preferencePanel; diff --git a/src/qtgui_util.cc b/src/qtgui_util.cc index 4951b99e2b..a5758822a9 100644 --- a/src/qtgui_util.cc +++ b/src/qtgui_util.cc @@ -170,6 +170,16 @@ bool Util::compareNatural(const std::string& a, const std::string& b) { return (compareNatural(a_new, b_new)); } +QDockWidget *DockerUtils::createDockWidget(QMainWindow *mainWindow, QWidget *widget, const QString &title) +{ + QDockWidget* dockWidget = new QDockWidget(title, mainWindow); + dockWidget->setFeatures(dockWidget->features() & ~QDockWidget::DockWidgetClosable); + dockWidget->setAllowedAreas(Qt::AllDockWidgetAreas); + dockWidget->setWidget(widget); + + return dockWidget; +} + void DockerUtils::configureTopBar(QDockWidget *docker) { connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index ae18c16a44..54c4121db9 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -601,44 +601,47 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, m_plot->enableTimeTrigger(false); - // Add docking plot + // plot widget + QWidget* centralWidget = new QWidget(); + QGridLayout *gridLayout = new QGridLayout(); + gridLayout->setVerticalSpacing(0); + gridLayout->setHorizontalSpacing(0); + gridLayout->setContentsMargins(0, 0, 0, 5); - QWidget* widget = new QWidget(); - QGridLayout *gridplot = new QGridLayout(); + gridLayout->addWidget(m_plot->topArea(), 0, 0); + gridLayout->addWidget(m_plot->topHandlesArea(), 1, 0); + gridLayout->addWidget(m_plot, 2, 0); - gridplot->addWidget(m_plot->topArea(), 0, 0); - gridplot->addWidget(m_plot->topHandlesArea(), 1, 0); - gridplot->addWidget(m_plot, 2, 0); - - gridplot->setVerticalSpacing(0); - gridplot->setHorizontalSpacing(0); - gridplot->setContentsMargins(0, 0, 0, 0); - widget->setLayout(gridplot); + centralWidget->setLayout(gridLayout); ui->plot->removeWidget(ui->instrumentNotes); - QMainWindow* m_centralMainWindow = new QMainWindow(this); - m_centralMainWindow->setCentralWidget(0); - m_centralMainWindow->setWindowFlags(Qt::Widget); - ui->plot->addWidget(m_centralMainWindow, 0, 0); + if(prefPanel->getCurrent_docking_enabled()) { + + // main window for dock widget + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + ui->plot->addWidget(mainWindow, 0, 0); - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::AllDockWidgetAreas); - docker->setWidget(widget); - docker->setContentsMargins(0, 0, 0, 10); + QDockWidget* dockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget); + dockWidget->setContentsMargins(0, 0, 0, 10); + + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); + DockerUtils::configureTopBar(dockWidget); #endif - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); - - ui->plot->addWidget(ui->instrumentNotes, 3, 0); + } else { + ui->plot->addWidget(centralWidget); + } connect(ui->toggleMenuBtn, &QPushButton::toggled, [=](bool toggled){ ui->rightMenu->toggleMenu(toggled); }); + + ui->plot->addWidget(ui->instrumentNotes, 1, 0); } SignalGenerator::~SignalGenerator() diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 7a282ee163..39b132eb9b 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -250,12 +250,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, measure_settings_init(); #endif - // Add dockable plot + // plot widget QWidget* centralWidget = new QWidget(this); QVBoxLayout* vLayout = new QVBoxLayout(centralWidget); vLayout->setContentsMargins(0, 0, 0, 0); - vLayout->setSpacing(6); + vLayout->setSpacing(10); centralWidget->setLayout(vLayout); #ifdef SPECTRAL_MSR @@ -270,23 +270,25 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, ui->widgetPlotContainer->layout()->removeWidget(ui->markerTable); vLayout->addWidget(ui->markerTable); + if(prefPanel->getCurrent_docking_enabled()) { - QMainWindow* m_centralMainWindow = new QMainWindow(this); - m_centralMainWindow->setCentralWidget(0); - m_centralMainWindow->setWindowFlags(Qt::Widget); - ui->gridLayout_plot->addWidget(m_centralMainWindow, 1, 0, 1, 1); + // main window for dock widget + QMainWindow* mainWindow = new QMainWindow(this); + mainWindow->setCentralWidget(0); + mainWindow->setWindowFlags(Qt::Widget); + ui->gridLayout_plot->addWidget(mainWindow, 1, 0, 1, 1); - QDockWidget* docker = new QDockWidget(m_centralMainWindow); - docker->setFeatures(docker->features() & ~QDockWidget::DockWidgetClosable); - docker->setAllowedAreas(Qt::AllDockWidgetAreas); - docker->setWidget(centralWidget); + QDockWidget* dockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget); + + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(docker); + DockerUtils::configureTopBar(dockWidget); #endif - - m_centralMainWindow->addDockWidget(Qt::LeftDockWidgetArea, docker); + } else { + ui->gridLayout_plot->addWidget(centralWidget, 1, 0, 1, 1); + } fft_plot->enableXaxisLabels(); diff --git a/src/utils.h b/src/utils.h index f0a2d83673..b2d431cad6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,6 +26,7 @@ #include #include #include +#include class QwtDblClickPlotPicker: public QwtPlotPicker { @@ -70,6 +71,7 @@ class Util class DockerUtils : public QObject { public: + static QDockWidget* createDockWidget(QMainWindow* mainWindow, QWidget* widget, const QString& title = ""); static void configureTopBar(QDockWidget* docker); }; diff --git a/ui/oscilloscope.ui b/ui/oscilloscope.ui index c60b7c6b64..0297862f7f 100644 --- a/ui/oscilloscope.ui +++ b/ui/oscilloscope.ui @@ -64,9 +64,6 @@ 40 - - 40 - 15 @@ -76,6 +73,22 @@ 9 + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 10 + + + + @@ -150,7 +163,7 @@ Signal View - 10 + 40 20 @@ -163,7 +176,7 @@ Signal View - 95 + 30 0 @@ -246,18 +259,6 @@ Signal View true - - 0 - - - 0 - - - 0 - - - 0 - @@ -318,10 +319,7 @@ Signal View 0 - - - - + 0 @@ -336,25 +334,23 @@ Signal View 0 - - - - 25 - - - 1 - - - 25 - - - 1 - - - 0 - - - + + + 25 + + + 0 + + + 25 + + + 0 + + + 0 + + @@ -369,7 +365,7 @@ Signal View 1 - 0 + 3 @@ -399,6 +395,310 @@ Signal View + + + + + 20 + + + 0 + + + 0 + + + 0 + + + 2 + + + 0 + + + + + 0 + + + + + + + + + + 0 + + + 20 + + + 20 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 58 + + + + + 16777215 + 58 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 399 + 0 + + + + + + + + 0 + + + 0 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 0 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 45 + + + + + 16777215 + 45 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 3 + + + 0 + + + 3 + + + + + + + Qt::Horizontal + + + + 0 + 10 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 2 + 10 + + + + @@ -689,36 +989,36 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } adiscope::CustomPushButton QPushButton -
gui/customPushButton.hpp
+
gui/customPushButton.hpp
adiscope::DetachDragZone QWidget -
gui/detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
gui/menu_anim.hpp
+
gui/menu_anim.hpp
1
adiscope::InstrumentNotes QWidget -
gui/instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
gui/runsinglewidget.h
+
gui/runsinglewidget.h
1
adiscope::LinkedButton QPushButton -
gui/linked_button.hpp
+
gui/linked_button.hpp
diff --git a/ui/preferences.ui b/ui/preferences.ui index 02aebae879..f6308ac150 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -72,9 +72,9 @@ 0 - -60 + 0 1068 - 829 + 849 @@ -225,248 +225,75 @@ 10 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 10 - - - - - - - - - - - - - - Enable graticule - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Enable sample rate filters - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - Enable mini histogram - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - - - - Show ADC digital filter config - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Enable labels on the plot - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - OSCILLOSCOPE - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - - - + + + + + + + + + + + + + Enable user notes in main page + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + + + + + + + + + + Enable automatic update checking + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - + 10 @@ -702,40 +529,15 @@
- - - - - - - - - - - - - Enable all instrument notes - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - + + + + + 0 + 0 + + + 0 @@ -752,26 +554,23 @@ 0 - + - + - + - Scriptable manual calibration - - - 0 + Run external scripts (Experimental) - + Qt::Horizontal @@ -788,197 +587,39 @@ - - + + - - - - 0 - 0 - - + - - true - - - - - - - Save session when closing Scopy - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 6 - - - 0 - - - - - - 0 - 0 - - - - SIGNAL GENERATOR - - - true - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - true - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 5 - - - - - - - - - - Number of displayed periods - - - - - - - - 0 - 0 - - - - -QLineEdit[invalid=true] { -border-color: red; -color: red; -} -QLineEdit[valid=true] { -border-color: grey; -color: white; -} - - - 1 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - + + + Enable all instrument notes + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - + + 6 @@ -989,20 +630,23 @@ color: white; 0 - + + + 6 + 0 - + - + 0 0 - LOGIC ANALYZER + SPECTRUM ANALYZER true @@ -1010,7 +654,13 @@ color: white; - + + + + 0 + 1 + + 16777215 @@ -1028,18 +678,27 @@ color: white; - + - + + + true + - + + + + 0 + 0 + + - Display sampling points when zoomed + Only search marker peaks in visible domain 0 @@ -1047,7 +706,7 @@ color: white; - + Qt::Horizontal @@ -1061,27 +720,143 @@ color: white; + + + + + + + + + + + - + + + Attempt temperature-based calibration (EXPERIMENTAL) + + + + + - Qt::Vertical + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + Scriptable manual calibration + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + + + 10 + + + 10 + + + + + Language (requires app restart) + + + + + + + Qt::Horizontal - QSizePolicy::Expanding + QSizePolicy::Fixed - 20 - 15 + 40 + 20 + + + + + 0 + 0 + + + + - - - + + + 6 @@ -1098,7 +873,7 @@ color: white; 0 - + 6 @@ -1106,7 +881,7 @@ color: white; 0 - + 0 @@ -1114,7 +889,7 @@ color: white; - NETWORK ANALYZER + SIGNAL GENERATOR true @@ -1122,93 +897,31 @@ color: white; - + - 200 - 1 - - - - - 16777215 + 0 1 - - Qt::Horizontal - - - true - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - - - - 16777215 - 16777215 - - - - - - - - - - - - 0 - 0 - - - - Always display 0db value on graph - - - 0 + + + 16777215 + 1 + - - - - Qt::Horizontal - - - 40 - 20 - + + true - + - + Qt::Vertical @@ -1223,51 +936,41 @@ color: white; - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - + - + - + Number of displayed periods - + + + + 0 + 0 + + + + +QLineEdit[invalid=true] { +border-color: red; +color: red; +} +QLineEdit[valid=true] { +border-color: grey; +color: white; +} + - Run external scripts (Experimental) + 1 - + Qt::Horizontal @@ -1281,13 +984,29 @@ color: white; + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 0 + 0 + + + + - - + + - + @@ -1297,14 +1016,14 @@ color: white; - + - Enable digital decoders + Enable animations - + Qt::Horizontal @@ -1318,10 +1037,32 @@ color: white; - - + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + - + + + + 0 + 0 + + @@ -1331,14 +1072,14 @@ color: white; - + - Enable animations + Save session when closing Scopy - + Qt::Horizontal @@ -1352,9 +1093,9 @@ color: white; - - - + + + 0 @@ -1367,41 +1108,234 @@ color: white; 0 - - - - - - + + 0 + + + 10 + + + + + + + + + + + + + + Enable graticule + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - Double click to detach a tool - - - 0 - - + + + + + + + + + Enable sample rate filters + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - + + + + + + + + + + + + + Enable mini histogram + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + - Qt::Horizontal + Qt::Vertical - 40 - 20 + 0 + 0 + + + + + + + + + Show ADC digital filter config + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Enable labels on the plot + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 0 + + + + + + 0 + 0 + + + + OSCILLOSCOPE + + + true + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + true + + + + + - - + + 6 @@ -1412,23 +1346,20 @@ color: white; 0 - - - 6 - + 0 - + - + 0 0 - SPECTRUM ANALYZER + LOGIC ANALYZER true @@ -1436,13 +1367,7 @@ color: white; - - - - 0 - 1 - - + 16777215 @@ -1460,27 +1385,18 @@ color: white; - + - + - - true - - - - - 0 - 0 - - + - Only search marker peaks in visible domain + Display sampling points when zoomed 0 @@ -1488,7 +1404,7 @@ color: white; - + Qt::Horizontal @@ -1502,40 +1418,45 @@ color: white; - - - - - - - Theme + + + Qt::Vertical - - - - + + QSizePolicy::Expanding + + + + 20 + 15 + + + - - + + - + + + true + - + - Attempt temperature-based calibration (EXPERIMENTAL) + Enable digital decoders - + Qt::Horizontal @@ -1549,6 +1470,54 @@ color: white; + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Double click to detach a tool + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -1580,11 +1549,11 @@ color: white; - - - + + + - 0 + 6 0 @@ -1599,109 +1568,213 @@ color: white; 0 - + - 0 + 6 - 10 - - - 10 + 0 - + + + + 0 + 0 + + - Language (requires app restart) + NETWORK ANALYZER + + + true - + + + + 200 + 1 + + + + + 16777215 + 1 + + Qt::Horizontal - - QSizePolicy::Fixed + + true - + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + + - 40 - 20 + 16777215 + 16777215 - + + + + - + - + 0 0 + + Always display 0db value on graph + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + - - - - 0 - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + - + - + Plotting refresh rate - - - Enable automatic update checking - + + + + 60 + + + + + 30 + + + + + 15 + + + + + 10 + + + + + 5 + + - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + - + - + - Enable user notes in main page - - - 0 + Enable dockable widgets - + Qt::Horizontal @@ -1715,6 +1788,20 @@ color: white; + + + + + + Theme + + + + + + + + @@ -1749,62 +1836,6 @@ color: white; - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - - - Plotting refresh rate - - - - - - - - 60 - - - - - 30 - - - - - 15 - - - - - 10 - - - - - 5 - - - - - - From b3aa7de5929d4dedf5fb55a3150d42ce0d3c9c5f Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Mon, 31 Jan 2022 16:18:30 +0200 Subject: [PATCH 113/125] gui: fix plots placement and spacing Signed-off-by: ioanachelaru --- src/network_analyzer.cpp | 4 +- src/oscilloscope.cpp | 4 +- src/patterngenerator/pattern_generator.cpp | 2 +- src/spectrum_analyzer.cpp | 2 +- ui/logic_analyzer.ui | 11 +- ui/measure_panel.ui | 110 +++++------- ui/network_analyzer.ui | 6 + ui/oscilloscope.ui | 193 +++++++++++++-------- ui/pattern_generator.ui | 4 +- ui/spectrum_analyzer.ui | 6 +- 10 files changed, 192 insertions(+), 150 deletions(-) diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index 5dd378a098..c06707e318 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -426,7 +426,9 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, // plot widget QWidget* centralWidget = new QWidget(this); QGridLayout* gridLayout = new QGridLayout(centralWidget); - gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setContentsMargins(9, 0, 0, 9); + gridLayout->setHorizontalSpacing(10); + gridLayout->setVerticalSpacing(0); gridLayout->addWidget(bufferPreviewer, 0, 1, 1, 1); gridLayout->addWidget(ui->statusWidget, 1, 1, 1, 1); diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index c923c5d6e7..ac0c1b6e9c 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -342,7 +342,7 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, gridPlot = new QGridLayout(plotWidget); gridPlot->setVerticalSpacing(0); gridPlot->setHorizontalSpacing(0); - gridPlot->setContentsMargins(0, 0, 0, 0); + gridPlot->setContentsMargins(9, 0, 9, 0); plotWidget->setLayout(gridPlot); QSpacerItem *plotSpacer = new QSpacerItem(0, 5, @@ -382,7 +382,7 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, QMainWindow* mainWindow = new QMainWindow(this); mainWindow->setCentralWidget(0); mainWindow->setWindowFlags(Qt::Widget); - ui->gridLayoutPlot->addWidget(mainWindow, 1, 0, 1, 1); + ui->gridLayoutPlot->addWidget(mainWindow, 0, 0, 1, 1); // time domain plot dock QDockWidget* plotDockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget, "TimeDomain"); diff --git a/src/patterngenerator/pattern_generator.cpp b/src/patterngenerator/pattern_generator.cpp index ce4b1ca682..a7b3031baa 100644 --- a/src/patterngenerator/pattern_generator.cpp +++ b/src/patterngenerator/pattern_generator.cpp @@ -237,7 +237,7 @@ void PatternGenerator::setupUi() QGridLayout* gridLayout = new QGridLayout(centralWidget); gridLayout->setVerticalSpacing(0); gridLayout->setHorizontalSpacing(0); - gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setContentsMargins(25, 0, 25, 0); centralWidget->setLayout(gridLayout); QSpacerItem *plotSpacer = new QSpacerItem(0, 5, diff --git a/src/spectrum_analyzer.cpp b/src/spectrum_analyzer.cpp index 39b132eb9b..b043f84d15 100644 --- a/src/spectrum_analyzer.cpp +++ b/src/spectrum_analyzer.cpp @@ -254,7 +254,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(struct iio_context *ctx, Filter *filt, // plot widget QWidget* centralWidget = new QWidget(this); QVBoxLayout* vLayout = new QVBoxLayout(centralWidget); - vLayout->setContentsMargins(0, 0, 0, 0); + vLayout->setContentsMargins(20, 0, 20, 20); vLayout->setSpacing(10); centralWidget->setLayout(vLayout); diff --git a/ui/logic_analyzer.ui b/ui/logic_analyzer.ui index 0211f3f4f0..91b3df96f3 100644 --- a/ui/logic_analyzer.ui +++ b/ui/logic_analyzer.ui @@ -269,6 +269,15 @@ 0 + + 0 + + + 0 + + + 0 + @@ -279,7 +288,7 @@ 20 - 0 + 5 20 diff --git a/ui/measure_panel.ui b/ui/measure_panel.ui index 9f308152f7..97d5c78a77 100644 --- a/ui/measure_panel.ui +++ b/ui/measure_panel.ui @@ -28,7 +28,10 @@ - + + + 0 + 0 @@ -41,10 +44,48 @@ 0 - - 0 - - + + + + + 0 + 0 + + + + + 16777215 + 10 + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 348 + 16 + + + + + + @@ -190,23 +231,7 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 0 - 20 - - - - - + @@ -228,47 +253,6 @@ - - - - - 0 - 0 - - - - - 16777215 - 10 - - - - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAsNeeded - - - true - - - - - 0 - 0 - 348 - 16 - - - - - diff --git a/ui/network_analyzer.ui b/ui/network_analyzer.ui index 40fe5402d1..cbfd3f6ede 100644 --- a/ui/network_analyzer.ui +++ b/ui/network_analyzer.ui @@ -262,12 +262,18 @@ 0 + + 0 + 0 0 + + 0 + diff --git a/ui/oscilloscope.ui b/ui/oscilloscope.ui index 0297862f7f..14e6b7b2c2 100644 --- a/ui/oscilloscope.ui +++ b/ui/oscilloscope.ui @@ -259,12 +259,33 @@ Signal View true + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + 0 + + 5 + + + 0 + @@ -283,6 +304,9 @@ Signal View 0 + + 0 + @@ -336,13 +360,13 @@ Signal View - 25 + 0 0 - 25 + 0 0 @@ -362,7 +386,7 @@ Signal View 1 - 1 + 0 3 @@ -426,86 +450,103 @@ Signal View + + + - - - 0 - - - 20 - - - 20 + + + + 16777215 + 100 + - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - - 0 + + + 0 + + + 20 + + + 9 + + + 20 + + + + + Qt::Horizontal - - 0 + + QSizePolicy::Fixed - - 0 + + + 20 + 20 + - - 0 + + + + + + + 0 + 0 + - - 0 + + + 20 + 0 + - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - + + + 16777215 + 70 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + diff --git a/ui/pattern_generator.ui b/ui/pattern_generator.ui index 6b93715f59..a99ce4db06 100644 --- a/ui/pattern_generator.ui +++ b/ui/pattern_generator.ui @@ -386,13 +386,13 @@ - 25 + 0 0 - 25 + 0 0 diff --git a/ui/spectrum_analyzer.ui b/ui/spectrum_analyzer.ui index 04dd3bdf2b..8bc6629fbf 100644 --- a/ui/spectrum_analyzer.ui +++ b/ui/spectrum_analyzer.ui @@ -296,16 +296,16 @@ min-width: 6em; - 20 + 0 0 - 20 + 0 - 20 + 0 0 From 02cf513fb2267ed7c46a7bc54c8e08fcbf9d8802 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 15 Feb 2022 14:33:53 +0200 Subject: [PATCH 114/125] android: disable floating dock widgets Signed-off-by: ioanachelaru --- src/qtgui_util.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qtgui_util.cc b/src/qtgui_util.cc index a5758822a9..9b2238057e 100644 --- a/src/qtgui_util.cc +++ b/src/qtgui_util.cc @@ -177,6 +177,11 @@ QDockWidget *DockerUtils::createDockWidget(QMainWindow *mainWindow, QWidget *wid dockWidget->setAllowedAreas(Qt::AllDockWidgetAreas); dockWidget->setWidget(widget); +#ifdef __ANDROID__ + dockWidget->setFeatures(dockWidget->features() & ~QDockWidget::DockWidgetClosable + & ~QDockWidget::DockWidgetFloatable); +#endif + return dockWidget; } From 8f016dab5ff34da7140f198173582319d8496383 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 15 Feb 2022 17:18:10 +0200 Subject: [PATCH 115/125] network_analyzer: added dockWidgets for nyquist and nichols graphs also Signed-off-by: ioanachelaru --- src/network_analyzer.cpp | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/network_analyzer.cpp b/src/network_analyzer.cpp index c06707e318..67a1059cb2 100644 --- a/src/network_analyzer.cpp +++ b/src/network_analyzer.cpp @@ -446,17 +446,43 @@ NetworkAnalyzer::NetworkAnalyzer(struct iio_context *ctx, Filter *filt, centralWidget->setLayout(gridLayout); if(prefPanel->getCurrent_docking_enabled()) { - QMainWindow* mainWindow = new QMainWindow(this); - mainWindow->setCentralWidget(0); - mainWindow->setWindowFlags(Qt::Widget); - ui->gridLayout_plots->addWidget(mainWindow, 0, 0); + // bode graph + QMainWindow* bodeWindow = new QMainWindow(this); + bodeWindow->setCentralWidget(0); + bodeWindow->setWindowFlags(Qt::Widget); + ui->gridLayout_plots->addWidget(bodeWindow, 0, 0); - QDockWidget* dockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget); + QDockWidget* bodeDockWidget = DockerUtils::createDockWidget(bodeWindow, centralWidget); + bodeWindow->addDockWidget(Qt::LeftDockWidgetArea, bodeDockWidget); - mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); + + // nyquist graph + QMainWindow* nyquistWindow = new QMainWindow(this); + nyquistWindow->setCentralWidget(0); + nyquistWindow->setWindowFlags(Qt::Widget); + + ui->stackedWidgetPage2->layout()->removeWidget(ui->xygraph); + ui->stackedWidgetPage2->layout()->addWidget(nyquistWindow); + + QDockWidget* nyquistDockWidget = DockerUtils::createDockWidget(nyquistWindow, ui->xygraph); + nyquistWindow->addDockWidget(Qt::LeftDockWidgetArea, nyquistDockWidget); + + + // nichols graph + QMainWindow* nicholsWindow = new QMainWindow(this); + nicholsWindow->setCentralWidget(0); + nicholsWindow->setWindowFlags(Qt::Widget); + + ui->stackedWidgetPage3->layout()->removeWidget(ui->nicholsgraph); + ui->stackedWidgetPage3->layout()->addWidget(nicholsWindow); + + QDockWidget* nicholsDockWidget = DockerUtils::createDockWidget(nicholsWindow, ui->nicholsgraph); + nicholsWindow->addDockWidget(Qt::LeftDockWidgetArea, nicholsDockWidget); #ifdef PLOT_MENU_BAR_ENABLED - DockerUtils::configureTopBar(dockWidget); + DockerUtils::configureTopBar(bodeDockWidget); + DockerUtils::configureTopBar(nyquistDockWidget); + DockerUtils::configureTopBar(nicholsDockWidget); #endif } else { gridLayout->setHorizontalSpacing(0); From 7586327e51953bb2e4d24d23641d8c16f7466361 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Tue, 15 Feb 2022 17:18:26 +0200 Subject: [PATCH 116/125] gui: re-arranged dockwidgets Signed-off-by: ioanachelaru --- src/gui/tool_view.cpp | 4 ++++ src/oscilloscope.cpp | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp index cbe13dfd49..ea0b32d7c9 100644 --- a/src/gui/tool_view.cpp +++ b/src/gui/tool_view.cpp @@ -185,6 +185,10 @@ QDockWidget *ToolView::createDockableWidget(QWidget *widget, const QString& dock docker->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas); docker->setWidget(widget); + // workaround, allows dockWidgets movement + widget->setMinimumHeight(50); + widget->setMinimumWidth(50); + #ifdef PLOT_MENU_BAR_ENABLED DockerUtils::configureTopBar(docker); #endif diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index ac0c1b6e9c..d02f453b2a 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -388,6 +388,17 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, QDockWidget* plotDockWidget = DockerUtils::createDockWidget(mainWindow, centralWidget, "TimeDomain"); + // necessary to move around the dockWidgets + centralWidget->setMinimumHeight(50); + centralWidget->setMinimumWidth(50); + + fft_plot.setMinimumHeight(50); + fft_plot.setMinimumWidth(50); + + xy_plot.setMinimumHeight(50); + xy_plot.setMinimumWidth(50); + + // histogram widget histWidget = new QWidget(this); QVBoxLayout* histLayout = new QVBoxLayout(histWidget); @@ -420,6 +431,7 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, // arange all dockers +#ifdef __ANDROID__ mainWindow->addDockWidget(Qt::LeftDockWidgetArea, fftDockWidget); mainWindow->addDockWidget(Qt::LeftDockWidgetArea, histDockWidget); mainWindow->addDockWidget(Qt::LeftDockWidgetArea, plotDockWidget); @@ -428,6 +440,15 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, mainWindow->tabifyDockWidget(plotDockWidget, histDockWidget); mainWindow->tabifyDockWidget(plotDockWidget, fftDockWidget); mainWindow->tabifyDockWidget(plotDockWidget, xyDockWidget); +#else + mainWindow->addDockWidget(Qt::RightDockWidgetArea, fftDockWidget); + mainWindow->addDockWidget(Qt::RightDockWidgetArea, xyDockWidget); + mainWindow->tabifyDockWidget(fftDockWidget, xyDockWidget); + + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, histDockWidget); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, plotDockWidget); + mainWindow->tabifyDockWidget(plotDockWidget, histDockWidget); +#endif #ifdef PLOT_MENU_BAR_ENABLED DockerUtils::configureTopBar(plotDockWidget); @@ -444,8 +465,18 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, QWidget *w = gridL->itemAtPosition(1, 0)->widget(); gridL->addWidget(&xy_plot, 1, 0); gridL->addWidget(w, 2, 0); + + fft_plot.setMinimumHeight(250); + fft_plot.setMinimumWidth(500); } + hist_plot.setMinimumHeight(60); + hist_plot.setMinimumWidth(25); + hist_plot.setMaximumWidth(25); + + // xy_plot.setMinimumHeight(50); + // xy_plot.setMinimumWidth(50); + /* Default plot settings */ plot.setSampleRate(active_sample_rate, 1, ""); plot.setActiveVertAxis(0); @@ -482,16 +513,6 @@ Oscilloscope::Oscilloscope(struct iio_context *ctx, Filter *filt, plot.setAxisVisible(QwtAxis::XBottom, false); plot.setUsingLeftAxisScales(false); - fft_plot.setMinimumHeight(250); - fft_plot.setMinimumWidth(500); - - hist_plot.setMinimumHeight(60); - hist_plot.setMinimumWidth(25); - hist_plot.setMaximumWidth(25); - -// xy_plot.setMinimumHeight(50); -// xy_plot.setMinimumWidth(50); - xy_plot.setVertUnitsPerDiv(5); xy_plot.setHorizUnitsPerDiv(5); From 65d457f5d7e729d9723e3af46875180d85445c1c Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 16 Feb 2022 15:49:35 +0200 Subject: [PATCH 117/125] logic_analyzer: fix general settings menu size Signed-off-by: ioanachelaru --- src/logicanalyzer/logic_analyzer.cpp | 2 +- ui/logic_analyzer.ui | 67 +++++++++++++++++----------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 2f22f19973..2012506afe 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -155,7 +155,7 @@ LogicAnalyzer::LogicAnalyzer(struct iio_context *ctx, adiscope::Filter *filt, channelBox->setChecked(true); - triggerBox->setStyleSheet("QComboBox QAbstractItemView { min-width: 130px; }"); + triggerBox->setStyleSheet("QComboBox { max-width: 60px; } QComboBox QAbstractItemView { min-width: 130px; }"); triggerBox->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); for (int i = 1; i < ui->triggerComboBox->count(); ++i) { diff --git a/ui/logic_analyzer.ui b/ui/logic_analyzer.ui index 91b3df96f3..8922fd8606 100644 --- a/ui/logic_analyzer.ui +++ b/ui/logic_analyzer.ui @@ -476,7 +476,7 @@ - 2 + 3 @@ -526,8 +526,8 @@ 0 0 - 200 - 329 + 400 + 474 @@ -1330,7 +1330,7 @@ color: rgba(255,255,255,51); - + 0 0 @@ -1362,6 +1362,12 @@ color: rgba(255,255,255,51); + + + 0 + 0 + + QFrame::NoFrame @@ -1377,11 +1383,11 @@ color: rgba(255,255,255,51); 0 0 420 - 304 + 474 - + 0 0 @@ -1449,9 +1455,15 @@ color: rgba(255,255,255,51); 0 + + + 300 + 30 + + - 400 + 300 30 @@ -1462,8 +1474,8 @@ color: rgba(255,255,255,51); QPushButton { min-height: 30px; max-height: 30px; -min-width: 400px; -max-width: 400px; +min-width: 300px; +max-width: 300px; background-color: black; border-radius: 4px; } @@ -1475,8 +1487,8 @@ background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.501, y2:0, stop QWidget#handle { min-height: 30px; max-height: 30px; -min-width: 200px; -max-width: 200px; +min-width: 150px; +max-width: 150px; background-color: #4a64ff; border-radius: 2px; } @@ -1495,8 +1507,8 @@ QLabel#on { margin-top: 0px; min-height:30px; max-height:30px; -min-width:200px; -max-width:200px; +min-width:150px; +max-width:150px; qproperty-alignment: AlignCenter AlignCenter; margin-left: 0px; color: white; @@ -1510,9 +1522,9 @@ QLabel#off { margin-top: 0px; min-height:30px; max-height:30px; -min-width:200px; -max-width:200px; -margin-left: 200px; +min-width:150px; +max-width:150px; +margin-left: 150px; color: white; qproperty-alignment: AlignCenter AlignCenter; } @@ -1552,7 +1564,7 @@ color: rgba(255,255,255,51); - 400 + 200 20 @@ -1967,46 +1979,49 @@ QPushButton:checked { border-image: url(:/icons/setup_btn_checked.svg); } adiscope::CustomPushButton QPushButton -
gui/customPushButton.hpp
+
gui/customPushButton.hpp
adiscope::DetachDragZone QWidget -
gui/detachdragzone.h
+
gui/detachdragzone.h
1
adiscope::MenuAnim QWidget -
gui/menu_anim.hpp
+
gui/menu_anim.hpp
1
adiscope::InstrumentNotes QWidget -
gui/instrumentnotes.h
+
gui/instrumentnotes.h
1
adiscope::RunSingleWidget QWidget -
gui/runsinglewidget.h
+
gui/runsinglewidget.h
1
- adiscope::CustomSwitch + adiscope::LinkedButton QPushButton -
gui/customSwitch.hpp
+
gui/linked_button.hpp
- adiscope::LinkedButton + adiscope::CustomSwitch QPushButton -
gui/linked_button.hpp
+
gui/customSwitch.hpp
+ + + From 1c6c16828253ffb57d82ab0daba7f96d98b93ac4 Mon Sep 17 00:00:00 2001 From: ioanachelaru Date: Wed, 16 Feb 2022 14:44:33 +0200 Subject: [PATCH 118/125] gui: added style for dockWidgets Signed-off-by: ioanachelaru --- resources/stylesheets/templates/default.qss.c | 16 +++++++++++++++- resources/stylesheets/templates/light.qss.c | 16 +++++++++++++++- src/qtgui_util.cc | 13 +++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/resources/stylesheets/templates/default.qss.c b/resources/stylesheets/templates/default.qss.c index 215d430853..604bd51b42 100644 --- a/resources/stylesheets/templates/default.qss.c +++ b/resources/stylesheets/templates/default.qss.c @@ -15,13 +15,27 @@ QWidget { } QDockWidget { - titlebar-normal-icon: url(); + titlebar-normal-icon: url(:/icons/sba_cmb_box_arrow_right.svg); } QDockWidget::title { background-color: transparent; } +QDockWidget::float-button { + background-color: transparent; + border: none; +} + +QDockWidget::float-button:hover { + background-color: rgba(255, 255, 255, 30); + padding: 2px; +} + +QDockWidget::float-button:hover { + background-color: rgba(0, 0, 0, 60); +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/resources/stylesheets/templates/light.qss.c b/resources/stylesheets/templates/light.qss.c index d1f436dc53..cf28ac36dd 100644 --- a/resources/stylesheets/templates/light.qss.c +++ b/resources/stylesheets/templates/light.qss.c @@ -15,7 +15,7 @@ QWidget { } QDockWidget { - titlebar-normal-icon: url(); + titlebar-normal-icon: url(:/icons/scopy-light/icons/sba_cmb_box_arrow_right.svg); background-color: #F7F7F7; } @@ -23,6 +23,20 @@ QDockWidget::title { background-color: transparent; } +QDockWidget::float-button { + background-color: transparent; + border: none; +} + +QDockWidget::float-button:hover { + background-color: rgba(0, 0, 0, 30); + padding: 2px; +} + +QDockWidget::float-button:hover { + background-color: rgba(0, 0, 0, 60); +} + QToolTip { padding: 6px; border: 1px solid rgba(149, 152, 154, 150); diff --git a/src/qtgui_util.cc b/src/qtgui_util.cc index 9b2238057e..0c4658baab 100644 --- a/src/qtgui_util.cc +++ b/src/qtgui_util.cc @@ -188,19 +188,28 @@ QDockWidget *DockerUtils::createDockWidget(QMainWindow *mainWindow, QWidget *wid void DockerUtils::configureTopBar(QDockWidget *docker) { connect(docker, &QDockWidget::topLevelChanged, [=](bool topLevel){ + QString icon_path = ""; + + if (QIcon::themeName() == "scopy-default") { + icon_path +=":/icons"; + } else { + icon_path +=":/icons/scopy-light/icons"; + } + if(topLevel) { docker->setWindowFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); docker->show(); + docker->setStyleSheet("QDockWidget {" - "titlebar-normal-icon: url(:/icons/close_hovered.svg);" + "titlebar-normal-icon: url(" + icon_path + "/sba_cmb_box_arrow.svg);" "}"); docker->setContentsMargins(10, 0, 10, 10); } else { docker->setStyleSheet("QDockWidget {" - "titlebar-normal-icon: url();" + "titlebar-normal-icon: url(" + icon_path + "/sba_cmb_box_arrow_right.svg);" "}"); docker->setContentsMargins(0, 0, 0, 0); } From 1b253f1454a97ce2b17501b088557da8525469fb Mon Sep 17 00:00:00 2001 From: AlexandraTrifan Date: Tue, 8 Mar 2022 15:55:19 +0200 Subject: [PATCH 119/125] FftDisplayPlot.cc: Use update instead of repaint (which tries to force an immediate repaint and causes hangs on MacOS - in software rendering mode). Signed-off-by: Alexandra Trifan --- src/FftDisplayPlot.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FftDisplayPlot.cc b/src/FftDisplayPlot.cc index 0a72364a92..9c088c5cd3 100644 --- a/src/FftDisplayPlot.cc +++ b/src/FftDisplayPlot.cc @@ -223,8 +223,8 @@ void FftDisplayPlot::replot() return; } - d_leftHandlesArea->repaint(); - d_bottomHandlesArea->repaint(); + d_leftHandlesArea->update(); + d_bottomHandlesArea->update(); BasicPlot::replot(); } @@ -268,7 +268,7 @@ void FftDisplayPlot::setupReadouts() void FftDisplayPlot::updateHandleAreaPadding() { - d_leftHandlesArea->repaint(); + d_leftHandlesArea->update(); d_bottomHandlesArea->setLeftPadding(d_leftHandlesArea->width()); d_bottomHandlesArea->setRightPadding(50); From 29684f175a687cd7d4e82835a1bfb3c47d7a7964 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 3 Mar 2022 19:45:38 +0200 Subject: [PATCH 120/125] SignalGenerator: implemented external load setting https://github.com/analogdevicesinc/scopy/issues/1154 Signed-off-by: Adrian Suciu --- src/gui/externalloadlineedit.cpp | 39 +++++++++++ src/gui/externalloadlineedit.h | 32 +++++++++ src/signal_generator.cpp | 31 ++++++++- src/signal_generator.hpp | 6 ++ src/signal_generator_api.cpp | 34 ++++++++++ src/signal_generator_api.hpp | 4 ++ ui/signal_generator.ui | 113 +++++++++++++++++++++---------- 7 files changed, 220 insertions(+), 39 deletions(-) create mode 100644 src/gui/externalloadlineedit.cpp create mode 100644 src/gui/externalloadlineedit.h diff --git a/src/gui/externalloadlineedit.cpp b/src/gui/externalloadlineedit.cpp new file mode 100644 index 0000000000..48e3a44b2c --- /dev/null +++ b/src/gui/externalloadlineedit.cpp @@ -0,0 +1,39 @@ +#include + +constexpr double ExternalLoadLineEdit::MAX_EXTERNAL_LOAD; +constexpr double ExternalLoadLineEdit::MIN_EXTERNAL_LOAD; +constexpr double ExternalLoadLineEdit::OUTPUT_AWG_RESISTANCE; + +ExternalLoadLineEdit::ExternalLoadLineEdit(QWidget *parent) : QLineEdit(parent) { + value = MAX_EXTERNAL_LOAD; + connect(this,SIGNAL(editingFinished()),this,SLOT(setValue())); +} + +ExternalLoadLineEdit::~ExternalLoadLineEdit() { + +}; + +double ExternalLoadLineEdit::getValue() { + return value; +} + +void ExternalLoadLineEdit::setValue() { + setValue(text().toDouble()); +} + +void ExternalLoadLineEdit::setValue(QString val) { + setValue(val.toDouble()); +} +void ExternalLoadLineEdit::setValue(double val) { + QString textVal; + val = std::max(val, MIN_EXTERNAL_LOAD); + if(val >= MAX_EXTERNAL_LOAD) { + textVal = "inf"; + val = MAX_EXTERNAL_LOAD; + } else { + textVal = QString::number(val); + } + setText(textVal); + value = val; + Q_EMIT valueChanged(value); +} diff --git a/src/gui/externalloadlineedit.h b/src/gui/externalloadlineedit.h new file mode 100644 index 0000000000..021aae3ade --- /dev/null +++ b/src/gui/externalloadlineedit.h @@ -0,0 +1,32 @@ +#ifndef EXTERNALLOADLINEEDIT_H +#define EXTERNALLOADLINEEDIT_H + +#include + +class ExternalLoadLineEdit : public QLineEdit +{ + Q_OBJECT +public: + static constexpr double MAX_EXTERNAL_LOAD = 100000.0; + static constexpr double MIN_EXTERNAL_LOAD = 50.0; + static constexpr double OUTPUT_AWG_RESISTANCE = 50.0; + + ExternalLoadLineEdit(QWidget *parent = nullptr); + ~ExternalLoadLineEdit(); + + Q_PROPERTY(double value READ getValue WRITE setValue); + + double getValue(); +public Q_SLOTS: + void setValue(); + void setValue(double val); + void setValue(QString val); + +Q_SIGNALS: + void valueChanged(double); +protected: + double value; + +}; + +#endif // EXTERNALLOADLINEEDIT diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 54c4121db9..5251f01d84 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -63,6 +63,7 @@ #include #include #include "scopyExceptionHandler.h" +#include "gnuradio/blocks/multiply_const.h" #ifdef MATLAB_SUPPORT_SIGGEN #include @@ -84,6 +85,7 @@ #define MULTIPLY_CT 4 #define FREQUENCY_CT 40 + using namespace adiscope; using namespace gr; using namespace libm2k::context; @@ -291,6 +293,8 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, {"%",1e0} }, tr("Duty Cycle"), -5, 100, true, false, this); + load = ui->externalLoad; + ui->waveformGrid->addWidget(amplitude, 0, 0, 1, 1); ui->waveformGrid->addWidget(offset, 0, 1, 1, 1); ui->waveformGrid->addWidget(frequency, 1, 0, 1, 1); @@ -413,6 +417,7 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, ptr->file_nr_of_channels=0; ptr->file_channel=0; ptr->lineThickness = 1.0; + ptr->load = ExternalLoadLineEdit::MAX_EXTERNAL_LOAD; ptr->type = SIGNAL_TYPE_CONSTANT; ptr->id = i; @@ -567,6 +572,9 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, connect(ui->cbLineThickness, SIGNAL(currentIndexChanged(int)), this, SLOT(lineThicknessChanged(int))); + connect(load, SIGNAL(valueChanged(double)), + this, SLOT(externalLoadChanged(double))); + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); @@ -644,6 +652,7 @@ SignalGenerator::SignalGenerator(struct iio_context *_ctx, Filter *filt, ui->plot->addWidget(ui->instrumentNotes, 1, 0); } + SignalGenerator::~SignalGenerator() { disconnect(prefPanel, &Preferences::notify, this, &SignalGenerator::readPreferences); @@ -923,6 +932,7 @@ void SignalGenerator::mathSampleRateChanged(double value) void SignalGenerator::noiseTypeChanged(int index) { + auto ptr = getCurrentData(); gr::analog::noise_type_t value = qvariant_cast(ui->cbNoiseType->itemData(index)); @@ -943,6 +953,17 @@ void SignalGenerator::noiseAmplitudeChanged(double value) } } +void SignalGenerator::externalLoadChanged(double value) { + + auto ptr = getCurrentData(); + + if (ptr->load != value) { + ptr->load = value; + resetZoom(); + } + +} + void SignalGenerator::lineThicknessChanged(int index) { auto ptr = getCurrentData(); @@ -1493,9 +1514,15 @@ void SignalGenerator::start() auto source = getSource(w, best_rate, top_block); auto head = blocks::head::make(sizeof(float), samples_count); auto vector = blocks::vector_sink_f::make(); + + auto load = getData(w)->load; + auto scaling_factor = ((load + ExternalLoadLineEdit::OUTPUT_AWG_RESISTANCE) / load); + auto load_scaling = blocks::multiply_const_ff::make(scaling_factor); + auto clamp = analog::rail_ff::make(-AMPLITUDE_VOLTS, AMPLITUDE_VOLTS); - top_block->connect(source, 0, clamp, 0); + top_block->connect(source, 0, load_scaling, 0); + top_block->connect(load_scaling, 0,clamp,0); top_block->connect(clamp,0, head,0); top_block->connect(head, 0, vector, 0); top_block->run(); @@ -2066,6 +2093,7 @@ void SignalGenerator::updateRightMenuForChn(int chIdx) int lineThicknessIndex = (int)(ptr->lineThickness / 0.5) - 1; ui->cbLineThickness->setCurrentIndex(lineThicknessIndex); + load->setValue(ptr->load); fallTime->setValue(ptr->fall); riseTime->setValue(ptr->rise); @@ -2466,4 +2494,3 @@ size_t SignalGenerator::get_samples_count(unsigned int chnIdx, return size; } - diff --git a/src/signal_generator.hpp b/src/signal_generator.hpp index 16e28a2857..368ad771b7 100644 --- a/src/signal_generator.hpp +++ b/src/signal_generator.hpp @@ -46,6 +46,9 @@ #include #include +#include + + extern "C" { struct iio_context; } @@ -162,6 +165,7 @@ class SignalGenerator : public Tool ScaleSpinButton *holdHighTime, *holdLowTime; ScaleSpinButton *fileSampleRate, *fileAmplitude; ScaleSpinButton *mathRecordLength, *noiseAmplitude, *mathSampleRate; + ExternalLoadLineEdit *load; FileManager *fileManager; @@ -258,6 +262,7 @@ private Q_SLOTS: void noiseAmplitudeChanged(double val); void noiseTypeChanged(int val); void lineThicknessChanged(int index); + void externalLoadChanged(double val); void trapezoidalComputeFrequency(); void riseChanged(double value); void fallChanged(double value); @@ -343,6 +348,7 @@ struct signal_generator_data { gr::analog::noise_type_t noiseType; float noiseAmplitude; float lineThickness; + double load; }; struct time_block_data { diff --git a/src/signal_generator_api.cpp b/src/signal_generator_api.cpp index ae00906597..fc7a38dda0 100644 --- a/src/signal_generator_api.cpp +++ b/src/signal_generator_api.cpp @@ -841,6 +841,40 @@ void SignalGenerator_API::setLineThickness(const QList& list) gen->ui->cbLineThickness->setCurrentIndex(index); } + + +QList SignalGenerator_API::getLoad() const +{ + QList list; + + for (int i = 0; i < gen->channels.size(); i++) { + auto ptr = gen->getData(gen->channels[i]); + + list.append(ptr->load); + } + + return list; +} + +void SignalGenerator_API::setLoad(const QList& list) +{ + if (list.size() != gen->channels.size()) { + return; + } + + for (int i = 0; i < gen->channels.size(); i++) { + auto ptr = gen->getData(gen->channels[i]); + + ptr->load = list.at(i); + if(i == gen->currentChannel){ + gen->resetZoom(); + gen->load->setValue(gen->getCurrentData()->load); + } + } + +} + + bool SignalGenerator_API::getAutoscale() const { return gen->ui->btnSigGenAutoscale->isChecked(); diff --git a/src/signal_generator_api.hpp b/src/signal_generator_api.hpp index 70944548cc..7b93ab843f 100644 --- a/src/signal_generator_api.hpp +++ b/src/signal_generator_api.hpp @@ -94,6 +94,7 @@ class SignalGenerator_API : public ApiObject READ getLineThickness WRITE setLineThickness) Q_PROPERTY(bool autoscale READ getAutoscale WRITE setAutoscale); + Q_PROPERTY(QList load READ getLoad WRITE setLoad); public: @@ -173,6 +174,9 @@ class SignalGenerator_API : public ApiObject QList getLineThickness() const; void setLineThickness(const QList& list); + QList getLoad() const; + void setLoad(const QList& list); + bool getAutoscale() const; void setAutoscale(bool checked); diff --git a/ui/signal_generator.ui b/ui/signal_generator.ui index 01f343edb5..d47683b58c 100644 --- a/ui/signal_generator.ui +++ b/ui/signal_generator.ui @@ -231,7 +231,7 @@ 0 0 314 - 604 + 592 @@ -274,7 +274,7 @@ - 0 + 3 false @@ -996,19 +996,19 @@ background-repeat: no-repeat; - 6 + 10 - 9 + 0 - 9 + 0 - 9 + 0 - 9 + 0 @@ -1159,6 +1159,21 @@ background-repeat: no-repeat; + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -1236,7 +1251,7 @@ background-repeat: no-repeat; - 0 + 10 0 @@ -1247,33 +1262,59 @@ background-repeat: no-repeat; 0 + + 0 + - + 0 - + 0 - - - 0 + + + Load (Ohm) - - 0 + + + + + + Qt::Horizontal - - 0 + + + 90 + 20 + - - - - Line thickness - - - - + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Line thickness + + @@ -1341,20 +1382,13 @@ background-repeat: no-repeat; - - - Qt::Horizontal + + + 0 - - - 40 - 20 - + + 0 - - - - @@ -1473,6 +1507,11 @@ background-repeat: no-repeat;
math.hpp
1 + + ExternalLoadLineEdit + QLineEdit +
gui/externalloadlineedit.h
+
From dd23354ee232e3202dcc29ec5d99415703442f6e Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 10 Mar 2022 15:13:34 +0200 Subject: [PATCH 121/125] oscilloscope: prevent channel handles from moving when resizing the canvas Signed-off-by: Adrian Suciu --- src/oscilloscope_plot.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index c486d1b8d8..6cb1b4ae54 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -1044,10 +1044,6 @@ bool CapturePlot::eventFilter(QObject *object, QEvent *event) /* update the size of the gates when the plot canvas is resized */ updateGateMargins(); - for (int i = 0; i < d_offsetHandles.size(); ++i) { - d_offsetHandles[i]->triggerMove(); - } - Q_EMIT canvasSizeChanged(); } From d426bf899471517591c5c8ae15f8bd446f750d45 Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 10 Mar 2022 16:38:43 +0200 Subject: [PATCH 122/125] general: change scopy namespace to adiscope This prevents clash between gr:scopy and scopy namespaces Signed-off-by: Adrian Suciu --- src/gui/channel_manager.cpp | 2 +- src/gui/channel_manager.hpp | 4 +--- src/gui/custom_menu_button.cpp | 2 +- src/gui/custom_menu_button.hpp | 2 +- src/gui/generic_menu.cpp | 2 +- src/gui/generic_menu.hpp | 2 +- src/gui/menu_header.cpp | 2 +- src/gui/menu_header.hpp | 2 +- src/gui/settings_pair_widget.cpp | 2 +- src/gui/settings_pair_widget.hpp | 2 +- src/gui/subsection_separator.cpp | 2 +- src/gui/subsection_separator.hpp | 2 +- src/gui/theme_manager.cpp | 2 +- src/gui/theme_manager.hpp | 2 +- src/gui/tool_view.cpp | 2 +- src/gui/tool_view.hpp | 2 +- src/gui/tool_view_builder.cpp | 2 +- src/gui/tool_view_builder.hpp | 2 +- src/oscilloscope.cpp | 4 ++-- src/signal_generator.cpp | 8 ++++---- ui/tool_view.ui | 4 ++-- 21 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/gui/channel_manager.cpp b/src/gui/channel_manager.cpp index ad703ef7b2..2818787a6a 100644 --- a/src/gui/channel_manager.cpp +++ b/src/gui/channel_manager.cpp @@ -4,8 +4,8 @@ #include "channel_manager.hpp" -using namespace scopy::gui; using namespace adiscope; +using namespace adiscope::gui; ChannelManager::ChannelManager(ChannelsPositionEnum position, QWidget* parent) : QWidget(parent) diff --git a/src/gui/channel_manager.hpp b/src/gui/channel_manager.hpp index f5bc544e35..974fbe1246 100644 --- a/src/gui/channel_manager.hpp +++ b/src/gui/channel_manager.hpp @@ -8,9 +8,7 @@ #include "channel_widget.hpp" #include "customPushButton.hpp" -using namespace adiscope; - -namespace scopy { +namespace adiscope { namespace gui { enum class ChannelsPositionEnum diff --git a/src/gui/custom_menu_button.cpp b/src/gui/custom_menu_button.cpp index c80a421986..1d4885b165 100644 --- a/src/gui/custom_menu_button.cpp +++ b/src/gui/custom_menu_button.cpp @@ -2,7 +2,7 @@ #include "custom_menu_button.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; CustomMenuButton::CustomMenuButton(QString labelText, bool checkboxVisible, bool checkBoxChecked, QWidget* parent) : CustomMenuButton(parent) diff --git a/src/gui/custom_menu_button.hpp b/src/gui/custom_menu_button.hpp index c21d6289e8..80636f3696 100644 --- a/src/gui/custom_menu_button.hpp +++ b/src/gui/custom_menu_button.hpp @@ -13,7 +13,7 @@ namespace Ui { class CustomMenuButton; } -namespace scopy { +namespace adiscope { namespace gui { class CustomMenuButton : public QWidget diff --git a/src/gui/generic_menu.cpp b/src/gui/generic_menu.cpp index 96612ae469..f5568874bf 100644 --- a/src/gui/generic_menu.cpp +++ b/src/gui/generic_menu.cpp @@ -2,7 +2,7 @@ #include "generic_menu.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; GenericMenu::GenericMenu(QWidget* parent) : QWidget(parent) diff --git a/src/gui/generic_menu.hpp b/src/gui/generic_menu.hpp index 7fdb391383..fd8fefb957 100644 --- a/src/gui/generic_menu.hpp +++ b/src/gui/generic_menu.hpp @@ -9,7 +9,7 @@ using namespace adiscope; -namespace scopy { +namespace adiscope { namespace gui { class GenericMenu : public QWidget diff --git a/src/gui/menu_header.cpp b/src/gui/menu_header.cpp index affd780e2f..20d3e61464 100644 --- a/src/gui/menu_header.cpp +++ b/src/gui/menu_header.cpp @@ -5,7 +5,7 @@ #include "menu_header.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; MenuHeader::MenuHeader(QWidget* parent) : QWidget(parent) diff --git a/src/gui/menu_header.hpp b/src/gui/menu_header.hpp index 32a032fc43..ffe3fee2e0 100644 --- a/src/gui/menu_header.hpp +++ b/src/gui/menu_header.hpp @@ -8,7 +8,7 @@ namespace Ui { class MenuHeader; } -namespace scopy { +namespace adiscope { namespace gui { class MenuHeader : public QWidget diff --git a/src/gui/settings_pair_widget.cpp b/src/gui/settings_pair_widget.cpp index 14c96a368d..e39e86e31f 100644 --- a/src/gui/settings_pair_widget.cpp +++ b/src/gui/settings_pair_widget.cpp @@ -2,7 +2,7 @@ #include "settings_pair_widget.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; SettingsPairWidget::SettingsPairWidget(QWidget* parent) : QWidget(parent) diff --git a/src/gui/settings_pair_widget.hpp b/src/gui/settings_pair_widget.hpp index 4372830c8b..de3dc90cf2 100644 --- a/src/gui/settings_pair_widget.hpp +++ b/src/gui/settings_pair_widget.hpp @@ -12,7 +12,7 @@ namespace Ui { class SettingsPairWidget; } -namespace scopy { +namespace adiscope { namespace gui { class SettingsPairWidget : public QWidget { diff --git a/src/gui/subsection_separator.cpp b/src/gui/subsection_separator.cpp index bedc4e15d7..dcf3fff9c2 100644 --- a/src/gui/subsection_separator.cpp +++ b/src/gui/subsection_separator.cpp @@ -4,7 +4,7 @@ #include "subsection_separator.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; SubsectionSeparator::SubsectionSeparator(QWidget* parent) : QWidget(parent) diff --git a/src/gui/subsection_separator.hpp b/src/gui/subsection_separator.hpp index 24b35ce651..dd6abe83da 100644 --- a/src/gui/subsection_separator.hpp +++ b/src/gui/subsection_separator.hpp @@ -9,7 +9,7 @@ namespace Ui { class SubsectionSeparator; } -namespace scopy { +namespace adiscope { namespace gui { class SubsectionSeparator : public QWidget { diff --git a/src/gui/theme_manager.cpp b/src/gui/theme_manager.cpp index fedc183873..727e4876ce 100644 --- a/src/gui/theme_manager.cpp +++ b/src/gui/theme_manager.cpp @@ -4,7 +4,7 @@ #include "theme_manager.hpp" #include "utils.h" -using namespace scopy::gui; +using namespace adiscope::gui; ThemeManager::ThemeManager() : m_app(nullptr) diff --git a/src/gui/theme_manager.hpp b/src/gui/theme_manager.hpp index c63584c894..a54ce96628 100644 --- a/src/gui/theme_manager.hpp +++ b/src/gui/theme_manager.hpp @@ -3,7 +3,7 @@ #include -namespace scopy { +namespace adiscope { namespace gui { class ThemeManager diff --git a/src/gui/tool_view.cpp b/src/gui/tool_view.cpp index ea0b32d7c9..6fd5fd2fdd 100644 --- a/src/gui/tool_view.cpp +++ b/src/gui/tool_view.cpp @@ -7,7 +7,7 @@ #include "utils.h" #include -using namespace scopy::gui; +using namespace adiscope::gui; ToolView::ToolView(QWidget* parent) : QWidget(parent) diff --git a/src/gui/tool_view.hpp b/src/gui/tool_view.hpp index 3692b3145a..8b722ee275 100644 --- a/src/gui/tool_view.hpp +++ b/src/gui/tool_view.hpp @@ -19,7 +19,7 @@ namespace Ui { class ToolView; } -namespace scopy { +namespace adiscope { namespace gui { class ToolView : public QWidget diff --git a/src/gui/tool_view_builder.cpp b/src/gui/tool_view_builder.cpp index 0367db659c..7c820dcfd9 100644 --- a/src/gui/tool_view_builder.cpp +++ b/src/gui/tool_view_builder.cpp @@ -2,7 +2,7 @@ #include "tool_view_builder.hpp" -using namespace scopy::gui; +using namespace adiscope::gui; ToolViewBuilder::ToolViewBuilder(const ToolViewRecipe& recipe, ChannelManager* channelManager) { diff --git a/src/gui/tool_view_builder.hpp b/src/gui/tool_view_builder.hpp index 63479b9066..6be02f193a 100644 --- a/src/gui/tool_view_builder.hpp +++ b/src/gui/tool_view_builder.hpp @@ -3,7 +3,7 @@ #include "tool_view.hpp" -namespace scopy { +namespace adiscope { namespace gui { struct ToolViewRecipe diff --git a/src/oscilloscope.cpp b/src/oscilloscope.cpp index d02f453b2a..252b52de2a 100644 --- a/src/oscilloscope.cpp +++ b/src/oscilloscope.cpp @@ -2465,7 +2465,7 @@ void Oscilloscope::add_math_channel(const std::string& function) } auto rail = gr::analog::rail_ff::make(MIN_MATH_RANGE, MAX_MATH_RANGE); - auto math = scopy::iio_math::make(function, nb_channels); + auto math = gr::scopy::iio_math::make(function, nb_channels); unsigned int curve_id = nb_channels + nb_math_channels + nb_ref_channels; unsigned int curve_number = find_curve_number(); @@ -4161,7 +4161,7 @@ void Oscilloscope::editMathChannelFunction(int id, const std::string& new_functi std::string name = qname.toStdString(); auto rail = gr::analog::rail_ff::make(MIN_MATH_RANGE, MAX_MATH_RANGE); - auto math = scopy::iio_math::make(new_function, nb_channels); + auto math = gr::scopy::iio_math::make(new_function, nb_channels); bool started = isIioManagerStarted(); if (started) diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index 5251f01d84..faaee2d40d 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -1657,7 +1657,7 @@ basic_block_sptr SignalGenerator::getSignalSource(gr::top_block_sptr top, } else - src = scopy::trapezoidal::make(samp_rate, data.frequency, amplitude, + src = gr::scopy::trapezoidal::make(samp_rate, data.frequency, amplitude, rise, holdh, fall, holdl, offset, phase*0.01745329); @@ -1939,19 +1939,19 @@ gr::basic_block_sptr SignalGenerator::getSource(QWidget *obj, if(ptr->math_sr < samp_rate) { - auto src = scopy::iio_math_gen::make(ptr->math_sr, str, (uint64_t)ptr->math_sr * ptr->math_record_length); + auto src = gr::scopy::iio_math_gen::make(ptr->math_sr, str, (uint64_t)ptr->math_sr * ptr->math_record_length); auto resamp = displayResampler(samp_rate, ptr->math_sr, top, src, noiseSrc, noiseAdd); top->connect(resamp, 0, skip_head, 0); return skip_head; } else { - auto src = scopy::iio_math_gen::make(samp_rate, str, (uint64_t)samp_rate * ptr->math_record_length); + auto src = gr::scopy::iio_math_gen::make(samp_rate, str, (uint64_t)samp_rate * ptr->math_record_length); top->connect(src, 0, skip_head, 0); generated_wave = skip_head; } } else { - generated_wave = scopy::iio_math_gen::make(samp_rate, str, (uint64_t)samp_rate * ptr->math_record_length); + generated_wave = gr::scopy::iio_math_gen::make(samp_rate, str, (uint64_t)samp_rate * ptr->math_record_length); } break; } diff --git a/ui/tool_view.ui b/ui/tool_view.ui index facb2bdb66..6576571344 100644 --- a/ui/tool_view.ui +++ b/ui/tool_view.ui @@ -212,7 +212,7 @@
- +
@@ -642,7 +642,7 @@ QPushButton:checked { border-image: url(:/icons/common/setup_btn_checked.svg); }
gui/linked_button.hpp
- scopy::gui::SettingsPairWidget + adiscope::gui::SettingsPairWidget QWidget
gui/settings_pair_widget.hpp
1 From 3ca878e765c0b8673a8b8e259c1681a4d531401f Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 10 Mar 2022 18:09:37 +0200 Subject: [PATCH 123/125] general: make scopy generate less output on connect Signed-off-by: Adrian Suciu --- src/BasicPlot.cpp | 2 +- src/DisplayPlot.cc | 1 - src/limitedplotzoomer.cpp | 5 ----- src/limitedplotzoomer.h | 1 - src/logicanalyzer/logic_analyzer.cpp | 2 +- src/oscilloscope_plot.cpp | 2 +- src/signal_generator.cpp | 1 - 7 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/BasicPlot.cpp b/src/BasicPlot.cpp index 4e8086b592..b71c97dd61 100644 --- a/src/BasicPlot.cpp +++ b/src/BasicPlot.cpp @@ -11,7 +11,7 @@ namespace adiscope { int BasicPlot::staticPlotId = 0; -BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotFrameRate(60) +BasicPlot::BasicPlot(QWidget* parent) : QwtPlot(parent), started(false), replotFrameRate(60), debug(false) { useOpenGlCanvas = getToolLauncherInstance()->isOpenGlLoaded(); connect(&replotTimer,SIGNAL(timeout()),this,SLOT(replotNow())); diff --git a/src/DisplayPlot.cc b/src/DisplayPlot.cc index 11fd48b090..f5782993f6 100644 --- a/src/DisplayPlot.cc +++ b/src/DisplayPlot.cc @@ -1663,7 +1663,6 @@ DisplayPlot::onPickerPointSelected6(const QPointF & p) void DisplayPlot::zoomBaseUpdate(bool force) { - qDebug()<<"ZoomBaseUpdate"; for (unsigned int i = 0; i < d_zoomer.size(); ++i) d_zoomer[i]->setZoomBase(force); } diff --git a/src/limitedplotzoomer.cpp b/src/limitedplotzoomer.cpp index 1946f0b584..f3878d1342 100644 --- a/src/limitedplotzoomer.cpp +++ b/src/limitedplotzoomer.cpp @@ -64,11 +64,6 @@ void LimitedPlotZoomer::zoom(const QRectF &rect) QwtPlotZoomer::zoom(boundedRect); } - - -void LimitedPlotZoomer::axesChanged() { - qDebug()<<"Axes changed"; -} QSizeF LimitedPlotZoomer::minZoomSize() const { const double eps = 10e12; diff --git a/src/limitedplotzoomer.h b/src/limitedplotzoomer.h index 0016087fed..0ae377836f 100644 --- a/src/limitedplotzoomer.h +++ b/src/limitedplotzoomer.h @@ -34,7 +34,6 @@ class LimitedPlotZoomer : public QwtPlotZoomer protected: virtual void zoom(const QRectF &); - virtual void axesChanged() override; virtual QSizeF minZoomSize() const override; private: diff --git a/src/logicanalyzer/logic_analyzer.cpp b/src/logicanalyzer/logic_analyzer.cpp index 2012506afe..bf3e5e732c 100644 --- a/src/logicanalyzer/logic_analyzer.cpp +++ b/src/logicanalyzer/logic_analyzer.cpp @@ -142,7 +142,7 @@ LogicAnalyzer::LogicAnalyzer(struct iio_context *ctx, adiscope::Filter *filt, for (uint8_t i = 0; i < m_nbChannels; ++i) { QCheckBox *channelBox = new QCheckBox("DIO " + QString::number(i)); - QHBoxLayout *hBoxLayout = new QHBoxLayout(this); + QHBoxLayout *hBoxLayout = new QHBoxLayout(); ui->channelEnumeratorLayout->addLayout(hBoxLayout, i % 8, i / 8); diff --git a/src/oscilloscope_plot.cpp b/src/oscilloscope_plot.cpp index 6cb1b4ae54..f6ab8b9ce1 100644 --- a/src/oscilloscope_plot.cpp +++ b/src/oscilloscope_plot.cpp @@ -1791,7 +1791,7 @@ void CapturePlot::setOffsetWidgetVisible(int chnIdx, bool visible) // if no group return if (hdlGroup == d_groupHandles.end()) { - qDebug() << "This handle is not in a group!"; + //qDebug() << "This handle is not in a group!"; return; } diff --git a/src/signal_generator.cpp b/src/signal_generator.cpp index faaee2d40d..2a78646bc4 100644 --- a/src/signal_generator.cpp +++ b/src/signal_generator.cpp @@ -1188,7 +1188,6 @@ void SignalGenerator::updatePreview() timer.start(); top->run(); - qDebug(CAT_SIGNAL_GENERATOR) << "The slow operation took" << timer.elapsed() << "milliseconds"; top->disconnect_all(); if (ui->run_button->runButtonChecked()) { From 1e862f6b16b99b297b4e196a264d1653efaa999b Mon Sep 17 00:00:00 2001 From: Adrian Suciu Date: Thu, 10 Mar 2022 17:45:27 +0200 Subject: [PATCH 124/125] dmm: use kernel buffers when using dmm Signed-off-by: Adrian Suciu --- src/dmm.cpp | 2 ++ src/iio_manager.cpp | 8 ++++++++ src/iio_manager.hpp | 1 + 3 files changed, 11 insertions(+) diff --git a/src/dmm.cpp b/src/dmm.cpp index 26b4c90942..f892ea573b 100644 --- a/src/dmm.cpp +++ b/src/dmm.cpp @@ -517,6 +517,7 @@ void DMM::toggleTimer(bool start) { enableDataLogging(start); if (start) { + manager->set_kernel_buffer_count(4); writeAllSettingsToHardware(); manager->start(id_ch1); manager->start(id_ch2); @@ -529,6 +530,7 @@ void DMM::toggleTimer(bool start) manager->stop(id_ch1); manager->stop(id_ch2); + manager->set_kernel_buffer_count(); } setDynamicProperty(ui->run_button, "running", start); diff --git a/src/iio_manager.cpp b/src/iio_manager.cpp index 5deb6ea7d9..b44ff13ede 100644 --- a/src/iio_manager.cpp +++ b/src/iio_manager.cpp @@ -365,6 +365,14 @@ void iio_manager::set_data_rate(double rate) { iio_block->set_data_rate(rate); } +void iio_manager::set_kernel_buffer_count(int kb) { + if(kb) { + m_analogin->setKernelBuffersCount(kb); + } else { + m_analogin->setKernelBuffersCount(KERNEL_BUFFERS_DEFAULT); + } +} + void iio_manager::enableMixedSignal(m2k::mixed_signal_source::sptr mixed_source) { for (int i = 0; i < nb_channels; ++i) { diff --git a/src/iio_manager.hpp b/src/iio_manager.hpp index 9dd1aefa04..7bc9cb1661 100644 --- a/src/iio_manager.hpp +++ b/src/iio_manager.hpp @@ -106,6 +106,7 @@ namespace adiscope { void set_buffer_size(port_id id, unsigned long size); void set_filter_parameters(int channel, int index, bool enable, float TC, float gain, float sample_rate ); void set_data_rate(double rate); + void set_kernel_buffer_count(int kb = 0); /* VERY ugly hack. The reconfiguration that happens after * locking/unlocking the flowgraph is sort of broken; the tags From fbe13dbc9830f77709fbe4dfc22d95b104ffa373 Mon Sep 17 00:00:00 2001 From: AlexandraTrifan Date: Mon, 14 Mar 2022 11:13:35 +0200 Subject: [PATCH 125/125] CMakeLists.txt: Bump to version v1.4.0. Signed-off-by: AlexandraTrifan --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fd4b138d0..e43b8b24f0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ cmake_minimum_required(VERSION 3.5) -project(scopy LANGUAGES C CXX VERSION 1.3.0) +project(scopy LANGUAGES C CXX VERSION 1.4.0) set(Python_ADDITIONAL_VERSIONS 3) FIND_PACKAGE(PythonInterp REQUIRED)