1
0
Fork 0
mirror of https://github.com/EdgeTX/edgetx.git synced 2025-07-25 01:05:08 +03:00

[Simulator] New main UI window, debug console, separate outputs window, more. (#4385)

* [package] Add QtSvg support DLLs for Windows.

* [Simulator] Add images/css resources for new version; Create SimulatorIcon and SimulatorStyle classes; Build with SVG support, remove unused bitmaps.

* [Simulator] Improve and simplify VirtualJoystickWidget resize event (sticks maintain X/Y position) & improve values layout w/out trims.

* [Simulator] Refactor Trainer simulator to use VirtualJoystickWidget, gains lock/hold buttons and X/Y position readout.

* [Simulator] More compact layout for Telemetry simulator, reduces min. width and adds scrolling columns to allow smaller window sizes.

* [Simulator] Add new SimulatorMainWindow and RadioOutputsWidget UI classes.

* [storage] Add non-persistent sessionId() to AppData for tracking currently active radio profile ID.

* [Simulator] Major UI updates:
    * Use a MainWindow interface with dockable/floatable windows, proper toolbar & menu;
    * Persistent size/layout state of all windows/docks/etc between uses, per radio profile;
    * Completely separate radio Outputs window with configurable layout (also persistent between uses);
    * Translations now enabled in standalone Simulator, follows main Companion setting (they need a lot of updates);
    * New custom icons throughout, toolbar icon sizes adhere to user preference in Companion.

* [Simulator] Debug Output console updates:
    * New filter feature with optional full RegEx support, can work in inclusive or exclusive modes (full help text is included in UI);
    * Up to 50 user-defined filters are saved to permanent settings, also a few predefined filters are provided;
    * Saves/restores the last filter used and if it was en/disabled;
    * Added word wrap and clear screen features;
    * Configurable scroll-back buffer size;
    * Increased efficiency & performance of the buffering/printing process.

* [Simulator][OSX] Visual UI tweaks for OS X.

* [Simulator] Fix some debug console settings not being restored properly.

* [Simulator] Add ability to hide/show menu bar; Persist view state of menu bar and radio title bar; Reduce font size on OS X; Other small visual tweaks and improvements.

* [build] Add QtSvg to Linux build scripts (a guess for Dockerfile).

* [build] Fix for Qt 5.3.

* Add OpenTx headers to new files.

* [Companion][storage] Update GeneralSettings() initializer to use current sessionId for profile data retrieval, removes workaround for standalone Simulator being started with arbitrary profile; AppData: always update sessionId when global id() changes.

* [X10] Fixes for new simulator
This commit is contained in:
Max Paperno 2017-02-04 05:59:42 -05:00 committed by Bertrand Songis
parent b493973d7d
commit 2f9d9c1a99
62 changed files with 6357 additions and 3095 deletions

View file

@ -70,7 +70,7 @@ install:
# - tar xjf gcc-arm-none-eabi-4_7-2013q3-20130916-linux.tar.bz2
# - mv gcc-arm-none-eabi-4_7-2013q3 /opt/gcc-arm-none-eabi
fi
- sudo apt-get install -qq qt${QT_BASE}base qt${QT_BASE}multimedia qt${QT_BASE}tools; source /opt/qt${QT_BASE}/bin/qt${QT_BASE}-env.sh
- sudo apt-get install -qq qt${QT_BASE}base qt${QT_BASE}multimedia qt${QT_BASE}svg qt${QT_BASE}tools; source /opt/qt${QT_BASE}/bin/qt${QT_BASE}-env.sh
script:
- ./tools/commit-tests.sh

View file

@ -49,8 +49,6 @@ endif()
if(SDL_FOUND)
include_directories(${SDL_INCLUDE_DIR})
add_definitions(-DJOYSTICKS)
else()
remove_definitions(-DSIMU_AUDIO)
endif()
add_definitions(-DSIMU)
@ -250,8 +248,6 @@ qt5_add_translation(companion_QM ${companion_TS})
qt5_add_resources(companion_RCC ${companion_RESOURCES})
add_custom_target(gen_qrc DEPENDS ${companion_RCC})
add_definitions(-DQT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}")
add_executable(${COMPANION_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${companion_SRCS} ${companion_RCC} ${companion_QM})
add_dependencies(${COMPANION_NAME} gen_qrc)
@ -282,9 +278,7 @@ if(WIN32)
endif()
add_executable(${SIMULATOR_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${simu_SRCS} ${companion_RCC})
add_dependencies(${SIMULATOR_NAME} gen_qrc)
qt5_use_modules(${SIMULATOR_NAME} Core Widgets Multimedia)
target_link_libraries(${SIMULATOR_NAME} PRIVATE simulation common shared storage qxtcommandoptions ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES})
@ -343,7 +337,7 @@ elseif(WIN32)
get_target_property(QtCore_LOCATION Qt5::Core LOCATION)
get_filename_component(QT_DLL_DIR ${QtCore_LOCATION} PATH)
set(INSTALL_TEMP_QTDLL_FILES Qt5Core Qt5Gui Qt5Widgets Qt5Xml Qt5Network Qt5PrintSupport Qt5Multimedia)
set(INSTALL_TEMP_QTDLL_FILES Qt5Core Qt5Gui Qt5Widgets Qt5Xml Qt5Network Qt5PrintSupport Qt5Multimedia Qt5Svg)
set(INSTALL_TEMP_ICUDLL_FILES icudt54.dll icuin54.dll icuuc54.dll)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(INSTALL_TEMP_QTDLL_SFX "d")

View file

@ -12,8 +12,26 @@
<file>images/track.png</file>
<file>images/track0.png</file>
<file>images/taranison.png</file>
<file>images/simulator/icons/8/lock-locked.png</file>
<file>images/simulator/icons/8/lock-unlocked.png</file>
<file>images/simulator/icons/svg/camera.svg</file>
<file>images/simulator/icons/svg/camera-active.svg</file>
<file>images/simulator/icons/svg/console.svg</file>
<file>images/simulator/icons/svg/eraser.svg</file>
<file>images/simulator/icons/svg/eraser-active.svg</file>
<file>images/simulator/icons/svg/info.svg</file>
<file>images/simulator/icons/svg/info-active.svg</file>
<file>images/simulator/icons/svg/joystick_settings.svg</file>
<file>images/simulator/icons/svg/joystick_settings-active.svg</file>
<file>images/simulator/icons/svg/toggle_lock.svg</file>
<file>images/simulator/icons/svg/toggle_lock-on.svg</file>
<file>images/simulator/icons/svg/radio_outputs.svg</file>
<file>images/simulator/icons/svg/reload_script.svg</file>
<file>images/simulator/icons/svg/reload_script-active.svg</file>
<file>images/simulator/icons/svg/restart.svg</file>
<file>images/simulator/icons/svg/restart-active.svg</file>
<file>images/simulator/icons/svg/telemetry.svg</file>
<file>images/simulator/icons/svg/trainer.svg</file>
<file>images/simulator/icons/svg/word_wrap.svg</file>
<file>images/simulator/icons/svg/word_wrap-active.svg</file>
<file>images/simulator/Horus/middle.png</file>
<file>images/simulator/Horus/left.png</file>
<file>images/simulator/Horus/right.png</file>
@ -180,6 +198,8 @@
<file>images/library/10702.png</file>
<file>images/library/10801.png</file>
<file>images/library/10802.png</file>
<file>themes/default/style.css</file>
<file>themes/default/style-osx.css</file>
<file>themes/monochrome/16/paintbrush.png</file>
<file>themes/monochrome/16/open.png</file>
<file>themes/monochrome/16/edit.png</file>

View file

@ -1120,23 +1120,23 @@ GeneralSettings::GeneralSettings()
strcpy(bluetoothName, "Taranis");
}
templateSetup = g.profile[g.id()].channelOrder();
stickMode = g.profile[g.id()].defaultMode();
templateSetup = g.profile[g.sessionId()].channelOrder();
stickMode = g.profile[g.sessionId()].defaultMode();
QString t_calib = g.profile[g.id()].stickPotCalib();
QString t_calib = g.profile[g.sessionId()].stickPotCalib();
int potsnum = getCurrentFirmware()->getCapability(Pots);
if (!t_calib.isEmpty()) {
QString t_trainercalib=g.profile[g.id()].trainerCalib();
int8_t t_txVoltageCalibration=(int8_t)g.profile[g.id()].txVoltageCalibration();
int8_t t_txCurrentCalibration=(int8_t)g.profile[g.id()].txCurrentCalibration();
int8_t t_PPM_Multiplier=(int8_t)g.profile[g.id()].ppmMultiplier();
uint8_t t_stickMode=(uint8_t)g.profile[g.id()].gsStickMode();
uint8_t t_vBatWarn=(uint8_t)g.profile[g.id()].vBatWarn();
QString t_DisplaySet=g.profile[g.id()].display();
QString t_BeeperSet=g.profile[g.id()].beeper();
QString t_HapticSet=g.profile[g.id()].haptic();
QString t_SpeakerSet=g.profile[g.id()].speaker();
QString t_CountrySet=g.profile[g.id()].countryCode();
QString t_trainercalib=g.profile[g.sessionId()].trainerCalib();
int8_t t_txVoltageCalibration=(int8_t)g.profile[g.sessionId()].txVoltageCalibration();
int8_t t_txCurrentCalibration=(int8_t)g.profile[g.sessionId()].txCurrentCalibration();
int8_t t_PPM_Multiplier=(int8_t)g.profile[g.sessionId()].ppmMultiplier();
uint8_t t_stickMode=(uint8_t)g.profile[g.sessionId()].gsStickMode();
uint8_t t_vBatWarn=(uint8_t)g.profile[g.sessionId()].vBatWarn();
QString t_DisplaySet=g.profile[g.sessionId()].display();
QString t_BeeperSet=g.profile[g.sessionId()].beeper();
QString t_HapticSet=g.profile[g.sessionId()].haptic();
QString t_SpeakerSet=g.profile[g.sessionId()].speaker();
QString t_CountrySet=g.profile[g.sessionId()].countryCode();
if ((t_calib.length()==(CPN_MAX_STICKS+potsnum)*12) && (t_trainercalib.length()==16)) {
QString Byte;

View file

@ -32,7 +32,7 @@
#include "appdata.h"
#include "helpers.h"
#include "modeledit/modeledit.h"
#include "simulatordialog.h"
#include "simulatormainwindow.h"
#include "storage/sdcard.h"
Stopwatch gStopwatch("global");
@ -823,7 +823,7 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
simuData->setCurrentModel(modelIdx);
}
SimulatorDialog * dialog = new SimulatorDialog(parent, simulator, flags);
SimulatorMainWindow * dialog = new SimulatorMainWindow(parent, simulator, flags);
if (IS_HORUS(getCurrentBoard()) && !dialog->useTempDataPath(true)) {
QMessageBox::critical(NULL, QObject::tr("Data Load Error"), QObject::tr("Error: Could not create temporary directory in '%1'").arg(QDir::tempPath()));
delete dialog;
@ -831,12 +831,15 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
return;
}
dialog->setRadioData(simuData);
qDebug() << __FILE__ << __LINE__ << "Starting" << getCurrentFirmware()->getName() << "simulation with SD path" << dialog->getSdPath() << "and models/settings path" << dialog->getDataPath();
dialog->setWindowModality(Qt::ApplicationModal);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->start();
dialog->exec();
delete dialog;
// TODO Horus tmp directory is deleted on simulator close OR we could use it to get back data from the simulation
delete simuData; // TODO same with simuData, could read it back and update the file data
dialog->show();
QObject::connect(dialog, &SimulatorMainWindow::destroyed, [simuData] (void) {
// TODO simuData and Horus tmp directory is deleted on simulator close OR we could use it to get back data from the simulation
delete simuData;
});
}
else {
QMessageBox::warning(NULL,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

View file

@ -0,0 +1 @@
<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 6h-1.586l-1-1c-.579-.579-1.595-1-2.414-1h-4c-.819 0-1.835.421-2.414 1l-1 1h-1.586c-1.654 0-3 1.346-3 3v8c0 1.654 1.346 3 3 3h14c1.654 0 3-1.346 3-3v-8c0-1.654-1.346-3-3-3zm-7 10c-1.933 0-3.5-1.568-3.5-3.5 0-1.934 1.567-3.5 3.5-3.5s3.5 1.566 3.5 3.5c0 1.932-1.567 3.5-3.5 3.5zm6-4.701c-.719 0-1.3-.58-1.3-1.299s.581-1.301 1.3-1.301 1.3.582 1.3 1.301-.581 1.299-1.3 1.299z"/></svg>

After

Width:  |  Height:  |  Size: 508 B

View file

@ -0,0 +1 @@
<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 20h-14c-1.654 0-3-1.346-3-3v-8c0-1.654 1.346-3 3-3h1.586l1-1c.579-.579 1.596-1 2.414-1h4c.818 0 1.835.421 2.414 1l1 1h1.586c1.654 0 3 1.346 3 3v8c0 1.654-1.346 3-3 3zm-14-12c-.552 0-1 .448-1 1v8c0 .552.448 1 1 1h14c.552 0 1-.448 1-1v-8c0-.552-.448-1-1-1h-2c-.266 0-.52-.105-.707-.293l-1.293-1.293c-.201-.201-.715-.414-1-.414h-4c-.285 0-.799.213-1 .414l-1.293 1.293c-.187.188-.441.293-.707.293h-2zM12 10c1.379 0 2.5 1.121 2.5 2.5s-1.121 2.5-2.5 2.5-2.5-1.121-2.5-2.5 1.121-2.5 2.5-2.5m0-1c-1.934 0-3.5 1.566-3.5 3.5 0 1.932 1.566 3.5 3.5 3.5s3.5-1.568 3.5-3.5c0-1.934-1.566-3.5-3.5-3.5zM18 8.699c-.719 0-1.3.582-1.3 1.301s.581 1.299 1.3 1.299 1.3-.58 1.3-1.299-.581-1.301-1.3-1.301z"/></svg>

After

Width:  |  Height:  |  Size: 819 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="68" height="53" viewBox="-0.462891 -0.018555 68 53" id="svg2" xml:space="preserve"><metadata id="metadata14"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
<defs id="defs4">
</defs>
<path d="M 60.37793,0 H 6.289551 C 2.81543,0 0,2.802734 0,6.260742 v 40.071289 c 0,3.458008 2.81543,6.260742 6.289551,6.260742 H 60.37793 c 3.473633,0 6.288086,-2.802734 6.288086,-6.260742 V 6.260742 C 66.666016,2.802734 63.851563,0 60.37793,0 z M 20.739258,5.193359 c 1.229492,0 2.226074,0.991699 2.226074,2.215332 0,1.224121 -0.996582,2.21582 -2.226074,2.21582 -1.229492,0 -2.226074,-0.991699 -2.226074,-2.21582 0,-1.223632 0.996582,-2.215332 2.226074,-2.215332 z m -6.660645,0 c 1.229492,0 2.226074,0.991699 2.226074,2.215332 0,1.224121 -0.996582,2.21582 -2.226074,2.21582 -1.229004,0 -2.226074,-0.991699 -2.226074,-2.21582 0,-1.223632 0.99707,-2.215332 2.226074,-2.215332 z m -6.67041,0 c 1.223633,0 2.215332,0.991699 2.215332,2.215332 0,1.224121 -0.991699,2.21582 -2.215332,2.21582 -1.223633,0 -2.215332,-0.991699 -2.215332,-2.21582 0,-1.223632 0.991699,-2.215332 2.215332,-2.215332 z M 64.444336,45.18457 c 0,2.962891 -2.22168,5.185547 -5.18457,5.185547 H 7.407715 c -2.963379,0 -5.185547,-2.222656 -5.185547,-5.185547 V 13.333496 c 0,0.0049 62.14502,0 62.222168,0 V 45.18457 z" id="path6" />
<path d="m 5.3691063,21.009388 0,6.863626 54.9094277,0 0,-6.863626 z m 0,13.727252 0,6.863625 34.3183947,0 0,-6.863625 z" id="path3386" /></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><metadata id="metadata11"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs9" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219">
<g transform="matrix(0.63466286,0,0,0.60080491,-55.333181,1055.0231)" id="g3488">
<path d="M 90.122,87.857 H 62.31 L 90.594,59.568 c 2.043,-2.042 2.043,-5.366 0,-7.409 L 80.59,42.157 c -0.795,-0.796 -1.785,-1.26 -2.817,-1.436 0.002,0 -4.577,0.241 -8.999,0.744 -1.358,0.153 -2.492,0.923 -3.284,1.714 L 37.616,71.055 c -2.043,2.043 -2.043,5.365 0,7.407 l 10.003,10.003 c 0.982,0.984 2.296,1.527 3.691,1.527 0.109,0 6.752,-0.131 6.821,-0.135 0.335,-0.031 1.14,-0.123 1.14,-0.123 h 30.851 c 0.518,0 0.938,-0.421 0.938,-0.939 0.001,-0.517 -0.421,-0.938 -0.938,-0.938 z M 60.339,86.862 c -1.226,1.225 -3.218,1.225 -4.446,0 L 45.89,76.86 c -1.226,-1.228 -1.226,-3.222 0,-4.445 L 60.138,58.167 74.544,72.655 60.339,86.862 z M 72.661,42.961 75.489,42.73 60.095,58.125 57.737,57.887 72.661,42.961 z M 39.098,76.983 c -1.227,-1.227 -1.227,-3.221 0,-4.448 l 15.049,-15.049 3.346,0.359 -13.086,13.086 c -2.042,2.042 -2.042,5.368 0,7.409 l 9.508,9.508 c -0.152,0.003 -0.289,0.005 -0.435,0.008 H 50.904 C 50.222,87.768 49.59,87.475 49.099,86.983 l -10.001,-10 z" id="path3495" style="fill:#042fb5" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<g transform="matrix(0.63466286,0,0,0.60080491,-55.333181,1055.0231)" id="g3488">
<path d="M 90.121527,87.856832 H 62.310073 l 28.28445,-28.288377 c 2.042733,-2.042734 2.042733,-5.366031 0,-7.408767 L 80.591011,42.156731 c -0.795799,-0.795799 -1.785502,-1.260389 -2.817798,-1.4358 0.0017,5.61e-4 -4.576959,0.241542 -8.998683,0.744239 -1.35902,0.153556 -2.493313,0.923575 -3.284066,1.713768 l -27.874789,27.87647 c -2.042735,2.042734 -2.042735,5.364349 0,7.407085 L 47.618633,88.46601 c 0.982417,0.9841 2.296044,1.526587 3.691493,1.526587 0.108722,0 6.752515,-0.131138 6.821447,-0.1345 0.335131,-0.03137 1.139336,-0.122734 1.139336,-0.122734 h 30.850057 c 0.51783,0 0.939264,-0.421436 0.939264,-0.939266 0.0012,-0.517828 -0.420314,-0.939265 -0.938703,-0.939265 z M 60.338511,86.862085 c -1.225641,1.223959 -3.218499,1.223959 -4.445821,0 L 45.889735,76.860247 c -1.226203,-1.227882 -1.226203,-3.221858 0,-4.446378 L 60.13788,58.166282 74.542946,72.65541 60.338511,86.862085 z M 72.66105,42.961495 75.488937,42.730042 60.094728,58.12481 57.736476,57.886631 72.66105,42.961495 z M 39.097989,76.98298 c -1.226762,-1.226201 -1.226762,-3.220177 0,-4.44806 l 15.048988,-15.04899 3.346276,0.359231 -13.085832,13.085834 c -2.042737,2.042734 -2.042737,5.368272 0,7.408766 l 9.508103,9.508104 c -0.151875,0.0034 -0.288618,0.0052 -0.434887,0.0084 H 50.90438 C 50.222349,87.768285 49.590193,87.47462 49.099826,86.98257 L 39.097989,76.98299 z" id="path3495" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><metadata id="metadata11"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs9" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219">
<g transform="matrix(0.63466286,0,0,0.60080491,-55.333181,1055.0231)" id="g3488">
<path d="m 89.528,64.797 c 0,13.331 -10.805,24.134 -24.135,24.134 -13.33,0 -24.134,-10.804 -24.134,-24.134 0,-13.331 10.804,-24.135 24.134,-24.135 13.33,0 24.135,10.803 24.135,24.135 z" id="path3474" style="fill:none;stroke:#7d0297;stroke-width:6;stroke-miterlimit:10" />
<path d="m 68.948,59.507 -3.057,14.439 c -0.129,0.552 -0.17,0.976 -0.17,1.358 0,1.189 0.551,1.572 1.742,1.572 0.762,0 1.104,-0.126 1.358,-0.253 -0.214,3.014 -2.38,4.373 -4.673,4.373 -2.548,0 -4.544,-1.528 -4.544,-5.099 0,-0.804 0.128,-1.738 0.34,-2.76 l 2.889,-13.632 6.115,0.002 0,0 z m -1.91,-11.035 c 1.868,0 3.398,1.528 3.398,3.396 0,1.87 -1.531,3.357 -3.398,3.357 -1.87,0 -3.355,-1.487 -3.355,-3.357 -10e-4,-1.868 1.485,-3.396 3.355,-3.396 z" id="path3476" style="fill:#9b0297;stroke:#7d0297;stroke-miterlimit:10" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<g transform="matrix(0.63466286,0,0,0.60080491,-55.333181,1055.0231)" id="g3488">
<path d="m 89.527433,64.796857 c 0,13.330947 -10.804079,24.134502 -24.134474,24.134502 -13.329856,0 -24.133935,-10.803555 -24.133935,-24.134502 0,-13.330947 10.804079,-24.135039 24.133935,-24.135039 13.330395,0 24.134474,10.803554 24.134474,24.135039 z" id="path3474" style="fill:none;stroke:#000000;stroke-width:6;stroke-miterlimit:10" />
<path d="m 68.948411,59.507519 -3.057829,14.43917 c -0.128038,0.551959 -0.168924,0.975881 -0.168924,1.358917 0,1.189456 0.549807,1.572493 1.741952,1.572493 0.761768,0 1.103917,-0.126424 1.358378,-0.253385 -0.214113,3.013719 -2.379985,4.372636 -4.673358,4.372636 -2.54837,0 -4.544783,-1.528378 -4.544783,-5.097822 0,-0.804269 0.128038,-1.739264 0.339998,-2.760335 l 2.888907,-13.632212 h 6.115659 z M 67.037536,48.472098 c 1.868375,0 3.397828,1.527841 3.397828,3.395679 0,1.869991 -1.529991,3.357484 -3.397828,3.357484 -1.869988,0 -3.354789,-1.487493 -3.354789,-3.357484 -5.38e-4,-1.867838 1.484801,-3.395679 3.354789,-3.395679 z" id="path3476" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><defs id="defs14" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219">
<path d="m -15.805,1110.28 c 0.028,-0.49 0.111,-1.315 0.185,-1.835 0.124,-0.872 0.086,-0.983 -0.492,-1.433 l -0.626,-0.488 -1.778,0.613 c -0.978,0.336 -1.851,0.593 -1.94,0.57 -0.209,-0.053 -2.69,-4.165 -2.633,-4.364 0.024,-0.084 0.698,-0.667 1.497,-1.297 l 1.454,-1.147 -0.125,-0.736 c -0.121,-0.715 -0.164,-0.754 -1.498,-1.329 -2.442,-1.053 -2.267,-0.627 -1.441,-3.512 l 0.721,-2.515 1.515,0.148 c 2.351,0.229 2.385,0.224 2.887,-0.387 0.461,-0.561 0.461,-0.563 -0.086,-2.002 -0.301,-0.793 -0.597,-1.58 -0.659,-1.749 -0.087,-0.24 0.416,-0.594 2.277,-1.604 l 2.389,-1.296 1.163,1.353 c 1.257,1.461 1.43,1.563 2.337,1.378 0.534,-0.108 0.666,-0.264 1.223,-1.445 1.103,-2.335 0.67,-2.168 3.638,-1.405 l 2.587,0.664 -0.083,1.073 c -0.045,0.59 -0.107,1.323 -0.136,1.629 -0.096,0.995 -0.056,1.161 0.365,1.491 0.662,0.52 0.897,0.508 2.701,-0.135 l 1.708,-0.608 1.334,2.195 c 0.734,1.207 1.316,2.259 1.294,2.337 -0.022,0.079 -0.683,0.649 -1.468,1.269 -1.428,1.125 -1.428,1.126 -1.332,1.817 0.104,0.75 0.136,0.774 2.283,1.7 l 1.385,0.597 -0.722,2.521 -0.723,2.52 -0.542,-0.037 c -0.299,-0.02 -1.117,-0.084 -1.819,-0.139 -1.469,-0.119 -1.685,-0.063 -2.141,0.568 -0.301,0.417 -0.273,0.62 0.286,2.103 0.341,0.902 0.595,1.724 0.566,1.827 -0.061,0.21 -4.383,2.584 -4.603,2.528 -0.079,-0.02 -0.697,-0.665 -1.372,-1.433 l -1.229,-1.397 -0.767,0.171 c -0.743,0.166 -0.792,0.221 -1.574,1.77 -0.444,0.88 -0.818,1.618 -0.83,1.642 -0.012,0.023 -1.194,-0.26 -2.627,-0.628 l -2.604,-0.669 0.055,-0.894 z m 4.699,-1.662 c 0.403,-0.767 0.767,-1.407 0.808,-1.42 0.042,-0.019 0.917,-0.124 1.947,-0.246 l 1.872,-0.222 1.078,1.219 1.077,1.219 0.773,-0.435 0.773,-0.435 -0.576,-1.473 -0.576,-1.472 1.224,-1.497 1.223,-1.496 1.603,0.143 1.602,0.145 0.291,-0.806 0.291,-0.805 -1.302,-0.573 c -0.716,-0.316 -1.401,-0.658 -1.521,-0.761 -0.12,-0.103 -0.33,-0.99 -0.467,-1.97 l -0.249,-1.781 1.26,-1.001 1.261,-1.001 -0.458,-0.73 -0.459,-0.73 -1.453,0.494 c -0.8,0.272 -1.54,0.473 -1.645,0.446 -0.335,-0.086 -2.939,-2.101 -3.011,-2.329 -0.038,-0.121 0,-0.833 0.083,-1.584 l 0.152,-1.365 -0.9,-0.231 -0.9,-0.231 -0.652,1.36 c -0.358,0.748 -0.76,1.401 -0.892,1.452 -0.293,0.114 -3.494,0.461 -3.709,0.404 -0.084,-0.022 -0.594,-0.55 -1.134,-1.173 l -0.98,-1.133 -0.802,0.42 -0.802,0.419 0.566,1.472 0.566,1.473 -0.474,0.566 c -0.26,0.311 -0.769,0.944 -1.129,1.406 -0.36,0.462 -0.808,0.868 -0.994,0.904 -0.186,0.035 -0.977,0.02 -1.758,-0.064 l -1.42,-0.127 -0.226,0.787 c -0.254,0.886 -0.301,0.836 1.483,1.596 l 1.282,0.546 0.283,1.875 0.283,1.875 -1.299,1.022 -1.299,1.023 0.363,0.629 c 0.199,0.347 0.453,0.654 0.563,0.682 0.111,0.028 0.865,-0.16 1.677,-0.421 l 1.476,-0.472 1.462,1.078 c 0.804,0.593 1.495,1.163 1.534,1.268 0.04,0.104 0.005,0.795 -0.077,1.535 -0.082,0.739 -0.109,1.411 -0.059,1.493 0.049,0.082 0.436,0.212 0.86,0.29 l 0.77,0.141 0.737,-1.398 z m -1.432,-4.941 c -2.207,-1.374 -3.184,-3.549 -2.667,-5.939 1.055,-4.879 7.628,-6.017 10.666,-1.848 0.253,0.348 0.578,1.186 0.721,1.863 0.468,2.204 -0.517,4.48 -2.466,5.699 -1.396,0.874 -2.844,1.122 -4.5,0.772 -0.73,-0.154 -1.518,-0.402 -1.754,-0.547 l 0,0 z m 3.043,-1.343 c 2.247,-0.02 3.962,-2.039 3.536,-4.18 -0.487,-2.451 -3.773,-3.601 -5.816,-2.036 -1.947,1.491 -1.879,4.26 0.138,5.587 0.793,0.522 1.176,0.635 2.143,0.628 l -10e-4,10e-4 z" id="path4448-4-4" style="fill:#007f00" />
<path d="m -24.424,1077.473 h 13.202 c 3.947,0 7.146,3.199 7.146,7.146 v 7.859 c 0,3.947 -3.199,7.146 -7.146,7.146 h -13.202 c -3.946,0 -7.146,-3.199 -7.146,-7.146 v -7.859 c 0,-3.947 3.199,-7.146 7.146,-7.146 z" id="rect3980-2-6" style="fill:#757575" />
<path d="m -24.761,1077.181 h 13.995 c 3.919,0 7.096,3.177 7.096,7.096 v 8.227 c 0,3.919 -3.177,7.096 -7.096,7.096 h -13.995 c -3.919,0 -7.096,-3.177 -7.096,-7.096 v -8.227 c 0,-3.919 3.177,-7.096 7.096,-7.096 z" id="rect3980-9" style="fill:none;stroke:#009100;stroke-width:1.23500001;stroke-linecap:round;stroke-linejoin:round" />
<g transform="matrix(1.4151129,0,0,1.4016181,-59.15926,-361.58526)" id="g4236-1">
<path d="m 34.219,1033.737 c 0,2.697 -2.186,4.884 -4.883,4.884 -2.697,0 -4.884,-2.186 -4.884,-4.884 0,-2.697 2.187,-4.884 4.884,-4.884 2.697,10e-4 4.883,2.187 4.883,4.884 z" id="path3984-8-3" style="fill:#008700;fill-opacity:1;stroke:#000100;stroke-width:0.38479999;stroke-linecap:round;stroke-linejoin:round" />
<path d="m 30.369,1028.415 -2.629,6.561 6.798,-2.771 -4.169,-3.79 z" id="path4007-7" style="fill:#ffffff" />
<path d="m 34.988,1030.354 c 0,1.421 -1.152,2.572 -2.574,2.572 -1.421,0 -2.574,-1.151 -2.574,-2.572 0,-1.421 1.152,-2.573 2.574,-2.573 1.421,10e-4 2.574,1.153 2.574,2.573 z" id="path4004-0" style="fill:#ffffff;stroke:#000000;stroke-width:0.43849999" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<path d="m -15.804948,1110.2798 c 0.02792,-0.4898 0.111136,-1.3157 0.184859,-1.8355 0.123657,-0.8723 0.08577,-0.9827 -0.492201,-1.433 l -0.626209,-0.4877 -1.777652,0.6121 c -0.977654,0.3367 -1.850646,0.5934 -1.93993,0.5706 -0.209502,-0.053 -2.689644,-4.1649 -2.632607,-4.3638 0.02374,-0.084 0.697501,-0.6667 1.497265,-1.2975 l 1.454108,-1.1468 -0.124527,-0.7365 c -0.120885,-0.7155 -0.164131,-0.7535 -1.497938,-1.3292 -2.44191,-1.0527 -2.267523,-0.6268 -1.440666,-3.5118 l 0.720895,-2.5154 1.514864,0.1484 c 2.351578,0.2286 2.38512,0.2241 2.886555,-0.3865 0.46072,-0.5613 0.460632,-0.5632 -0.08611,-2.0025 -0.301026,-0.7927 -0.597509,-1.5798 -0.658862,-1.7492 -0.08688,-0.2399 0.415749,-0.5938 2.277221,-1.604 l 2.388763,-1.296 1.163547,1.3525 c 1.256899,1.4608 1.429425,1.5626 2.336111,1.3777 0.534545,-0.1079 0.6655389,-0.2639 1.2237319,-1.4449 1.103253,-2.3349 0.669842,-2.1676 3.63823,-1.4051 l 2.5864222,0.6644 -0.08233,1.0727 c -0.04525,0.5897 -0.106624,1.3225 -0.13632,1.6292 -0.09658,0.9949 -0.05598,1.1608 0.364627,1.491 0.662023,0.52 0.896696,0.5082 2.700966,-0.1353 l 1.7083605,-0.6084 1.3346706,2.1946 c 0.7340447,1.2072 1.3163479,2.2587 1.293957,2.3369 -0.022277,0.079 -0.6830622,0.6486 -1.4682671,1.2681 -1.4272235,1.1253 -1.4275789,1.126 -1.3315798,1.8165 0.1043322,0.7506 0.1364398,0.7747 2.2822984,1.7003 l 1.3858308,0.5978 -0.7223669,2.5201 -0.7223225,2.5202 -0.5422814,-0.037 c -0.298063,-0.02 -1.1164862,-0.084 -1.8185439,-0.139 -1.4690987,-0.1191 -1.6855317,-0.063 -2.1413967,0.5689 -0.301053,0.4164 -0.273356,0.6196 0.286068,2.1021 0.340623,0.9027 0.595434,1.7246 0.566195,1.8267 -0.06023,0.2102 -4.382702,2.5844 -4.602492,2.5278 -0.07857,-0.02 -0.6959462,-0.665 -1.3718942,-1.433 l -1.229018,-1.3965 -0.766457,0.1704 c -0.7422,0.1664 -0.792091,0.2214 -1.573926,1.7702 -0.4440739,0.8797 -0.8174139,1.6184 -0.8295809,1.6415 -0.01212,0.023 -1.193959,-0.2601 -2.626425,-0.6279 l -2.604472,-0.6689 z m 4.698467,-1.6615 c 0.403855,-0.7674 0.767853,-1.4066 0.808909,-1.4203 0.04113,-0.019 0.9169169,-0.1243 1.9464349,-0.2459 l 1.871875,-0.222 1.077619,1.2191 1.0776392,1.2188 0.773489,-0.4349 0.773546,-0.4351 -0.576268,-1.4725 -0.576236,-1.4723 1.223979,-1.4966 1.223979,-1.4964 1.602289,0.1426 1.6022575,0.1447 0.2911834,-0.8051 0.2911833,-0.8052 -1.3025821,-0.5732 c -0.7163951,-0.3152 -1.4008151,-0.6576 -1.5208931,-0.761 -0.120186,-0.1023 -0.330355,-0.9896 -0.467303,-1.9693 l -0.248971,-1.781 1.260465,-1.0013 1.260498,-1.0011 -0.4582964,-0.7303 -0.4582966,-0.73 -1.453181,0.4944 c -0.799225,0.2721 -1.539222,0.4726 -1.644418,0.4457 -0.334467,-0.086 -2.9395742,-2.1009 -3.0111132,-2.3288 -0.03776,-0.1207 -2.67e-4,-0.8331 0.08334,-1.5839 l 0.151989,-1.3651 -0.899838,-0.2311 -0.899838,-0.2312 -0.6519,1.3603 c -0.358534,0.7477 -0.760015,1.4012 -0.892146,1.4517 -0.292503,0.1138 -3.4946249,0.461 -3.7094449,0.4042 -0.08435,-0.022 -0.594311,-0.5499 -1.133242,-1.1734 l -0.979951,-1.1327 -0.802188,0.4195 -0.802183,0.4194 0.565688,1.4723 0.565656,1.4727 -0.473649,0.5663 c -0.260497,0.3113 -0.768691,0.9439 -1.129281,1.4055 -0.360602,0.4618 -0.807843,0.8684 -0.993907,0.9035 -0.186153,0.035 -0.977356,0.02 -1.758448,-0.064 l -1.420172,-0.1263 -0.225496,0.7868 c -0.254113,0.8864 -0.301382,0.8358 1.483423,1.5961 l 1.282044,0.5461 0.282546,1.8745 0.282558,1.875 -1.298844,1.022 -1.298875,1.0223 0.362538,0.6296 c 0.199386,0.3465 0.452953,0.653 0.563447,0.6813 0.110489,0.028 0.865211,-0.1591 1.677192,-0.4206 l 1.476309,-0.4724 1.462237,1.0779 c 0.804213,0.5929 1.494834,1.1635 1.534647,1.2682 0.03986,0.1047 0.0051,0.7951 -0.07726,1.5345 -0.08232,0.7394 -0.1092,1.4112 -0.05978,1.4929 0.04931,0.082 0.43654,0.2121 0.86019,0.2901 l 0.770373,0.141 z m -1.430842,-4.9409 c -2.207116,-1.3739 -3.183887,-3.5489 -2.667303,-5.9392 1.054562,-4.879 7.6275429,-6.0176 10.6654331,-1.8479 0.253141,0.3474 0.577949,1.1857 0.721795,1.8626 0.468134,2.2036 -0.516507,4.4794 -2.4659632,5.6991 -1.396417,0.8738 -2.844407,1.1219 -4.5005469,0.7719 -0.729507,-0.1537 -1.518069,-0.4011 -1.75356,-0.5459 z m 3.0421429,-1.3428 c 2.247258,-0.02 3.961584,-2.0393 3.53624,-4.1801 -0.486895,-2.451 -3.773255,-3.6015 -5.8168439,-2.0363 -1.947032,1.4913 -1.878862,4.2604 0.137582,5.5872 0.793779,0.5223 1.176595,0.6349 2.1432949,0.6282 z" id="path4448-4-4" style="fill:#000000;fill-opacity:1;display:inline" />
<rect width="27.494143" height="22.15127" ry="7.1462564" x="-31.570469" y="1077.4731" id="rect3980-2-6" style="fill:#757575;fill-opacity:1;stroke:none" />
<rect width="28.186502" height="22.418978" ry="7.0958796" x="-31.85692" y="1077.1814" id="rect3980-9" style="fill:none;stroke:#000100;stroke-width:1.23500371;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g transform="matrix(1.4151129,0,0,1.4016181,-59.15926,-361.58526)" id="g4236-1">
<path d="m -4.4642859,77.678574 a 4.2857141,4.2857141 0 1 1 -8.5714281,0 4.2857141,4.2857141 0 1 1 8.5714281,0 z" transform="matrix(1.13953,0,0,1.13953,39.306585,945.21999)" id="path3984-8-3" style="fill:#0c0000;fill-opacity:1;stroke:#000100;stroke-width:0.38477078;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path d="m 30.368507,1028.4151 -2.629324,6.5608 6.79834,-2.7712 z" id="path4007-7" style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path d="m 34.987833,1030.3543 c 0,1.4208 -1.152235,2.5723 -2.573586,2.5723 -1.421352,0 -2.573587,-1.1515 -2.573587,-2.5723 0,-1.4207 1.152235,-2.5724 2.573587,-2.5724 1.421351,0 2.573586,1.1517 2.573586,2.5724 z" id="path4004-0" style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.43845785;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="matrix(1.1234718,0,0,1.1234718,-4.5608191,-1.4655251)" id="layer1">
<g transform="translate(-72.000133,-1072.4267)" id="g3209" style="display:inline">
<path d="m 99.349441,1094.4654 0,8.8551 -8.045911,0 0,22.5193 37.84818,0 0,-22.5193 -8.27712,0 0,-8.8551 -21.525149,0 z" id="rect5128-4-3" style="fill:none;stroke:#000000;stroke-width:3.11534309;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path d="m 100.09052,1103.3954 0,2.9404 20.02752,0 0.0179,-3.0711" id="path5261-5-2" style="fill:none;stroke:#000000;stroke-width:1.70797408;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g transform="translate(59.145039,46.442959)" id="g5674-3-35">
<path d="m 35.901786,1064.1033 0,12.0312 13.9375,0 0,-8.1562 -9,3.625 3.0625,-7.5 -8,0 z" id="rect5190-1-4-3-4" style="fill:#000000;fill-opacity:1;stroke:none" />
<path d="m 50.388469,1065.2434 c 0,1.7268 -1.39996,3.1264 -3.1269,3.1264 -1.72692,0 -3.12688,-1.3996 -3.12688,-3.1264 0,-1.7268 1.39996,-3.1266 3.12688,-3.1266 1.72694,0 3.1269,1.3998 3.1269,3.1266 z" id="path4004-69-7-4-5-5-7-1" style="fill:none;stroke:#000000;stroke-width:1.19000506;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
<g transform="translate(75.782819,46.495259)" id="g5674-0-8">
<path d="m 35.901786,1064.1033 0,12.0312 13.9375,0 0,-8.1562 -9,3.625 3.0625,-7.5 -8,0 z" id="rect5190-1-4-7-8" style="fill:#000000;fill-opacity:1;stroke:none" />
<path d="m 50.388469,1065.2434 c 0,1.7268 -1.39996,3.1264 -3.1269,3.1264 -1.72692,0 -3.12688,-1.3996 -3.12688,-3.1264 0,-1.7268 1.39996,-3.1266 3.12688,-3.1266 1.72694,0 3.1269,1.3998 3.1269,3.1266 z" id="path4004-69-7-4-5-5-0-9" style="fill:none;stroke:#000000;stroke-width:1.19000506;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
<g transform="translate(-120.71403,-1067.1752)" id="g3138" style="display:inline">
<path d="m 144.85461,1081.6667 -4.46467,0 -3.88859,-3.8125 -3.85772,3.7822 -4.51611,0 8.43041,-8.2653 z" id="path3956-37" style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.89009798px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path d="m 145.06641,1091.87 -4.46467,0 -3.88858,-3.8125 -3.85772,3.7822 -4.51611,0 8.43041,-8.2653 z" id="path3956-3-0" style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.89009798px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><metadata id="metadata10"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs8" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219">
<path d="m -14.05,1077.797 c -9.455,0 -17.192,7.323 -17.192,16.275 0,8.951 7.736,16.275 17.192,16.275 4.727,0 9.112,-1.749 12.206,-4.719 l -3.095,-2.93 c -2.32,2.197 -5.545,3.58 -9.155,3.58 -7.135,0 -12.894,-5.452 -12.894,-12.206 0,-6.754 5.759,-12.206 12.894,-12.206 3.567,0 6.662,1.465 8.982,3.702 l -4.684,4.435 H 3.1 v -12.206 l -5.114,4.841 c -3.095,-2.93 -7.349,-4.841 -12.077,-4.841 h 0.041 z" id="path3441" style="fill:#002ad8" />
<path d="m -17.177,1085.626 c 2.553,-0.958 5.429,0.219 6.441,2.636 l -3.075,1.154 c -0.341,-0.815 -1.286,-1.202 -2.147,-0.879 -0.861,0.323 -1.27,1.218 -0.928,2.032 0.341,0.815 5.075,3.621 6.122,6.125 1.049,2.504 -0.232,5.14 -2.785,6.098 -2.553,0.958 -5.43,-0.219 -6.441,-2.637 l 3.075,-1.153 c 0.341,0.815 1.286,1.202 2.147,0.879 0.861,-0.323 1.27,-1.218 0.928,-2.032 -0.439,-1.048 -5.318,-4.204 -6.122,-6.125 -0.987,-2.359 0.232,-5.14 2.785,-6.098 l 0,0 z" id="path3452" style="fill:#002ad8" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<path d="m -14.0496,1077.7967 c -9.455526,0 -17.191866,7.3233 -17.191866,16.2746 0,8.9506 7.73634,16.2746 17.191866,16.2746 4.7277622,0 9.1116881,-1.7493 12.2062302,-4.7194 l -3.0945421,-2.9297 c -2.3209027,2.1969 -5.5443771,3.5805 -9.1546711,3.5805 -7.134623,0 -12.893898,-5.4523 -12.893898,-12.206 0,-6.7545 5.759275,-12.206 12.893898,-12.206 3.567319,0 6.6618533,1.4649 8.9827563,3.7023 l -4.6847867,4.4351 H 3.0992902 v -12.206 l -5.1145825,4.8414 c -3.0945344,-2.9297 -7.3495203,-4.8414 -12.0772907,-4.8414 z" id="path3441" />
<path d="m -17.17734,1085.626 c 2.552571,-0.9574 5.429568,0.2195 6.44112,2.6363 l -3.075396,1.1539 c -0.341196,-0.8151 -1.285932,-1.2018 -2.147034,-0.8789 -0.86111,0.3231 -1.269543,1.2173 -0.928348,2.0325 0.341197,0.8151 5.074068,3.6209 6.122113,6.1244 1.0481409,2.5039 -0.232373,5.1401 -2.784956,6.0978 -2.552568,0.9575 -5.429564,-0.2194 -6.441212,-2.6365 l 3.075393,-1.1538 c 0.341197,0.815 1.28603,1.2019 2.147128,0.879 0.861114,-0.323 1.269453,-1.2176 0.928257,-2.0327 -0.438699,-1.0479 -5.317867,-4.2032 -6.122117,-6.1243 -0.987208,-2.3585 0.232468,-5.1399 2.785052,-6.0977 z" id="path3452" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><metadata id="metadata10"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs8" />
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219">
<path d="m -14.747,1077.753 v 15.251 h 4.027 v -15.251 h -4.027 z" id="path3397" style="fill:#720000" />
<path d="m -28.493,1098.125 c 1.281,7.768 9.042,13.131 17.248,11.919 8.205,-1.212 13.871,-8.56 12.59,-16.328 -0.64,-3.884 -2.837,-7.249 -5.98,-9.389 l -2.266,2.939 c 2.328,1.609 4.033,4.07 4.522,7.036 0.966,5.861 -3.252,11.331 -9.443,12.245 -6.192,0.915 -11.97,-3.079 -12.936,-8.939 -0.483,-2.931 0.441,-5.671 2.178,-7.881 l 4.7,3.248 -1.746,-10.593 -11.19,1.653 5.131,3.546 c -2.267,2.939 -3.443,6.694 -2.803,10.578 l -0.005,-0.034 z" id="path3408" style="fill:#720000" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<path d="m -14.746549,1077.7527 0,15.2513 4.027731,0 0,-15.2513 z" id="path3397" />
<path d="m -28.493102,1098.125 c 1.280572,7.768 9.04186,13.1318 17.247791,11.9196 8.205389,-1.2122 13.8717377,-8.5597 12.5911658,-16.3278 -0.64028535,-3.8839 -2.8375946,-7.2486 -5.9796213,-9.3887 l -2.2664742,2.9392 c 2.3282028,1.609 4.0331007,4.0698 4.5220467,7.036 0.9662505,5.8611 -3.2519743,11.331 -9.443375,12.2456 -6.192048,0.9148 -11.969592,-3.0784 -12.935843,-8.9396 -0.483125,-2.9307 0.440618,-5.6713 2.177425,-7.881 l 4.700237,3.248 -1.746236,-10.5928 -11.189608,1.653 5.130941,3.5462 c -2.266583,2.939 -3.442915,6.6935 -2.802629,10.5777 z" id="path3408" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<g transform="matrix(0.63466286,0,0,0.60080491,-55.333181,1055.0231)" id="g3488">
<path d="M 60.353163,39.726048 57.225223,49.908485 52.034177,66.945769 51.83452,66.546458 51.235553,64.283694 H 38.723799 v 6.655188 h 7.720019 l 2.528972,7.720019 3.127938,9.783127 3.127939,-9.982783 5.191046,-16.637971 5.191049,16.637971 2.728627,8.917954 3.52725,-8.518642 3.926561,-9.783127 0.865176,1.863452 H 92.031861 V 64.283694 H 80.784593 L 78.25562,59.292302 74.928027,52.836768 72.199398,59.691615 69.07146,67.611288 63.481101,49.908485 60.353163,39.726048 z" id="path3506" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3 0c-1.1 0-2 .9-2 2v1h-1v4h6v-4h-1v-1c0-1.1-.9-2-2-2zm0 1c.56 0 1 .44 1 1v1h-2v-1c0-.56.44-1 1-1z" transform="translate(1)" />
</svg>

After

Width:  |  Height:  |  Size: 225 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
<path d="M3 0c-1.1 0-2 .9-2 2h1c0-.56.44-1 1-1s1 .44 1 1v2h-4v4h6v-4h-1v-2c0-1.1-.9-2-2-2z" transform="translate(1)" />
</svg>

After

Width:  |  Height:  |  Size: 208 B

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.5756397,0,0,1.6644338,54.170963,-1788.6456)" id="g3219" style="display:inline">
<rect width="35.110096" height="17.12899" ry="5.0985756" x="-31.308317" y="1079.7126" id="rect3980-2-6" style="fill:#757575;fill-opacity:1;stroke:none" />
<rect width="35.110096" height="17.008244" ry="5.062634" x="-31.51074" y="1079.5045" id="rect3980-9" style="fill:none;stroke:#000100;stroke-width:1.23500371;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g transform="translate(-50.80414,53.000428)" id="g4236-1">
<path d="m -4.4642859,77.678574 a 4.2857141,4.2857141 0 1 1 -8.5714281,0 4.2857141,4.2857141 0 1 1 8.5714281,0 z" transform="matrix(1.13953,0,0,1.13953,39.306585,945.21999)" id="path3984-8-3" style="fill:#0c0000;fill-opacity:1;stroke:#000100;stroke-width:0.54189169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path d="m 30.368507,1028.4151 -2.629324,6.5608 6.79834,-2.7712 z" id="path4007-7" style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path d="m 34.753087,1030.3543 c 0,1.2916 -1.047136,2.3385 -2.338841,2.3385 -1.291706,0 -2.338842,-1.0469 -2.338842,-2.3385 0,-1.2916 1.047136,-2.3386 2.338842,-2.3386 1.291705,0 2.338841,1.047 2.338841,2.3386 z" id="path4004-0" style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.61750185;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
<g transform="translate(-35.406682,53.115808)" id="g4236-8-9">
<path d="m -4.4642859,77.678574 a 4.2857141,4.2857141 0 1 1 -8.5714281,0 4.2857141,4.2857141 0 1 1 8.5714281,0 z" transform="matrix(1.13953,0,0,1.13953,39.306585,945.21999)" id="path3984-8-7-6" style="fill:#0c0000;fill-opacity:1;stroke:#000100;stroke-width:0.54189169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path d="m 30.368507,1028.4151 -2.629324,6.5608 6.79834,-2.7712 z" id="path4007-0-6" style="fill:#ffffff;fill-opacity:1;stroke:none" />
<path d="m 34.753087,1030.3543 c 0,1.2916 -1.047136,2.3385 -2.338841,2.3385 -1.291706,0 -2.338842,-1.0469 -2.338842,-2.3385 0,-1.2916 1.047136,-2.3386 2.338842,-2.3386 1.291705,0 2.338841,1.047 2.338841,2.3386 z" id="path4004-6-2" style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.61750185;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
<g transform="matrix(1,0,0,-1,-92.41515,1131.1661)" id="g3138" style="display:inline">
<path d="m 144.85461,1081.6667 -4.46467,0 -3.88859,-3.8125 -3.85772,3.7822 -4.51611,0 8.43041,-8.2653 z" id="path3956-37" style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path d="m 145.06641,1091.87 -4.46467,0 -3.88858,-3.8125 -3.85772,3.7822 -4.51611,0 8.43041,-8.2653 z" id="path3956-3-0" style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" width="64" height="64" viewBox="0 0 64 64" id="svg3254" xml:space="preserve"><metadata id="metadata9"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs id="defs7" />
<g id="layer1">
<g transform="matrix(1.2799322,0,0,1.3520619,50.823596,-1446.5167)" id="g3219">
<path d="m -31.496,1077.031 v 4.123 H 3.353 v -4.123 h -34.849 z m 0,8.247 v 4.124 h 21.78 v -4.124 h -21.78 z m 0,12.371 v 4.124 H 3.353 v -4.124 h -34.849 z m 0,8.247 v 4.124 h 26.137 v -4.124 h -26.137 z" id="path3386" style="fill:#041eb5" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1,018 B

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3254">
<defs id="defs3256" />
<metadata id="metadata3259">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(1.2799322,0,0,1.3520619,50.823596,-1446.5167)" id="g3219" style="display:inline">
<path d="m -31.495574,1077.0304 0,4.1237 34.8489745,0 0,-4.1237 z m 0,8.2474 0,4.1237 21.7806103,0 0,-4.1237 z m 0,12.3711 0,4.1237 34.8489745,0 0,-4.1237 z m 0,8.2474 0,4.1237 26.1367347,0 0,-4.1237 z" id="path3386" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,12 +1,14 @@
set(simulation_SRCS
debugoutput.cpp
radiooutputswidget.cpp
simulatordialog.cpp
simulatorinterface.cpp
simulateduiwidget.cpp
simulateduiwidget9X.cpp
simulateduiwidgetX7.cpp
simulateduiwidgetX9.cpp
simulateduiwidgetX12.cpp
simulatorinterface.cpp
simulatormainwindow.cpp
simulatorstartupdialog.cpp
telemetrysimu.cpp
trainersimu.cpp
@ -16,11 +18,13 @@ set(simulation_SRCS
set(simulation_UIS
debugoutput.ui
radiooutputswidget.ui
simulatordialog.ui
simulateduiwidget9X.ui
simulateduiwidgetX7.ui
simulateduiwidgetX9.ui
simulateduiwidgetX12.ui
simulatormainwindow.ui
simulatorstartupdialog.ui
telemetrysimu.ui
trainersimu.ui
@ -28,10 +32,12 @@ set(simulation_UIS
set(simulation_HDRS
debugoutput.h
radiooutputswidget.h
radiouiaction.h
simulateduiwidget.h
# simulator.h
simulatordialog.h
simulatormainwindow.h
simulatorstartupdialog.h
telemetrysimu.h
trainersimu.h
@ -77,4 +83,4 @@ qt5_wrap_ui(simulation_SRCS ${simulation_UIS})
qt5_wrap_cpp(simulation_SRCS ${simulation_HDRS})
add_library(simulation ${simulation_SRCS} ${simulation_HDRS})
qt5_use_modules(simulation Widgets Xml)
qt5_use_modules(simulation Core Widgets Svg)

View file

@ -21,40 +21,361 @@
#include "debugoutput.h"
#include "ui_debugoutput.h"
DebugOutput::DebugOutput(QWidget * parent):
QDialog(parent),
ui(new Ui::DebugOutput)
#include "appdata.h"
#include <QMessageBox>
#include <QScrollBar>
#include <QDebug>
#define DEBUG_OUTPUT_STATE_VERSION 1
extern AppData g; // ensure what "g" means
DebugOutput * traceCallbackInstance = 0;
const int DebugOutput::m_dataBufferMaxSize = 100; // lines of text (this is not the display buffer)
const int DebugOutput::m_dataPrintFreqDefault = 10; // ms
const quint16 DebugOutput::m_savedViewStateVersion = 1;
void traceCb(const char * text)
{
// divert C callback into simulator instance
if (traceCallbackInstance) {
traceCallbackInstance->traceCallback(text);
}
}
DebugOutput::DebugOutput(QWidget * parent, SimulatorInterface *simulator):
QWidget(parent),
ui(new Ui::DebugOutput),
m_simulator(simulator),
m_tmrDataPrint(new QTimer()),
m_dataBuffer(QByteArray()),
m_radioProfileId(g.sessionId()),
m_dataPrintFreq(m_dataPrintFreqDefault),
m_running(false),
m_filterExclude(true)
{
ui->setupUi(this);
#ifdef __APPLE__
QFont newFont("Courier", 13);
ui->Output->setFont(newFont);
ui->Output->setAttribute(Qt::WA_MacNormalSize);
#endif
#if defined WIN32 || !defined __GNUC__
QFont newFont("Courier", 9);
ui->Output->setFont(newFont);
ui->console->setFont(newFont);
#endif
// TODO : allow selecting multiple filters, but needs to be efficient at output stage
QStringList stockFilters;
stockFilters << "^lua[A-Z].*";
stockFilters << "/(error|warning|-(E|W)-)/i";
stockFilters << "!^(GC Use|(play|load|write|find(True)?)File|convert(To|From)Simu|\\tfound( in map)?:|eeprom |f_[a-z]+\\(|(push|(p|P)op(up)?|chain)? ?Menu( .+ display)?|RamBackup).+$";
foreach (const QString & fltr, stockFilters)
ui->filterText->addItem(fltr, "no_delete");
ui->filterText->setValidator(new DebugOutputFilterValidator(ui->filterText));
ui->filterText->installEventFilter(new DeleteComboBoxItemEventFilter());
ui->actionShowFilterHelp->setIcon(SimulatorIcon("info"));
ui->actionWordWrap->setIcon(SimulatorIcon("word_wrap"));
ui->actionClearScr->setIcon(SimulatorIcon("eraser"));
ui->btnShowFilterHelp->setDefaultAction(ui->actionShowFilterHelp);
ui->btnWordWrap->setDefaultAction(ui->actionWordWrap);
ui->btnClearScr->setDefaultAction(ui->actionClearScr);
restoreState();
ui->bufferSize->setValue(ui->console->maximumBlockCount());
// install simulator TRACE hook
traceCallbackInstance = this;
m_simulator->installTraceHook(traceCb);
m_tmrDataPrint->setInterval(m_dataPrintFreq);
connect(ui->filterText, &QComboBox::currentTextChanged, this, &DebugOutput::onFilterTextChanged);
connect(m_tmrDataPrint, &QTimer::timeout, this, &DebugOutput::processBytesReceived);
}
DebugOutput::~DebugOutput()
{
traceCallbackInstance = 0;
stop();
saveState();
if (m_tmrDataPrint)
delete m_tmrDataPrint;
delete ui;
}
void DebugOutput::traceCallback(const QString & text)
void DebugOutput::start()
{
// ui->Output->appendPlainText(text);
QTextCursor cursor(ui->Output->textCursor());
m_tmrDataPrint->start();
m_running = true;
}
// is the scrollbar at the end?
bool atEnd = (ui->Output->verticalScrollBar()->value() == ui->Output->verticalScrollBar()->maximum());
void DebugOutput::stop()
{
m_tmrDataPrint->stop();
m_running = false;
}
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
cursor.insertText(text);
void DebugOutput::saveState()
{
QStringList filters;
for (int i = 0; i < ui->filterText->count(); ++i) {
if (!ui->filterText->itemText(i).isEmpty() && ui->filterText->itemData(i).toString() != "no_delete")
filters << ui->filterText->itemText(i);
}
g.simuDbgFilters(filters);
if (atEnd) {
ui->Output->verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
QByteArray state;
QDataStream stream(&state, QIODevice::WriteOnly);
stream << m_savedViewStateVersion
<< (qint16)ui->filterText->currentIndex() << (qint32)ui->console->maximumBlockCount()
<< ui->btnFilter->isChecked() << ui->actionWordWrap->isChecked();
SimulatorOptions opts = g.profile[m_radioProfileId].simulatorOptions();
opts.dbgConsoleState = state;
g.profile[m_radioProfileId].simulatorOptions(opts);
}
void DebugOutput::restoreState()
{
quint16 ver = 0;
qint16 fci = -1;
qint32 mbc = 10000;
bool flten = false, wwen = false;
QByteArray state = g.profile[m_radioProfileId].simulatorOptions().dbgConsoleState;
QDataStream stream(state);
stream >> ver;
if (ver && ver <= m_savedViewStateVersion)
stream >> fci >> mbc >> flten >> wwen;
ui->filterText->insertItems(0, g.simuDbgFilters());
ui->filterText->setCurrentIndex(fci);
ui->btnFilter->setChecked(flten);
ui->console->setMaximumBlockCount(mbc);
ui->actionWordWrap->setChecked(wwen);
onFilterTextEdited();
}
void DebugOutput::traceCallback(const char * text)
{
const static QRegExp blank("^[\\r\\n]+$");
bool isBlank;
if (!m_running)
return;
QString line(text);
isBlank = line.contains(blank);
m_mtxDataBuffer.lock();
if (isBlank && m_dataBuffer.size())
m_dataBuffer[m_dataBuffer.size()-1] += line;
else
m_dataBuffer.append(text);
if (m_dataBuffer.size() > m_dataBufferMaxSize) {
m_dataBuffer.removeFirst();
qDebug() << __FILE__ << __LINE__ << "Line buffer overflow! size >" << m_dataBufferMaxSize;
}
m_mtxDataBuffer.unlock();
}
void DebugOutput::processBytesReceived()
{
QString text;
bool fltMatch;
const QTextCursor savedCursor(ui->console->textCursor());
const int sbValue = ui->console->verticalScrollBar()->value();
const bool sbAtBottom = (sbValue == ui->console->verticalScrollBar()->maximum());
m_tmrDataPrint->stop();
while (m_dataBuffer.size() > 1) {
m_mtxDataBuffer.lock();
text = m_dataBuffer.takeFirst();
m_mtxDataBuffer.unlock();
// filter
if (ui->btnFilter->isChecked()) {
fltMatch = text.contains(m_filterRegEx);
if ((m_filterExclude && fltMatch) || (!m_filterExclude && !fltMatch)) {
continue;
}
}
ui->console->moveCursor(QTextCursor::End);
ui->console->textCursor().insertText(text);
if (sbAtBottom) {
ui->console->moveCursor(QTextCursor::End);
ui->console->verticalScrollBar()->setValue(ui->console->verticalScrollBar()->maximum());
}
else {
ui->console->setTextCursor(savedCursor);
ui->console->verticalScrollBar()->setValue(sbValue);
}
QCoreApplication::processEvents();
}
m_tmrDataPrint->start();
}
/*
* UI handlers
*/
void DebugOutput::onFilterTextEdited()
{
const QString fText = ui->filterText->currentText();
if (fText.isEmpty()) {
ui->btnFilter->setChecked(false);
m_filterRegEx = QRegularExpression();
return;
}
m_filterRegEx = makeRegEx(fText, &m_filterExclude);
if (m_filterRegEx.isValid()) {
//ui->btnFilter->setChecked(true);
ui->filterText->setStyleSheet("");
}
else {
ui->btnFilter->setChecked(false);
m_filterRegEx = QRegularExpression();
ui->filterText->setStyleSheet("background-color: rgba(255, 205, 185, 200);");
}
}
void DebugOutput::onFilterTextChanged(const QString &)
{
onFilterTextEdited();
}
void DebugOutput::on_bufferSize_editingFinished()
{
ui->console->setMaximumBlockCount(ui->bufferSize->value());
}
void DebugOutput::on_actionWordWrap_toggled(bool checked)
{
ui->console->setLineWrapMode(checked ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
}
void DebugOutput::on_actionClearScr_triggered()
{
ui->console->clear();
}
void DebugOutput::on_actionShowFilterHelp_triggered()
{
// TODO : find some place better for this.
QString help = \
"<html><head><style>kbd {background-color: ghostwhite; font-size: large; white-space: nowrap;}</style></head><body>"
"<p>The filter supports two syntax types: basic matching with common wildcards and well as full Perl-style (<code>pcre</code>) Regular Expressions.</p>"
"<p>By default a filter will only show lines which match (<b>inclusive</b>). To make an <b>exclusive</b> filter which removes matching lines, "
"prefix the filter expression with a <kbd>!</kbd> (exclamation mark).</p>"
"<p>To use <b>Regular Expressions</b> (RegEx), prefix the filter text with a <kbd>/</kbd> (slash) or <kbd>^</kbd> (up caret). "
"<ul>"
"<li>Put the <kbd>/</kbd> or <kbd>^</kbd> after the exclusive <kbd>!</kbd> indicator if you're using one.</li>"
"<li>By default the match is case-sensitive. To make it insensitive, add the typical <kbd>/i</kbd> (slash i) operator at the end of your RegEx.</li>"
"<li>If you use a caret (^) to denote a RegEx, it will become part of the Reg. Ex. (that is, matches from start of line).</li>"
"<li>If the RegEx is invalid, the filter edit field should show a red border and you will not be able to enable the filter.</li>"
"<li>A useful resource for testing REs (with a full reference) can be found at <a href=\"http://www.regexr.com/\">http://www.regexr.com/</a></li>"
"</ul></p>"
"<p>To use <b>basic matching</b> just type any text. Wildcards <kbd>*</kbd> (asterisk) matches any text and <kbd>?</kbd> (question mark) matches any single character."
"<ul>"
"<li>The match is always case-insensitive.</li>"
"<li>The match always starts from the beginning of a log line. To ignore characters at the start, use a leading <kbd>*</kbd> wildcard.</li>"
"<li>A trailing <kbd>*</kbd> is always implied (that is, matches anything to the end of the log line). To avoid this, use a RegEx.</li>"
"<li>You can match literal wildcard characters by prefixing them with a <kbd>\\</kbd> (backslash) character (eg. \"foo\\*bar\" matches \"foo*bar\").</li>"
"</ul></p>"
"<p>After <b>editing text</b>, press ENTER or TAB key (or click anywhere outside the box) to update the filter.</p>"
"<p>To <b>remove an entry</b> from the filter selector list, first choose it, and while in the line editor press <kbd>Shift-Delete</kbd> (or <kbd>Shift-Backspace</kbd>) key combination. "
"The default filters cannot be removed. Up to 50 filters are stored.</p>"
"</body></html>"
;
QMessageBox * msgbox = new QMessageBox(QMessageBox::NoIcon, tr("Debug Console Filter Help"), help, QMessageBox::Ok, this);
msgbox->exec();
}
// static
QRegularExpression DebugOutput::makeRegEx(const QString & input, bool * isExlusive)
{
QString output(input);
QRegularExpression re;
QRegularExpression::PatternOptions reFlags = QRegularExpression::DontCaptureOption;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
reFlags |= QRegularExpression::OptimizeOnFirstUsageOption;
#endif
if (input.left(1) == "!") {
output.remove(0, 1);
if (isExlusive)
*isExlusive = true;
}
else if (isExlusive) {
*isExlusive = false;
}
// regex?
if (output.left(1) == "/" || output.left(1) == "^") {
if (output.left(1) == "/")
output.remove(0, 1);
// check for case-insensitive flag at end ("/.../i")
if (output.endsWith("/i")) {
output.chop(2);
reFlags |= QRegularExpression::CaseInsensitiveOption;
}
else if (output.endsWith("/")) {
output.chop(1);
}
re.setPattern(output);
}
// no, convert arbitrary string to regex
else {
output.replace(QRegExp("^\\\\/"), "/"); // remove escape before fwd-slash ("\/...")
// escape all special chars except * and ?
output.replace(QRegExp("(\\\\|\\.|\\+|\\^|\\$|\\||\\)|\\(|\\]|\\[|\\}|\\{)"), "\\\\1");
output.replace("\\*", "\x30").replace("\\?", "\x31"); // save escaped wildcard chars
output.replace("*", ".*").replace("?", "."); // convert common wildcards
output.replace("\x30", "\\\\*").replace("\x31", "\\\\?"); // replace escaped wildcard chars
output.prepend("^"); // match from start of line; .append("$"); // match whole line
reFlags |= QRegularExpression::CaseInsensitiveOption;
re.setPattern(output);
}
// TODO : user option?
re.setPatternOptions(reFlags);
return re;
}
/*
* Filter input validator for RegEx syntax.
*/
QValidator::State DebugOutputFilterValidator::validate(QString & input, int &) const
{
QRegularExpression re = DebugOutput::makeRegEx(input);
if (re.isValid())
return QValidator::Acceptable;
else
return QValidator::Intermediate;
}
/*
* Event filter for editable QComboBox to allow deleting items with Shift-Delete/Backspace
*/
bool DeleteComboBoxItemEventFilter::eventFilter(QObject *obj, QEvent *event)
{
QComboBox * cb = dynamic_cast<QComboBox *>(obj);
if (cb && cb->isEditable() && cb->currentIndex() > -1 && cb->currentData().toString() != "no_delete") {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if ((keyEvent->key() == Qt::Key::Key_Delete || keyEvent->key() == Qt::Key::Key_Backspace) && keyEvent->modifiers() == Qt::ShiftModifier) {
cb->removeItem(cb->currentIndex());
cb->setCurrentIndex(-1);
return true;
}
}
}
return QObject::eventFilter(obj, event);
}

View file

@ -21,30 +21,76 @@
#ifndef _DEBUGOUTPUT_H_
#define _DEBUGOUTPUT_H_
#include <QtWidgets>
#include "simulator.h"
#include "simulatorinterface.h"
#include <QMutex>
#include <QTimer>
#include <QValidator>
#include <QWidget>
namespace Ui {
class DebugOutput;
}
class QAbstractButton;
class DebugOutput : public QDialog
using namespace Simulator;
class DebugOutput : public QWidget
{
Q_OBJECT
public:
explicit DebugOutput(QWidget * parent);
explicit DebugOutput(QWidget * parent, SimulatorInterface * simulator);
virtual ~DebugOutput();
void traceCallback(const QString & text);
void start();
void stop();
void traceCallback(const char * text);
static QRegularExpression makeRegEx(const QString & input, bool * isExlusive = NULL);
protected slots:
void saveState();
void restoreState();
void processBytesReceived();
void onFilterTextEdited();
void onFilterTextChanged(const QString &);
void on_bufferSize_editingFinished();
void on_actionWordWrap_toggled(bool checked);
void on_actionClearScr_triggered();
void on_actionShowFilterHelp_triggered();
protected:
private:
Ui::DebugOutput * ui;
SimulatorInterface * m_simulator;
QTimer * m_tmrDataPrint;
QStringList m_dataBuffer;
QMutex m_mtxDataBuffer;
QRegularExpression m_filterRegEx;
private slots:
int m_radioProfileId;
int m_dataPrintFreq;
bool m_running;
bool m_filterExclude;
const static int m_dataBufferMaxSize;
const static int m_dataPrintFreqDefault;
const static quint16 m_savedViewStateVersion;
};
class DebugOutputFilterValidator : public QValidator
{
public:
DebugOutputFilterValidator(QObject *parent = Q_NULLPTR) : QValidator(parent) { }
virtual State validate(QString & input, int &) const;
};
class DeleteComboBoxItemEventFilter : public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event);
};
#endif // _DEBUGOUTPUT_H_

View file

@ -1,15 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebugOutput</class>
<widget class="QDialog" name="DebugOutput">
<widget class="QWidget" name="DebugOutput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>470</height>
<width>555</width>
<height>733</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Debug Output</string>
</property>
@ -18,30 +24,286 @@
<normaloff>:/icon.png</normaloff>:/icon.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>7</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPlainTextEdit" name="Output">
<property name="font">
<font>
<family>Courier New</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>7</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QToolButton" name="btnFilter">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable the filter. If the button won't stay enabled, it is likely there is a syntax error in the Regular Expression entered.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="styleSheet">
<string notr="true">padding: 0.15em</string>
</property>
<property name="text">
<string>Filter:</string>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filterText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter filter text here. Click the help/info button for details about using the filter. &lt;/p&gt;&lt;p&gt;
To &lt;b&gt;remove a remembered entry&lt;/b&gt; from the filter list, first choose it, and then press &lt;code&gt;Shift-Delete&lt;/code&gt; (or &lt;code&gt;Shift-Backspace&lt;/code&gt;) key combination.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<string/>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
<property name="maxCount">
<number>50</number>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertAtTop</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
<property name="minimumContentsLength">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnShowFilterHelp">
<property name="text">
<string notr="true">H</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Buffer:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="bufferSize">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Number of lines to keep in display.</string>
</property>
<property name="suffix">
<string/>
</property>
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>999999</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="btnWordWrap">
<property name="text">
<string notr="true">WW</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnClearScr">
<property name="text">
<string notr="true">C</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QPlainTextEdit" name="console">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Courier New</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="maximumBlockCount">
<number>10000</number>
</property>
</widget>
</item>
</layout>
<action name="actionShowFilterHelp">
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/images/simulator/icons/svg/info.svg</normaloff>:/images/simulator/icons/svg/info.svg</iconset>
</property>
<property name="text">
<string>Filter &amp;Help</string>
</property>
<property name="toolTip">
<string>Show information about using the filter.</string>
</property>
</action>
<action name="actionWordWrap">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/images/simulator/icons/svg/word_wrap.svg</normaloff>:/images/simulator/icons/svg/word_wrap.svg</iconset>
</property>
<property name="text">
<string>Word &amp;Wrap</string>
</property>
<property name="toolTip">
<string>Toggle word wrapping on/off.</string>
</property>
</action>
<action name="actionClearScr">
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/images/simulator/icons/svg/eraser.svg</normaloff>:/images/simulator/icons/svg/eraser.svg</iconset>
</property>
<property name="text">
<string>&amp;Clear</string>
</property>
<property name="toolTip">
<string>Clear the output window of all text.</string>
</property>
</action>
</widget>
<resources>
<include location="../companion.qrc"/>

View file

@ -0,0 +1,365 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "radiooutputswidget.h"
#include "ui_radiooutputswidget.h"
#include "appdata.h"
#include "constants.h"
#include "eeprominterface.h"
#include "radiodata.h"
#include "simulator.h"
#include "simulatorinterface.h"
extern AppData g; // ensure what "g" means
const int RadioOutputsWidget::m_dataUpdateFreqDefault = 10; // ms
const quint16 RadioOutputsWidget::m_savedViewStateVersion = 1;
RadioOutputsWidget::RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget *parent) :
QWidget(parent),
m_simulator(simulator),
m_firmware(firmware),
m_tmrUpdateData(new QTimer),
m_radioProfileId(g.sessionId()),
m_started(false),
ui(new Ui::RadioOutputsWidget)
{
ui->setupUi(this);
restoreState();
m_dataUpdateFreq = m_dataUpdateFreqDefault;
m_tmrUpdateData->setInterval(m_dataUpdateFreq);
connect(m_tmrUpdateData, &QTimer::timeout, this, &RadioOutputsWidget::setValues);
}
RadioOutputsWidget::~RadioOutputsWidget()
{
stop();
saveState();
if (m_tmrUpdateData)
delete m_tmrUpdateData;
delete ui;
}
void RadioOutputsWidget::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void RadioOutputsWidget::showEvent(QShowEvent * event)
{
if (m_started)
m_tmrUpdateData->start();
}
void RadioOutputsWidget::hideEvent(QHideEvent * event)
{
m_tmrUpdateData->stop();
}
void RadioOutputsWidget::start()
{
setupChannelsDisplay();
setupGVarsDisplay();
setupLsDisplay();
m_tmrUpdateData->start();
m_started = true;
}
void RadioOutputsWidget::stop()
{
m_tmrUpdateData->stop();
m_started = false;
}
void RadioOutputsWidget::restart()
{
stop();
start();
}
void RadioOutputsWidget::saveState()
{
QByteArray state;
QDataStream stream(&state, QIODevice::WriteOnly);
stream << m_savedViewStateVersion
<< ui->btnLogiSw->isChecked() << ui->btnGlobalVars->isChecked() << ui->btnChannels->isChecked()
<< ui->splitter->saveState();
SimulatorOptions opts = g.profile[m_radioProfileId].simulatorOptions();
opts.radioOutputsState = state;
g.profile[m_radioProfileId].simulatorOptions(opts);
}
void RadioOutputsWidget::restoreState()
{
quint16 ver = 0;
QByteArray splitterState;
bool ls = true, gv = true, ch = true;
QByteArray state = g.profile[m_radioProfileId].simulatorOptions().radioOutputsState;
QDataStream stream(state);
stream >> ver;
if (ver && ver <= m_savedViewStateVersion)
stream >> ls >> gv >> ch >> splitterState;
ui->btnLogiSw->setChecked(ls);
ui->btnGlobalVars->setChecked(gv);
ui->btnChannels->setChecked(ch);
if (!splitterState.isEmpty())
ui->splitter->restoreState(splitterState);
}
void RadioOutputsWidget::setupChannelsDisplay()
{
int outputs = std::min(32, m_firmware->getCapability(Capability(Outputs)));
// delete old widgets if already exist
m_channelsMap.clear();
QWidget * oldChanW = ui->channelsScroll->takeWidget();
if (oldChanW)
oldChanW->deleteLater();
if (!outputs)
return;
QWidget * channelsWidget = new QWidget();
QGridLayout * channelsLayout = new QGridLayout(channelsWidget);
channelsLayout->setHorizontalSpacing(4);
channelsLayout->setVerticalSpacing(3);
channelsLayout->setContentsMargins(5, 5, 5, 5);
ui->channelsScroll->setWidget(channelsWidget);
// populate outputs
int column = 0;
for (int i=0; i < outputs; i++) {
QLabel * label = new QLabel(channelsWidget);
label->setText(" " + RawSource(SOURCE_TYPE_CH, i).toString() + " ");
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelsLayout->addWidget(label, 0, column, 1, 1);
QSlider * slider = new QSlider(channelsWidget);
slider->setEnabled(false);
slider->setMinimum(-1024);
slider->setMaximum(1024);
slider->setPageStep(128);
slider->setTracking(false);
slider->setOrientation(Qt::Vertical);
slider->setInvertedAppearance(false);
slider->setTickPosition(QSlider::TicksRight);
slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
channelsLayout->addWidget(slider, 2, column, 1, 1);
channelsLayout->setAlignment(slider, Qt::AlignHCenter);
QLabel * value = new QLabel(channelsWidget);
value->setMinimumSize(QSize(value->fontMetrics().size(Qt::TextSingleLine, "-100.0").width(), 0));
value->setAlignment(Qt::AlignCenter);
value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelsLayout->addWidget(value, 1, column, 1, 1);
++column;
m_channelsMap.insert(i, QPair<QLabel *, QSlider *>(value, slider));
}
}
void RadioOutputsWidget::setupGVarsDisplay()
{
int gvars = m_firmware->getCapability(Capability(Gvars));
int fmodes = m_firmware->getCapability(Capability(FlightModes));
// delete old widgets if already exist
m_globalVarsMap.clear();
QWidget * oldGv = ui->globalVarsScroll->takeWidget();
if (oldGv)
oldGv->deleteLater();
if (!gvars)
return;
QWidget * gvarsWidget = new QWidget();
QGridLayout * gvarsLayout = new QGridLayout(gvarsWidget);
gvarsLayout->setContentsMargins(5, 5, 5, 5);
gvarsLayout->setHorizontalSpacing(6);
gvarsLayout->setVerticalSpacing(3);
ui->globalVarsScroll->setWidget(gvarsWidget);
QPalette::ColorRole bgrole = QPalette::AlternateBase;
for (int fm=0; fm < fmodes; fm++) {
QLabel * label = new QLabel(gvarsWidget);
label->setText(QString("FM%1").arg(fm));
label->setAlignment(Qt::AlignCenter);
label->setBackgroundRole(bgrole);
gvarsLayout->addWidget(label, 0, fm+1);
}
QHash<int, QLabel *> fmMap;
for (int gv=0; gv < gvars; gv++) {
bgrole = ((gv % 2) ? QPalette::Background : QPalette::AlternateBase);
QLabel * label = new QLabel(gvarsWidget);
label->setText(QString("GV%1").arg(gv+1));
label->setAutoFillBackground(true);
label->setBackgroundRole(bgrole);
gvarsLayout->addWidget(label, gv+1, 0);
for (int fm=0; fm < fmodes; fm++) {
QLabel * value = new QLabel(gvarsWidget);
value->setAutoFillBackground(true);
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
value->setBackgroundRole(bgrole);
value->setText("0");
value->setStyleSheet("padding-right: .06em;");
gvarsLayout->addWidget(value, gv+1, fm+1);
fmMap.insert(fm, value);
}
m_globalVarsMap.insert(gv, fmMap);
}
}
void RadioOutputsWidget::setupLsDisplay()
{
int switches = m_firmware->getCapability(LogicalSwitches);
// delete old widgets if already exist
m_logicSwitchMap.clear();
QWidget * oldLsW = ui->logicalSwitchesScroll->takeWidget();
if (oldLsW)
oldLsW->deleteLater();
if (!switches)
return;
QWidget * logicalSwitches = new QWidget();
logicalSwitches->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
QGridLayout * logicalSwitchesLayout = new QGridLayout(logicalSwitches);
logicalSwitchesLayout->setHorizontalSpacing(3);
logicalSwitchesLayout->setVerticalSpacing(2);
logicalSwitchesLayout->setContentsMargins(5, 5, 5, 5);
ui->logicalSwitchesScroll->setWidget(logicalSwitches);
// populate logical switches
int rows = switches / (switches > 16 ? 4 : 2);
for (int i=0; i < switches; i++) {
QLabel * lsLbl = new QLabel;
logicalSwitchesLayout->addWidget(createLogicalSwitch(logicalSwitches, i, lsLbl), i / rows, i % rows, 1, 1);
m_logicSwitchMap.insert(i, lsLbl);
}
}
QWidget * RadioOutputsWidget::createLogicalSwitch(QWidget * parent, int switchNo, QLabel * label)
{
QFrame * swtch = new QFrame(parent);
swtch->setAutoFillBackground(true);
swtch->setFrameShape(QFrame::Panel);
swtch->setFrameShadow(QFrame::Raised);
swtch->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
swtch->setMaximumHeight(18);
QVBoxLayout * layout = new QVBoxLayout(swtch);
layout->setContentsMargins(2, 0, 2, 0);
if (label) {
QFont font;
font.setPointSize(8);
label->setParent(swtch);
label->setFont(font);
label->setText(RawSwitch(SWITCH_TYPE_VIRTUAL, switchNo+1).toString());
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
label->setAutoFillBackground(true);
layout->addWidget(label);
}
return swtch;
}
// Read various values from firmware simulator and populate values in this UI
void RadioOutputsWidget::setValues()
{
static int lastPhase = 0;
static TxOutputs prevOutputs = TxOutputs();
int currentPhase;
TxOutputs outputs;
m_simulator->getValues(outputs);
currentPhase = m_simulator->getPhase();
if (ui->channelsWidget->isVisible()) {
QHash<int, QPair<QLabel *, QSlider *> >::const_iterator ch;
for (ch = m_channelsMap.constBegin(); ch != m_channelsMap.constEnd(); ++ch) {
if (ch.key() >= CPN_MAX_CHNOUT)
continue;
ch.value().first->setText(QString("%1%").arg(outputs.chans[ch.key()] * 100 / 1024));
ch.value().second->setValue(qMin(1024, qMax(-1024, outputs.chans[ch.key()])));
}
}
if (ui->logicalSwitchesWidget->isVisible()) {
QHash<int, QLabel* >::const_iterator ls;
for (ls = m_logicSwitchMap.constBegin(); ls != m_logicSwitchMap.constEnd(); ++ls) {
if (ls.key() >= CPN_MAX_CSW || prevOutputs.vsw[ls.key()] == outputs.vsw[ls.key()])
continue;
ls.value()->setBackgroundRole(outputs.vsw[ls.key()] ? QPalette::Highlight : QPalette::Background);
prevOutputs.vsw[ls.key()] = outputs.vsw[ls.key()];
}
}
if (ui->globalVarsWidget->isVisible()) {
QFont font;
QPalette::ColorRole bgrole;
QHash<int, QHash<int, QLabel *> >::const_iterator gv;
QHash<int, QLabel *>::const_iterator fm;
for (gv = m_globalVarsMap.constBegin(); gv != m_globalVarsMap.constEnd(); ++gv) {
if (gv.key() >= CPN_MAX_GVARS)
continue;
for (fm = gv.value().constBegin(); fm != gv.value().constEnd(); ++fm) {
if (fm.key() >= CPN_MAX_FLIGHT_MODES)
continue;
if (currentPhase != lastPhase || prevOutputs.gvars[fm.key()][gv.key()] != outputs.gvars[fm.key()][gv.key()]) {
font = fm.value()->font();
bgrole = ((gv.key() % 2) ? QPalette::Background : QPalette::AlternateBase);
if (fm.key() == lastPhase) {
font.setBold(true);
bgrole = QPalette::Highlight;
}
fm.value()->setText(QString::number(outputs.gvars[fm.key()][gv.key()]));
fm.value()->setFont(font);
fm.value()->setBackgroundRole(bgrole);
prevOutputs.gvars[fm.key()][gv.key()] = outputs.gvars[fm.key()][gv.key()];
}
}
}
}
if (currentPhase != lastPhase)
lastPhase = currentPhase;
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef RADIOOUTPUTSWIDGET_H
#define RADIOOUTPUTSWIDGET_H
#include "simulator.h"
#include <QTimer>
#include <QWidget>
namespace Ui {
class RadioOutputsWidget;
}
class Firmware;
class SimulatorInterface;
class QFrame;
class QLabel;
class QSlider;
using namespace Simulator;
class RadioOutputsWidget : public QWidget
{
Q_OBJECT
public:
explicit RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget * parent = 0);
~RadioOutputsWidget();
void start();
void stop();
void restart();
protected slots:
void saveState();
void restoreState();
void setValues();
void showEvent(QShowEvent *event);
void hideEvent(QHideEvent *event);
protected:
void changeEvent(QEvent *e);
void setupChannelsDisplay();
void setupLsDisplay();
void setupGVarsDisplay();
QWidget * createLogicalSwitch(QWidget * parent, int switchNo, QLabel * label);
SimulatorInterface * m_simulator;
Firmware * m_firmware;
QTimer * m_tmrUpdateData;
QHash<int, QPair<QLabel *, QSlider *> > m_channelsMap; // m_channelsMap[chanIndex] = {QLabel*, QSlider*}
QHash<int, QLabel *> m_logicSwitchMap; // m_logicSwitchMap[lsIndex] = QLabel*
QHash<int, QHash<int, QLabel *> > m_globalVarsMap; // m_globalVarsMap[gvarIndex][fmodeIndex] = QLabel*
int m_radioProfileId;
int m_dataUpdateFreq;
bool m_started;
const static int m_dataUpdateFreqDefault;
const static quint16 m_savedViewStateVersion;
private:
Ui::RadioOutputsWidget * ui;
};
#endif // RADIOOUTPUTSWIDGET_H

View file

@ -0,0 +1,440 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RadioOutputsWidget</class>
<widget class="QWidget" name="RadioOutputsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>762</width>
<height>436</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>7</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>View:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnLogiSw">
<property name="text">
<string>Logical Switches</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnGlobalVars">
<property name="text">
<string>Global Variables</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnChannels">
<property name="text">
<string>Channel Outputs</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QWidget" name="logicalSwitchesWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="midLineWidth">
<number>3</number>
</property>
<property name="text">
<string>L
o
g
i
c</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="heading" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="logicalSwitchesScroll">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>740</width>
<height>85</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="globalVarsWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="midLineWidth">
<number>3</number>
</property>
<property name="text">
<string>G
l
o
b
a
l</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="heading" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="globalVarsScroll">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>739</width>
<height>101</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="channelsWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="midLineWidth">
<number>3</number>
</property>
<property name="text">
<string>C
h
a
n
n
e
l
s</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="heading" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="channelsScroll">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>739</width>
<height>194</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>btnChannels</sender>
<signal>toggled(bool)</signal>
<receiver>channelsWidget</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>236</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>346</x>
<y>435</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnGlobalVars</sender>
<signal>toggled(bool)</signal>
<receiver>globalVarsWidget</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>157</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>346</x>
<y>275</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnLogiSw</sender>
<signal>toggled(bool)</signal>
<receiver>logicalSwitchesWidget</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>62</x>
<y>19</y>
</hint>
<hint type="destinationlabel">
<x>346</x>
<y>114</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -127,8 +127,10 @@ class RadioUiAction : public QObject
{
if (toggle(active)) {
emit triggered(m_hwIndex, active);
if (active)
if (active) {
emit pushed(m_hwIndex);
emit pushed();
}
}
}
@ -146,6 +148,7 @@ class RadioUiAction : public QObject
void toggled(int index, bool active); // on programmatic or user interaction change
void triggered(int index, bool active); // on user interaction change only
void pushed(int index); // only emitted on user interaction && when 'active' is true
void pushed();
};
#endif // RADIOUIACTION_H

View file

@ -40,7 +40,7 @@ SimulatedUIWidget::SimulatedUIWidget(SimulatorInterface * simulator, SimulatorDi
{
m_rotEncClickAction = addRadioUiAction(-1, 0, tr("Rotary encoder click"));
m_screenshotAction = addRadioUiAction(-1, Qt::Key_Print, tr("Take Screenshot"));
connect(m_screenshotAction, &RadioUiAction::pushed, this, &SimulatedUIWidget::saveScreenshot);
connect(m_screenshotAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), this, &SimulatedUIWidget::captureScreenshot);
}
SimulatedUIWidget::~SimulatedUIWidget()
@ -114,9 +114,8 @@ void SimulatedUIWidget::updateUi()
} */
}
void SimulatedUIWidget::saveScreenshot(int idx)
void SimulatedUIWidget::captureScreenshot()
{
Q_UNUSED(idx)
QString fileName = "";
if (!g.snapToClpbrd()) {
QString path = g.snapshotDir();
@ -124,7 +123,8 @@ void SimulatedUIWidget::saveScreenshot(int idx)
path = "./";
QDir dir(path);
if (!dir.exists() || !dir.isReadable()) {
m_simuDialog->traceCallback("SIMULATOR ERROR - Cannot open screenshot folder, check your settings.\n");
// m_simulator->traceCallback("SIMULATOR ERROR - Cannot open screenshot folder, check your settings.\n");
qDebug() << "SIMULATOR ERROR - Cannot open screenshot folder, check your settings.";
return;
}
fileName += QString("%1/screenshot_%2.png").arg(dir.absolutePath(), QDateTime::currentDateTime().toString("yy-MM-dd_HH-mm-ss"));
@ -180,12 +180,12 @@ void SimulatedUIWidget::setLcd(LcdWidget * lcd)
void SimulatedUIWidget::connectScrollActions()
{
connect(m_scrollUpAction, &RadioUiAction::pushed, [this](void) {
connect(m_scrollUpAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) {
this->simulatorWheelEvent(-1);
m_scrollUpAction->toggle(false);
});
connect(m_scrollDnAction, &RadioUiAction::pushed, [this](void) {
connect(m_scrollDnAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) {
simulatorWheelEvent(1);
m_scrollDnAction->toggle(false);
});

View file

@ -64,8 +64,7 @@ class SimulatedUIWidget : public QWidget
public slots:
void updateUi();
void saveScreenshot(int idx = -1);
void captureScreenshot() { saveScreenshot(); }
void captureScreenshot();
void simulatorWheelEvent(qint8 steps);
void wheelEvent(QWheelEvent *event);

View file

@ -24,16 +24,85 @@
#include <QColor>
#include <QDataStream>
#include <QDebug>
#include <QFile>
#include <QIcon>
#include <QPair>
#include <QString>
#define SIMULATOR_OPTIONS_VERSION 1
#define SIMULATOR_FLAGS_NOTX 0x01 // simulating a single model from Companion
#define SIMULATOR_FLAGS_STANDALONE 0x02 // started from stanalone simulator
namespace Simulator {
#define SIMULATOR_OPTIONS_VERSION 2
typedef QPair<QString, QString> keymapHelp_t;
namespace Simulator
{
}
typedef QPair<QString, QString> keymapHelp_t;
class SimulatorStyle
{
public:
SimulatorStyle() { }
static QString const basePath() { return ":/themes/default"; }
static QString const styleSheet()
{
QString css;
QFile fh(QString("%1/style.css").arg(basePath()));
if (fh.open(QFile::ReadOnly | QFile::Text)) {
css = fh.readAll();
fh.close();
}
#ifdef __APPLE__
fh.setFileName(QString("%1/style-osx.css").arg(basePath()));
if (fh.open(QFile::ReadOnly | QFile::Text)) {
css.append(fh.readAll());
fh.close();
}
#endif
return css;
}
}; // SimulatorStyle
class SimulatorIcon : public QIcon
{
public:
SimulatorIcon(const QString & baseImage)
{
QString baseFile = QString("%1/%2").arg(basePath(), baseImage);
addFile(QString("%1.svg").arg(baseFile));
QString addfile = QString("%1-on.svg").arg(baseFile);
if (QFile(addfile).exists())
addFile(addfile, QSize(), QIcon::Normal, QIcon::On);
addfile = QString("%1-active.svg").arg(baseFile);
if (QFile(addfile).exists())
addFile(addfile, QSize(), QIcon::Active, QIcon::Off);
addfile = QString("%1-disabled.svg").arg(baseFile);
if (QFile(addfile).exists())
addFile(addfile, QSize(), QIcon::Disabled, QIcon::Off);
}
static QString const basePath() { return ":/images/simulator/icons/svg"; }
static QSize const toolbarIconSize(int setting)
{
switch(setting) {
case 0:
return QSize(16, 16);
case 2:
return QSize(32, 32);
case 3:
return QSize(48, 48);
case 1:
default:
return QSize(24, 24);
}
}
}; // class SimulatorIcon
} // namespace Simulator
struct SimulatorOptions
{
@ -54,14 +123,18 @@ struct SimulatorOptions
QString dataFile;
QString dataFolder;
QString sdPath;
QByteArray windowGeometry;
QByteArray windowGeometry; // SimulatorMainWindow geometry
QList<QByteArray> controlsState; // saved switch/pot/stick settings
QColor lcdColor;
// added in v2
QByteArray windowState; // SimulatorMainWindow dock/toolbar/options UI state
QByteArray dbgConsoleState; // DebugOutput UI state
QByteArray radioOutputsState; // RadioOutputsWidget UI state
friend QDataStream & operator << (QDataStream &out, const SimulatorOptions & o)
{
out << quint16(SIMULATOR_OPTIONS_VERSION) << o.startupDataType << o.firmwareId << o.dataFile << o.dataFolder
<< o.sdPath << o.windowGeometry << o.controlsState << o.lcdColor;
<< o.sdPath << o.windowGeometry << o.controlsState << o.lcdColor << o.windowState << o.dbgConsoleState << o.radioOutputsState;
return out;
}
@ -70,6 +143,8 @@ struct SimulatorOptions
if (o._version <= SIMULATOR_OPTIONS_VERSION) {
in >> o._version >> o.startupDataType >> o.firmwareId >> o.dataFile >> o.dataFolder
>> o.sdPath >> o.windowGeometry >> o.controlsState >> o.lcdColor;
if (o._version >= 2)
in >> o.windowState >> o.dbgConsoleState >> o.radioOutputsState;
}
return in;
}

View file

@ -22,7 +22,6 @@
#include "ui_simulatordialog.h"
#include "appdata.h"
#include "debugoutput.h"
#include "radiofaderwidget.h"
#include "radioknobwidget.h"
#include "radioswitchwidget.h"
@ -31,8 +30,6 @@
#include "simulateduiwidget.h"
#include "simulatorinterface.h"
#include "storage.h"
#include "telemetrysimu.h"
#include "trainersimu.h"
#include "virtualjoystickwidget.h"
#ifdef JOYSTICKS
#include "joystick.h"
@ -43,18 +40,8 @@
#include <QFile>
#include <iostream>
SimulatorDialog * traceCallbackInstance = 0;
void traceCb(const char * text)
{
// divert C callback into simulator instance
if (traceCallbackInstance) {
traceCallbackInstance->traceCallback(text);
}
}
SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator, quint8 flags):
QDialog(parent),
QWidget(parent),
ui(new Ui::SimulatorDialog),
simulator(simulator),
firmware(getCurrentFirmware()),
@ -63,12 +50,8 @@ SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator
radioUiWidget(NULL),
vJoyLeft(NULL),
vJoyRight(NULL),
TelemetrySimu(NULL),
TrainerSimu(NULL),
DebugOut(NULL),
m_board(getCurrentBoard()),
flags(flags),
radioProfileId(g.id()),
lastPhase(-1),
buttonPressed(0),
trimPressed(TRIM_NONE),
@ -78,40 +61,74 @@ SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator
middleButtonPressed(false),
firstShow(true)
{
setWindowFlags(Qt::Window);
// install simulator TRACE hook
traceCallbackInstance = this;
simulator->installTraceHook(traceCb);
#ifdef JOYSTICKS
joystick = NULL;
#endif
// defaults
setRadioProfileId(radioProfileId);
setRadioProfileId(g.sessionId());
setSdPath(g.profile[radioProfileId].sdPath());
setupUi();
ui->setupUi(this);
windowName = tr("Radio Simulator (%1)").arg(firmware->getName());
setWindowTitle(windowName);
switch(m_board) {
case Board::BOARD_TARANIS_X7 :
radioUiWidget = new SimulatedUIWidgetX7(simulator, this);
break;
case Board::BOARD_TARANIS_X9D :
case Board::BOARD_TARANIS_X9DP :
case Board::BOARD_TARANIS_X9E :
radioUiWidget = new SimulatedUIWidgetX9(simulator, this);
break;
case Board::BOARD_X12S:
case Board::BOARD_X10:
radioUiWidget = new SimulatedUIWidgetX12(simulator, this);
break;
default:
radioUiWidget = new SimulatedUIWidget9X(simulator, this);
break;
}
foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp())
keymapHelp.append(item);
ui->radioUiWidget->layout()->removeItem(ui->radioUiTempSpacer);
ui->radioUiWidget->layout()->addWidget(radioUiWidget);
vJoyLeft = new VirtualJoystickWidget(this, 'L');
ui->leftStickLayout->addWidget(vJoyLeft);
vJoyRight = new VirtualJoystickWidget(this, 'R');
ui->rightStickLayout->addWidget(vJoyRight);
connect(vJoyLeft, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyLeft, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyLeft, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
connect(vJoyRight, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyRight, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyRight, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
}
SimulatorDialog::~SimulatorDialog()
{
traceCallbackInstance = 0;
shutdown();
if (timer) {
timer->stop();
if (timer)
timer->deleteLater();
}
if (radioUiWidget)
radioUiWidget->deleteLater();
delete radioUiWidget;
if (vJoyLeft)
vJoyLeft->deleteLater();
delete vJoyLeft;
if (vJoyRight)
vJoyRight->deleteLater();
delete vJoyRight;
#ifdef JOYSTICKS
if (joystick)
joystick->deleteLater();
delete joystick;
#endif
firmware = NULL; // Not sure we should delete this but at least release our pointer.
@ -125,13 +142,6 @@ SimulatorDialog::~SimulatorDialog()
* Public slots/setters
*/
void SimulatorDialog::setRadioProfileId(int value)
{
radioProfileId = value;
if (simulator)
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
}
void SimulatorDialog::setSdPath(const QString & sdPath)
{
setPaths(sdPath, radioDataPath);
@ -355,25 +365,20 @@ void SimulatorDialog::deleteTempData()
void SimulatorDialog::saveState()
{
SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions();
opts.windowGeometry = saveGeometry();
//opts.windowGeometry = saveGeometry();
opts.controlsState = saveRadioWidgetsState();
g.profile[radioProfileId].simulatorOptions(opts);
}
void SimulatorDialog::setUiAreaStyle(const QString & style)
{
ui->radioUiTab->setStyleSheet(style);
ui->radioUiWidget->setStyleSheet(style);
}
void SimulatorDialog::traceCallback(const char * text)
void SimulatorDialog::captureScreenshot(bool)
{
// this function is called from other threads
traceMutex.lock();
// limit the size of list
if (traceList.size() < 1000) {
traceList.append(QString(text));
}
traceMutex.unlock();
if (radioUiWidget)
radioUiWidget->captureScreenshot();
}
/*
@ -384,8 +389,6 @@ void SimulatorDialog::start()
{
setupRadioWidgets();
setupJoysticks();
setupOutputsDisplay();
setupGVarsDisplay();
restoreRadioWidgetsState();
if (startupData.isEmpty())
@ -414,94 +417,30 @@ void SimulatorDialog::restart()
start();
}
void SimulatorDialog::shutdown()
{
stop();
saveState();
if (saveTempRadioData)
saveTempData();
if (deleteTempRadioData)
deleteTempData();
}
/*
* Setup
*/
void SimulatorDialog::setupUi()
void SimulatorDialog::setRadioProfileId(int value)
{
ui->setupUi(this);
windowName = tr("Simulating Radio (%1)").arg(firmware->getName());
setWindowTitle(windowName);
switch(m_board) {
case Board::BOARD_TARANIS_X7:
radioUiWidget = new SimulatedUIWidgetX7(simulator, this);
break;
case Board::BOARD_TARANIS_X9D:
case Board::BOARD_TARANIS_X9DP:
case Board::BOARD_TARANIS_X9E:
radioUiWidget = new SimulatedUIWidgetX9(simulator, this);
break;
case Board::BOARD_X12S:
case Board::BOARD_X10:
radioUiWidget = new SimulatedUIWidgetX12(simulator, this);
break;
default:
radioUiWidget = new SimulatedUIWidget9X(simulator, this);
break;
}
foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp()) {
keymapHelp.append(item);
}
ui->radioUiTab->layout()->addWidget(radioUiWidget);
ui->tabWidget->setTabText(0, firmware->getName());
ui->tabWidget->setFixedHeight(radioUiWidget->height() + ui->tabWidget->tabBar()->height() - 8);
vJoyLeft = new VirtualJoystickWidget(this, 'L');
ui->leftStickLayout->addWidget(vJoyLeft);
vJoyRight = new VirtualJoystickWidget(this, 'R');
ui->rightStickLayout->addWidget(vJoyRight);
ui->tabWidget->setCurrentIndex(flags & SIMULATOR_FLAGS_NOTX);
connect(vJoyLeft, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyLeft, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyLeft, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
connect(vJoyRight, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
connect(vJoyRight, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
connect(vJoyRight, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
connect(ui->btn_help, SIGNAL(released()), this, SLOT(showHelp()));
connect(ui->btn_joystickDialog, SIGNAL(released()), this, SLOT(openJoystickDialog()));
connect(ui->btn_telemSim, SIGNAL(released()), this, SLOT(openTelemetrySimulator()));
connect(ui->btn_trainerSim, SIGNAL(released()), this, SLOT(openTrainerSimulator()));
connect(ui->btn_debugConsole, SIGNAL(released()), this, SLOT(openDebugOutput()));
connect(ui->btn_luaReload, SIGNAL(released()), this, SLOT(luaReload()));
connect(ui->btn_screenshot, SIGNAL(released()), radioUiWidget, SLOT(captureScreenshot()));
connect(ui->btn_reloadSimu, SIGNAL(released()), this, SLOT(restart()));
#ifdef JOYSTICKS
bool showJoystick = true;
#else
bool showJoystick = false;
#endif
// Hide some main UI buttons based on board capabilities, and add keymap help texts.
QString role;
foreach (QPushButton * btn, ui->buttonBox->findChildren<QPushButton *>()) {
role = btn->property("role").toString();
if ((role == "joystick" && !showJoystick) ||
(role == "telemetry" && !firmware->getCapability(Capability(SportTelemetry))) ||
(role == "reloadLua" && !firmware->getCapability(Capability(LuaInputsPerScript))) )
{
btn->hide();
continue;
}
if (!btn->shortcut().isEmpty())
keymapHelp.append(keymapHelp_t(btn->shortcut().toString(QKeySequence::NativeText), btn->statusTip()));
}
radioProfileId = value;
if (simulator)
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
}
void SimulatorDialog::setupRadioWidgets()
{
int i, midpos, aIdx, wval;
int i, midpos, aIdx;
QString wname;
Board::Type board = firmware->getBoard();
@ -535,13 +474,12 @@ void SimulatorDialog::setupRadioWidgets()
continue;
swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
wval = (swcfg == Board::SWITCH_3POS ? -1 : 0);
if ((wname = QString(radioSettings.switchName[i])).isEmpty()) {
switchInfo = getSwitchInfo(board, i);
wname = QString(switchInfo.name);
}
RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, wval, ui->radioWidgetsHT);
RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, -1, ui->radioWidgetsHT);
sw->setIndex(i);
ui->radioWidgetsHTLayout->addWidget(sw);
switches.append(sw);
@ -593,172 +531,6 @@ void SimulatorDialog::setupRadioWidgets()
}
}
void SimulatorDialog::setupOutputsDisplay()
{
// setup Outputs tab
QWidget * outputsWidget;
// delete old widget if already exists
if (ui->tabWidget->count() > 1 && ui->tabWidget->widget(1)->objectName() == "RadioOutputsWidget") {
outputsWidget = ui->tabWidget->widget(1);
ui->tabWidget->removeTab(1);
outputsWidget->deleteLater();
outputsWidget = NULL;
}
channelValues.clear();
channelSliders.clear();
logicalSwitchLabels.clear();
outputsWidget = new QWidget();
outputsWidget->setObjectName("RadioOutputsWidget");
QGridLayout * gridLayout = new QGridLayout(outputsWidget);
gridLayout->setHorizontalSpacing(0);
gridLayout->setVerticalSpacing(3);
gridLayout->setContentsMargins(5, 3, 5, 3);
// logical switches area
QWidget * logicalSwitches = new QWidget(outputsWidget);
logicalSwitches->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
QGridLayout * logicalSwitchesLayout = new QGridLayout(logicalSwitches);
logicalSwitchesLayout->setHorizontalSpacing(3);
logicalSwitchesLayout->setVerticalSpacing(2);
logicalSwitchesLayout->setContentsMargins(0, 0, 0, 0);
gridLayout->addWidget(logicalSwitches, 0, 0, 1, 1);
// channels area
QScrollArea * scrollArea = new QScrollArea(outputsWidget);
QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
sp.setHorizontalStretch(0);
sp.setVerticalStretch(0);
scrollArea->setSizePolicy(sp);
scrollArea->setWidgetResizable(true);
QWidget * channelsWidget = new QWidget();
QGridLayout * channelsLayout = new QGridLayout(channelsWidget);
channelsLayout->setHorizontalSpacing(4);
channelsLayout->setVerticalSpacing(3);
channelsLayout->setContentsMargins(0, 0, 0, 3);
scrollArea->setWidget(channelsWidget);
gridLayout->addWidget(scrollArea, 1, 0, 1, 1);
ui->tabWidget->insertTab(1, outputsWidget, QString(tr("Outputs")));
// populate outputs
int outputs = std::min(32, firmware->getCapability(Outputs));
int column = 0;
for (int i=0; i<outputs; i++) {
QLabel * label = new QLabel(outputsWidget);
label->setText(" " + RawSource(SOURCE_TYPE_CH, i).toString() + " ");
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelsLayout->addWidget(label, 0, column, 1, 1);
QSlider * slider = new QSlider(outputsWidget);
slider->setEnabled(false);
slider->setMinimum(-1024);
slider->setMaximum(1024);
slider->setPageStep(128);
slider->setTracking(false);
slider->setOrientation(Qt::Vertical);
slider->setInvertedAppearance(false);
slider->setTickPosition(QSlider::TicksRight);
slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
QLabel * value = new QLabel(outputsWidget);
value->setMinimumSize(QSize(value->fontMetrics().size(Qt::TextSingleLine, "-100.0").width(), 0));
value->setAlignment(Qt::AlignCenter);
value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
channelValues << value;
channelsLayout->addWidget(value, 1, column, 1, 1);
channelSliders << slider;
channelsLayout->addWidget(slider, 2, column++, 1, 1);
channelsLayout->setAlignment(slider, Qt::AlignHCenter);
}
// populate logical switches
int switches = firmware->getCapability(LogicalSwitches);
int rows = switches / (switches > 16 ? 4 : 2);
for (int i=0; i<switches; i++) {
QFrame * swtch = createLogicalSwitch(outputsWidget, i, logicalSwitchLabels);
logicalSwitchesLayout->addWidget(swtch, i / rows, i % rows, 1, 1);
}
}
void SimulatorDialog::setupGVarsDisplay()
{
// setup GVars tab
int gvars = firmware->getCapability(Gvars);
int fmodes = firmware->getCapability(FlightModes);
QWidget * gvarsWidget;
// delete old widget if already exists
if (ui->tabWidget->count() > 2 && ui->tabWidget->widget(2)->objectName() == "RadioGVOutputsWidget") {
gvarsWidget = ui->tabWidget->widget(2);
ui->tabWidget->removeTab(2);
gvarsWidget->deleteLater();
gvarsWidget = NULL;
}
gvarValues.clear();
if (!gvars)
return;
gvarsWidget = new QWidget();
gvarsWidget->setObjectName("RadioGVOutputsWidget");
QGridLayout * gvarsLayout = new QGridLayout(gvarsWidget);
ui->tabWidget->insertTab(2, gvarsWidget, QString(tr("GVars")));
for (int fm=0; fm<fmodes; fm++) {
QLabel * label = new QLabel(gvarsWidget);
label->setText(QString("FM%1").arg(fm));
label->setAlignment(Qt::AlignCenter);
gvarsLayout->addWidget(label, 0, fm+1);
}
for (int i=0; i<gvars; i++) {
QLabel * label = new QLabel(gvarsWidget);
label->setText(QString("GV%1").arg(i+1));
label->setAutoFillBackground(true);
if ((i % 2) ==0 ) {
label->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
gvarsLayout->addWidget(label, i+1, 0);
for (int fm=0; fm<fmodes; fm++) {
QLabel * value = new QLabel(gvarsWidget);
value->setAutoFillBackground(true);
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
if ((i % 2) ==0 ) {
value->setStyleSheet("QLabel { background-color: rgb(220, 220, 220) }");
}
value->setText("0");
gvarValues << value;
gvarsLayout->addWidget(value, i+1, fm+1);
}
}
}
QFrame * SimulatorDialog::createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels)
{
QFrame * swtch = new QFrame(parent);
swtch->setAutoFillBackground(true);
swtch->setFrameShape(QFrame::Panel);
swtch->setFrameShadow(QFrame::Raised);
swtch->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
swtch->setMaximumHeight(18);
QVBoxLayout * layout = new QVBoxLayout(swtch);
layout->setContentsMargins(2, 0, 2, 0);
QFont font;
font.setPointSize(8);
QLabel * label = new QLabel(swtch);
label->setFont(font);
label->setText(RawSwitch(SWITCH_TYPE_VIRTUAL, switchNo+1).toString());
label->setAlignment(Qt::AlignCenter);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
labels << label;
layout->addWidget(label);
return swtch;
}
void SimulatorDialog::setupJoysticks()
{
#ifdef JOYSTICKS
@ -855,6 +627,16 @@ void SimulatorDialog::restoreRadioWidgetsState()
if (switchesMap.contains(switches[i]->getIndex()))
switches[i]->setStateData(switchesMap.value(switches[i]->getIndex()));
}
// Set throttle stick down and locked, side depends on mode
if (radioSettings.stickMode & 1) {
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickY(1);
}
else {
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickY(1);
}
}
QList<QByteArray> SimulatorDialog::saveRadioWidgetsState()
@ -877,44 +659,8 @@ QList<QByteArray> SimulatorDialog::saveRadioWidgetsState()
// Read various values from firmware simulator and populate values in this UI
void SimulatorDialog::setValues()
{
static const int numGvars = firmware->getCapability(Gvars);
static const int numFlightModes = firmware->getCapability(FlightModes);
static TxOutputs prevOutputs;
TxOutputs outputs;
simulator->getValues(outputs);
int currentPhase = simulator->getPhase();
// Outputs tab visible?
if (ui->tabWidget->currentIndex() == 1) {
for (int i = 0; i < channelSliders.size() && i < CPN_MAX_CHNOUT; i++) {
channelSliders[i]->setValue(qMin(1024, qMax(-1024, outputs.chans[i])));
channelValues[i]->setText(QString("%1").arg((qreal)outputs.chans[i]*100/1024, 0, 'f', 1));
}
for (int i = 0; i < logicalSwitchLabels.size() && i < CPN_MAX_CSW; i++) {
// setStyleSheet() is very CPU time consuming, do it only if the actual switch state changes
if (prevOutputs.vsw[i] != outputs.vsw[i]) {
logicalSwitchLabels[i]->setStyleSheet(outputs.vsw[i] ? CSWITCH_ON : CSWITCH_OFF);
prevOutputs.vsw[i] = outputs.vsw[i];
}
}
}
// GVars tab visible?
if (ui->tabWidget->currentIndex() == 2) {
for (int gv = 0; gv < numGvars; gv++) {
for (int fm = 0; fm < numFlightModes; fm++) {
// same trick for GVARS, but this has far less effect on CPU usage as setStyleSheet()
if (currentPhase != lastPhase || prevOutputs.gvars[fm][gv] != outputs.gvars[fm][gv]) {
gvarValues[gv*numFlightModes+fm]->setText(QString((fm == lastPhase) ? "<b>%1</b>" : "%1").arg(outputs.gvars[fm][gv]));
prevOutputs.gvars[fm][gv] = outputs.gvars[fm][gv];
}
}
}
}
// display current flight mode in window title
if (currentPhase != lastPhase) {
lastPhase = currentPhase;
@ -923,7 +669,6 @@ void SimulatorDialog::setValues()
phase_name = QString::number(currentPhase);
setWindowTitle(windowName + QString(" - Flight Mode %1").arg(phase_name));
}
}
// "get" values from this UI and send them to the firmware simulator.
@ -995,34 +740,16 @@ void SimulatorDialog::setTrims()
* Event handlers/private slots
*/
void SimulatorDialog::closeEvent(QCloseEvent *)
{
stop();
saveState();
if (saveTempRadioData)
saveTempData();
if (deleteTempRadioData)
deleteTempData();
}
//void SimulatorDialog::showEvent(QShowEvent *)
//{
// if (firstShow && isVisible()) {
// firstShow = false;
// }
//}
void SimulatorDialog::showEvent(QShowEvent *)
{
if (firstShow) {
restoreGeometry(g.profile[radioProfileId].simulatorOptions().windowGeometry);
// The stick position needs to be set after the final show event, otherwise resizes during dialog creation will screw it up.
if (radioSettings.stickMode & 1) {
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickY(1);
}
else {
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickY(1);
}
firstShow = false;
}
}
//void SimulatorDialog::closeEvent(QCloseEvent *)
//{
//}
void SimulatorDialog::mousePressEvent(QMouseEvent *event)
{
@ -1059,8 +786,6 @@ void SimulatorDialog::onTimerEvent()
setTrims();
centerSticks();
}
updateDebugOutput();
}
void SimulatorDialog::onTrimPressed(int which)
@ -1087,111 +812,9 @@ void SimulatorDialog::centerSticks()
vJoyRight->centerStick();
}
void SimulatorDialog::openTelemetrySimulator()
{
// allow only one instance
if (TelemetrySimu == 0) {
TelemetrySimu = new TelemetrySimulator(this, simulator);
TelemetrySimu->setAttribute(Qt::WA_DeleteOnClose);
TelemetrySimu->show();
connect(TelemetrySimu, &TelemetrySimulator::destroyed, [this](QObject *) {
this->TelemetrySimu = NULL;
});
}
else if (!TelemetrySimu->isVisible()) {
TelemetrySimu->show();
}
}
void SimulatorDialog::openTrainerSimulator()
{
// allow only one instance
if (TrainerSimu == 0) {
TrainerSimu = new TrainerSimulator(this, simulator);
TrainerSimu->setAttribute(Qt::WA_DeleteOnClose);
TrainerSimu->show();
connect(TrainerSimu, &TrainerSimulator::destroyed, [this](QObject *) {
this->TrainerSimu = NULL;
});
}
else if (!TrainerSimu->isVisible()) {
TrainerSimu->show();
}
}
void SimulatorDialog::openJoystickDialog()
{
#ifdef JOYSTICKS
joystickDialog * jd = new joystickDialog(this);
if (jd->exec() == QDialog::Accepted)
setupJoysticks();
jd->deleteLater();
#endif
}
void SimulatorDialog::openDebugOutput()
{
// allow only one instance, but install signal handler to catch dialog destruction just in case
if (!DebugOut) {
DebugOut = new DebugOutput(this);
DebugOut->traceCallback(traceBuffer);
DebugOut->setAttribute(Qt::WA_DeleteOnClose);
DebugOut->show();
connect(DebugOut, &DebugOutput::destroyed, [this](QObject *) {
this->DebugOut = NULL;
});
}
else if (!DebugOut->isVisible()) {
DebugOut->show();
}
}
void SimulatorDialog::updateDebugOutput()
{
traceMutex.lock();
while (!traceList.isEmpty()) {
QString text = traceList.takeFirst();
traceBuffer.append(text);
// limit the size of traceBuffer
if (traceBuffer.size() > 10*1024) {
traceBuffer.remove(0, 1*1024);
}
if (DebugOut) {
DebugOut->traceCallback(QString(text));
}
}
traceMutex.unlock();
}
void SimulatorDialog::luaReload()
{
// force a reload of the lua environment
simulator->setLuaStateReloadPermanentScripts();
}
void SimulatorDialog::showHelp()
{
QString helpText = tr("Simulator Controls:");
helpText += "<table cellspacing=4 cellpadding=0>";
helpText += tr("<tr><th>Key/Mouse</td><th>Action</td></tr>");
QString keyTemplate = "<tr><td align='center'><pre>%1</pre></td><td align='center'>%2</td></tr>";
foreach (keymapHelp_t pair, keymapHelp)
helpText += keyTemplate.arg(pair.first, pair.second);
helpText += "</table>";
QMessageBox * msgBox = new QMessageBox(this);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
msgBox->setStandardButtons( QMessageBox::Ok );
msgBox->setWindowTitle(tr("Simulator Help"));
msgBox->setText(helpText);
msgBox->setModal(false);
msgBox->show();
}
#ifdef JOYSTICKS
void SimulatorDialog::onjoystickAxisValueChanged(int axis, int value)
{
#ifdef JOYSTICKS
int stick;
if (axis>=0 && axis<=8) {
stick=jsmap[axis];
@ -1225,5 +848,5 @@ void SimulatorDialog::onjoystickAxisValueChanged(int axis, int value)
analogs[stick-5]->setValue(stickval);
}
}
}
#endif
}

View file

@ -26,17 +26,11 @@
#include "radiodata.h"
#include "simulator.h"
#include <QDialog>
#include <QWidget>
#include <QVector>
#include <QMutex>
#define SIMULATOR_FLAGS_NOTX 0x01 // simulating a single model from Companion
#define SIMULATOR_FLAGS_STANDALONE 0x02 // started from stanalone simulator
#define FLASH_DURATION 10
#define CSWITCH_ON "QLabel { background-color: #4CC417 }"
#define CSWITCH_OFF "QLabel { }"
#define CBEEP_ON "QLabel { background-color: #FF364E }"
#define CBEEP_OFF "QLabel { }"
@ -45,9 +39,6 @@ void traceCb(const char * text);
class Firmware;
class SimulatorInterface;
class SimulatedUIWidget;
class TelemetrySimulator;
class TrainerSimulator;
class DebugOutput;
class RadioWidget;
class RadioSwitchWidget;
class VirtualJoystickWidget;
@ -57,7 +48,6 @@ class Joystick;
class QWidget;
class QSlider;
class QDial;
class QLabel;
class QFrame;
@ -67,7 +57,7 @@ namespace Ui {
using namespace Simulator;
class SimulatorDialog : public QDialog
class SimulatorDialog : public QWidget
{
Q_OBJECT
@ -75,7 +65,6 @@ class SimulatorDialog : public QDialog
explicit SimulatorDialog(QWidget * parent, SimulatorInterface *simulator, quint8 flags=0);
virtual ~SimulatorDialog();
void setRadioProfileId(int value);
void setSdPath(const QString & sdPath);
void setDataPath(const QString & dataPath);
void setPaths(const QString & sdPath, const QString & dataPath);
@ -89,23 +78,22 @@ class SimulatorDialog : public QDialog
void deleteTempData();
void saveState();
void setUiAreaStyle(const QString & style);
void traceCallback(const char * text);
void captureScreenshot(bool);
void setupJoysticks();
QString getSdPath() const { return sdCardPath; }
QString getDataPath() const { return radioDataPath; }
QVector<keymapHelp_t> * getKeymapHelp() { return &keymapHelp; }
public slots:
void start();
void stop();
void restart();
void shutdown();
private:
void setupUi();
void setRadioProfileId(int value);
void setupRadioWidgets();
void setupOutputsDisplay();
void setupGVarsDisplay();
void setupJoysticks();
QFrame * createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels);
void setupTimer();
void restoreRadioWidgetsState();
QList<QByteArray> saveRadioWidgetsState();
@ -122,24 +110,14 @@ class SimulatorDialog : public QDialog
QTimer * timer;
QString windowName;
QString traceBuffer;
QMutex traceMutex;
QList<QString> traceList;
QVector<keymapHelp_t> keymapHelp;
SimulatedUIWidget * radioUiWidget;
VirtualJoystickWidget * vJoyLeft;
VirtualJoystickWidget * vJoyRight;
TelemetrySimulator * TelemetrySimu;
TrainerSimulator * TrainerSimu;
DebugOutput * DebugOut;
QVector<RadioSwitchWidget *> switches;
QVector<RadioWidget *> analogs;
QVector<QLabel *> logicalSwitchLabels;
QVector<QSlider *> channelSliders;
QVector<QLabel *> channelValues;
QVector<QLabel *> gvarValues;
QString sdCardPath;
QString radioDataPath;
@ -163,8 +141,8 @@ class SimulatorDialog : public QDialog
#endif
private slots:
virtual void closeEvent(QCloseEvent *);
virtual void showEvent(QShowEvent *);
//virtual void showEvent(QShowEvent *);
//virtual void closeEvent(QCloseEvent *);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);
@ -174,16 +152,7 @@ class SimulatorDialog : public QDialog
void onTrimReleased();
void onTrimSliderMoved(int which, int value);
void centerSticks();
void openTelemetrySimulator();
void openTrainerSimulator();
void openJoystickDialog();
void openDebugOutput();
void updateDebugOutput();
void luaReload();
void showHelp();
#ifdef JOYSTICKS
void onjoystickAxisValueChanged(int axis, int value);
#endif
};

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulatorDialog</class>
<widget class="QDialog" name="SimulatorDialog">
<widget class="QWidget" name="SimulatorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>528</height>
<width>448</width>
<height>302</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -23,29 +23,26 @@
<iconset resource="../companion.qrc">
<normaloff>:/icon.png</normaloff>:/icon.png</iconset>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>3</number>
<property name="leftMargin">
<number>0</number>
</property>
<item row="1" column="0">
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="controlFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -54,7 +51,7 @@
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
@ -62,414 +59,248 @@
<property name="lineWidth">
<number>2</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout" rowstretch="1,4">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>7</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>6</number>
<number>7</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
<number>0</number>
</property>
<property name="verticalSpacing">
<number>5</number>
<number>10</number>
</property>
<item row="1" column="0">
<widget class="QWidget" name="leftStickWidget" native="true">
<item row="0" column="0" colspan="2" alignment="Qt::AlignBottom">
<widget class="QFrame" name="radioWidgetsHT">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="leftStickLayout">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QWidget" name="rightStickWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="rightStickLayout">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="radioWidgetsVC" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="4,1">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="VCGridLayout">
<property name="horizontalSpacing">
<number>16</number>
</property>
<property name="verticalSpacing">
<number>9</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QWidget" name="radioWidgetsHT" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="midLineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="radioWidgetsHTLayout">
<property name="spacing">
<number>11</number>
<number>10</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
<number>9</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
<number>9</number>
</property>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="controlsLower">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0" rowspan="2">
<widget class="QWidget" name="leftStickWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="leftStickLayout">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QWidget" name="radioWidgetsVC" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="3,1">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="VCGridLayout">
<property name="horizontalSpacing">
<number>16</number>
</property>
<property name="verticalSpacing">
<number>9</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<widget class="QWidget" name="rightStickWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="rightStickLayout">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
<zorder>leftStickWidget</zorder>
<zorder>rightStickWidget</zorder>
<zorder>radioWidgetsVC</zorder>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="enabled">
<bool>true</bool>
</property>
<item alignment="Qt::AlignBottom">
<widget class="QWidget" name="radioUiWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QTabWidget::pane,
QStackedWidget {
padding: 0px;
margin: 0px;
}
QTabWidget::tab-bar{
margin-bottom: 0px;
padding-bottom: 0px;
}</string>
<string notr="true"/>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<widget class="QWidget" name="radioUiTab">
<property name="styleSheet">
<string notr="true">background-color: qlineargradient(spread:reflect, x1:0.22, y1:0, x2:0.55065, y2:0.54, stop:0 rgba(7, 7, 7, 250), stop:0.864407 rgba(66, 66, 66, 255));
margin: 0;
padding: 0;</string>
</property>
<attribute name="title">
<string>Radio Simulator</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
<item row="0" column="0">
<widget class="QWidget" name="buttonBox" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="buttonsLayout">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
<number>0</number>
</property>
<property name="leftMargin">
<number>1</number>
<number>0</number>
</property>
<property name="topMargin">
<number>1</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>1</number>
<number>0</number>
</property>
<property name="bottomMargin">
<number>1</number>
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="btn_help">
<property name="toolTip">
<string>Show Help/Keymap (F1)</string>
<spacer name="radioUiTempSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="statusTip">
<string>Show Help/Keymap</string>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="text">
<string>Keymap</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
<property name="shortcut">
<string>F1</string>
</property>
<property name="role" stdset="0">
<string>help</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_joystickDialog">
<property name="toolTip">
<string>Open the Joystick Settings window (F3)</string>
</property>
<property name="statusTip">
<string>Joystick Configuration</string>
</property>
<property name="text">
<string>Joystick Config.</string>
</property>
<property name="shortcut">
<string>F3</string>
</property>
<property name="role" stdset="0">
<string>joystick</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_telemSim">
<property name="toolTip">
<string>Open Telemetry Simulator window (F4)</string>
</property>
<property name="statusTip">
<string>Telemetry Simulator</string>
</property>
<property name="text">
<string>Telemetry Sim.</string>
</property>
<property name="shortcut">
<string>F4</string>
</property>
<property name="role" stdset="0">
<string>telemetry</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_trainerSim">
<property name="toolTip">
<string>Open Trainer Simulator window (F5)</string>
</property>
<property name="statusTip">
<string>Trainer Simulator</string>
</property>
<property name="text">
<string>Trainer Sim.</string>
</property>
<property name="shortcut">
<string>F5</string>
</property>
<property name="role" stdset="0">
<string>trainer</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_debugConsole">
<property name="toolTip">
<string>Open Debug Console window (F6)</string>
</property>
<property name="statusTip">
<string>Debug Console</string>
</property>
<property name="text">
<string>Debug Console</string>
</property>
<property name="shortcut">
<string>F6</string>
</property>
<property name="role" stdset="0">
<string>debug</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_luaReload">
<property name="toolTip">
<string>Reload Lua Scripts (F7)</string>
</property>
<property name="statusTip">
<string>Reload Lua Scripts</string>
</property>
<property name="text">
<string>Reload Lua</string>
</property>
<property name="shortcut">
<string>F7</string>
</property>
<property name="role" stdset="0">
<string>reloadLua</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_screenshot">
<property name="toolTip">
<string>Save a screenshot of the radio LCD screen (F8)</string>
</property>
<property name="statusTip">
<string>LCD Screenshot</string>
</property>
<property name="text">
<string>Screenshot</string>
</property>
<property name="shortcut">
<string>F8</string>
</property>
<property name="role" stdset="0">
<string>lcdScreenshot</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_reloadSimu">
<property name="toolTip">
<string>Reload all radio data (F9)</string>
</property>
<property name="statusTip">
<string>Reload Radio Data</string>
</property>
<property name="text">
<string>Reload Radio</string>
</property>
<property name="shortcut">
<string>F9</string>
</property>
<property name="role" stdset="0">
<string>reloadradio</string>
</property>
</widget>
</spacer>
</item>
</layout>
</widget>

View file

@ -0,0 +1,350 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include "simulatormainwindow.h"
#include "ui_simulatormainwindow.h"
#include "appdata.h"
#include "debugoutput.h"
#include "radiooutputswidget.h"
#include "simulatordialog.h"
#include "simulatorinterface.h"
#include "telemetrysimu.h"
#include "trainersimu.h"
#ifdef JOYSTICKS
#include "joystickdialog.h"
#endif
#include <QDebug>
extern AppData g; // ensure what "g" means
const quint16 SimulatorMainWindow::m_savedUiStateVersion = 1;
SimulatorMainWindow::SimulatorMainWindow(QWidget *parent, SimulatorInterface * simulator, quint8 flags, Qt::WindowFlags wflags) :
QMainWindow(parent, wflags),
m_simulator(simulator),
ui(new Ui::SimulatorMainWindow),
m_simulatorWidget(NULL),
m_consoleWidget(NULL),
m_outputsWidget(NULL),
m_simulatorDockWidget(NULL),
m_consoleDockWidget(NULL),
m_telemetryDockWidget(NULL),
m_trainerDockWidget(NULL),
m_outputsDockWidget(NULL),
m_radioProfileId(g.sessionId()),
m_firstShow(true),
m_showRadioTitlebar(true),
m_showMenubar(true)
{
ui->setupUi(this);
ui->centralwidget->hide();
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
// setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
//#endif
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
m_simulatorWidget = new SimulatorDialog(this, m_simulator, flags);
m_simulatorDockWidget = new QDockWidget(m_simulatorWidget->windowTitle(), this);
m_simulatorDockWidget->setObjectName("RADIO_SIMULATOR");
m_simulatorDockWidget->setWidget(m_simulatorWidget);
m_simulatorDockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
addDockWidget(Qt::BottomDockWidgetArea, m_simulatorDockWidget);
// setCentralWidget(m_simulatorWidget);
createDockWidgets();
ui->actionReloadLua->setIcon(SimulatorIcon("reload_script"));
ui->actionReloadRadioData->setIcon(SimulatorIcon("restart"));
ui->actionJoystickSettings->setIcon(SimulatorIcon("joystick_settings"));
ui->actionScreenshot->setIcon(SimulatorIcon("camera"));
ui->actionShowKeymap->setIcon(SimulatorIcon("info"));
ui->actionToggleRadioTitle->setIcon(ui->toolBar->toggleViewAction()->icon());
ui->actionToggleMenuBar->setIcon(ui->toolBar->toggleViewAction()->icon());
ui->toolBar->toggleViewAction()->setShortcut(tr("Alt+T"));
ui->toolBar->setIconSize(SimulatorIcon::toolbarIconSize(g.iconSize()));
ui->toolBar->insertSeparator(ui->actionReloadLua);
// add these to this window directly to maintain shorcuts when menubar is hidden
addAction(ui->toolBar->toggleViewAction());
addAction(ui->actionToggleMenuBar);
ui->menuView->addSeparator();
ui->menuView->addAction(ui->toolBar->toggleViewAction());
ui->menuView->addAction(ui->actionToggleMenuBar);
ui->menuView->addAction(ui->actionToggleRadioTitle);
// Hide some actions based on board capabilities.
Firmware * firmware = getCurrentFirmware();
if(!firmware->getCapability(Capability(LuaInputsPerScript)))
ui->actionReloadLua->setDisabled(true);
if (!firmware->getCapability(Capability(SportTelemetry)))
m_telemetryDockWidget->toggleViewAction()->setDisabled(true);
#ifndef JOYSTICKS
ui->actionJoystickSettings->setDisabled(true);
#endif
// Add radio-specific help text from simulator widget
foreach (keymapHelp_t item, *m_simulatorWidget->getKeymapHelp())
m_keymapHelp.append(item);
restoreUiState();
setStyleSheet(SimulatorStyle::styleSheet());
connect(ui->actionShowKeymap, &QAction::triggered, this, &SimulatorMainWindow::showHelp);
connect(ui->actionJoystickSettings, &QAction::triggered, this, &SimulatorMainWindow::openJoystickDialog);
connect(ui->actionReloadLua, &QAction::triggered, this, &SimulatorMainWindow::luaReload);
connect(ui->actionToggleRadioTitle, &QAction::toggled, this, &SimulatorMainWindow::showRadioTitlebar);
connect(ui->actionToggleMenuBar, &QAction::toggled, this, &SimulatorMainWindow::toggleMenuBar);
if (m_simulatorWidget) {
connect(ui->actionScreenshot, &QAction::triggered, m_simulatorWidget, &SimulatorDialog::captureScreenshot);
connect(ui->actionReloadRadioData, &QAction::triggered, m_simulatorWidget, &SimulatorDialog::restart);
}
if (m_outputsWidget)
connect(ui->actionReloadRadioData, &QAction::triggered, m_outputsWidget, &RadioOutputsWidget::restart);
// this sets the radio widget to a fixed width while docked, freeform while floating
connect(m_simulatorDockWidget, &QDockWidget::topLevelChanged, [this](bool top) {
if (top)
m_simulatorWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
else
m_simulatorWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
});
}
SimulatorMainWindow::~SimulatorMainWindow()
{
delete ui;
}
void SimulatorMainWindow::closeEvent(QCloseEvent *)
{
saveUiState();
if (m_consoleDockWidget)
delete m_consoleDockWidget;
if (m_telemetryDockWidget)
delete m_telemetryDockWidget;
if (m_trainerDockWidget)
delete m_trainerDockWidget;
if (m_outputsDockWidget)
delete m_outputsDockWidget;
if (m_simulatorDockWidget)
delete m_simulatorDockWidget;
}
void SimulatorMainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
QMenu* SimulatorMainWindow::createPopupMenu(){
QMenu * menu = QMainWindow::createPopupMenu();
menu->addAction(ui->actionToggleMenuBar);
menu->addAction(ui->actionToggleRadioTitle);
return menu;
}
void SimulatorMainWindow::saveUiState()
{
QByteArray state;
QDataStream stream(&state, QIODevice::WriteOnly);
stream << m_savedUiStateVersion << saveState() << m_showMenubar << m_showRadioTitlebar;
SimulatorOptions opts = g.profile[m_radioProfileId].simulatorOptions();
opts.windowState = state;
opts.windowGeometry = saveGeometry();
g.profile[m_radioProfileId].simulatorOptions(opts);
}
void SimulatorMainWindow::restoreUiState()
{
quint16 ver = 0;
QByteArray windowState;
QByteArray state = g.profile[m_radioProfileId].simulatorOptions().windowState;
QDataStream stream(state);
stream >> ver;
if (ver && ver <= m_savedUiStateVersion)
stream >> windowState >> m_showMenubar >> m_showRadioTitlebar;
restoreState(windowState);
restoreGeometry(g.profile[m_radioProfileId].simulatorOptions().windowGeometry);
showRadioTitlebar(m_showRadioTitlebar);
toggleMenuBar(m_showMenubar);
}
bool SimulatorMainWindow::setRadioData(RadioData * radioData)
{
return m_simulatorWidget->setRadioData(radioData);
}
bool SimulatorMainWindow::useTempDataPath(bool deleteOnClose, bool saveOnClose)
{
return m_simulatorWidget->useTempDataPath(deleteOnClose, saveOnClose);
}
bool SimulatorMainWindow::setOptions(SimulatorOptions & options, bool withSave)
{
return m_simulatorWidget->setOptions(options, withSave);
}
void SimulatorMainWindow::start()
{
if (m_consoleWidget)
m_consoleWidget->start();
if (m_simulatorWidget)
m_simulatorWidget->start();
if (m_outputsWidget)
m_outputsWidget->start();
}
void SimulatorMainWindow::createDockWidgets()
{
if (!m_outputsDockWidget) {
SimulatorIcon icon("radio_outputs");
m_outputsDockWidget = new QDockWidget(tr("Radio Outputs"), this);
m_outputsWidget = new RadioOutputsWidget(m_simulator, getCurrentFirmware(), this);
m_outputsWidget->setWindowIcon(icon);
m_outputsDockWidget->setWidget(m_outputsWidget);
m_outputsDockWidget->setObjectName("OUTPUTS");
addTool(m_outputsDockWidget, Qt::BottomDockWidgetArea, icon, QKeySequence(tr("F2")));
}
if (!m_telemetryDockWidget) {
SimulatorIcon icon("telemetry");
m_telemetryDockWidget = new QDockWidget(tr("Telemetry Simulator"), this);
TelemetrySimulator * telem = new TelemetrySimulator(this, m_simulator);
telem->setWindowIcon(icon);
m_telemetryDockWidget->setWidget(telem);
m_telemetryDockWidget->setObjectName("TELEMETRY_SIMULATOR");
addTool(m_telemetryDockWidget, Qt::LeftDockWidgetArea, icon, QKeySequence(tr("F4")));
}
if (!m_trainerDockWidget) {
SimulatorIcon icon("trainer");
m_trainerDockWidget = new QDockWidget(tr("Trainer Simulator"), this);
TrainerSimulator * trainer = new TrainerSimulator(this, m_simulator);
trainer->setWindowIcon(icon);
m_trainerDockWidget->setWidget(trainer);
m_trainerDockWidget->setObjectName("TRAINER_SIMULATOR");
addTool(m_trainerDockWidget, Qt::TopDockWidgetArea, icon, QKeySequence(tr("F5")));
}
if (!m_consoleDockWidget) {
SimulatorIcon icon("console");
m_consoleDockWidget = new QDockWidget(tr("Debug Output"), this);
m_consoleWidget = new DebugOutput(this, m_simulator);
m_consoleWidget->setWindowIcon(icon);
m_consoleDockWidget->setWidget(m_consoleWidget);
m_consoleDockWidget->setObjectName("CONSOLE");
addTool(m_consoleDockWidget, Qt::RightDockWidgetArea, icon, QKeySequence(tr("F6")));
}
}
void SimulatorMainWindow::addTool(QDockWidget * widget, Qt::DockWidgetArea area, QIcon icon, QKeySequence shortcut)
{
QAction* tempAction = widget->toggleViewAction();
tempAction->setIcon(icon);
tempAction->setShortcut(shortcut);
ui->menuView->addAction(tempAction);
ui->toolBar->insertAction(ui->actionReloadLua, tempAction);
widget->setAllowedAreas(Qt::AllDockWidgetAreas);
widget->setWindowIcon(icon);
addDockWidget(area, widget);
widget->hide();
widget->setFloating(true);
}
void SimulatorMainWindow::showRadioTitlebar(bool show)
{
m_showRadioTitlebar = show;
if (show) {
QWidget * w = m_simulatorDockWidget->titleBarWidget();
if (w)
w->deleteLater();
m_simulatorDockWidget->setTitleBarWidget(0);
}
else {
m_simulatorDockWidget->setTitleBarWidget(new QWidget());
}
if (ui->actionToggleRadioTitle->isChecked() != show)
ui->actionToggleRadioTitle->setChecked(show);
}
void SimulatorMainWindow::toggleMenuBar(bool show)
{
m_showMenubar = show;
ui->menubar->setVisible(show);
if (ui->actionToggleMenuBar->isChecked() != show)
ui->actionToggleMenuBar->setChecked(show);
}
void SimulatorMainWindow::luaReload(bool)
{
// force a reload of the lua environment
if (m_simulator)
m_simulator->setLuaStateReloadPermanentScripts();
}
void SimulatorMainWindow::openJoystickDialog(bool)
{
#ifdef JOYSTICKS
joystickDialog * jd = new joystickDialog(this);
if (jd->exec() == QDialog::Accepted && m_simulatorWidget)
m_simulatorWidget->setupJoysticks();
jd->deleteLater();
#endif
}
void SimulatorMainWindow::showHelp(bool show)
{
QString helpText = tr("Simulator Controls:");
helpText += "<table cellspacing=4 cellpadding=0>";
helpText += tr("<tr><th>Key/Mouse</td><th>Action</td></tr>");
QString keyTemplate = "<tr><td align='center'><pre>%1</pre></td><td align='center'>%2</td></tr>";
foreach (keymapHelp_t pair, m_keymapHelp)
helpText += keyTemplate.arg(pair.first, pair.second);
helpText += "</table>";
QMessageBox * msgBox = new QMessageBox(this);
msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
msgBox->setStandardButtons( QMessageBox::Ok );
msgBox->setWindowTitle(tr("Simulator Help"));
msgBox->setText(helpText);
msgBox->setModal(false);
msgBox->show();
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef SIMULATORMAINWINDOW_H
#define SIMULATORMAINWINDOW_H
#include "simulator.h"
#include <QDockWidget>
#include <QMainWindow>
#include <QPointer>
class DebugOutput;
class RadioData;
class RadioOutputsWidget;
class SimulatorDialog;
class SimulatorInterface;
class TrainerSimulator;
class TelemetrySimulator;
class QKeySequence;
namespace Ui {
class SimulatorMainWindow;
}
using namespace Simulator;
class SimulatorMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SimulatorMainWindow(QWidget * parent, SimulatorInterface * simulator, quint8 flags=0, Qt::WindowFlags wflags = Qt::WindowFlags());
~SimulatorMainWindow();
bool setRadioData(RadioData * radioData);
bool useTempDataPath(bool deleteOnClose = true, bool saveOnClose = false);
bool setOptions(SimulatorOptions & options, bool withSave = true);
QMenu * createPopupMenu();
public slots:
void start();
void showRadioTitlebar(bool show);
void toggleMenuBar(bool show);
protected slots:
virtual void closeEvent(QCloseEvent *);
virtual void changeEvent(QEvent *e);
void restoreUiState();
void saveUiState();
void luaReload(bool);
void openJoystickDialog(bool);
void showHelp(bool show);
protected:
void createDockWidgets();
void addTool(QDockWidget * widget, Qt::DockWidgetArea area, QIcon icon = QIcon(), QKeySequence shortcut = QKeySequence());
SimulatorInterface * m_simulator;
Ui::SimulatorMainWindow * ui;
SimulatorDialog * m_simulatorWidget;
DebugOutput * m_consoleWidget;
RadioOutputsWidget * m_outputsWidget;
QDockWidget * m_simulatorDockWidget;
QDockWidget * m_consoleDockWidget;
QDockWidget * m_telemetryDockWidget;
QDockWidget * m_trainerDockWidget;
QDockWidget * m_outputsDockWidget;
QVector<keymapHelp_t> m_keymapHelp;
int m_radioProfileId;
bool m_firstShow;
bool m_showRadioTitlebar;
bool m_showMenubar;
const static quint16 m_savedUiStateVersion;
};
#endif // SIMULATORMAINWINDOW_H

View file

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulatorMainWindow</class>
<widget class="QMainWindow" name="SimulatorMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>644</width>
<height>371</height>
</rect>
</property>
<property name="windowTitle">
<string>OpenTx Simulator</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/icon.png</normaloff>:/icon.png</iconset>
</property>
<property name="dockNestingEnabled">
<bool>true</bool>
</property>
<property name="dockOptions">
<set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks</set>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>644</width>
<height>21</height>
</rect>
</property>
<property name="nativeMenuBar">
<bool>false</bool>
</property>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
</widget>
<widget class="QMenu" name="menuReload">
<property name="title">
<string>Reload...</string>
</property>
<addaction name="actionReloadLua"/>
<addaction name="actionReloadRadioData"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionScreenshot"/>
<addaction name="actionJoystickSettings"/>
<addaction name="actionShowKeymap"/>
</widget>
<addaction name="menuView"/>
<addaction name="menuReload"/>
<addaction name="menuTools"/>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>Toolbar</string>
</property>
<attribute name="toolBarArea">
<enum>LeftToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionReloadLua"/>
<addaction name="actionReloadRadioData"/>
<addaction name="separator"/>
<addaction name="actionScreenshot"/>
<addaction name="actionJoystickSettings"/>
<addaction name="actionShowKeymap"/>
</widget>
<action name="actionReloadLua">
<property name="icon">
<iconset>
<normaloff>:/images/simulator/icons/svg/reload_script.svg</normaloff>:/images/simulator/icons/svg/reload_script.svg</iconset>
</property>
<property name="text">
<string>Reload Lua Scripts</string>
</property>
<property name="toolTip">
<string>Reload the Lua environment on the simulated radio.</string>
</property>
<property name="shortcut">
<string>F7</string>
</property>
</action>
<action name="actionReloadRadioData">
<property name="icon">
<iconset>
<normaloff>:/images/simulator/icons/svg/restart.svg</normaloff>:/images/simulator/icons/svg/restart.svg</iconset>
</property>
<property name="text">
<string>Reload Radio Data</string>
</property>
<property name="toolTip">
<string>Reload all radio data without restarting the simulator.</string>
</property>
<property name="shortcut">
<string>F9</string>
</property>
</action>
<action name="actionShowKeymap">
<property name="icon">
<iconset>
<normaloff>:/images/simulator/icons/svg/info.svg</normaloff>:/images/simulator/icons/svg/info.svg</iconset>
</property>
<property name="text">
<string>Key Mapping</string>
</property>
<property name="toolTip">
<string>Show keyboard maping reference.</string>
</property>
<property name="shortcut">
<string>F1</string>
</property>
</action>
<action name="actionJoystickSettings">
<property name="icon">
<iconset>
<normaloff>:/images/simulator/icons/svg/joystick_settings.svg</normaloff>:/images/simulator/icons/svg/joystick_settings.svg</iconset>
</property>
<property name="text">
<string>Joystick Settings</string>
</property>
<property name="toolTip">
<string>Open joystick configuration settings dialog.</string>
</property>
<property name="shortcut">
<string>F3</string>
</property>
</action>
<action name="actionScreenshot">
<property name="icon">
<iconset>
<normaloff>:/images/simulator/icons/svg/camera.svg</normaloff>:/images/simulator/icons/svg/camera.svg</iconset>
</property>
<property name="text">
<string>LCD Screenshot</string>
</property>
<property name="toolTip">
<string>Save a screenshot of the current simulated LCD screen.</string>
</property>
<property name="shortcut">
<string>F8</string>
</property>
</action>
<action name="actionToggleRadioTitle">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Radio Titlebar</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The title bar on the main radio window allows you to undock it or move it around separately from the other windows. Hiding the title makes a more compact and streamlined appearance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</action>
<action name="actionToggleMenuBar">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Menu Bar</string>
</property>
<property name="toolTip">
<string>Show or hide the top menu bar.</string>
</property>
<property name="shortcut">
<string>Alt+M</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -6,10 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>219</height>
<width>550</width>
<height>217</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>550</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>OpenTX Simulator - Startup Options</string>
</property>
@ -22,13 +28,28 @@
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Simulator Startup Options:</string>
</property>
<layout class="QFormLayout" name="layout_options">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
@ -56,6 +77,12 @@
</item>
<item row="0" column="1">
<widget class="QComboBox" name="radioProfile">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Existing radio profiles are shown here.&lt;br /&gt;
Create or edit profiles using the Companion application.</string>
@ -71,6 +98,12 @@ Create or edit profiles using the Companion application.</string>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="radioType">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Existing radio simulators are shown here.&lt;br /&gt;
The radio type specified in the selected profile is used by default.</string>
@ -107,6 +140,12 @@ The radio type specified in the selected profile is used by default.</string>
</item>
<item row="3" column="1">
<widget class="QWidget" name="wdgt_dataFile" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layout_dataFile" stretch="1,0">
<property name="spacing">
<number>4</number>
@ -140,7 +179,7 @@ The radio type specified in the selected profile is used by default.</string>
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<iconset>
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
@ -153,6 +192,12 @@ The radio type specified in the selected profile is used by default.</string>
</item>
<item row="4" column="1">
<widget class="QWidget" name="wdgt_dataFolder" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layout_dataFolder" stretch="1,0">
<property name="spacing">
<number>4</number>
@ -186,7 +231,7 @@ New folder(s) with default radio/model will be created here if necessary.</strin
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<iconset>
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
@ -199,6 +244,12 @@ New folder(s) with default radio/model will be created here if necessary.</strin
</item>
<item row="5" column="1">
<widget class="QWidget" name="wdgt_sdPath" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="layout_sdPath" stretch="1,0">
<property name="spacing">
<number>4</number>
@ -232,7 +283,7 @@ The default is configured in the chosen Radio Profile.</string>
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<iconset>
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
@ -245,6 +296,12 @@ The default is configured in the chosen Radio Profile.</string>
</item>
<item row="2" column="1">
<widget class="QWidget" name="wdgt_dataSource" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select which of the data sources (File/Folder/SD Card) you would like to start the simulator with.</string>
</property>
@ -312,9 +369,7 @@ The default is configured in the chosen Radio Profile.</string>
</item>
</layout>
</widget>
<resources>
<include location="../companion.qrc"/>
</resources>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>

View file

@ -25,24 +25,20 @@
#include "radio/src/telemetry/frsky.h"
TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator):
QDialog(parent),
QWidget(parent),
ui(new Ui::TelemetrySimulator),
simulator(simulator)
{
ui->setupUi(this);
QPoint dialogCenter = mapToGlobal(rect().center());
QPoint parentWindowCenter = parent->window()->mapToGlobal(
parent->window()->rect().center());
move(parentWindowCenter - dialogCenter);
timer = new QTimer(this);
timer->setInterval(10);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
logTimer = new QTimer(this);
connect(logTimer, SIGNAL(timeout()), this, SLOT(onLogTimerEvent()));
connect(ui->Simulate, SIGNAL(clicked(bool)), this, SLOT(onSimulateToggled(bool)));
connect(ui->Simulate, SIGNAL(toggled(bool)), this, SLOT(onSimulateToggled(bool)));
connect(ui->loadLogFile, SIGNAL(released()), this, SLOT(onLoadLogFile()));
connect(ui->play, SIGNAL(released()), this, SLOT(onPlay()));
connect(ui->rewind, SIGNAL(clicked()), this, SLOT(onRewind()));
@ -76,7 +72,7 @@ TelemetrySimulator::~TelemetrySimulator()
void TelemetrySimulator::onSimulateToggled(bool isChecked)
{
if (isChecked) {
timer->start(10);
timer->start();
}
else {
timer->stop();
@ -197,9 +193,6 @@ void TelemetrySimulator::showEvent(QShowEvent *event)
ui->rxbt_ratio->setValue(simulator->getSensorRatio(BATT_ID) / 10.0);
ui->A1_ratio->setValue(simulator->getSensorRatio(ADC1_ID) / 10.0);
ui->A2_ratio->setValue(simulator->getSensorRatio(ADC2_ID) / 10.0);
ui->Simulate->setChecked(true);
onSimulateToggled(true); // not sure why this doesn't fire automatically
}
void setSportPacketCrc(uint8_t * packet)
@ -216,7 +209,7 @@ void setSportPacketCrc(uint8_t * packet)
//TRACE("crc set: %x", packet[FRSKY_SPORT_PACKET_SIZE-1]);
}
uint8_t getBit(uint8_t position, uint8_t value)
uint8_t getBit(uint8_t position, uint8_t value)
{
return (value & (uint8_t)(1 << position)) ? 1 : 0;
}
@ -224,7 +217,7 @@ uint8_t getBit(uint8_t position, uint8_t value)
bool generateSportPacket(uint8_t * packet, uint8_t dataId, uint8_t prim, uint16_t appId, uint32_t data)
{
if (dataId > 0x1B ) return false;
// generate Data ID field
uint8_t bit5 = getBit(0, dataId) ^ getBit(1, dataId) ^ getBit(2, dataId);
uint8_t bit6 = getBit(2, dataId) ^ getBit(3, dataId) ^ getBit(4, dataId);
@ -483,7 +476,7 @@ uint32_t TelemetrySimulator::FlvssEmulator::setAllCells_GetNextPair(double cellV
// encode the double values into telemetry format
encodeAllCells();
// return the value for the current pair
// return the value for the current pair
uint32_t cellData = 0;
if (nextCellNum >= numCells) {
nextCellNum = 0;
@ -644,7 +637,7 @@ TelemetrySimulator::LogPlaybackController::LogPlaybackController(Ui::TelemetrySi
colToFuncMap.insert("AccX(g)", ACCX);
colToFuncMap.insert("AccY(g)", ACCY);
colToFuncMap.insert("AccZ(g)", ACCZ);
// ACCX Y and Z
}
@ -839,7 +832,7 @@ double TelemetrySimulator::LogPlaybackController::convertDegMin(QString input)
QString TelemetrySimulator::LogPlaybackController::convertGPS(QString input)
{
// input format is DDmm.mmmmH DDDmm.mmmmH (longitude latitude - degrees (2 places) minutes (2 places) decimal minutes (4 places))
// input format is DDmm.mmmmH DDDmm.mmmmH (longitude latitude - degrees (2 places) minutes (2 places) decimal minutes (4 places))
QStringList lonLat = input.simplified().split(' ');
if (lonLat.count() < 2) {
return ""; // invalid format

View file

@ -22,7 +22,7 @@
#define _TELEMETRYSIMU_H_
#include <QCloseEvent>
#include <QDialog>
#include <QWidget>
#include <QTimer>
#include <QDateTime>
#include <QtCore/qmath.h>
@ -37,102 +37,19 @@ namespace Ui {
class TelemetrySimulator;
}
class TelemetrySimulator : public QDialog
class TelemetrySimulator : public QWidget
{
Q_OBJECT
public:
explicit TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator);
virtual ~TelemetrySimulator();
protected:
protected slots:
virtual void closeEvent(QCloseEvent *event);
virtual void showEvent(QShowEvent *event);
private:
class LogPlaybackController
{
public:
LogPlaybackController(Ui::TelemetrySimulator * ui);
bool isReady();
void loadLogFile();
void play();
void stop();
void rewind();
void stepForward(bool focusOnStop);
void stepBack();
void updatePositionLabel(int32_t percentage);
void setUiDataValues();
double logFrequency; // in seconds
private:
enum CONVERT_TYPE {
RXBT_V,
RSSI,
SWR,
A1,
A2,
A3,
A4,
T1_DEGC,
T1_DEGF,
T2_DEGC,
T2_DEGF,
RPM,
FUEL,
VSPD_MS,
VSPD_FS,
ALT_FEET,
ALT_METERS,
FASV,
FASC,
CELS_GRE,
ASPD_KTS,
ASPD_KMH,
ASPD_MPH,
GALT_FEET,
GALT_METERS,
GSPD_KNTS,
GSPD_KMH,
GSPD_MPH,
GHDG_DEG,
GDATE,
G_LATLON,
ACCX,
ACCY,
ACCZ,
FUEL_QTY,
};
QMap<QString, CONVERT_TYPE> colToFuncMap; // contains all 'known' column headings and how they are to be processed
Ui::TelemetrySimulator * ui;
struct DATA_TO_FUNC_XREF {
CONVERT_TYPE functionIndex;
int32_t dataIndex;
};
QStringList csvRecords; // contents of the log file (one string per line);
QStringList columnNames;
QList<DATA_TO_FUNC_XREF> supportedCols;
int32_t recordIndex;
double convertFeetToMeters(QString input);
double convertFahrenheitToCelsius(QString input);
QString convertGPSDate(QString input);
QString convertGPS(QString input);
void addColumnHash(QString key, CONVERT_TYPE functionIndex);
double convertDegMin(QString input);
bool stepping;
QDateTime parseTransmittterTimestamp(QString row);
void calcLogFrequency();
};
private:
Ui::TelemetrySimulator * ui;
QTimer * timer;
QTimer * logTimer;
SimulatorInterface *simulator;
void generateTelemetryFrame();
TelemetrySimulator::LogPlaybackController *logPlayback;
private slots:
void onSimulateToggled(bool isChecked);
void onTimerEvent();
void onLogTimerEvent();
@ -145,49 +62,138 @@ private:
void onPositionIndicatorChanged(int value);
void onReplayRateChanged(int value);
protected:
Ui::TelemetrySimulator * ui;
QTimer * timer;
QTimer * logTimer;
SimulatorInterface *simulator;
void generateTelemetryFrame();
// protected classes follow
class LogPlaybackController
{
public:
LogPlaybackController(Ui::TelemetrySimulator * ui);
bool isReady();
void loadLogFile();
void play();
void stop();
void rewind();
void stepForward(bool focusOnStop);
void stepBack();
void updatePositionLabel(int32_t percentage);
void setUiDataValues();
double logFrequency; // in seconds
private:
enum CONVERT_TYPE {
RXBT_V,
RSSI,
SWR,
A1,
A2,
A3,
A4,
T1_DEGC,
T1_DEGF,
T2_DEGC,
T2_DEGF,
RPM,
FUEL,
VSPD_MS,
VSPD_FS,
ALT_FEET,
ALT_METERS,
FASV,
FASC,
CELS_GRE,
ASPD_KTS,
ASPD_KMH,
ASPD_MPH,
GALT_FEET,
GALT_METERS,
GSPD_KNTS,
GSPD_KMH,
GSPD_MPH,
GHDG_DEG,
GDATE,
G_LATLON,
ACCX,
ACCY,
ACCZ,
FUEL_QTY,
};
struct DATA_TO_FUNC_XREF {
CONVERT_TYPE functionIndex;
int32_t dataIndex;
};
double convertFeetToMeters(QString input);
double convertFahrenheitToCelsius(QString input);
QString convertGPSDate(QString input);
QString convertGPS(QString input);
void addColumnHash(QString key, CONVERT_TYPE functionIndex);
double convertDegMin(QString input);
QDateTime parseTransmittterTimestamp(QString row);
void calcLogFrequency();
QMap<QString, CONVERT_TYPE> colToFuncMap; // contains all 'known' column headings and how they are to be processed
Ui::TelemetrySimulator * ui;
QStringList csvRecords; // contents of the log file (one string per line);
QStringList columnNames;
QList<DATA_TO_FUNC_XREF> supportedCols;
int32_t recordIndex;
bool stepping;
}; // LogPlaybackController
LogPlaybackController *logPlayback;
private: // private classes follow
class FlvssEmulator
{
public:
public:
uint32_t setAllCells_GetNextPair(double cellValues[6]);
static const uint32_t MAXCELLS = 6;
private:
void encodeAllCells();
void splitIntoCells(double totalVolts);
static uint32_t encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2);
double cellFloats[6];
uint32_t nextCellNum;
uint32_t numCells;
uint32_t cellData1;
uint32_t cellData2;
uint32_t cellData3;
};
private:
void encodeAllCells();
void splitIntoCells(double totalVolts);
static uint32_t encodeCellPair(uint8_t cellNum, uint8_t firstCellNo, double cell1, double cell2);
double cellFloats[6];
uint32_t nextCellNum;
uint32_t numCells;
uint32_t cellData1;
uint32_t cellData2;
uint32_t cellData3;
}; // FlvssEmulator
class GPSEmulator
{
public:
GPSEmulator();
uint32_t getNextPacketData(uint32_t packetType);
void setGPSDateTime(QString dateTime);
void setGPSLatLon(QString latLon);
void setGPSCourse(double course);
void setGPSSpeedKMH(double speed);
void setGPSAltitude(double altitude);
private:
QDateTime dt;
bool sendLat;
bool sendDate;
double lat;
double lon;
double course;
double speedKNTS;
double altitude; // in meters
uint32_t encodeLatLon(double latLon, bool isLat);
uint32_t encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate);
};
public:
GPSEmulator();
uint32_t getNextPacketData(uint32_t packetType);
void setGPSDateTime(QString dateTime);
void setGPSLatLon(QString latLon);
void setGPSCourse(double course);
void setGPSSpeedKMH(double speed);
void setGPSAltitude(double altitude);
};
private:
QDateTime dt;
bool sendLat;
bool sendDate;
double lat;
double lon;
double course;
double speedKNTS;
double altitude; // in meters
uint32_t encodeLatLon(double latLon, bool isLat);
uint32_t encodeDateTime(uint8_t yearOrHour, uint8_t monthOrMinute, uint8_t dayOrSecond, bool isDate);
}; // GPSEmulator
}; // TelemetrySimulator
#endif // _TELEMETRYSIMU_H_

File diff suppressed because it is too large Load diff

View file

@ -22,37 +22,45 @@
#include "trainersimu.h"
#include "ui_trainersimu.h"
#include "helpers.h"
#define GBALL_SIZE 20
#define RESX 512
#include "virtualjoystickwidget.h"
TrainerSimulator::TrainerSimulator(QWidget * parent, SimulatorInterface * simulator):
QDialog(parent),
QWidget(parent),
ui(new Ui::TrainerSimulator),
simulator(simulator)
{
ui->setupUi(this);
leftStick = ui->leftStick;
rightStick = ui->rightStick;
setupSticks();
vJoyLeft = new VirtualJoystickWidget(this, 'L', false);
vJoyLeft->setStickColor(Qt::cyan);
ui->leftStickLayout->addWidget(vJoyLeft);
// resize(0, 0); // to force min height, min width
setFixedSize(width(), height());
vJoyRight = new VirtualJoystickWidget(this, 'R', false);
vJoyRight->setStickColor(Qt::cyan);
ui->rightStickLayout->addWidget(vJoyRight);
timer = new QTimer(this);
timer->setInterval(10);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
}
TrainerSimulator::~TrainerSimulator()
{
timer->stop();
if (timer) {
timer->stop();
delete timer;
}
if (vJoyLeft)
delete vJoyLeft;
if (vJoyRight)
delete vJoyRight;
delete ui;
}
void TrainerSimulator::showEvent(QShowEvent *event)
{
timer->start(10);
timer->start();
event->accept();
}
@ -64,71 +72,13 @@ void TrainerSimulator::closeEvent(QCloseEvent *event)
void TrainerSimulator::centerSticks()
{
if (leftStick->scene())
nodeLeft->stepToCenter();
if (vJoyLeft)
vJoyLeft->centerStick();
if (rightStick->scene())
nodeRight->stepToCenter();
if (vJoyRight)
vJoyRight->centerStick();
}
void TrainerSimulator::setupSticks()
{
QGraphicsScene *leftScene = new QGraphicsScene(leftStick);
leftScene->setItemIndexMethod(QGraphicsScene::NoIndex);
leftStick->setScene(leftScene);
// leftStick->scene()->addLine(0,10,20,30);
QGraphicsScene *rightScene = new QGraphicsScene(rightStick);
rightScene->setItemIndexMethod(QGraphicsScene::NoIndex);
rightStick->setScene(rightScene);
// rightStick->scene()->addLine(0,10,20,30);
nodeLeft = new Node();
nodeLeft->setPos(-GBALL_SIZE/2,-GBALL_SIZE/2);
nodeLeft->setBallSize(GBALL_SIZE);
leftScene->addItem(nodeLeft);
nodeRight = new Node();
nodeRight->setPos(-GBALL_SIZE/2,-GBALL_SIZE/2);
nodeRight->setBallSize(GBALL_SIZE);
rightScene->addItem(nodeRight);
}
void TrainerSimulator::resizeEvent(QResizeEvent *event)
{
if (leftStick->scene()) {
QRect qr = leftStick->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
leftStick->scene()->setSceneRect(-cx,-cy,w,h);
QPointF p = nodeLeft->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
nodeLeft->setPos(p);
}
if (rightStick->scene()) {
QRect qr = rightStick->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
rightStick->scene()->setSceneRect(-cx,-cy,w,h);
QPointF p = nodeRight->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
nodeRight->setPos(p);
}
QDialog::resizeEvent(event);
}
void TrainerSimulator::onTimerEvent()
{
centerSticks();
@ -137,8 +87,15 @@ void TrainerSimulator::onTimerEvent()
void TrainerSimulator::setTrainerInputs()
{
simulator->setTrainerInput(0, int( 512* nodeLeft->getX())); // LEFT HORZ
simulator->setTrainerInput(1, int(-512* nodeLeft->getY())); // LEFT VERT
simulator->setTrainerInput(2, int(-512*nodeRight->getY())); // RGHT VERT
simulator->setTrainerInput(3, int( 512*nodeRight->getX())); // RGHT HORZ
}
if (!simulator)
return;
if (vJoyLeft) {
simulator->setTrainerInput(0, int( 512 * vJoyLeft->getStickX())); // LEFT HORZ
simulator->setTrainerInput(1, int(-512 * vJoyLeft->getStickY())); // LEFT VERT
}
if (vJoyRight) {
simulator->setTrainerInput(2, int(-512 * vJoyRight->getStickY())); // RGHT VERT
simulator->setTrainerInput(3, int( 512 * vJoyRight->getStickX())); // RGHT HORZ
}
}

View file

@ -24,17 +24,17 @@
#include <QShowEvent>
#include <QCloseEvent>
#include <QDialog>
#include <QWidget>
#include <QTimer>
#include "modeledit/node.h"
#include "simulatorinterface.h"
namespace Ui {
class TrainerSimulator;
}
class VirtualJoystickWidget;
class TrainerSimulator : public QDialog
class TrainerSimulator : public QWidget
{
Q_OBJECT
@ -43,26 +43,20 @@ class TrainerSimulator : public QDialog
virtual ~TrainerSimulator();
protected:
protected slots:
virtual void showEvent(QShowEvent *event);
virtual void closeEvent(QCloseEvent *event);
void centerSticks();
void setTrainerInputs();
void onTimerEvent();
private:
protected:
Ui::TrainerSimulator * ui;
QTimer * timer;
SimulatorInterface *simulator;
QGraphicsView * leftStick, * rightStick;
Node *nodeLeft;
Node *nodeRight;
void centerSticks();
void setupSticks();
void resizeEvent(QResizeEvent *event = 0);
void setTrainerInputs();
private slots:
void onTimerEvent();
VirtualJoystickWidget * vJoyLeft;
VirtualJoystickWidget * vJoyRight;
};

View file

@ -1,94 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrainerSimulator</class>
<widget class="QDialog" name="TrainerSimulator">
<widget class="QWidget" name="TrainerSimulator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>566</width>
<height>268</height>
<width>385</width>
<height>187</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Trainer simulator</string>
</property>
<widget class="QFrame" name="frame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>563</width>
<height>267</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>5</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>15</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="leftStickLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>17</width>
<height>20</height>
</size>
</property>
</spacer>
</layout>
</item>
<item row="0" column="2">
<widget class="QGraphicsView" name="rightStick">
<property name="minimumSize">
<size>
<width>245</width>
<height>245</height>
</size>
<item>
<layout class="QHBoxLayout" name="rightStickLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QGraphicsView" name="leftStick">
<property name="minimumSize">
<size>
<width>245</width>
<height>245</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>

View file

@ -47,7 +47,7 @@ class RadioFaderWidget : public RadioWidget
SliderWidget * sl = new SliderWidget(this);
sl->setOrientation(Qt::Vertical);
sl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
sl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
sl->setMinimumHeight(75);
//sl->setMaximumHeight(75);
sl->setTickPosition(QSlider::TicksBothSides);

View file

@ -23,6 +23,7 @@
#include "radiowidget.h"
#include "boards.h"
#include "simulator.h"
#include <QSlider>
#include <QTimer>
@ -65,11 +66,8 @@ class RadioSwitchWidget : public RadioWidget
m_slider->setValue(m_value);
if (swType == Board::SWITCH_TOGGLE) {
QIcon icon;
icon.addFile(QStringLiteral(":/images/simulator/icons/8/lock-locked.png"), QSize(8, 8), QIcon::Normal, QIcon::On);
icon.addFile(QStringLiteral(":/images/simulator/icons/8/lock-unlocked.png"), QSize(8, 8), QIcon::Normal, QIcon::Off);
QToolButton * lockBtn = new QToolButton(this);
lockBtn->setIcon(icon);
lockBtn->setIcon(Simulator::SimulatorIcon("toggle_lock"));
lockBtn->setIconSize(QSize(8, 8));
lockBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
lockBtn->setToolButtonStyle(Qt::ToolButtonIconOnly);
@ -78,7 +76,7 @@ class RadioSwitchWidget : public RadioWidget
lockBtn->setToolTip(tr("Latch/unlatch the momentary switch."));
QWidget * container = new QWidget(this);
container->setFixedHeight(50);
container->setFixedHeight(56);
container->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QVBoxLayout * cl = new QVBoxLayout(container);
cl->setContentsMargins(0, 0, 0, 0);
@ -94,7 +92,7 @@ class RadioSwitchWidget : public RadioWidget
connect(this, &RadioWidget::flagsChanged, lockBtn, &QToolButton::setChecked);
}
else {
m_slider->setFixedHeight(50);
m_slider->setFixedHeight(56);
setWidget(m_slider);
}

View file

@ -18,6 +18,9 @@
* GNU General Public License for more details.
*/
#define GBALL_SIZE 20
#define RESX 1024
#include "virtualjoystickwidget.h"
#include "constants.h"
#include "sliderwidget.h"
@ -50,7 +53,9 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
gv = new QGraphicsView(this);
gv->setSizePolicy(sizePolicy);
gv->setMinimumSize(QSize(150, 150));
gv->setMinimumSize(size);
// gv->setMaximumSize(size + size * 3);
// gv->setFixedSize(prefSize);
gv->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
gv->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@ -88,6 +93,9 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
vTrimSlider = vTrimWidget->findChild<SliderWidget *>();
extraSize += QSize(vTrimWidget->sizeHint().width(), hTrimWidget->sizeHint().height());
}
else {
colvx = colvy = colvt;
}
if (showBtns) {
QVBoxLayout * btnbox = new QVBoxLayout();
@ -106,8 +114,17 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
if (showValues) {
QLayout * valX = createNodeValueLayout('X', nodeLabelX);
QLayout * valY = createNodeValueLayout('Y', nodeLabelY);
layout->addLayout(valX, 2, colvx, 1, 1);
layout->addLayout(valY, 2, colvy, 1, 1);
if (!showTrims) {
QVBoxLayout * vertXY = new QVBoxLayout();
vertXY->addLayout(valX);
vertXY->addLayout(valY);
layout->addLayout(vertXY, 1, colvx, 1, 1);
extraSize += QSize(nodeLabelX->sizeHint().width(), 0);
}
else {
layout->addLayout(valX, 2, colvx, 1, 1);
layout->addLayout(valY, 2, colvy, 1, 1);
}
}
layout->addItem(new QSpacerItem(0, 0), 0, 0, 1, 5); // r0 c0-4: top v spacer
@ -119,7 +136,7 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
connect(node, SIGNAL(xChanged()), this, SLOT(updateNodeValueLabels()));
connect(node, SIGNAL(yChanged()), this, SLOT(updateNodeValueLabels()));
setSize(prefSize);
setSize(prefSize, frameSize());
}
void VirtualJoystickWidget::setStickX(qreal x)
@ -144,12 +161,12 @@ void VirtualJoystickWidget::centerStick()
qreal VirtualJoystickWidget::getStickX()
{
return node->getX();
return getStickPos().x();
}
qreal VirtualJoystickWidget::getStickY()
{
return node->getY();
return getStickPos().y();
}
QPointF VirtualJoystickWidget::getStickPos()
@ -205,7 +222,22 @@ void VirtualJoystickWidget::setStickConstraint(int which, bool active)
}
}
void VirtualJoystickWidget::setSize(QSize size)
void VirtualJoystickWidget::setStickColor(const QColor & color)
{
node->setColor(color);
}
QSize VirtualJoystickWidget::sizeHint() const {
return prefSize;
}
void VirtualJoystickWidget::resizeEvent(QResizeEvent * event)
{
QWidget::resizeEvent(event);
setSize(event->size(), event->oldSize());
}
void VirtualJoystickWidget::setSize(const QSize & size, const QSize &)
{
float thisAspectRatio = (float)size.width() / size.height();
float newGvSz, spacerSz;
@ -237,28 +269,24 @@ void VirtualJoystickWidget::setSize(QSize size)
layout->setColumnStretch(2, newGvSz);
layout->setRowStretch(1, newGvSz);
prefSize = QSize(newGvSz + extraSize.width(), newGvSz + extraSize.height());
//prefSize = QSize(newGvSz + extraSize.width(), newGvSz + extraSize.height());
gv->resize(newGvSz, newGvSz);
gv->updateGeometry();
repositionNode();
//qDebug() << thisAspectRatio << size << newGvSz << spacerSz << extraSize << gv->geometry() << gv->contentsRect() << gv->frameRect() << getStickPos();
}
void VirtualJoystickWidget::repositionNode()
{
QRect qr = gv->contentsRect();
qreal w = (qreal)qr.width() - GBALL_SIZE;
qreal h = (qreal)qr.height() - GBALL_SIZE;
qreal cx = (qreal)qr.width()/2;
qreal cy = (qreal)qr.height()/2;
QRectF qr = (QRectF)gv->contentsRect();
qreal w = qr.width() - GBALL_SIZE;
qreal h = qr.height() - GBALL_SIZE;
qreal cx = qr.width() / 2;
qreal cy = qr.height() / 2;
qreal nodeX = node->getX();
qreal nodeY = node->getY();
scene->setSceneRect(-cx,-cy,w,h);
QPointF p = node->pos();
p.setX(qMin(cx, qMax(p.x(), -cx)));
p.setY(qMin(cy, qMax(p.y(), -cy)));
node->setPos(p);
node->setX(nodeX);
node->setY(nodeY);
updateNodeValueLabels();
//qDebug() << thisAspectRatio << size << newGvSz << spacerSz << extraSize << gv->geometry() << gv->contentsRect() << gv->frameRect() << getStickPos();
}
QWidget *VirtualJoystickWidget::createTrimWidget(QChar type)
@ -382,6 +410,7 @@ QLayout *VirtualJoystickWidget::createNodeValueLayout(QChar type, QLabel *& valL
val->setObjectName(QString("val_%1").arg(type));
val->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
val->setAlignment(Qt::AlignCenter);
val->setMinimumWidth(val->fontMetrics().width("-100 "));
QVBoxLayout * layout = new QVBoxLayout();
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(2);

View file

@ -33,9 +33,6 @@
class Node;
class SliderWidget;
#define GBALL_SIZE 20
#define RESX 1024
class VirtualJoystickWidget : public QWidget
{
Q_OBJECT
@ -55,7 +52,7 @@ class VirtualJoystickWidget : public QWidget
FIX_Y
};
explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(245, 245));
explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(125, 125));
void setStickX(qreal x);
void setStickY(qreal y);
@ -70,17 +67,10 @@ class VirtualJoystickWidget : public QWidget
int getTrimValue(int which);
void setStickConstraint(int which, bool active);
void setStickColor(const QColor & color);
virtual QSize sizeHint() const
{
return prefSize;
}
virtual void resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
setSize(event->size());
}
virtual QSize sizeHint() const;
virtual void resizeEvent(QResizeEvent *event);
signals:
void trimButtonPressed(int which);
@ -94,8 +84,7 @@ class VirtualJoystickWidget : public QWidget
void updateNodeValueLabels();
protected:
void setSize(QSize size);
void repositionNode();
void setSize(const QSize & size, const QSize &);
QWidget * createTrimWidget(QChar type);
QPushButton * createButtonWidget(int type);
QLayout * createNodeValueLayout(QChar type, QLabel *& valLabel);

View file

@ -19,13 +19,14 @@
*/
#include <QApplication>
//#include <QTranslator>
#include <QTranslator>
#include <QLibraryInfo>
#include <QLocale>
#include <QString>
#include <QDir>
#include <QDebug>
#include <QTextStream>
#include <QDialog>
#include <QMessageBox>
#if defined(JOYSTICKS) || defined(SIMU_AUDIO)
#include <SDL.h>
#undef main
@ -35,10 +36,11 @@
#include "constants.h"
#include "eeprominterface.h"
#include "simulator.h"
#include "simulatordialog.h"
#include "simulatormainwindow.h"
#include "simulatorstartupdialog.h"
#include "storage.h"
#include "qxtcommandoptions.h"
#include "version.h"
#ifdef WIN32
#include <windows.h>
@ -50,21 +52,6 @@
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <QProxyStyle>
class MyProxyStyle : public QProxyStyle
{
public:
void polish ( QWidget * w ) {
QMenu* mn = dynamic_cast<QMenu*>(w);
QPushButton* pb = dynamic_cast<QPushButton*>(w);
if(!(mn || pb) && !w->testAttribute(Qt::WA_MacNormalSize))
w->setAttribute(Qt::WA_MacSmallSize);
}
};
#endif
using namespace Simulator;
int finish(int exitCode);
@ -126,19 +113,12 @@ int main(int argc, char *argv[])
g.init();
#ifdef __APPLE__
app.setStyle(new MyProxyStyle);
#endif
/* QTranslator companionTranslator;
companionTranslator.load(":/companion_" + locale);
QTranslator companionTranslator;
companionTranslator.load(":/companion_" + g.locale());
QTranslator qtTranslator;
qtTranslator.load((QString)"qt_" + locale.left(2), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
qtTranslator.load((QString)"qt_" + g.locale().left(2), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&companionTranslator);
app.installTranslator(&qtTranslator);
*/
// QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#if defined(JOYSTICKS) || defined(SIMU_AUDIO)
uint32_t sdlFlags = 0;
@ -269,23 +249,20 @@ int main(int argc, char *argv[])
current_firmware_variant = getFirmware(simOptions.firmwareId);
int oldProfId = g.id();
g.id(profileId);
g.sessionId(profileId);
g.simuLastProfId(profileId);
int result = 1;
SimulatorDialog * dialog = new SimulatorDialog(NULL, simulator, SIMULATOR_FLAGS_STANDALONE);
dialog->setRadioProfileId(profileId);
if (dialog->setOptions(simOptions, true)) {
dialog->start();
dialog->show();
SimulatorMainWindow * mainWindow = new SimulatorMainWindow(NULL, simulator, SIMULATOR_FLAGS_STANDALONE);
if (mainWindow->setOptions(simOptions, true)) {
mainWindow->start();
mainWindow->show();
result = app.exec();
}
else {
result = 3;
}
g.id(oldProfId);
delete dialog;
delete mainWindow;
delete simulator;
return finish(result);

View file

@ -428,7 +428,9 @@ QString Profile::groupId()
// ** AppData class********************
// Get declarations
QStringList AppData::recentFiles() { return _recentFiles; }
QStringList AppData::recentFiles() { return _recentFiles; }
QStringList AppData::simuDbgFilters() { return _simuDbgFilters; }
QByteArray AppData::mainWinGeo() { return _mainWinGeo; }
QByteArray AppData::mainWinState() { return _mainWinState; }
QByteArray AppData::modelEditGeo() { return _modelEditGeo; }
@ -475,9 +477,12 @@ int AppData::id() { return _id; }
int AppData::theme() { return _theme; }
int AppData::warningId() { return _warningId; }
int AppData::simuLastProfId() { return _simuLastProfId; }
int AppData::sessionId() { return _sessionId; }
// Set declarations
void AppData::recentFiles (const QStringList x) { store(x, _recentFiles, "recentFileList" );}
void AppData::simuDbgFilters (const QStringList x) { store(x, _simuDbgFilters, "simuDbgFilters" );}
void AppData::mainWinGeo (const QByteArray x) { store(x, _mainWinGeo, "mainWindowGeometry" );}
void AppData::mainWinState (const QByteArray x) { store(x, _mainWinState, "mainWindowState" );}
void AppData::modelEditGeo (const QByteArray x) { store(x, _modelEditGeo, "modelEditGeometry" );}
@ -520,11 +525,19 @@ void AppData::generalEditTab (const int x) { store(x, _generalEditTab,
void AppData::iconSize (const int x) { store(x, _iconSize, "icon_size" );}
void AppData::historySize (const int x) { store(x, _historySize, "history_size" );}
void AppData::jsCtrl (const int x) { store(x, _jsCtrl, "js_ctrl" );}
void AppData::id (const int x) { store(x, _id, "profileId" );}
void AppData::theme (const int x) { store(x, _theme, "theme" );}
void AppData::warningId (const int x) { store(x, _warningId, "warningId" );}
void AppData::simuLastProfId (const int x) { store(x, _simuLastProfId, "simuLastProfId" );}
void AppData::id(const int x)
{
store(x, _id, "profileId");
sessionId(x);
}
// currently loaded radio profile ID, NOT saved to persistent storage
void AppData::sessionId (const int x) { _sessionId = x; }
// Constructor
AppData::AppData()
{
@ -667,6 +680,8 @@ void AppData::init()
getset( _tempString, "settings_version" ,"220" ); // This is a version marker. Will be used to upgrade the settings later on.
getset( _recentFiles, "recentFileList" ,"" );
getset( _simuDbgFilters, "simuDbgFilters" ,"" );
getset( _mainWinGeo, "mainWindowGeometry" ,"" );
getset( _mainWinState, "mainWindowState" ,"" );
getset( _modelEditGeo, "modelEditGeometry" ,"" );
@ -722,6 +737,7 @@ void AppData::init()
getset( _warningId, "warningId" ,0 );
getset( _simuLastProfId, "simuLastProfId" ,-1 );
sessionId(id());
}
QMap<int, QString> AppData::getActiveProfiles()

View file

@ -250,6 +250,8 @@ class AppData: protected CompStoreObj
private:
QStringList _recentFiles;
QStringList _simuDbgFilters;
QByteArray _mainWinGeo;
QByteArray _mainWinState;
QByteArray _modelEditGeo;
@ -296,10 +298,14 @@ class AppData: protected CompStoreObj
int _theme;
int _warningId;
int _simuLastProfId;
// currently loaded radio profile ID, NOT saved to persistent storage
int _sessionId;
public:
// All the get definitions
QStringList recentFiles();
QStringList simuDbgFilters();
QByteArray mainWinGeo();
QByteArray mainWinState();
QByteArray modelEditGeo();
@ -347,9 +353,12 @@ class AppData: protected CompStoreObj
int theme();
int warningId();
int simuLastProfId();
int sessionId();
// All the set definitions
void recentFiles (const QStringList x);
void simuDbgFilters (const QStringList x);
void mainWinGeo (const QByteArray);
void mainWinState (const QByteArray);
void modelEditGeo (const QByteArray);
@ -398,6 +407,7 @@ class AppData: protected CompStoreObj
void theme (const int);
void warningId (const int);
void simuLastProfId (const int);
void sessionId (const int);
// Constructor
AppData();

View file

@ -0,0 +1,31 @@
* {
font-size: 11pt;
}
QToolButton {
border: .05em solid #8f8f91;
border-radius: .3em;
background-color: white;
font-size: 12pt;
}
QToolButton[autoRaise="true"] {
border: .05em solid transparent;
background-color: transparent;
}
QToolButton:hover {
border-color: black;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(77, 77, 77, 35), stop:1 rgba(149, 149, 149, 10));
}
QToolButton:checked {
border-color: black;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(77, 77, 77, 85), stop:1 rgba(149, 149, 149, 50));
}
QToolButton:pressed {
color: white;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(77, 77, 77, 165), stop:1 rgba(77, 77, 77, 255));
}

View file

@ -0,0 +1,77 @@
/*
MainWindow separators and Splitter
*/
QMainWindow::separator,
QSplitter::handle {
border: .05em solid palette(dark);
}
QMainWindow::separator:horizontal {
height: .15em;
margin: .2em 0em;
border-left: 0;
border-right: 0;
}
QMainWindow::separator:vertical {
width: .15em;
margin: 0em .2em;
border-top: 0;
border-bottom: 0;
}
QSplitter[orientation="1"]::handle,
QSplitter[orientation="1"] QSplitterHandle {
margin: 0em .15em 0em;
max-width: .15em;
min-width: .15em;
border-top: 0;
border-bottom: 0;
}
QSplitter[orientation="2"]::handle,
QSplitter[orientation="2"] QSplitterHandle {
margin: .15em 0em 0em;
max-height: .15em;
min-height: .15em;
border-left: 0;
border-right: 0;
}
QSplitterHandle:hover {}
QMainWindow::separator:hover,
QSplitter::handle:hover {
background-color: palette(highlight);
}
/*
Specific UI class styles
*/
/* default radio "case" background */
SimulatorDialog > QWidget {
background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(242, 242, 242, 255), stop:0.757062 rgba(222, 220, 220, 255), stop:1 rgba(232, 230, 230, 255));
}
/* radio LCD/controls area under "case" */
SimulatorDialog #radioUiWidget {
background-color: qlineargradient(spread:reflect, x1:0.22, y1:0, x2:0.55065, y2:0.54, stop:0 rgba(7, 7, 7, 250), stop:0.864407 rgba(66, 66, 66, 255));
margin: 0;
padding: 0;
}
/* outputs display splitter areas (channels, logical switches, global vars) */
RadioOutputsWidget > QSplitter > QWidget {
border: 1px solid #a1a1a1;
}
/* vertical headings with the area names */
RadioOutputsWidget QLabel[heading=true] {
background-color: qlineargradient(spread:reflect, x1:0.22, y1:0, x2:0.5, y2:0.55, stop:0 rgba(20, 20, 20, 250), stop:0.85 rgba(86, 86, 86, 255));
color: rgb(248, 248, 248);
border-color: rgb(180, 180, 180);
padding: 0.8em 0.45em;
font-size: 8pt;
}

View file

@ -123,6 +123,7 @@ Section "OpenTX Companion" SecDummy
File "@QT_DLL_DIR@\Qt5PrintSupport.dll"
File "@QT_DLL_DIR@\Qt5Network.dll"
File "@QT_DLL_DIR@\Qt5Multimedia.dll"
File "@QT_DLL_DIR@\Qt5Svg.dll"
File "@QT_DLL_DIR@\Qt5Xml.dll"
File "@SDL_DIR@\SDL.dll"
@ -137,6 +138,7 @@ Section "OpenTX Companion" SecDummy
SetOutPath "$INSTDIR\imageformats"
File "@QT_DLL_DIR@\..\plugins\imageformats\qjpeg.dll"
File "@QT_DLL_DIR@\..\plugins\imageformats\qsvg.dll"
SetOutPath "$INSTDIR\mediaservice"
File "@QT_DLL_DIR@\..\plugins\mediaservice\dsengine.dll"

View file

@ -18,6 +18,7 @@ RUN apt-get update && \
qtmultimedia5-dev \
qttools5-dev \
qttools5-dev-tools \
libqt5svg5-dev \
software-properties-common \
wget \
zip \