├── .gitignore ├── Anvedi.pro ├── Anvedi.sln ├── Anvedi ├── Anvedi.pri ├── Anvedi.pro ├── Anvedi.vcxproj ├── Anvedi.vcxproj.filters ├── Anvedi.vcxproj.user ├── GraphPresenter.cpp ├── GraphPresenter.h ├── PlotCursor.cpp ├── PlotCursor.h ├── PlotHandle.cpp ├── PlotHandle.h ├── PlotInfo.cpp ├── PlotInfo.h ├── RTInteractiveFileSender.cpp ├── RTInteractiveFileSender.h ├── RTInteractiveSender.cpp ├── RTInteractiveSender.h ├── RTSender.cpp ├── RTSender.h ├── RTSocketPlayer.cpp ├── RTSocketPlayer.h ├── RTUtils.cpp ├── RTUtils.h ├── RealTimePlayer.cpp ├── RealTimePlayer.h ├── RealTimePresenter.cpp ├── RealTimePresenter.h ├── RectZoomer.cpp ├── RectZoomer.h ├── ScriptManager.cpp ├── ScriptManager.h ├── Signal.cpp ├── Signal.h ├── SignalData.cpp ├── SignalData.h ├── SignalHandle.cpp ├── SignalHandle.h ├── SignalInfoPresenter.cpp ├── SignalInfoPresenter.h ├── SignalListPresenter.cpp ├── SignalListPresenter.h ├── Utils.cpp ├── Utils.h ├── WorkspaceSerializer.cpp ├── WorkspaceSerializer.h ├── anvedi.cpp ├── anvedi.h ├── anvedi.qrc ├── anvedi.ui ├── json │ └── physx.json ├── qcustomplot.cpp ├── qcustomplot.h ├── qml-lib │ ├── README.md │ ├── RegisterAll.cpp │ ├── RegisterAll.h │ ├── qmlAxis.cpp │ ├── qmlAxis.h │ ├── qmlGraph.cpp │ ├── qmlGraph.h │ ├── qmlLabel.cpp │ ├── qmlLabel.h │ ├── qmlLegend.cpp │ ├── qmlLegend.h │ ├── qmlPen.cpp │ ├── qmlPen.h │ ├── qmlPlotPaintedItem.cpp │ ├── qmlPlotPaintedItem.h │ ├── qmlScatterStyle.cpp │ ├── qmlScatterStyle.h │ ├── qmlTick.cpp │ └── qmlTick.h ├── qml │ └── main.qml ├── rtConfig.ui └── signalInfo.ui ├── AnvediCheExe ├── AnvediCheExe.pri ├── AnvediCheExe.pro ├── AnvediCheExe.vcxproj ├── AnvediCheExe.vcxproj.filters ├── AnvediCheExe.vcxproj.user ├── anvedicheexe.ui ├── main.cpp └── mario.jpg ├── AnvediCheQml ├── AnvediCheQml.vcxproj ├── AnvediCheQml.vcxproj.filters └── main.cpp ├── AnvediCheTests ├── AnvediCheTests.pri ├── AnvediCheTests.pro ├── AnvediCheTests.vcxproj ├── AnvediCheTests.vcxproj.filters ├── GraphPresenterTests.cpp ├── GraphPresenterTests.h ├── PerformanceTests.cpp ├── PerformanceTests.h ├── SignalDataTests.cpp ├── SignalDataTests.h ├── SignalListPresenterTests.cpp ├── SignalListPresenterTests.h ├── TestRunner.cpp ├── TestRunner.h ├── WorkspaceSerializerTests.cpp ├── WorkspaceSerializerTests.h ├── anvedichetests.qrc ├── main.cpp └── test-data │ └── cubic.json ├── HelloQCustomPlot ├── HelloQCustomPlot.vcxproj ├── HelloQCustomPlot.vcxproj.filters └── main.cpp ├── QShell ├── ClearConsole.cpp ├── ClearConsole.h ├── CommandProvider.cpp ├── CommandProvider.h ├── CompleterKeyHandler.cpp ├── CompleterKeyHandler.h ├── DefaultKeyHandler.cpp ├── DefaultKeyHandler.h ├── Forms │ └── ShellWidget.ui ├── HistoryKeyHandler.cpp ├── HistoryKeyHandler.h ├── HistoryRecorder.h ├── KeyHandlersManager.cpp ├── KeyHandlersManager.h ├── KeyPressHandler.h ├── MiscKeyHandler.cpp ├── MiscKeyHandler.h ├── QShell.pri ├── QShell.pro ├── QShell.vcxproj ├── QShell.vcxproj.filters ├── QShell.vcxproj.user ├── QShellCompleter.cpp ├── QShellCompleter.h ├── QShellContextMenuBuilder.cpp ├── QShellContextMenuBuilder.h ├── QShellEngine.h ├── QShellEngineResult.h ├── QShellEngineResult_qt.cpp ├── QShellEngineResult_qt.h ├── QShellEngine_Qt.cpp ├── QShellEngine_Qt.h ├── QShellScriptUtilis.cpp ├── QShellScriptUtilis.h ├── QShellSyntaxHighlighter.cpp ├── QShellSyntaxHighlighter.h ├── QShellUtils.cpp ├── QShellUtils.h ├── README.md ├── ScriptEvaluatorKeyHandler.cpp ├── ScriptEvaluatorKeyHandler.h ├── qshell.cpp ├── qshell.h ├── qshell_global.h └── resource.h ├── README.md └── pics ├── main-view.png ├── qml-example.png ├── qml-real-time.gif └── real-time.gif /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /Anvedi/GeneratedFiles 6 | /Anvedi/x64 7 | /QShell/GeneratedFiles 8 | /QShell/x64/Debug 9 | /x64/Debug 10 | /Anvedi.sdf 11 | /Anvedi.v12.suo 12 | /Anvedi/Anvedi.v12.suo 13 | *.opensdf 14 | /x64/Release 15 | /QShell/x64/Release/QShell.tlog 16 | *.obj 17 | *.log 18 | /AnvediCheExe/x64/Debug 19 | /AnvediCheTests/GeneratedFiles/Debug 20 | /AnvediCheTests/x64/Debug 21 | /AnvediCheExe/x64/Release/AnvediCheExe.tlog 22 | /AnvediCheTests/x64/Release/AnvediCheTests.tlog 23 | /AnvediCheTests/GeneratedFiles 24 | /AnvediCheTests/file 25 | /AnvediCheTests/tmpLog 26 | -------------------------------------------------------------------------------- /Anvedi.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | TEMPLATE = subdirs 11 | SUBDIRS += QShell/QShell.pro \ 12 | Anvedi/Anvedi.pro \ 13 | AnvediCheTests/AnvediCheTests.pro \ 14 | AnvediCheExe/AnvediCheExe.pro 15 | -------------------------------------------------------------------------------- /Anvedi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QShell", "QShell\QShell.vcxproj", "{4BA98B41-D68F-47A1-BF4D-A76A64575B78}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Anvedi", "Anvedi\Anvedi.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvediCheTests", "AnvediCheTests\AnvediCheTests.vcxproj", "{FD2AD455-8E00-4607-BDC7-B38417F2A2E6}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvediCheExe", "AnvediCheExe\AnvediCheExe.vcxproj", "{F3EE3FF2-E78E-42B0-A476-B2EF187EA200}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvediCheQml", "AnvediCheQml\AnvediCheQml.vcxproj", "{5D926744-2374-4669-93AE-B54297D12D0A}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloQCustomPlot", "HelloQCustomPlot\HelloQCustomPlot.vcxproj", "{0D29C39B-CB2A-4723-BF5F-01BD932D0922}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Mixed Platforms = Debug|Mixed Platforms 21 | Debug|Win32 = Debug|Win32 22 | Debug|x64 = Debug|x64 23 | Release|Mixed Platforms = Release|Mixed Platforms 24 | Release|Win32 = Release|Win32 25 | Release|x64 = Release|x64 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 29 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|Mixed Platforms.Build.0 = Debug|Win32 30 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|Win32.ActiveCfg = Debug|Win32 31 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|Win32.Build.0 = Debug|Win32 32 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|x64.ActiveCfg = Debug|x64 33 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Debug|x64.Build.0 = Debug|x64 34 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|Mixed Platforms.ActiveCfg = Release|Win32 35 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|Mixed Platforms.Build.0 = Release|Win32 36 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|Win32.ActiveCfg = Release|Win32 37 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|Win32.Build.0 = Release|Win32 38 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|x64.ActiveCfg = Release|x64 39 | {4BA98B41-D68F-47A1-BF4D-A76A64575B78}.Release|x64.Build.0 = Release|x64 40 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 41 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.Build.0 = Debug|Win32 42 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Debug|Win32 43 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.Build.0 = Debug|Win32 44 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64 45 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64 46 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.ActiveCfg = Release|Win32 47 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.Build.0 = Release|Win32 48 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.ActiveCfg = Release|Win32 49 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.Build.0 = Release|Win32 50 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64 51 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64 52 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 53 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|Mixed Platforms.Build.0 = Debug|Win32 54 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|Win32.ActiveCfg = Debug|Win32 55 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|Win32.Build.0 = Debug|Win32 56 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|x64.ActiveCfg = Debug|x64 57 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Debug|x64.Build.0 = Debug|x64 58 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|Mixed Platforms.ActiveCfg = Release|Win32 59 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|Mixed Platforms.Build.0 = Release|Win32 60 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|Win32.ActiveCfg = Release|Win32 61 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|Win32.Build.0 = Release|Win32 62 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|x64.ActiveCfg = Release|x64 63 | {FD2AD455-8E00-4607-BDC7-B38417F2A2E6}.Release|x64.Build.0 = Release|x64 64 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 65 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|Mixed Platforms.Build.0 = Debug|Win32 66 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|Win32.ActiveCfg = Debug|Win32 67 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|Win32.Build.0 = Debug|Win32 68 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|x64.ActiveCfg = Debug|x64 69 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Debug|x64.Build.0 = Debug|x64 70 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|Mixed Platforms.ActiveCfg = Release|Win32 71 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|Mixed Platforms.Build.0 = Release|Win32 72 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|Win32.ActiveCfg = Release|Win32 73 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|Win32.Build.0 = Release|Win32 74 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|x64.ActiveCfg = Release|x64 75 | {F3EE3FF2-E78E-42B0-A476-B2EF187EA200}.Release|x64.Build.0 = Release|x64 76 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 77 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|Mixed Platforms.Build.0 = Debug|Win32 78 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|Win32.ActiveCfg = Debug|Win32 79 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|Win32.Build.0 = Debug|Win32 80 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|x64.ActiveCfg = Debug|x64 81 | {5D926744-2374-4669-93AE-B54297D12D0A}.Debug|x64.Build.0 = Debug|x64 82 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|Mixed Platforms.ActiveCfg = Release|Win32 83 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|Mixed Platforms.Build.0 = Release|Win32 84 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|Win32.ActiveCfg = Release|Win32 85 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|Win32.Build.0 = Release|Win32 86 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|x64.ActiveCfg = Release|x64 87 | {5D926744-2374-4669-93AE-B54297D12D0A}.Release|x64.Build.0 = Release|x64 88 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 89 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|Mixed Platforms.Build.0 = Debug|Win32 90 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|Win32.ActiveCfg = Debug|Win32 91 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|Win32.Build.0 = Debug|Win32 92 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|x64.ActiveCfg = Debug|x64 93 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Debug|x64.Build.0 = Debug|x64 94 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|Mixed Platforms.ActiveCfg = Release|Win32 95 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|Mixed Platforms.Build.0 = Release|Win32 96 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|Win32.ActiveCfg = Release|Win32 97 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|Win32.Build.0 = Release|Win32 98 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|x64.ActiveCfg = Release|x64 99 | {0D29C39B-CB2A-4723-BF5F-01BD932D0922}.Release|x64.Build.0 = Release|x64 100 | EndGlobalSection 101 | GlobalSection(SolutionProperties) = preSolution 102 | HideSolutionNode = FALSE 103 | EndGlobalSection 104 | EndGlobal 105 | -------------------------------------------------------------------------------- /Anvedi/Anvedi.pri: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | HEADERS += ./Utils.h \ 11 | ./anvedi.h \ 12 | ./PlotInfo.h \ 13 | ./PlotHandle.h \ 14 | ./RectZoomer.h \ 15 | ./SignalHandle.h \ 16 | ./SignalData.h \ 17 | ./ScriptManager.h \ 18 | ./Signal.h \ 19 | ./SignalListPresenter.h \ 20 | ./PlotCursor.h \ 21 | ./GraphPresenter.h \ 22 | ./qcustomplot.h \ 23 | ./qml-lib/qmlPen.h \ 24 | ./qml-lib/qmlScatterStyle.h \ 25 | ./qml-lib/qmlLegend.h \ 26 | ./qml-lib/qmlAxis.h \ 27 | ./qml-lib/qmlGraph.h \ 28 | ./qml-lib/qmlPlotPaintedItem.h \ 29 | ./qml-lib/RegisterAll.h 30 | SOURCES += ./anvedi.cpp \ 31 | ./GraphPresenter.cpp \ 32 | ./PlotCursor.cpp \ 33 | ./PlotHandle.cpp \ 34 | ./PlotInfo.cpp \ 35 | ./qcustomplot.cpp \ 36 | ./RectZoomer.cpp \ 37 | ./ScriptManager.cpp \ 38 | ./SignalData.cpp \ 39 | ./SignalHandle.cpp \ 40 | ./SignalListPresenter.cpp \ 41 | ./Utils.cpp \ 42 | ./qml-lib/qmlPen.cpp \ 43 | ./qml-lib/qmlScatterStyle.cpp \ 44 | ./qml-lib/qmlLegend.cpp \ 45 | ./qml-lib/qmlAxis.cpp \ 46 | ./qml-lib/qmlGraph.cpp \ 47 | ./qml-lib/qmlPlotPaintedItem.cpp \ 48 | ./qml-lib/RegisterAll.cpp 49 | FORMS += ./anvedi.ui 50 | RESOURCES += anvedi.qrc 51 | -------------------------------------------------------------------------------- /Anvedi/Anvedi.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | TEMPLATE = lib 6 | TARGET = Anvedi 7 | DESTDIR = ../Win32/Debug 8 | QT += core script widgets gui qml printsupport quick 9 | CONFIG += debug 10 | DEFINES += WIN64 QT_DLL QT_PRINTSUPPORT_LIB QT_SCRIPT_LIB QT_WIDGETS_LIB QT_QML_LIB QT_QUICK_LIB 11 | INCLUDEPATH += ./GeneratedFiles \ 12 | . \ 13 | ./GeneratedFiles/Debug 14 | DEPENDPATH += . 15 | MOC_DIR += ./GeneratedFiles/debug 16 | OBJECTS_DIR += debug 17 | UI_DIR += ./GeneratedFiles 18 | RCC_DIR += ./GeneratedFiles 19 | include(Anvedi.pri) 20 | -------------------------------------------------------------------------------- /Anvedi/Anvedi.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | PATH="$(QTDIR)\bin%3b$(PATH) 5 | 6 | 7 | C:\Qt-5.2\5.5\msvc2013_64 8 | PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH) 9 | 10 | 11 | C:\Qt-5.2\5.5\msvc2013_64 12 | PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH) 13 | 14 | 15 | PATH="$(QTDIR)\bin%3b$(PATH) 16 | 17 | 18 | false 19 | 20 | -------------------------------------------------------------------------------- /Anvedi/GraphPresenter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "qcustomplot.h" 6 | #include "SignalData.h" 7 | 8 | class QCustomPlot; 9 | class SignalData; 10 | class PlotInfo; 11 | 12 | class GraphPresenter : public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | GraphPresenter(QCustomPlot* plot, QScrollBar* rangeScroll, const SignalData& data, PlotInfo& plotInfo); 17 | public slots: 18 | // data 19 | void OnNewData(const DataMap& data); 20 | void OnSignalRemoved(const QString& who); 21 | void OnSignalRenamed(const QString& oldName, const Signal& signal); 22 | void OnClearData(); 23 | void OnGraphDataChanged(const Signal& signal); 24 | void OnGraphsDataAdded(const QVector&, const std::map>&); 25 | void OnGraphStyleInfoChanged(const Signal& signal); 26 | void OnGraphVisibilityChanged(const Signal& signal); 27 | void OnGraphRangeChanged(const Signal& signal); 28 | void OnGraphTicksChanged(const Signal& signal); 29 | void OnDomainChanged(const Signal& domain); 30 | void OnCursorValueChanged(qreal, size_t); 31 | // plot info 32 | void OnBackgroundChanged(const QColor& color); 33 | void OnXRangeChanged(const QCPRange&); 34 | // xrange scrollbar 35 | void rangeScrollbarValueChanged(int); 36 | private: 37 | void MakeGraphOrUseExistent(const Signal& signal, std::function action); 38 | void MakeGraphOrUseExistent_WithFinalReplot(const Signal& signal, std::function action); 39 | // utilities to set graph properties 40 | void SetGraphDataFrom(QCPGraph& graph, const Signal& signal); 41 | void SetGraphicInfoFrom(QCPGraph& graph, const Signal& signal); 42 | void SetAxisInfo(QCPGraph& graph, const Signal& signal); 43 | void SetRangeInfo(QCPGraph& graph, const Signal& signal); 44 | // setting axis, grid, etc color and pen 45 | void SetAxisColor(QCPAxis * yAxis); 46 | // RT 47 | bool IsFirstRTPacket(const std::map>& dataSlice); 48 | 49 | QCustomPlot* plot; 50 | QScrollBar* rangeScroll; 51 | std::map displayedGraphs; 52 | const SignalData& data; 53 | PlotInfo& plotInfo; 54 | }; -------------------------------------------------------------------------------- /Anvedi/PlotCursor.cpp: -------------------------------------------------------------------------------- 1 | #include "PlotCursor.h" 2 | #include "qcustomplot.h" 3 | #include "utils.h" 4 | #include 5 | #include 6 | #include "SignalData.h" 7 | 8 | PlotCursor::PlotCursor(QCustomPlot* parent, SignalData& data) 9 | : cursor(nullptr), plot(parent), data(data) 10 | { 11 | // a straight line that spans infinitely in both directions 12 | cursor = new QCPItemStraightLine(parent); 13 | initLinePos(); 14 | 15 | // todo: switch to complementary color if background color is close to cursor color 16 | cursor->setPen({ QBrush(Qt::black), 1.5 }); 17 | 18 | // connect to QCustomPlot events 19 | QObject::connect(plot, &QCustomPlot::mousePress, this, &PlotCursor::OnMouseEvent); 20 | QObject::connect(plot, &QCustomPlot::mouseMove, this, &PlotCursor::OnMouseEvent); 21 | } 22 | 23 | void PlotCursor::initLinePos() 24 | { 25 | cursor->point1->setCoords(0.0, 0.0); 26 | cursor->point2->setCoords(0.0, 1.0); 27 | } 28 | 29 | void PlotCursor::reset() 30 | { 31 | initLinePos(); 32 | set(std::numeric_limits::min()); 33 | } 34 | 35 | void PlotCursor::set(qreal xVal) 36 | { 37 | auto domainValInfo = data.domainLowerBound(xVal); 38 | if (!std::isnan(domainValInfo.first)) 39 | { 40 | const auto domainVal = domainValInfo.first; 41 | cursor->point1->setCoords(domainVal, cursor->point1->value()); 42 | cursor->point2->setCoords(domainVal, cursor->point2->value()); 43 | plot->replot(QCustomPlot::rpHint); 44 | 45 | emit CursorChanged(domainVal, domainValInfo.second); 46 | } 47 | } 48 | 49 | void PlotCursor::moveForward() 50 | { 51 | set(data.domainNextValue(cursor->point1->coords().x())); 52 | } 53 | 54 | void PlotCursor::moveBackward() 55 | { 56 | set(data.domainPrevValue(cursor->point1->coords().x())); 57 | } 58 | 59 | qreal PlotCursor::xPos() const 60 | { 61 | return cursor->point1->key(); 62 | } 63 | 64 | void PlotCursor::OnMouseEvent(QMouseEvent* e) 65 | { 66 | if (e->buttons() == Qt::LeftButton) 67 | { 68 | const auto mouseClickAxesCoords = plot->xAxis->pixelToCoord(e->pos().x()); 69 | set(mouseClickAxesCoords); 70 | } 71 | } 72 | 73 | void PlotCursor::OnBackgroundChanged(const QColor& c) 74 | { 75 | const auto currPen = cursor->pen(); 76 | if (close(currPen.color(), c)) 77 | cursor->setPen(QPen(invert(c), currPen.width())); 78 | } 79 | 80 | void PlotCursor::OnKeyboardPressed(QKeyEvent* e) 81 | { 82 | switch (e->key()) 83 | { 84 | case Qt::Key_Left: 85 | moveBackward(); 86 | break; 87 | case Qt::Key_Right: 88 | moveForward(); 89 | break; 90 | case Qt::Key_Home: 91 | moveBegin(); 92 | break; 93 | case Qt::Key_End: 94 | moveEnd(); 95 | break; 96 | } 97 | } 98 | 99 | void PlotCursor::OnCursorInRT(const QVector& domainSlice) 100 | { 101 | set(domainSlice.back()); 102 | } 103 | 104 | void PlotCursor::moveBegin() 105 | { 106 | set(std::numeric_limits::min()); 107 | } 108 | 109 | void PlotCursor::moveEnd() 110 | { 111 | set(std::numeric_limits::max()); 112 | } 113 | 114 | void PlotCursor::OnSetCursorFollowingInRealTime(bool flag) 115 | { 116 | if (!flag) 117 | { 118 | QObject::disconnect(&data, &SignalData::SignalAdded, this, 0); 119 | } 120 | else 121 | { 122 | QObject::connect(&data, &SignalData::SignalAdded, this, &PlotCursor::OnCursorInRT); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Anvedi/PlotCursor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class QCPItemStraightLine; 5 | class QCustomPlot; 6 | class QMouseEvent; 7 | class QKeyEvent; 8 | class QCPItemText; 9 | class SignalData; 10 | 11 | class PlotCursor : public QObject 12 | { 13 | Q_OBJECT 14 | public: 15 | PlotCursor(QCustomPlot* parent, SignalData& data); 16 | 17 | // movement 18 | void reset(); 19 | void set(qreal xVal); 20 | void moveForward(); 21 | void moveBackward(); 22 | void moveBegin(); 23 | void moveEnd(); 24 | // getters 25 | qreal xPos() const; 26 | // setters 27 | void setStep(qreal newStep); 28 | public slots: 29 | void OnMouseEvent(QMouseEvent*); 30 | void OnBackgroundChanged(const QColor&); 31 | void OnKeyboardPressed(QKeyEvent*); 32 | void OnCursorInRT(const QVector& domainSlice); 33 | void OnSetCursorFollowingInRealTime(bool flag); 34 | signals: 35 | void CursorChanged(qreal, size_t); 36 | private: 37 | void initLinePos(); 38 | 39 | QCPItemStraightLine *cursor; 40 | QCustomPlot* plot; 41 | SignalData& data; 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /Anvedi/PlotHandle.cpp: -------------------------------------------------------------------------------- 1 | #include "PlotHandle.h" 2 | #include "PlotInfo.h" 3 | #include 4 | 5 | PlotHandle::PlotHandle(PlotInfo& plot) 6 | : plot(plot) 7 | { 8 | } 9 | 10 | QString PlotHandle::getBackground() const 11 | { 12 | return plot.getBackgroundColor().name(); 13 | } 14 | 15 | void PlotHandle::setBackground(const QString& color) 16 | { 17 | plot.setBackgroundColor(color); 18 | } 19 | 20 | QVariant PlotHandle::getXRange() const 21 | { 22 | const auto xRange = plot.getXRange(); 23 | QVariantList vals; 24 | vals.push_back(xRange.first); 25 | vals.push_back(xRange.second); 26 | return vals; 27 | } 28 | 29 | inline std::pair ToRange(const QVariant& val) 30 | { 31 | const auto vList = val.toList(); 32 | return { vList.front().toReal(), vList.back().toReal() }; 33 | } 34 | 35 | void PlotHandle::setXRange(const QVariant& val) const 36 | { 37 | if ((val.type() == QVariant::String) && val.toString() == "auto") 38 | plot.autoScaleX(); 39 | else 40 | plot.setXRange(ToRange(val)); 41 | } 42 | 43 | void PlotHandle::savePdf(const QString& fileName) 44 | { 45 | plot.exportToPdf(fileName); 46 | } 47 | 48 | void PlotHandle::savePdf(const QString& fileName, qreal w, qreal h) 49 | { 50 | plot.exportToPdf(fileName, w, h); 51 | } 52 | 53 | int PlotHandle::getRTPageSize() const 54 | { 55 | return plot.getRealTimePageSize(); 56 | } 57 | 58 | void PlotHandle::setRTPageSize(int ps) 59 | { 60 | plot.setRealTimePageSize(ps); 61 | } 62 | -------------------------------------------------------------------------------- /Anvedi/PlotHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class PlotInfo; 6 | 7 | class PlotHandle : public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(QString background READ getBackground WRITE setBackground) 11 | Q_PROPERTY(int rtPageSize READ getRTPageSize WRITE setRTPageSize) 12 | Q_PROPERTY(QVariant xRange READ getXRange WRITE setXRange) 13 | public: 14 | PlotHandle(PlotInfo& plot); 15 | 16 | QString getBackground() const; 17 | void setBackground(const QString& color); 18 | QVariant getXRange() const; 19 | void setXRange(const QVariant& val) const; 20 | int getRTPageSize() const; 21 | void setRTPageSize(int ps); 22 | public slots: 23 | void savePdf(const QString& fileName); 24 | void savePdf(const QString& fileName, qreal w, qreal h); 25 | private: 26 | PlotInfo& plot; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /Anvedi/PlotInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "PlotInfo.h" 2 | #include "qcustomplot.h" 3 | 4 | void PlotInfo::setPlot(QCustomPlot* p) 5 | { 6 | plot = p; 7 | } 8 | 9 | const QColor& PlotInfo::getBackgroundColor() const 10 | { 11 | return backgroundColor; 12 | } 13 | 14 | void PlotInfo::setBackgroundColor(const QColor& color) 15 | { 16 | backgroundColor = color; 17 | emit BackgroundColorChanged(color); 18 | } 19 | 20 | void PlotInfo::setXRange(const std::pair& range) 21 | { 22 | plot->xAxis->setRange(range.first, range.second); 23 | } 24 | 25 | const std::pair PlotInfo::getXRange() const 26 | { 27 | const auto range = plot->xAxis->range(); 28 | return { range.lower, range.upper }; 29 | } 30 | 31 | template 32 | void OnGraph(QCustomPlot* plot, const QString& name, Action action) 33 | { 34 | for (auto i = 0; i < plot->graphCount(); ++i) 35 | { 36 | if (plot->graph(i)->name() == name) 37 | { 38 | action(plot->graph(i)); 39 | break; 40 | } 41 | } 42 | } 43 | 44 | template 45 | void OnAllGraphs(QCustomPlot* plot, Action action) 46 | { 47 | for (auto i = 0; i < plot->graphCount(); ++i) 48 | { 49 | action(plot->graph(i)); 50 | } 51 | } 52 | 53 | void PlotInfo::autoScaleX() 54 | { 55 | plot->xAxis->rescale(); 56 | plot->replot(); 57 | } 58 | 59 | void PlotInfo::exportToPdf(const QString& fileName, qreal w, qreal h) 60 | { 61 | plot->savePdf(fileName, false, w, h); 62 | } 63 | 64 | int PlotInfo::getRealTimePageSize() const 65 | { 66 | return realTimePageSize; 67 | } 68 | 69 | void PlotInfo::setRealTimePageSize(int ps) 70 | { 71 | realTimePageSize = ps; 72 | } 73 | -------------------------------------------------------------------------------- /Anvedi/PlotInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class QCustomPlot; 8 | 9 | class PlotInfo : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | void setPlot(QCustomPlot* p); 14 | const QColor& getBackgroundColor() const; 15 | void setBackgroundColor(const QColor& color); 16 | int getRealTimePageSize() const; 17 | void setRealTimePageSize(int ps); 18 | // axis ranges 19 | const std::pair getXRange() const; 20 | void setXRange(const std::pair& range); 21 | void autoScaleX(); 22 | // export 23 | void exportToPdf(const QString& fileName, qreal w = 0, qreal h = 0); 24 | signals: 25 | void BackgroundColorChanged(const QColor&); 26 | private: 27 | QColor backgroundColor; 28 | int realTimePageSize = 50; 29 | QCustomPlot* plot; 30 | }; 31 | -------------------------------------------------------------------------------- /Anvedi/RTInteractiveFileSender.cpp: -------------------------------------------------------------------------------- 1 | #include "RTInteractiveFileSender.h" 2 | #include "SignalData.h" 3 | #include "RTUtils.h" 4 | 5 | using namespace std; 6 | 7 | RTInteractiveFileSender::RTInteractiveFileSender(SignalData& data) 8 | : data(data), sender(data) 9 | { 10 | 11 | } 12 | 13 | RTSender* RTInteractiveFileSender::getSender() 14 | { 15 | return &sender; 16 | } 17 | 18 | void RTInteractiveFileSender::ReStart() 19 | { 20 | auto toSend = PrepareReplay(fileToReplay, data); 21 | sender.SetDataToSend(move(toSend)); 22 | } 23 | 24 | const QString& RTInteractiveFileSender::getFileToReplay() const 25 | { 26 | return fileToReplay; 27 | } 28 | 29 | void RTInteractiveFileSender::setFileToReplay(const QString& file) 30 | { 31 | fileToReplay = file; 32 | } 33 | -------------------------------------------------------------------------------- /Anvedi/RTInteractiveFileSender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "RTSender.h" 4 | 5 | class SignalData; 6 | 7 | class RTInteractiveFileSender : public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(RTSender* sender READ getSender) 11 | Q_PROPERTY(QString fileToReplay READ getFileToReplay WRITE setFileToReplay) 12 | public: 13 | RTInteractiveFileSender(SignalData& data); 14 | 15 | RTSender* getSender(); 16 | const QString& getFileToReplay() const; 17 | void setFileToReplay(const QString&); 18 | public slots: 19 | void ReStart(); 20 | private: 21 | SignalData& data; 22 | RTSender sender; 23 | QString fileToReplay = R"(C:\Users\Marco\Source\Repos\anvedi\Anvedi\json\physx-0204.json)"; 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /Anvedi/RTInteractiveSender.cpp: -------------------------------------------------------------------------------- 1 | #include "RTInteractiveSender.h" 2 | #include "SignalData.h" 3 | #include "Utils.h" 4 | 5 | RTInteractiveSender::RTInteractiveSender(SignalData& data) 6 | : data(data), sender(data) 7 | { 8 | 9 | } 10 | 11 | void RTInteractiveSender::Start(const QString& domainName, QVariantList signalNames) 12 | { 13 | currentPacket.clear(); 14 | DataMap toSet; 15 | for (const auto& name : signalNames) 16 | { 17 | Signal sign{name.toString()}; 18 | toSet.emplace(name.toString(), sign); 19 | currentPacket.emplace_back(sign.name, QVector{}); 20 | } 21 | if (!toSet.count(domainName)) 22 | { 23 | toSet.emplace(domainName, Signal{ domainName }); 24 | currentPacket.emplace_back(domainName, QVector{}); 25 | } 26 | for (auto& signal : toSet) 27 | { 28 | data.addIfNotExists(std::move(signal.second)); 29 | } 30 | data.setAsDomain(domainName); 31 | } 32 | 33 | RTSender* RTInteractiveSender::getSender() 34 | { 35 | return &sender; 36 | } 37 | 38 | QVariantMap RTInteractiveSender::getData() const 39 | { 40 | QVariantMap currData; 41 | for (const auto& signal : currentPacket) 42 | { 43 | currData[signal.first] = ToVariant(signal.second); 44 | } 45 | return currData; 46 | } 47 | 48 | void RTInteractiveSender::setData(const QVariantMap& data) 49 | { 50 | currentPacket.clear(); 51 | for (auto it = data.begin(); it != data.end(); ++it) 52 | { 53 | currentPacket.emplace_back(it.key(), ToVector(it->toList())); 54 | } 55 | sender.SetDataToSend(currentPacket); 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /Anvedi/RTInteractiveSender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "RTSender.h" 3 | #include 4 | #include 5 | 6 | class RTInteractiveSender : public QObject 7 | { 8 | Q_OBJECT 9 | Q_PROPERTY(RTSender* sender READ getSender) 10 | Q_PROPERTY(QVariantMap data READ getData WRITE setData) 11 | public: 12 | RTInteractiveSender(SignalData& data); 13 | 14 | RTSender* getSender(); 15 | QVariantMap getData() const; 16 | void setData(const QVariantMap& data); 17 | public slots: 18 | void Start(const QString& domainName, QVariantList signalNames); 19 | private: 20 | SignalData& data; 21 | RTSender sender; 22 | DataToReplay currentPacket; 23 | }; 24 | -------------------------------------------------------------------------------- /Anvedi/RTSender.cpp: -------------------------------------------------------------------------------- 1 | #include "RTSender.h" 2 | #include "SignalData.h" 3 | 4 | using namespace std; 5 | 6 | RTSender::RTSender(SignalData& receiver) 7 | : receiver(receiver) 8 | { 9 | 10 | } 11 | 12 | void RTSender::SetDataToSend(std::vector < std::pair>> toSend) 13 | { 14 | dataToSend = move(toSend); 15 | currentSampleIdx = 0u; 16 | } 17 | 18 | void RTSender::SendData() 19 | { 20 | SendData(packetCount); 21 | } 22 | 23 | void RTSender::SendData(int count) 24 | { 25 | map> sliceToSend; 26 | for (const auto& elem : dataToSend) 27 | { 28 | QVector data; data.reserve(count); 29 | for (auto i = 0; i < count && (currentSampleIdx + i < elem.second.size()); ++i) 30 | { 31 | data.push_back(elem.second.at(currentSampleIdx + i)); 32 | } 33 | sliceToSend.emplace(elem.first, std::move(data)); 34 | } 35 | currentSampleIdx += count; 36 | if (!sliceToSend.begin()->second.empty()) 37 | { 38 | receiver.addValues(sliceToSend); 39 | emit DataSent(); 40 | } 41 | else 42 | { 43 | emit NoMoreData(); 44 | } 45 | } 46 | 47 | int RTSender::getPacketCount() const 48 | { 49 | return packetCount; 50 | } 51 | 52 | void RTSender::setPacketCount(int pc) 53 | { 54 | packetCount = pc; 55 | } 56 | -------------------------------------------------------------------------------- /Anvedi/RTSender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "RTUtils.h" 7 | 8 | class SignalData; 9 | 10 | class RTSender : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(int packetCount READ getPacketCount WRITE setPacketCount) 14 | public: 15 | RTSender(SignalData& receiver); 16 | 17 | void SetDataToSend(DataToReplay toSend); 18 | int getPacketCount() const; 19 | void setPacketCount(int pc); 20 | public slots: 21 | void SendData(); 22 | void SendData(int count); 23 | signals: 24 | void DataSent(); 25 | void NoMoreData(); 26 | private: 27 | DataToReplay dataToSend; 28 | SignalData& receiver; 29 | size_t currentSampleIdx = 0u; 30 | int packetCount = 20; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /Anvedi/RTSocketPlayer.cpp: -------------------------------------------------------------------------------- 1 | #include "RTSocketPlayer.h" 2 | #include "SignalData.h" 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | RTSocketPlayer::RTSocketPlayer(SignalData& data) 9 | : m_data(data), sender(m_data) 10 | { 11 | if (!server.listen("\\\\.\\pipe\\anvedi")) 12 | { 13 | QMessageBox::critical(nullptr, tr("Anvedi Server"), 14 | tr("Unable to start the server: %1.") 15 | .arg(server.errorString())); 16 | } 17 | else 18 | { 19 | QObject::connect(&server, SIGNAL(newConnection()), this, SLOT(Start())); 20 | } 21 | 22 | sender.setPacketCount(100); 23 | } 24 | 25 | void RTSocketPlayer::Start() 26 | { 27 | clientConnection = server.nextPendingConnection(); 28 | 29 | // blocking - temporary 30 | if (clientConnection->waitForReadyRead()) 31 | { 32 | auto block = clientConnection->readAll(); 33 | auto str = block.toStdString(); 34 | stringstream ss(str, ios::binary | ios::in); 35 | 36 | string domainName; 37 | DataMap data; 38 | names.clear(); 39 | 40 | // number of signals 41 | int numberOfSignals; 42 | ss.read((char*) &numberOfSignals, sizeof(int)); 43 | 44 | // names 45 | int currSignNameSize; 46 | for (auto i = 0; i < numberOfSignals; ++i) 47 | { 48 | ss.read((char*) &currSignNameSize, sizeof(int)); 49 | vector currName(currSignNameSize); 50 | ss.read((char*) currName.data(), currName.size()); 51 | if (i == 0) 52 | domainName.assign(currName.begin(), currName.end()); 53 | QString currNameAsQString(string(currName.begin(), currName.end()).c_str()); 54 | data.emplace(currNameAsQString, Signal{currNameAsQString}); 55 | names.push_back(currNameAsQString); 56 | } 57 | for (auto& signal : data) 58 | { 59 | m_data.addIfNotExists(std::move(signal.second)); 60 | } 61 | m_data.setAsDomain(domainName.c_str()); 62 | } 63 | else 64 | { 65 | QMessageBox::critical(nullptr, tr("Anvedi Server"), 66 | tr("Unable to receive data from client") 67 | ); 68 | clientConnection->disconnectFromServer(); 69 | return; 70 | } 71 | 72 | 73 | connect(clientConnection, &QLocalSocket::readyRead, [this]{ 74 | auto block = clientConnection->readAll(); 75 | auto str = block.toStdString(); 76 | stringstream ss(str, ios::binary | ios::in); 77 | unsigned int samplesCount; 78 | ss.read((char*) &samplesCount, sizeof(unsigned int)); 79 | 80 | vector>> slice; 81 | 82 | for (auto i = 0; i < names.size(); ++i) 83 | { 84 | QVector samples(samplesCount); 85 | for (auto j = 0; j < samplesCount; ++j) 86 | { 87 | ss.read((char*) &samples[j], sizeof(qreal)); 88 | } 89 | 90 | slice.emplace_back(names[i], std::move(samples)); 91 | } 92 | 93 | sender.SetDataToSend(std::move(slice)); 94 | sender.SendData(); 95 | }); 96 | } 97 | 98 | void RTSocketPlayer::Stop() 99 | { 100 | clientConnection->disconnectFromServer(); 101 | } 102 | -------------------------------------------------------------------------------- /Anvedi/RTSocketPlayer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "RTSender.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class SignalData; 11 | 12 | class RTSocketPlayer : public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | RTSocketPlayer(SignalData& data); 17 | private slots: 18 | void Start(); 19 | public slots : 20 | void Stop(); 21 | private: 22 | SignalData& m_data; 23 | QLocalServer server; 24 | QLocalSocket* clientConnection; 25 | RTSender sender; 26 | std::vector names; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /Anvedi/RTUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "RTUtils.h" 2 | #include "SignalData.h" 3 | #include "PlotInfo.h" 4 | #include "WorkspaceSerializer.h" 5 | 6 | using namespace std; 7 | 8 | DataToReplay PrepareReplay(const QString& file, SignalData& signalData) 9 | { 10 | SignalData data; PlotInfo info; 11 | WorkspaceSerializer::Read(file, data, info); 12 | 13 | DataMap toSet; 14 | vector < pair>> toSend; 15 | data.onSignals([&](const Signal& signal){ 16 | toSet.emplace(signal.name, Signal{ signal.name, {}, signal.graphic }); 17 | toSend.emplace_back(make_pair(signal.name, std::move(signal.y))); 18 | }); 19 | 20 | for (auto& signal : toSet) 21 | { 22 | signalData.addIfNotExists(std::move(signal.second)); 23 | } 24 | signalData.setAsDomain(data.getDomain()->name); 25 | return toSend; 26 | } 27 | -------------------------------------------------------------------------------- /Anvedi/RTUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class SignalData; 7 | 8 | using DataToReplay = std::vector>>; 9 | 10 | DataToReplay PrepareReplay(const QString& file, SignalData& data); -------------------------------------------------------------------------------- /Anvedi/RealTimePlayer.cpp: -------------------------------------------------------------------------------- 1 | #include "RealTimePlayer.h" 2 | #include "SignalData.h" 3 | #include "PlotInfo.h" 4 | #include "WorkspaceSerializer.h" 5 | #include "RTUtils.h" 6 | 7 | using namespace std; 8 | 9 | RealTimePlayer::RealTimePlayer(SignalData& data) 10 | : m_data(data), sender(m_data) 11 | { 12 | connect(&dataTimer, SIGNAL(timeout()), &sender, SLOT(SendData())); 13 | connect(&sender, SIGNAL(NoMoreData()), this, SLOT(Stop())); 14 | } 15 | 16 | void RealTimePlayer::PrepareSender() 17 | { 18 | auto toSend = PrepareReplay(fileToReplay, m_data); 19 | sender.SetDataToSend(move(toSend)); 20 | } 21 | 22 | void RealTimePlayer::Start() 23 | { 24 | if (isPaused) 25 | { 26 | dataTimer.start(timerInterval); 27 | emit RTResumed(); 28 | return; 29 | } 30 | 31 | try 32 | { 33 | PrepareSender(); 34 | } 35 | catch (const exception&) 36 | { 37 | return; 38 | } 39 | emit RTStarted(); 40 | dataTimer.start(timerInterval); 41 | } 42 | 43 | void RealTimePlayer::Pause() 44 | { 45 | dataTimer.stop(); 46 | isPaused = true; 47 | emit RTPaused(); 48 | } 49 | 50 | void RealTimePlayer::Stop() 51 | { 52 | dataTimer.stop(); 53 | isPaused = false; 54 | emit RTStopped(); 55 | } 56 | 57 | void RealTimePlayer::setFileToReplay(QString f) 58 | { 59 | fileToReplay = std::move(f); 60 | } 61 | 62 | const QString& RealTimePlayer::getFileToReplay() const 63 | { 64 | return fileToReplay; 65 | } 66 | 67 | void RealTimePlayer::setTimerInterval(int ti) 68 | { 69 | timerInterval = ti; 70 | } 71 | 72 | int RealTimePlayer::getTimerInterval() const 73 | { 74 | return timerInterval; 75 | } 76 | 77 | void RealTimePlayer::setPacketCount(int pc) 78 | { 79 | sender.setPacketCount(pc); 80 | } 81 | 82 | int RealTimePlayer::getPacketCount() const 83 | { 84 | return sender.getPacketCount(); 85 | } 86 | -------------------------------------------------------------------------------- /Anvedi/RealTimePlayer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "RTSender.h" 6 | #include 7 | 8 | class SignalData; 9 | 10 | class RealTimePlayer : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(QString file READ getFileToReplay WRITE setFileToReplay) 14 | Q_PROPERTY(int timerInterval READ getTimerInterval WRITE setTimerInterval) 15 | Q_PROPERTY(int packetCount READ getPacketCount WRITE setPacketCount) 16 | public: 17 | RealTimePlayer(SignalData& data); 18 | 19 | // props 20 | void setFileToReplay(QString f); 21 | const QString& getFileToReplay() const; 22 | void setTimerInterval(int ti); 23 | int getTimerInterval() const; 24 | void setPacketCount(int pc); 25 | int getPacketCount() const; 26 | public slots: 27 | void Start(); 28 | void Pause(); 29 | void Stop(); 30 | signals: 31 | void RTStarted(); 32 | void RTPaused(); 33 | void RTResumed(); 34 | void RTStopped(); 35 | private: 36 | void PrepareSender(); 37 | 38 | SignalData& m_data; 39 | bool isPaused = false; 40 | QTimer dataTimer; 41 | // props 42 | QString fileToReplay = R"(C:\Users\Marco\Source\Repos\anvedi\Anvedi\json\physx-0204.json)"; 43 | int timerInterval = 200; 44 | RTSender sender; 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /Anvedi/RealTimePresenter.cpp: -------------------------------------------------------------------------------- 1 | #include "RealTimePresenter.h" 2 | #include 3 | 4 | RealTimePresenter::RealTimePresenter(SignalData& data, RTMenuInfo rtMenuActions) 5 | : player(data), socketPlayer(data), rtMenuInfo(rtMenuActions) 6 | { 7 | ui.setupUi(&rtDialog); 8 | 9 | // RT menu -> player 10 | QObject::connect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Start())); 11 | QObject::connect(rtMenuInfo.actionStopRT, SIGNAL(triggered()), &player, SLOT(Stop())); 12 | // RT config -> this 13 | QObject::connect(rtMenuInfo.actionConfig, SIGNAL(triggered()), this, SLOT(OnConfigure())); 14 | // player -> this 15 | QObject::connect(&player, SIGNAL(RTStarted()), this, SLOT(OnStart())); 16 | QObject::connect(&player, SIGNAL(RTPaused()), this, SLOT(OnPause())); 17 | QObject::connect(&player, SIGNAL(RTResumed()), this, SLOT(OnStart())); 18 | QObject::connect(&player, SIGNAL(RTStopped()), this, SLOT(OnStop())); 19 | // RT config dialog choose file button 20 | QObject::connect(ui.btnFile, &QPushButton::pressed, [this]{ 21 | auto file = QFileDialog::getOpenFileName(&rtDialog, "Choose file to RT", ".", "*.json"); 22 | if (!file.isEmpty()) 23 | ui.editFile->setText(file); 24 | }); 25 | 26 | rtMenuInfo.actionStopRT->setEnabled(false); 27 | } 28 | 29 | void RealTimePresenter::OnConfigure() 30 | { 31 | if (SetupRTDialog(), rtDialog.exec() == QDialog::Accepted) 32 | { 33 | player.setFileToReplay(ui.editFile->text()); 34 | player.setPacketCount(ui.spinPacket->value()); 35 | player.setTimerInterval(ui.spinInterval->value()); 36 | } 37 | } 38 | 39 | void RealTimePresenter::OnStart() 40 | { 41 | SetRunning(true); 42 | QObject::disconnect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Start())); 43 | QObject::connect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Pause())); 44 | } 45 | 46 | void RealTimePresenter::OnPause() 47 | { 48 | SetRunning(false); 49 | QObject::disconnect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Pause())); 50 | QObject::connect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Start())); 51 | rtMenuInfo.actionStopRT->setEnabled(true); 52 | } 53 | 54 | void RealTimePresenter::OnStop() 55 | { 56 | SetRunning(false); 57 | QObject::disconnect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Pause())); 58 | QObject::connect(rtMenuInfo.actionStartRT, SIGNAL(triggered()), &player, SLOT(Start())); 59 | } 60 | 61 | void RealTimePresenter::SetupRTDialog() 62 | { 63 | ui.editFile->setText(player.getFileToReplay()); 64 | ui.spinPacket->setValue(player.getPacketCount()); 65 | ui.spinInterval->setValue(player.getTimerInterval()); 66 | } 67 | 68 | void RealTimePresenter::SetRunning(bool running) 69 | { 70 | rtMenuInfo.actionImport->setEnabled(!running); 71 | rtMenuInfo.actionExport->setEnabled(!running); 72 | rtMenuInfo.actionClear->setEnabled(!running); 73 | rtMenuInfo.actionConfig->setEnabled(!running); 74 | rtMenuInfo.actionStartRT->setText(running ? "Pause" : "Start"); 75 | rtMenuInfo.actionStopRT->setEnabled(running); 76 | } 77 | -------------------------------------------------------------------------------- /Anvedi/RealTimePresenter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "RealTimePlayer.h" 4 | #include "ui_rtConfig.h" 5 | #include 6 | #include "RTSocketPlayer.h" 7 | 8 | struct RTMenuInfo 9 | { 10 | QAction* actionStartRT; 11 | QAction* actionStopRT; 12 | QAction* actionConfig; 13 | // data menu 14 | QAction* actionImport; 15 | QAction* actionExport; 16 | QAction* actionClear; 17 | }; 18 | 19 | class RealTimePresenter : public QObject 20 | { 21 | Q_OBJECT 22 | public: 23 | RealTimePresenter(SignalData& data, RTMenuInfo rtMenuActions); 24 | public slots: 25 | void OnConfigure(); 26 | void OnStart(); 27 | void OnPause(); 28 | void OnStop(); 29 | private: 30 | void SetupRTDialog(); 31 | void SetRunning(bool enable); 32 | 33 | Ui::Dialog ui; 34 | QDialog rtDialog; 35 | RTMenuInfo rtMenuInfo; 36 | RealTimePlayer player; 37 | RTSocketPlayer socketPlayer; 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /Anvedi/RectZoomer.cpp: -------------------------------------------------------------------------------- 1 | #include "RectZoomer.h" 2 | #include "qcustomplot.h" 3 | #include 4 | #include "SignalData.h" 5 | #include "Utils.h" 6 | 7 | class IZoomAction 8 | { 9 | public: 10 | virtual QPoint OnMousePress(const QCustomPlot& plot, const QPoint& clickPos) = 0; 11 | virtual QPoint OnMouseMove(const QCustomPlot& plot, const QPoint& clickPos) = 0; 12 | }; 13 | 14 | struct RectZoomAction : IZoomAction 15 | { 16 | QPoint OnMousePress(const QCustomPlot&, const QPoint& clickPos) override { return clickPos; } 17 | QPoint OnMouseMove(const QCustomPlot&, const QPoint& clickPos) override { return clickPos; } 18 | }; 19 | 20 | struct HorizontalZoomAction : IZoomAction 21 | { 22 | static std::pair GetYAxisRect(const QCustomPlot& plot) 23 | { 24 | int xp1, yp1, xp2, yp2; 25 | plot.axisRect()->rect().getCoords(&xp1, &yp1, &xp2, &yp2); 26 | return{ yp1-1, yp2 }; 27 | } 28 | 29 | virtual QPoint OnMousePress(const QCustomPlot& plot, const QPoint& clickPos) override 30 | { 31 | return{ clickPos.x(), GetYAxisRect(plot).first }; 32 | } 33 | 34 | virtual QPoint OnMouseMove(const QCustomPlot& plot, const QPoint& clickPos) override 35 | { 36 | return{ clickPos.x(), GetYAxisRect(plot).second }; 37 | } 38 | }; 39 | 40 | IZoomAction* GetZoomAction(QMouseEvent* mevent) 41 | { 42 | static RectZoomAction rectZoomAction; 43 | static HorizontalZoomAction horizontalZoomAction; 44 | 45 | if (mevent && (mevent->modifiers().testFlag(Qt::ControlModifier))) 46 | return &rectZoomAction; 47 | return &horizontalZoomAction; 48 | } 49 | 50 | 51 | RectZoomer::RectZoomer(QCustomPlot* plot, const SignalData& data) 52 | // note: QCustomPlot as parent is ok because RectZoomer is destroyed before QCustomPlot in Anvedi 53 | : rubberBand(QRubberBand::Rectangle, plot), plot(plot), data(data), zoomAction(GetZoomAction(nullptr)) 54 | { 55 | // connect to QCustomPlot events 56 | QObject::connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(OnMousePress(QMouseEvent*))); 57 | QObject::connect(plot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(OnMouseMove(QMouseEvent*))); 58 | QObject::connect(plot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(OnMouseRelease(QMouseEvent*))); 59 | } 60 | 61 | void RectZoomer::OnMousePress(QMouseEvent* mevent) 62 | { 63 | if (!plot->graphCount()) 64 | return; 65 | 66 | switch (mevent->button()) 67 | { 68 | case Qt::LeftButton: 69 | if (mevent->modifiers().testFlag(Qt::KeyboardModifier::ShiftModifier)) 70 | { 71 | OnResetZoom(); 72 | } 73 | break; 74 | case Qt::RightButton: 75 | zoomAction = GetZoomAction(mevent); 76 | origin = zoomAction->OnMousePress(*plot, mevent->pos()); 77 | rubberBand.setGeometry(QRect(origin, QSize())); 78 | rubberBand.show(); 79 | break; 80 | case Qt::MiddleButton: 81 | OnResetZoom(); 82 | break; 83 | } 84 | } 85 | 86 | void RectZoomer::OnMouseRelease(QMouseEvent*) 87 | { 88 | if (rubberBand.isVisible()) 89 | { 90 | int topLeftX, topLeftY, bottomRightX, bottomRightY; 91 | const auto zoomRect = rubberBand.geometry(); 92 | zoomRect.getCoords(&topLeftX, &topLeftY, &bottomRightX, &bottomRightY); 93 | if (topLeftX < (bottomRightX + 1)) // skip just click 94 | { 95 | ZoomInPixelCoordinates(topLeftX, bottomRightX, bottomRightY, topLeftY); 96 | } 97 | rubberBand.hide(); 98 | } 99 | } 100 | 101 | void RectZoomer::OnMouseMove(QMouseEvent* mevent) 102 | { 103 | if (rubberBand.isVisible()) 104 | { 105 | rubberBand.setGeometry(QRect( 106 | origin, 107 | zoomAction->OnMouseMove(*plot, mevent->pos())).normalized()); 108 | } 109 | } 110 | 111 | void RectZoomer::OnResetZoom() 112 | { 113 | for (auto i = 0; i < plot->graphCount(); ++i) 114 | { 115 | auto graph = plot->graph(i); 116 | const auto& signal = data.get(graph->name()); 117 | graph->valueAxis()->setRange(signal.graphic.rangeLower, signal.graphic.rangeUpper); 118 | } 119 | plot->xAxis->rescale(); 120 | } 121 | 122 | void SetAxisRangeInPixelCoordsWithSaturation(QCPAxis& axis, double rangeLo, double rangeUp) 123 | { 124 | const auto currentRange = axis.range(); 125 | axis.setRange(SaturateLeft(axis.pixelToCoord(rangeLo), currentRange.lower), SaturateRight(axis.pixelToCoord(rangeUp), currentRange.upper)); 126 | } 127 | 128 | void RectZoomer::ZoomInPixelCoordinates(double loX, double upX, double loY, double upY) 129 | { 130 | SetAxisRangeInPixelCoordsWithSaturation(*plot->xAxis, loX, upX); 131 | 132 | for (auto i = 0; i < plot->graphCount(); ++i) 133 | { 134 | SetAxisRangeInPixelCoordsWithSaturation(*plot->graph(i)->valueAxis(), loY, upY); 135 | } 136 | plot->replot(); 137 | } 138 | -------------------------------------------------------------------------------- /Anvedi/RectZoomer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class QCustomPlot; 7 | class QMouseEvent; 8 | class SignalData; 9 | 10 | class RectZoomer : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | RectZoomer(QCustomPlot* plot, const SignalData& data); 15 | public slots: 16 | void OnMousePress(QMouseEvent*); 17 | void OnMouseRelease(QMouseEvent*); 18 | void OnMouseMove(QMouseEvent*); 19 | void OnResetZoom(); 20 | private: 21 | void ZoomInPixelCoordinates(double loX, double upX, double loY, double upY); 22 | 23 | QRubberBand rubberBand; 24 | QPoint origin; 25 | QCustomPlot* plot; 26 | const SignalData& data; 27 | class IZoomAction* zoomAction; 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /Anvedi/ScriptManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptManager.h" 2 | #include "QShellEngine_Qt.h" 3 | #include "SignalData.h" 4 | #include "SignalHandle.h" 5 | #include "qshell.h" 6 | #include "PlotInfo.h" 7 | #include "PlotHandle.h" 8 | #include "RealTimePlayer.h" 9 | #include "RTInteractiveFileSender.h" 10 | #include "RTInteractiveSender.h" 11 | 12 | using namespace std; 13 | 14 | QScriptValue GraphWrapperCtor(QScriptContext *context, QScriptEngine *engine) 15 | { 16 | if (context->isCalledAsConstructor()) 17 | { 18 | QScriptValue calleeData = context->callee().data(); 19 | auto data = qobject_cast(calleeData.toQObject()); 20 | auto signalName = context->argument(0).toString(); 21 | data->addEmptyIfNotExists(signalName); 22 | return engine->newQObject(new SignalHandle(move(signalName), *data), QScriptEngine::ScriptOwnership); 23 | } 24 | return context->throwError(QScriptContext::TypeError, "graph should be constructed"); 25 | } 26 | 27 | QScriptValue SetDomain(QScriptContext *context, QScriptEngine *engine) 28 | { 29 | QScriptValue calleeData = context->callee().data(); 30 | auto data = qobject_cast(calleeData.toQObject()); 31 | if (context->argument(0).isString()) 32 | { 33 | try 34 | { 35 | data->setAsDomain(context->argument(0).toString()); 36 | } 37 | catch (...) 38 | { 39 | return context->throwError(QScriptContext::UnknownError, "the specified signal does not exist"); 40 | } 41 | return QScriptValue::UndefinedValue; 42 | } 43 | else if (auto val = qscriptvalue_cast(context->argument(0))) 44 | { 45 | val->SetThisAsDomain(); 46 | return QScriptValue::UndefinedValue; 47 | } 48 | return context->throwError(QScriptContext::TypeError, "parameter is incorrect (should be either a string or a SignalHandle)"); 49 | } 50 | 51 | QScriptValue GetDomain(QScriptContext *context, QScriptEngine *engine) 52 | { 53 | QScriptValue calleeData = context->callee().data(); 54 | auto data = qobject_cast(calleeData.toQObject()); 55 | if (auto dom = data->getDomain()) 56 | { 57 | return engine->newQObject(new SignalHandle(dom->name, *data), QScriptEngine::ScriptOwnership); 58 | } 59 | else 60 | { 61 | return context->throwError(QScriptContext::TypeError, "Domain is not set yet"); 62 | } 63 | } 64 | 65 | QScriptValue Remove(QScriptContext *context, QScriptEngine *engine) 66 | { 67 | QScriptValue calleeData = context->callee().data(); 68 | auto data = qobject_cast(calleeData.toQObject()); 69 | if (context->argument(0).isString()) 70 | { 71 | try 72 | { 73 | data->remove(context->argument(0).toString()); 74 | } 75 | catch (...) 76 | { 77 | return context->throwError(QScriptContext::UnknownError, "the specified signal does not exist"); 78 | } 79 | } 80 | return QScriptValue::UndefinedValue; 81 | } 82 | 83 | struct AnvediScriptEngine : QShellEngine_Qt 84 | { 85 | AnvediScriptEngine(SignalData& data, PlotInfo& plot) 86 | { 87 | // global properties 88 | m_engine.globalObject().setProperty("plot", m_engine.newQObject(new PlotHandle(plot), QScriptEngine::ScriptOwnership)); 89 | m_engine.globalObject().setProperty("rtPlayer", m_engine.newQObject(new RTInteractiveFileSender(data), QScriptEngine::ScriptOwnership)); 90 | m_engine.globalObject().setProperty("rtInteractive", m_engine.newQObject(new RTInteractiveSender(data), QScriptEngine::ScriptOwnership)); 91 | // global functions 92 | m_engine.globalObject().setProperty("graph", MakeSignalDataFunction(data, GraphWrapperCtor)); 93 | m_engine.globalObject().setProperty("SetDomain", MakeSignalDataFunction(data, SetDomain)); 94 | m_engine.globalObject().setProperty("GetDomain", MakeSignalDataFunction(data, GetDomain)); 95 | m_engine.globalObject().setProperty("Remove", MakeSignalDataFunction(data, Remove)); 96 | // fill function 97 | m_engine.evaluate("Array.prototype.fill = function(fn) { var O = Object(this); for(i=0; i(data, plot); 111 | shell.SetEngine((shared_ptr)scriptEngine); 112 | } 113 | 114 | -------------------------------------------------------------------------------- /Anvedi/ScriptManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class SignalData; 4 | class QShell; 5 | class PlotInfo; 6 | 7 | class ScriptManager 8 | { 9 | public: 10 | static void InitWorkspace(SignalData& data, PlotInfo& plot, QShell& shell); 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /Anvedi/Signal.cpp: -------------------------------------------------------------------------------- 1 | #pragma warning (disable:4996) // due to QVector operator== 2 | 3 | #include "Signal.h" 4 | 5 | bool Signal::operator==(const Signal& other) const 6 | { 7 | return std::tie(name, y, graphic) == 8 | std::tie(other.name, other.y, other.graphic); 9 | } 10 | 11 | bool SignalGraphic::operator==(const SignalGraphic& other) const 12 | { 13 | return std::tie(color, visible, rangeLower, rangeUpper, ticks, tickLabels) == 14 | std::tie(other.color, other.visible, other.rangeLower, other.rangeUpper, other.ticks, other.tickLabels); 15 | } 16 | -------------------------------------------------------------------------------- /Anvedi/Signal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "QMetaType" 7 | 8 | struct SignalGraphic 9 | { 10 | QColor color; 11 | bool visible; 12 | double rangeLower, rangeUpper; 13 | QVector ticks; 14 | QVector tickLabels; 15 | 16 | bool operator==(const SignalGraphic& other) const; 17 | }; 18 | 19 | struct Signal 20 | { 21 | QString name; 22 | QVector y; 23 | SignalGraphic graphic; // (*) 24 | 25 | bool operator==(const Signal& other) const; 26 | }; 27 | 28 | /* (*) 29 | Important Note: this is a design shortcut because domain data (e.g. y values) 30 | should be completely detached from the graphic info. 31 | */ 32 | 33 | 34 | Q_DECLARE_METATYPE(Signal) -------------------------------------------------------------------------------- /Anvedi/SignalData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Signal.h" 7 | #include 8 | 9 | using DataMap = std::map < QString, Signal > ; 10 | 11 | class SignalData : public QObject 12 | { 13 | Q_OBJECT 14 | public: 15 | void add(DataMap data); 16 | void addEmptyIfNotExists(const QString& name); 17 | void addIfNotExists(Signal signal); 18 | void clear(); 19 | void rename(const QString& what, const QString& newName); 20 | void remove(const QString& what); 21 | // getters 22 | const Signal& get(const QString& name) const; 23 | const Signal* getDomain() const; 24 | // setters 25 | void setAsDomain(const QString& name); 26 | void setValues(const QString& name, QVector vec); 27 | void addValues(const std::map>& data); 28 | void setSignalGraphic(const QString& name, SignalGraphic info); 29 | void setColor(const QString& name, const QColor& col); 30 | void setVisible(const QString& name, bool visible); 31 | void setRange(const QString& name, double lo, double up); 32 | void setAutoRange(const QString& name); 33 | void setAutoRangeAll(); 34 | void setTicks(const QString& name, QVector ticks); 35 | void setTickLabels(const QString& name, QVector ticks); 36 | // visitor 37 | void onSignals(std::function fun) const; 38 | // utils 39 | std::pair domainLowerBound(qreal val) const; 40 | qreal domainNextValue(qreal refValue) const; 41 | qreal domainPrevValue(qreal refValue) const; 42 | signals: 43 | void DataAdded(const DataMap&); 44 | void SignalRemoved(const QString&); 45 | void SignalRenamed(const QString& oldName, const Signal&); 46 | void SignalValuesChanged(const Signal&); 47 | void SignalValuesAdded(const Signal&, const QVector&, const QVector&); 48 | void SignalAdded(const QVector&, const std::map>&); 49 | void SignalGraphicChanged(const Signal&); 50 | void SignalColorChanged(const Signal&); 51 | void SignalVisibilityChanged(const Signal&); 52 | void SignalRangeChanged(const Signal&); 53 | void SignalTicksChanged(const Signal&); 54 | void DataCleared(); 55 | void DomainChanged(const Signal&); 56 | private: 57 | Signal& getOrInsert(const QString& name); 58 | 59 | DataMap m_data; 60 | Signal* domain = nullptr; 61 | }; 62 | 63 | Q_DECLARE_METATYPE(DataMap) -------------------------------------------------------------------------------- /Anvedi/SignalHandle.cpp: -------------------------------------------------------------------------------- 1 | #include "SignalHandle.h" 2 | #include "SignalData.h" 3 | #include "Utils.h" 4 | 5 | SignalHandle::SignalHandle(QString name, SignalData& data) 6 | : signalName(std::move(name)), data(data) 7 | { 8 | 9 | } 10 | 11 | bool SignalHandle::isVisible() const 12 | { 13 | return data.get(signalName).graphic.visible; 14 | } 15 | 16 | void SignalHandle::setVisible(bool visible) 17 | { 18 | data.setVisible(signalName, visible); 19 | } 20 | 21 | QString SignalHandle::getColor() const 22 | { 23 | return data.get(signalName).graphic.color.name(); 24 | } 25 | 26 | void SignalHandle::setColor(const QString& color) 27 | { 28 | data.setColor(signalName, color); 29 | } 30 | 31 | QVariant SignalHandle::getValues() const 32 | { 33 | return ToVariant(data.get(signalName).y); 34 | } 35 | 36 | void SignalHandle::setValues(const QVariant& values) 37 | { 38 | data.setValues(signalName, ToVector(values)); 39 | } 40 | 41 | void SignalHandle::SetThisAsDomain() 42 | { 43 | data.setAsDomain(signalName); 44 | } 45 | 46 | QString SignalHandle::toString() const 47 | { 48 | return QString("Handle to signal '%1'").arg(signalName); 49 | } 50 | 51 | QVariant SignalHandle::getRange() const 52 | { 53 | const auto& signal = data.get(signalName); 54 | return QVariantList{ signal.graphic.rangeLower, signal.graphic.rangeUpper }; 55 | } 56 | 57 | void SignalHandle::setRange(const QVariant& values) 58 | { 59 | if ((values.type() == QVariant::String) && (values.toString() == "auto")) 60 | { 61 | data.setAutoRange(signalName); 62 | } 63 | else 64 | { 65 | const auto rangeToVec = ToVector(values); 66 | if (rangeToVec.size() == 2) 67 | { 68 | data.setRange(signalName, rangeToVec.front(), rangeToVec.back()); 69 | } 70 | } 71 | } 72 | 73 | QVariant SignalHandle::getTicks() const 74 | { 75 | return ToVariant(data.get(signalName).graphic.ticks); 76 | } 77 | 78 | void SignalHandle::setTicks(const QVariant& values) 79 | { 80 | data.setTicks(signalName, ToVector(values)); 81 | } 82 | 83 | QString SignalHandle::getName() const 84 | { 85 | return signalName; 86 | } 87 | 88 | void SignalHandle::setName(const QString& name) 89 | { 90 | try 91 | { 92 | data.rename(signalName, name); 93 | signalName = name; 94 | } 95 | catch (...) 96 | {} 97 | } 98 | -------------------------------------------------------------------------------- /Anvedi/SignalHandle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class SignalData; 8 | 9 | class SignalHandle : public QObject 10 | { 11 | Q_OBJECT 12 | Q_PROPERTY(bool visible READ isVisible WRITE setVisible) 13 | Q_PROPERTY(QString name READ getName WRITE setName) 14 | Q_PROPERTY(QString color READ getColor WRITE setColor) 15 | Q_PROPERTY(QVariant values READ getValues WRITE setValues) 16 | Q_PROPERTY(QVariant range READ getRange WRITE setRange) 17 | Q_PROPERTY(QVariant ticks READ getTicks WRITE setTicks) 18 | public: 19 | SignalHandle(QString name, SignalData& data); 20 | 21 | // props 22 | QString getName() const; 23 | void setName(const QString& name); 24 | bool isVisible() const; 25 | void setVisible(bool visible); 26 | QString getColor() const; 27 | void setColor(const QString& color); 28 | QVariant getValues() const; 29 | void setValues(const QVariant& values); 30 | QVariant getRange() const; 31 | void setRange(const QVariant& values); 32 | QVariant getTicks() const; 33 | void setTicks(const QVariant& values); 34 | 35 | void SetThisAsDomain(); 36 | public slots: 37 | QString toString() const; 38 | private: 39 | QString signalName; 40 | SignalData& data; 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /Anvedi/SignalInfoPresenter.cpp: -------------------------------------------------------------------------------- 1 | #include "SignalInfoPresenter.h" 2 | #include "SignalData.h" 3 | #include 4 | #include 5 | 6 | SignalInfoPresenter::SignalInfoPresenter(SignalData& data) 7 | : data(data) 8 | { 9 | ui.setupUi(&configDialog); 10 | } 11 | 12 | template 13 | QString ToString(const QVector& vec, char sep = ' ') 14 | { 15 | QString ss; 16 | for (const auto& elem : vec) 17 | ss.append(QString("%1%2").arg(elem).arg(sep)); 18 | return ss; 19 | } 20 | 21 | QVector GetTicks(const QString& vec) 22 | { 23 | auto strList = vec.split(' ', QString::SkipEmptyParts); 24 | QVector ticks; ticks.reserve(strList.size()); 25 | std::transform(strList.begin(), strList.end(), std::back_inserter(ticks), [](const QString& str){ 26 | return str.toDouble(); 27 | }); 28 | return ticks; 29 | } 30 | 31 | QVector GetTickLabels(const QString& vec) 32 | { 33 | return vec.split(',', QString::SkipEmptyParts).toVector(); 34 | } 35 | 36 | void SignalInfoPresenter::Config(const QString& signalName) 37 | { 38 | const auto& signal = data.get(signalName); 39 | configDialog.setWindowTitle(QString("Edit Signal Info - %1").arg(signalName)); 40 | ui.editName->setText(signal.name); 41 | ui.spinRangeMin->setValue(signal.graphic.rangeLower); 42 | ui.spinRangeMax->setValue(signal.graphic.rangeUpper); 43 | ui.editTicks->setText(ToString(signal.graphic.ticks)); 44 | ui.editTickLabels->setText(ToString(signal.graphic.tickLabels, ',')); 45 | 46 | if (configDialog.exec() == QDialog::Accepted) 47 | { 48 | if (ui.spinRangeMin->value() == ui.spinRangeMax->value() && ui.spinRangeMax->value() == 0.0) 49 | data.setAutoRange(signal.name); 50 | else 51 | data.setRange(signal.name, ui.spinRangeMin->value(), ui.spinRangeMax->value()); 52 | data.setTicks(signalName, GetTicks(ui.editTicks->text())); 53 | data.setTickLabels(signalName, GetTickLabels(ui.editTickLabels->text())); 54 | try 55 | { 56 | if (ui.editName->text() != signalName) 57 | data.rename(signalName, ui.editName->text()); 58 | } 59 | catch (const std::exception& ex) 60 | { 61 | QMessageBox::critical(nullptr, tr("Anvedi"), 62 | tr("The name already exists...")); 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Anvedi/SignalInfoPresenter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ui_signalInfo.h" 4 | 5 | class SignalData; 6 | 7 | class SignalInfoPresenter : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | SignalInfoPresenter(SignalData& data); 12 | void Config(const QString& signalName); 13 | private: 14 | Ui::SignalInfoDlg ui; 15 | QDialog configDialog; 16 | SignalData& data; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /Anvedi/SignalListPresenter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "GraphPresenter.h" 4 | #include "SignalInfoPresenter.h" 5 | 6 | class QTableWidget; 7 | class QLabel; 8 | class SignalData; 9 | class QLineEdit; 10 | class QTableWidgetItem; 11 | 12 | class SignalListPresenter : public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | SignalListPresenter(QTableWidget* signalList, QLineEdit* filterEdit, QLabel* signalCntLabel, QLabel* domainLabel, SignalData& data); 17 | public slots: 18 | void OnNewData(const DataMap& data); 19 | void OnSignalRemoved(const QString& name); 20 | void OnSignalRenamed(const QString& oldName, const Signal& signal); 21 | void OnClearData(); 22 | void OnSignalFilterEdited(const QString& filter); 23 | void OnSignalGraphicChanged(const Signal& signal); 24 | void OnSignalVisibilityChanged(const Signal& signal); 25 | void OnSignalColorChanged(const Signal& signal); 26 | void OnCursorValueChanged(qreal xVal, size_t idx); 27 | void OnDomainChanged(const Signal& newDomain); 28 | private: 29 | SignalInfoPresenter infoPresenter; 30 | QTableWidget* signalList; 31 | QLineEdit* filterEdit; 32 | QLabel* signalCntLabel; 33 | QLabel* domainLabel; 34 | QTableWidgetItem* domain; 35 | SignalData& data; 36 | }; -------------------------------------------------------------------------------- /Anvedi/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | #include 3 | #include 4 | 5 | bool close(const QColor& c1, const QColor& c2) 6 | { 7 | return std::sqrt( 8 | std::norm(c1.red() - c2.red()) + 9 | std::norm(c1.green() - c2.green()) + 10 | std::norm(c1.blue() - c2.blue())) < 50; 11 | } 12 | 13 | QVector ToVector(const QVariant& var) 14 | { 15 | const auto varList = var.toList(); 16 | return ToVector(varList); 17 | } 18 | 19 | QVector ToVector(const QVariantList& varList) 20 | { 21 | QVector vals; vals.reserve(varList.size()); 22 | std::transform(varList.begin(), varList.end(), std::back_inserter(vals), [](const QVariant& v) { 23 | return v.toReal(); 24 | }); 25 | return vals; 26 | } 27 | 28 | QVariant ToVariant(const QVector& vec) 29 | { 30 | QVariantList vals; vals.reserve(vec.size()); 31 | std::copy(vec.begin(), vec.end(), std::back_inserter(vals)); 32 | return vals; 33 | } 34 | 35 | bool InvertPenColorIfNearTo(QPen& pen, const QColor& color) 36 | { 37 | auto areClose = close(color, pen.color()); 38 | if (areClose) 39 | { 40 | pen.setColor(invert(color)); 41 | } 42 | return areClose; 43 | } 44 | -------------------------------------------------------------------------------- /Anvedi/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class QPen; 7 | 8 | bool close(const QColor& c1, const QColor& c2); 9 | 10 | inline QColor invert(const QColor& col) 11 | { 12 | return{ 255 - col.red(), 255 - col.green(), 255 - col.blue() }; 13 | } 14 | 15 | bool InvertPenColorIfNearTo(QPen& pen, const QColor& color); 16 | 17 | template 18 | T SaturateLeft(const T& what, const T& within) 19 | { 20 | return (what < within) ? within : what; 21 | } 22 | 23 | template 24 | T SaturateRight(const T& what, const T& within) 25 | { 26 | return (what > within) ? within : what; 27 | } 28 | 29 | QVector ToVector(const QVariant& var); 30 | QVector ToVector(const QVariantList& varList); 31 | QVariant ToVariant(const QVector& vec); -------------------------------------------------------------------------------- /Anvedi/WorkspaceSerializer.cpp: -------------------------------------------------------------------------------- 1 | #include "WorkspaceSerializer.h" 2 | #include "SignalData.h" 3 | #include "PlotInfo.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | QVector ToVector(const QJsonArray& arr) 13 | { 14 | QVector vec; vec.reserve(arr.size()); 15 | std::transform(arr.begin(), arr.end(), std::back_inserter(vec), [](const QJsonValue& value) { 16 | return value.toDouble(); 17 | }); 18 | return vec; 19 | } 20 | 21 | QVector ToStrVector(const QJsonArray& arr) 22 | { 23 | QVector vec; vec.reserve(arr.size()); 24 | std::transform(arr.begin(), arr.end(), std::back_inserter(vec), [](const QJsonValue& value) { 25 | return value.toString(); 26 | }); 27 | return vec; 28 | } 29 | 30 | Signal ReadGraph(const QJsonObject& obj) 31 | { 32 | Signal signal{}; 33 | 34 | auto it = obj.find("name"); 35 | if (it != obj.end()) 36 | { 37 | const auto sigName = it->toString(); 38 | signal.name = std::move(sigName); 39 | 40 | it = obj.find("color"); 41 | if (it != obj.end()) 42 | signal.graphic.color = it->toString(); 43 | 44 | it = obj.find("visible"); 45 | if (it != obj.end()) 46 | signal.graphic.visible = it->toBool(); 47 | 48 | it = obj.find("range"); 49 | if (it != obj.end()) 50 | { 51 | auto vec = ToVector(it->toArray()); 52 | signal.graphic.rangeLower = vec.front(); 53 | signal.graphic.rangeUpper = vec.back(); 54 | } 55 | 56 | it = obj.find("values"); 57 | if (it != obj.end() && it->isArray()) 58 | { 59 | signal.y = ToVector(it->toArray()); 60 | } 61 | 62 | it = obj.find("ticks"); 63 | if (it != obj.end() && it->isArray()) 64 | { 65 | signal.graphic.ticks = ToVector(it->toArray()); 66 | } 67 | 68 | it = obj.find("tickLabels"); 69 | if (it != obj.end() && it->isArray()) 70 | { 71 | signal.graphic.tickLabels = ToStrVector(it->toArray()); 72 | } 73 | } 74 | 75 | return signal; 76 | } 77 | 78 | void Read(const QString& fileName, SignalData& data, PlotInfo& plotInfo, std::function onSignal, std::function onAfterSignalRead) 79 | { 80 | QFile in(fileName); 81 | if (!in.open(QIODevice::ReadOnly | QFile::Text)) 82 | { 83 | auto mex = QString("Cannot open specified file -> %1").arg(fileName); 84 | QMessageBox::critical(nullptr, "Anvedi", mex); 85 | throw std::exception(mex.toUtf8().data()); 86 | } 87 | const auto json = QJsonDocument::fromJson(in.readAll()).object(); 88 | 89 | auto backgroundIt = json.find("background"); 90 | if (backgroundIt != json.end()) 91 | { 92 | plotInfo.setBackgroundColor(backgroundIt->toString()); 93 | } 94 | auto graphIt = json.find("signals"); 95 | if ((graphIt != json.end()) && graphIt->isArray()) 96 | { 97 | for (const auto& elem : graphIt->toArray()) 98 | { 99 | if (elem.isObject()) 100 | { 101 | onSignal(ReadGraph(elem.toObject())); 102 | } 103 | } 104 | } 105 | 106 | onAfterSignalRead(); 107 | 108 | auto domainIt = json.find("domain"); 109 | if (domainIt != json.end()) 110 | { 111 | const auto domainName = domainIt->toString(); 112 | try 113 | { 114 | data.setAsDomain(domainName); 115 | } 116 | catch (const std::exception&){} 117 | } 118 | } 119 | 120 | void WorkspaceSerializer::Read(const QString& fileName, SignalData& data, PlotInfo& plotInfo) 121 | { 122 | DataMap newData; 123 | ::Read(fileName, data, plotInfo, [&](Signal&& signal){ 124 | auto signalName = signal.name; 125 | newData.emplace(std::move(signalName), std::move(signal)); 126 | }, [&]{ 127 | data.add(std::move(newData)); 128 | }); 129 | } 130 | 131 | void WorkspaceSerializer::Read(const QString& fileName, SignalData& data, PlotInfo& plotInfo, std::function onSignal) 132 | { 133 | ::Read(fileName, data, plotInfo, onSignal, []{}); 134 | } 135 | 136 | QJsonArray ToJsonArray(const QVector& vec) 137 | { 138 | QJsonArray arr; 139 | std::copy(vec.begin(), vec.end(), std::back_inserter(arr)); 140 | return arr; 141 | } 142 | 143 | QJsonArray ToJsonArray(const QVector& vec) 144 | { 145 | QJsonArray arr; 146 | std::copy(vec.begin(), vec.end(), std::back_inserter(arr)); 147 | return arr; 148 | } 149 | 150 | void WorkspaceSerializer::Write(const QString& fileName, const SignalData& data, const PlotInfo& plotInfo, bool writeValues /*= true*/) 151 | { 152 | QJsonObject json; 153 | json["background"] = plotInfo.getBackgroundColor().name(); 154 | if (writeValues) 155 | { 156 | if (auto domain = data.getDomain()) 157 | json["domain"] = domain->name; 158 | } 159 | 160 | QJsonArray signArray; 161 | data.onSignals([&](const Signal& signal){ 162 | QJsonObject signObj; 163 | signObj["name"] = signal.name; 164 | signObj["color"] = signal.graphic.color.name(); 165 | signObj["visible"] = signal.graphic.visible; 166 | signObj["range"] = ToJsonArray(QVector < qreal > {signal.graphic.rangeLower, signal.graphic.rangeUpper}); 167 | if (writeValues) 168 | signObj["values"] = ToJsonArray(signal.y); 169 | signObj["ticks"] = ToJsonArray(signal.graphic.ticks); 170 | signObj["tickLabels"] = ToJsonArray(signal.graphic.tickLabels); 171 | signArray.push_back(std::move(signObj)); 172 | }); 173 | json["signals"] = signArray; 174 | 175 | QJsonDocument saveDoc(json); 176 | QFile outF(fileName); 177 | outF.open(QIODevice::WriteOnly); 178 | outF.write(saveDoc.toJson(QJsonDocument::Indented)); 179 | } -------------------------------------------------------------------------------- /Anvedi/WorkspaceSerializer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "Signal.h" 5 | 6 | class SignalData; 7 | class PlotInfo; 8 | 9 | class WorkspaceSerializer 10 | { 11 | public: 12 | static void Read(const QString& fileName, SignalData& data, PlotInfo& plotInfo); 13 | static void Read(const QString& fileName, SignalData& data, PlotInfo& plotInfo, std::function onSignal); 14 | static void Write(const QString& fileName, const SignalData& data, const PlotInfo& plotInfo, bool writeValues = true); 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /Anvedi/anvedi.cpp: -------------------------------------------------------------------------------- 1 | #include "anvedi.h" 2 | #include 3 | #include "SignalHandle.h" 4 | #include "WorkspaceSerializer.h" 5 | #include "ScriptManager.h" 6 | 7 | using namespace std; 8 | 9 | Anvedi::Anvedi(QWidget *parent) 10 | : QMainWindow(parent) 11 | { 12 | ui.setupUi(this); 13 | 14 | m_plotInfo.setPlot(ui.plot); 15 | m_plotInfo.setBackgroundColor(Qt::white); 16 | 17 | cursor = make_unique(ui.plot, m_data); 18 | rectZoomer = make_unique(ui.plot, m_data); 19 | signalListPresenter = make_unique(ui.signalList, ui.filterEdit, ui.signalCountLabel, ui.domainLabel, m_data); 20 | graphPresenter = make_unique(ui.plot, ui.rangeScroll, m_data, m_plotInfo); 21 | rtPresenter = make_unique(m_data, RTMenuInfo{ui.actionStartRT, ui.actionStopRT, ui.actionConfig, ui.actionImport, ui.actionExport, ui.actionClear}); 22 | 23 | ScriptManager::InitWorkspace(m_data, m_plotInfo, *ui.console); 24 | 25 | QObject::connect(ui.actionResetAxes, &QAction::triggered, [this]{ 26 | m_data.setAutoRangeAll(); 27 | }); 28 | // cursor -> list 29 | QObject::connect(cursor.get(), SIGNAL(CursorChanged(qreal, size_t)), signalListPresenter.get(), SLOT(OnCursorValueChanged(qreal, size_t))); 30 | // cursor -> graph 31 | QObject::connect(cursor.get(), SIGNAL(CursorChanged(qreal, size_t)), graphPresenter.get(), SLOT(OnCursorValueChanged(qreal, size_t))); 32 | // RT cursor -> cursor 33 | QObject::connect(ui.actionCursorRT, SIGNAL(triggered(bool)), cursor.get(), SLOT(OnSetCursorFollowingInRealTime(bool))); 34 | // plot info -> cursor 35 | QObject::connect(&m_plotInfo, SIGNAL(BackgroundColorChanged(const QColor&)), cursor.get(), SLOT(OnBackgroundChanged(const QColor&))); 36 | // window -> plot info 37 | QObject::connect(ui.actionChangeBackground, &QAction::triggered, [this]{ 38 | const auto color = QColorDialog::getColor(m_plotInfo.getBackgroundColor(), this, "Change the background color"); 39 | if (color.isValid()) 40 | m_plotInfo.setBackgroundColor(color); 41 | }); 42 | 43 | ui.actionCursorRT->trigger(); 44 | } 45 | 46 | void Anvedi::OnExit() 47 | { 48 | QApplication::exit(); 49 | } 50 | 51 | void Anvedi::OnDataImport() 52 | { 53 | const auto files = QFileDialog::getOpenFileNames(this, "Import as JSON", ".", "*.json"); 54 | if (files.isEmpty()) 55 | return; 56 | for (const auto& file : files) 57 | { 58 | WorkspaceSerializer::Read(file, m_data, m_plotInfo); 59 | } 60 | cursor->reset(); 61 | } 62 | 63 | void Anvedi::OnDataExport() 64 | { 65 | auto file = QFileDialog::getSaveFileName(this, "Export as JSON", ".", "*.json"); 66 | if (file.isEmpty()) 67 | return; 68 | if (!file.endsWith(".json")) 69 | file.append(".json"); 70 | WorkspaceSerializer::Write(file, m_data, m_plotInfo); 71 | } 72 | 73 | void Anvedi::OnDataClear() 74 | { 75 | m_data.clear(); 76 | ScriptManager::InitWorkspace(m_data, m_plotInfo, *ui.console); 77 | cursor->reset(); 78 | } 79 | 80 | void Anvedi::keyPressEvent(QKeyEvent * e) 81 | { 82 | if (e->key() == Qt::Key_Escape) 83 | QApplication::exit(); 84 | cursor->OnKeyboardPressed(e); 85 | } 86 | 87 | void Anvedi::OnChartImport() 88 | { 89 | const auto file = QFileDialog::getOpenFileName(this, "Import plot", ".", "*.plt"); 90 | if (file.isEmpty()) 91 | return; 92 | WorkspaceSerializer::Read(file, m_data, m_plotInfo, [this](Signal&& signal){ 93 | m_data.addEmptyIfNotExists(signal.name); 94 | m_data.setSignalGraphic(signal.name, std::move(signal.graphic)); 95 | }); 96 | } 97 | 98 | void Anvedi::OnChartExport() 99 | { 100 | auto file = QFileDialog::getSaveFileName(this, "Export plot", ".", "*.plt"); 101 | if (file.isEmpty()) 102 | return; 103 | if (!file.endsWith(".plt")) 104 | file.append(".plt"); 105 | WorkspaceSerializer::Write(file, m_data, m_plotInfo, false); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /Anvedi/anvedi.h: -------------------------------------------------------------------------------- 1 | #ifndef ANVEDI_H 2 | #define ANVEDI_H 3 | 4 | #include 5 | #include "ui_anvedi.h" 6 | #include "PlotCursor.h" 7 | #include 8 | #include "SignalListPresenter.h" 9 | #include "GraphPresenter.h" 10 | #include 11 | #include "SignalData.h" 12 | #include "RectZoomer.h" 13 | #include "PlotInfo.h" 14 | #include "RealTimePlayer.h" 15 | #include "RealTimePresenter.h" 16 | 17 | class Anvedi : public QMainWindow 18 | { 19 | Q_OBJECT 20 | public: 21 | Anvedi(QWidget *parent = 0); 22 | public slots: 23 | void OnExit(); 24 | void OnDataImport(); 25 | void OnDataExport(); 26 | void OnDataClear(); 27 | void OnChartImport(); 28 | void OnChartExport(); 29 | protected: 30 | void keyPressEvent(QKeyEvent * e); 31 | private: 32 | Ui::AnvediClass ui; 33 | 34 | std::unique_ptr cursor; 35 | std::unique_ptr rectZoomer; 36 | std::unique_ptr signalListPresenter; 37 | std::unique_ptr graphPresenter; 38 | std::unique_ptr rtPresenter; 39 | 40 | SignalData m_data; 41 | PlotInfo m_plotInfo; 42 | }; 43 | 44 | #endif // ANVEDI_H 45 | -------------------------------------------------------------------------------- /Anvedi/anvedi.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/README.md: -------------------------------------------------------------------------------- 1 | ## QCustomPlot QML 2 | 3 | [QCustomPlot](http://qcustomplot.com) can be wrapped to be used into QML snippets. Inheriting from [QQuickPaintedItem](http://doc.qt.io/qt-5/qquickpainteditem.html) is a good compromise. 4 | 5 | I also wrapped some QCustomPlot components so they can be instantiated in QML code. This way one can create graphs easily in QML code. This is just an experimental porting, use it judiciously. 6 | 7 | ### Example 8 | 9 | Just create a Qt project depending on QCustomPlot (you can also add .h and .cpp files directly into your project) and on these Qt modules: 10 | * Core 11 | * GUI 12 | * Print Support (needed by QCustomPlot) 13 | * QML 14 | * Quick 15 | * Widgets 16 | 17 | Here is a possible main file: 18 | 19 | ```cpp 20 | #include 21 | #include 22 | #include "../Anvedi/qml-lib/RegisterAll.h" 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | QApplication a(argc, argv); 27 | QmlModulesInstaller::Install(); 28 | QQuickView view(QUrl("../Anvedi/qml/main.qml")); // qml to load 29 | view.setResizeMode(QQuickView::SizeRootObjectToView); 30 | view.resize(800, 600); 31 | view.show(); 32 | return a.exec(); 33 | } 34 | ``` 35 | 36 | QmlModulesInstaller::Install() will register all implemented types. 37 | 38 | #### A QML file 39 | 40 | Once you instantiate the main component (*CustomPlotItem*), you can customize it a bit: 41 | 42 | ```css 43 | import QtQuick 2.0 44 | import CustomPlot 1.0 45 | 46 | Item { 47 | CustomPlotItem { 48 | id : customPlot 49 | background : "white" 50 | legend : Legend { 51 | font : "helvetica,-1,18,5,0,0,0,0,0,0" 52 | } 53 | ... 54 | ``` 55 | 56 | *Legend* is a custom type (it's pretty much incomplete, but it just to give you an idea). 57 | 58 | CustomPlotItem has a property called *graphs* that is an array of *Graph*: 59 | 60 | ```css 61 | graphs : [ 62 | Graph { 63 | name : "parabola" 64 | pen : Pen { 65 | color : "blue" 66 | width : 2.0 67 | } 68 | scatter : ScatterStyle { 69 | brush : "white" 70 | type : Scatter.ssCircle 71 | size : 9 72 | pen : Pen { 73 | color : "black" 74 | width: 1.5 75 | } 76 | } 77 | xAxis : Axis { 78 | useDefault : true 79 | label : Label { 80 | text : "domain" 81 | } 82 | } 83 | yAxis : Axis { 84 | useDefault : true 85 | label : Label { 86 | text : "parabola" 87 | } 88 | } 89 | }, 90 | ... 91 | ``` 92 | 93 | As you see, I wrapped several properties to customize a graph. Obviously this porting is really incomplete, but it should be simple - and long - to implement missing things. 94 | 95 | Adding data and setting range can be done via Javascript, for example in the [OnCompleted](http://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal) signal: 96 | 97 | ```js 98 | Component.onCompleted: { 99 | var domain = []; 100 | var y = []; 101 | 102 | for (var i=0; i<20; ++i) 103 | { 104 | domain[i] = i; 105 | y[i] = i*i; 106 | } 107 | 108 | addData(0, domain, y); // adding data to 0th graph 109 | setYRange(0, [-10, 10]); // setting range of 0th graph 110 | } 111 | ``` 112 | 113 | ![Rendering of the previous example](https://github.com/ilpropheta/anvedi/blob/master/pics/qml-example.png) 114 | 115 | QCustomPlot's addData function is provided also to simulate real-time behavior. For example: 116 | 117 | ```js 118 | Timer { 119 | interval: 300; running: true; repeat: true 120 | 121 | property double xCurr: 0.0 122 | 123 | onTriggered: { 124 | 125 | var newX = []; 126 | var newY = []; 127 | for (var i = 0; i < 5; i++) 128 | { 129 | newX[i] = xCurr; 130 | newY[i] = Math.sin(xCurr*Math.PI/100.0); 131 | xCurr += i; 132 | } 133 | customPlot.addData(0, newX, newY); 134 | } 135 | } 136 | ``` 137 | 138 | ![Real-Time via QML](https://github.com/ilpropheta/anvedi/blob/master/pics/qml-real-time.gif) 139 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/RegisterAll.cpp: -------------------------------------------------------------------------------- 1 | #include "RegisterAll.h" 2 | #include 3 | #include "qmlPlotPaintedItem.h" 4 | #include "qmlGraph.h" 5 | #include "qcustomplot.h" 6 | #include "qmlLabel.h" 7 | #include "qmlTick.h" 8 | 9 | void QmlModulesInstaller::Install() 10 | { 11 | qmlRegisterType("CustomPlot", 1, 0, "CustomPlotItem"); 12 | qmlRegisterType("CustomPlot", 1, 0, "Graph"); 13 | qmlRegisterType("CustomPlot", 1, 0, "ScatterStyle"); 14 | qmlRegisterUncreatableType("CustomPlot", 1, 0, "Scatter", "Objects of this type cannot be created"); 15 | qmlRegisterUncreatableType("CustomPlot", 1, 0, "Plot", "Objects of this type cannot be created"); 16 | qmlRegisterType("CustomPlot", 1, 0, "Pen"); 17 | qmlRegisterType("CustomPlot", 1, 0, "Legend"); 18 | qmlRegisterType("CustomPlot", 1, 0, "Axis"); 19 | qmlRegisterType("CustomPlot", 1, 0, "Label"); 20 | qmlRegisterType("CustomPlot", 1, 0, "Tick"); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/RegisterAll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct QmlModulesInstaller 4 | { 5 | static void Install(); 6 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlAxis.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlAxis.h" 2 | #include "qmlLabel.h" 3 | #include "qmlTick.h" 4 | 5 | qmlLabel* qmlAxis::getLabel() const 6 | { 7 | return label; 8 | } 9 | 10 | void qmlAxis::setLabel(qmlLabel* l) 11 | { 12 | label = l; 13 | } 14 | 15 | qmlTick* qmlAxis::getTick() const 16 | { 17 | return tick; 18 | } 19 | 20 | void qmlAxis::setTick(qmlTick* t) 21 | { 22 | tick = t; 23 | } 24 | 25 | bool qmlAxis::isDefault() const 26 | { 27 | return default; 28 | } 29 | 30 | void qmlAxis::setUseDefault(bool d) 31 | { 32 | default = d; 33 | } 34 | 35 | bool qmlAxis::isVisible() const 36 | { 37 | return visible; 38 | } 39 | 40 | void qmlAxis::setVisible(bool d) 41 | { 42 | visible = d; 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlAxis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class qmlLabel; 5 | class qmlTick; 6 | 7 | class qmlAxis : public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(qmlLabel* label READ getLabel WRITE setLabel) 11 | Q_PROPERTY(qmlTick* tick READ getTick WRITE setTick) 12 | Q_PROPERTY(bool useDefault READ isDefault WRITE setUseDefault) 13 | Q_PROPERTY(bool visible READ isVisible WRITE setVisible) 14 | public: 15 | qmlLabel* getLabel() const; 16 | void setLabel(qmlLabel* l); 17 | qmlTick* getTick() const; 18 | void setTick(qmlTick* t); 19 | bool isDefault() const; 20 | void setUseDefault(bool d); 21 | bool isVisible() const; 22 | void setVisible(bool d); 23 | private: 24 | qmlLabel* label = nullptr; 25 | qmlTick* tick = nullptr; 26 | bool default = true; 27 | bool visible = true; 28 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlGraph.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlGraph.h" 2 | 3 | const QString& qmlGraph::getName() const 4 | { 5 | return name; 6 | } 7 | 8 | void qmlGraph::setName(const QString& arg) 9 | { 10 | name = arg; 11 | } 12 | 13 | qmlAxis* qmlGraph::getXAxis() const 14 | { 15 | return xAxis; 16 | } 17 | 18 | void qmlGraph::setXAxis(qmlAxis* axis) 19 | { 20 | xAxis = axis; 21 | } 22 | 23 | qmlAxis* qmlGraph::getYAxis() const 24 | { 25 | return yAxis; 26 | } 27 | 28 | void qmlGraph::setYAxis(qmlAxis* axis) 29 | { 30 | yAxis = axis; 31 | } 32 | 33 | qmlScatterStyle* qmlGraph::getScatter() 34 | { 35 | return scatter; 36 | } 37 | 38 | void qmlGraph::setScatter(qmlScatterStyle* s) 39 | { 40 | scatter = s; 41 | } 42 | 43 | qmlPen* qmlGraph::getPen() 44 | { 45 | return pen; 46 | } 47 | 48 | void qmlGraph::setPen(qmlPen* p) 49 | { 50 | pen = p; 51 | } 52 | 53 | QCPGraph::LineStyle qmlGraph::getLineStyle() const 54 | { 55 | return lineStyle; 56 | } 57 | 58 | void qmlGraph::setLineStyle(QCPGraph::LineStyle l) 59 | { 60 | lineStyle = l; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "qcustomplot.h" 4 | #include "qmlScatterStyle.h" 5 | #include "qmlAxis.h" 6 | 7 | class qmlGraph : public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(QString name READ getName WRITE setName) 11 | Q_PROPERTY(qmlAxis* xAxis READ getXAxis WRITE setXAxis) 12 | Q_PROPERTY(qmlAxis* yAxis READ getYAxis WRITE setYAxis) 13 | Q_PROPERTY(qmlScatterStyle* scatter READ getScatter WRITE setScatter) 14 | Q_PROPERTY(qmlPen* pen READ getPen WRITE setPen) 15 | Q_PROPERTY(QCPGraph::LineStyle style READ getLineStyle WRITE setLineStyle) 16 | public: 17 | const QString& getName() const; 18 | void setName(const QString& arg); 19 | 20 | qmlAxis* getXAxis() const; 21 | void setXAxis(qmlAxis* axis); 22 | 23 | qmlAxis* getYAxis() const; 24 | void setYAxis(qmlAxis* axis); 25 | 26 | qmlScatterStyle* getScatter(); 27 | void setScatter(qmlScatterStyle* s); 28 | 29 | qmlPen* getPen(); 30 | void setPen(qmlPen* p); 31 | 32 | QCPGraph::LineStyle getLineStyle() const; 33 | void setLineStyle(QCPGraph::LineStyle l); 34 | 35 | private: 36 | QString name; 37 | qmlScatterStyle* scatter = nullptr; 38 | qmlPen* pen = nullptr; 39 | QCPGraph::LineStyle lineStyle = QCPGraph::lsLine; 40 | qmlAxis* xAxis = nullptr; 41 | qmlAxis* yAxis = nullptr; 42 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlLabel.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlLabel.h" 2 | 3 | const QString& qmlLabel::getText() const 4 | { 5 | return text; 6 | } 7 | 8 | void qmlLabel::setText(const QString& txt) 9 | { 10 | text = txt; 11 | } 12 | 13 | const QString& qmlLabel::getFont() const 14 | { 15 | return font; 16 | } 17 | 18 | void qmlLabel::setFont(const QString& f) 19 | { 20 | font = f; 21 | } 22 | 23 | const QColor& qmlLabel::getColor() const 24 | { 25 | return color; 26 | } 27 | 28 | void qmlLabel::setColor(const QColor& c) 29 | { 30 | color = c; 31 | } 32 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlLabel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class qmlLabel : public QObject 6 | { 7 | Q_OBJECT 8 | Q_PROPERTY(QString text READ getText WRITE setText) 9 | Q_PROPERTY(QString font READ getFont WRITE setFont) 10 | Q_PROPERTY(QColor color READ getColor WRITE setColor) 11 | public: 12 | const QString& getText() const; 13 | void setText(const QString& txt); 14 | const QString& getFont() const; 15 | void setFont(const QString& f); 16 | const QColor& getColor() const; 17 | void setColor(const QColor& c); 18 | private: 19 | QString text; 20 | QString font; 21 | QColor color; 22 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlLegend.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlLegend.h" 2 | 3 | QString qmlLegend::getFontString() const 4 | { 5 | return font.toString(); 6 | } 7 | 8 | void qmlLegend::setFontString(const QString& f) 9 | { 10 | font.fromString(f); 11 | } 12 | 13 | const QFont& qmlLegend::getFont() const 14 | { 15 | return font; 16 | } 17 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlLegend.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "qcustomplot.h" 4 | 5 | class qmlLegend : public QObject 6 | { 7 | Q_OBJECT 8 | Q_PROPERTY(QString font READ getFontString WRITE setFontString) 9 | public: 10 | QString getFontString() const; 11 | void setFontString(const QString& f); 12 | 13 | const QFont& getFont() const; 14 | private: 15 | QFont font; 16 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlPen.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlPen.h" 2 | 3 | QColor qmlPen::getColor() const 4 | { 5 | return pen.color(); 6 | } 7 | 8 | void qmlPen::setColor(const QColor& c) 9 | { 10 | pen.setColor(c); 11 | } 12 | 13 | QColor qmlPen::getBrush() const 14 | { 15 | return pen.brush().color(); 16 | } 17 | 18 | void qmlPen::setBrush(const QColor& c) 19 | { 20 | pen.setBrush(c); 21 | } 22 | 23 | qreal qmlPen::getWidth() const 24 | { 25 | return pen.width(); 26 | } 27 | 28 | void qmlPen::setWidth(qreal w) 29 | { 30 | pen.setWidthF(w); 31 | } 32 | 33 | const QPen& qmlPen::getPen() const 34 | { 35 | return pen; 36 | } 37 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlPen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class qmlPen : public QObject 6 | { 7 | Q_OBJECT 8 | Q_PROPERTY(QColor color READ getColor WRITE setColor) 9 | Q_PROPERTY(QColor brush READ getBrush WRITE setBrush) 10 | Q_PROPERTY(qreal width READ getWidth WRITE setWidth) 11 | public: 12 | QColor getColor() const; 13 | void setColor(const QColor& c); 14 | QColor getBrush() const; 15 | void setBrush(const QColor& c); 16 | qreal getWidth() const; 17 | void setWidth(qreal w); 18 | 19 | const QPen& getPen() const; 20 | private: 21 | QPen pen; 22 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlPlotPaintedItem.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlPlotPaintedItem.h" 2 | #include "qcustomplot.h" 3 | #include "qmlLabel.h" 4 | #include "qmlTick.h" 5 | #include 6 | 7 | qmlPlotPaintedItem::qmlPlotPaintedItem(QQuickItem* parent) : QQuickPaintedItem(parent) 8 | { 9 | setFlag(QQuickItem::ItemHasContents, true); 10 | setAcceptedMouseButtons(Qt::AllButtons); 11 | 12 | connect(this, &QQuickPaintedItem::widthChanged, this, &qmlPlotPaintedItem::onUpdateCustomPlotSize); 13 | connect(this, &QQuickPaintedItem::heightChanged, this, &qmlPlotPaintedItem::onUpdateCustomPlotSize); 14 | 15 | m_CustomPlot.setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); 16 | connect(&m_CustomPlot, SIGNAL(plottableClick(QCPAbstractPlottable*, QMouseEvent*)), this, SLOT(onGraphClicked(QCPAbstractPlottable*))); 17 | connect(&m_CustomPlot, &QCustomPlot::afterReplot, this, &qmlPlotPaintedItem::onCustomReplot); 18 | 19 | m_listInfo.plot = &m_CustomPlot; 20 | } 21 | 22 | void qmlPlotPaintedItem::addData(int index, QVariantList x, QVariantList y) 23 | { 24 | auto g = m_CustomPlot.graph(index); 25 | QVector xx, yy; 26 | for (auto i = 0; i < x.size(); ++i) 27 | { 28 | xx.push_back(x[i].toReal()); 29 | yy.push_back(y[i].toReal()); 30 | } 31 | g->addData(xx, yy); 32 | g->rescaleAxes(); 33 | m_CustomPlot.replot(); 34 | } 35 | 36 | void SetRange(QCustomPlot& plot, int index, QVariantMap range, QCPAxis::AxisType type) 37 | { 38 | auto g = plot.graph(index); 39 | 40 | auto it = range.find("lo"); 41 | if (it != range.end()) 42 | { 43 | switch (type) 44 | { 45 | case QCPAxis::atLeft: 46 | g->valueAxis()->setRangeLower(it->toReal()); 47 | break; 48 | case QCPAxis::atBottom: 49 | g->keyAxis()->setRangeLower(it->toReal()); 50 | break; 51 | } 52 | } 53 | it = range.find("up"); 54 | if (it != range.end()) 55 | { 56 | switch (type) 57 | { 58 | case QCPAxis::atLeft: 59 | g->valueAxis()->setRangeUpper(it->toReal()); 60 | break; 61 | case QCPAxis::atBottom: 62 | g->keyAxis()->setRangeUpper(it->toReal()); 63 | break; 64 | } 65 | } 66 | } 67 | 68 | void qmlPlotPaintedItem::setXRange(int index, QVariantMap range) 69 | { 70 | SetRange(m_CustomPlot, index, range, QCPAxis::atBottom); 71 | } 72 | 73 | void qmlPlotPaintedItem::setYRange(int index, QVariantMap range) 74 | { 75 | SetRange(m_CustomPlot, index, range, QCPAxis::atLeft); 76 | } 77 | 78 | void qmlPlotPaintedItem::paint(QPainter* painter) 79 | { 80 | QPixmap picture(boundingRect().size().toSize()); 81 | QCPPainter qcpPainter(&picture); 82 | m_CustomPlot.toPainter(&qcpPainter); 83 | painter->drawPixmap(QPoint(), picture); 84 | } 85 | 86 | void qmlPlotPaintedItem::setBackground(QColor c) 87 | { 88 | m_backgroundColor = c; 89 | m_CustomPlot.setBackground(c); 90 | } 91 | 92 | QColor qmlPlotPaintedItem::getBackground() const 93 | { 94 | return m_backgroundColor; 95 | } 96 | 97 | QQmlListProperty qmlPlotPaintedItem::getGraphs() 98 | { 99 | return QQmlListProperty( 100 | this, 101 | &m_listInfo, 102 | &qmlPlotPaintedItem::appendGraph, 103 | &qmlPlotPaintedItem::graphSize, 104 | &qmlPlotPaintedItem::graphAt, 105 | &qmlPlotPaintedItem::clearGraphs 106 | ); 107 | } 108 | 109 | qmlLegend* qmlPlotPaintedItem::getLegend() const 110 | { 111 | return nullptr; 112 | } 113 | 114 | void qmlPlotPaintedItem::setLegend(qmlLegend* g) 115 | { 116 | m_CustomPlot.legend->setVisible(true); 117 | m_CustomPlot.legend->setFont(g->getFont()); 118 | } 119 | 120 | inline ListInfo& Info(QQmlListProperty *list) 121 | { 122 | return *static_cast(list->data); 123 | } 124 | 125 | void qmlPlotPaintedItem::appendGraph(QQmlListProperty *list, qmlGraph *pdt) 126 | { 127 | auto& info = Info(list); 128 | auto& m_CustomPlot = *info.plot; 129 | info.m_graphs.append(pdt); 130 | 131 | auto makeAxis = [&](QCPAxis* ref, QCPAxis::AxisType type, qmlAxis* qmlAx){ 132 | if (qmlAx) 133 | { 134 | if (!qmlAx->isDefault()) 135 | ref = m_CustomPlot.axisRect(0)->addAxis(type); 136 | 137 | ref->setVisible(qmlAx->isVisible()); 138 | 139 | if (auto label = qmlAx->getLabel()) 140 | { 141 | if (!label->getText().isEmpty()) 142 | ref->setLabel(label->getText()); 143 | if (label->getColor().isValid()) 144 | ref->setLabelColor(label->getColor()); 145 | if (!label->getFont().isEmpty()) 146 | { 147 | QFont axFont; axFont.fromString(label->getFont()); 148 | ref->setLabelFont(axFont); 149 | } 150 | } 151 | 152 | if (auto tick = qmlAx->getTick()) 153 | { 154 | if (!tick->getFont().isEmpty()) 155 | ref->setTickLabelFont(tick->getFont()); 156 | const auto& tickVec = tick->getTickVector(); 157 | if (!tickVec.empty()) 158 | { 159 | ref->setAutoTicks(false); 160 | ref->setTickVector(tickVec); 161 | } 162 | const auto& tickLab = tick->getTickLabels(); 163 | if (!tickLab.empty()) 164 | { 165 | ref->setAutoTickLabels(false); 166 | ref->setTickVectorLabels(tickLab); 167 | } 168 | } 169 | } 170 | return ref; 171 | }; 172 | 173 | auto graph = m_CustomPlot.addGraph( 174 | makeAxis(m_CustomPlot.xAxis, QCPAxis::atBottom, pdt->getXAxis()), 175 | makeAxis(m_CustomPlot.yAxis, QCPAxis::atLeft, pdt->getYAxis())); 176 | 177 | graph->setName(pdt->getName()); 178 | graph->setPen(pdt->getPen()->getPen()); 179 | graph->setLineStyle(pdt->getLineStyle()); 180 | 181 | if (auto scatterInfo = pdt->getScatter()) 182 | { 183 | graph->setScatterStyle(scatterInfo->getStyle()); 184 | } 185 | } 186 | 187 | void qmlPlotPaintedItem::clearGraphs(QQmlListProperty *p) 188 | { 189 | auto& info = Info(p); 190 | info.m_graphs.clear(); 191 | info.plot->clearGraphs(); 192 | } 193 | 194 | int qmlPlotPaintedItem::graphSize(QQmlListProperty *p) 195 | { 196 | return Info(p).m_graphs.size(); 197 | } 198 | 199 | qmlGraph * qmlPlotPaintedItem::graphAt(QQmlListProperty *p, int index) 200 | { 201 | return Info(p).m_graphs.at(index); 202 | } 203 | 204 | void qmlPlotPaintedItem::mousePressEvent(QMouseEvent* event) 205 | { 206 | routeMouseEvents(event); 207 | } 208 | 209 | void qmlPlotPaintedItem::mouseReleaseEvent(QMouseEvent* event) 210 | { 211 | routeMouseEvents(event); 212 | } 213 | 214 | void qmlPlotPaintedItem::mouseMoveEvent(QMouseEvent* event) 215 | { 216 | routeMouseEvents(event); 217 | } 218 | 219 | void qmlPlotPaintedItem::mouseDoubleClickEvent(QMouseEvent* event) 220 | { 221 | routeMouseEvents(event); 222 | } 223 | 224 | void qmlPlotPaintedItem::routeWheelEvents(QWheelEvent* event) 225 | { 226 | QWheelEvent* newEvent = new QWheelEvent(event->pos(), event->delta(), event->buttons(), event->modifiers(), event->orientation()); 227 | QCoreApplication::postEvent(&m_CustomPlot, newEvent); 228 | } 229 | 230 | void qmlPlotPaintedItem::wheelEvent(QWheelEvent *event) 231 | { 232 | routeWheelEvents(event); 233 | } 234 | 235 | void qmlPlotPaintedItem::onGraphClicked(QCPAbstractPlottable* plottable) 236 | { 237 | } 238 | 239 | void qmlPlotPaintedItem::routeMouseEvents(QMouseEvent* event) 240 | { 241 | QMouseEvent* newEvent = new QMouseEvent(event->type(), event->localPos(), event->button(), event->buttons(), event->modifiers()); 242 | QCoreApplication::postEvent(&m_CustomPlot, newEvent); 243 | } 244 | 245 | void qmlPlotPaintedItem::onUpdateCustomPlotSize() 246 | { 247 | m_CustomPlot.setGeometry(0, 0, width(), height()); 248 | m_CustomPlot.setViewport(QRect(0, 0, (int) width(), (int) height())); 249 | } 250 | 251 | void qmlPlotPaintedItem::onCustomReplot() 252 | { 253 | update(); 254 | } 255 | 256 | void qmlPlotPaintedItem::exportPDF(const QString& name, int w/*=0*/, int h/*=0*/) 257 | { 258 | m_CustomPlot.savePdf(name, false, w, h); 259 | } 260 | 261 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlPlotPaintedItem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "qcustomplot.h" 5 | #include "qmlGraph.h" 6 | #include "qmlLegend.h" 7 | 8 | class QCPAbstractPlottable; 9 | 10 | struct ListInfo 11 | { 12 | QList m_graphs; 13 | QCustomPlot* plot; 14 | }; 15 | 16 | class qmlPlotPaintedItem : public QQuickPaintedItem 17 | { 18 | Q_OBJECT 19 | Q_PROPERTY(QColor background READ getBackground WRITE setBackground) 20 | Q_PROPERTY(qmlLegend* legend READ getLegend WRITE setLegend) 21 | Q_PROPERTY(QQmlListProperty graphs READ getGraphs) 22 | public: 23 | qmlPlotPaintedItem(QQuickItem* parent = 0); 24 | 25 | void paint(QPainter* painter); 26 | 27 | QColor getBackground() const; 28 | void setBackground(QColor); 29 | QQmlListProperty getGraphs(); 30 | qmlLegend* getLegend() const; 31 | void setLegend(qmlLegend* g); 32 | 33 | // support for QQmlListProperty 34 | static void appendGraph(QQmlListProperty *list, qmlGraph *pdt); 35 | static int graphSize(QQmlListProperty *p); 36 | static qmlGraph *graphAt(QQmlListProperty *p, int index); 37 | static void clearGraphs(QQmlListProperty *p); 38 | 39 | public slots: 40 | void addData(int index, QVariantList x, QVariantList y); 41 | void setXRange(int index, QVariantMap range); 42 | void setYRange(int index, QVariantMap range); 43 | void exportPDF(const QString& name, int w=0, int h=0); 44 | private slots: 45 | void onGraphClicked(QCPAbstractPlottable* plottable); 46 | void onCustomReplot(); 47 | void onUpdateCustomPlotSize(); 48 | protected: 49 | void routeMouseEvents(QMouseEvent* event); 50 | void routeWheelEvents(QWheelEvent* event); 51 | void mousePressEvent(QMouseEvent* event) override; 52 | void mouseReleaseEvent(QMouseEvent* event) override; 53 | void mouseMoveEvent(QMouseEvent* event) override; 54 | void mouseDoubleClickEvent(QMouseEvent* event) override; 55 | void wheelEvent(QWheelEvent *event) override; 56 | private: 57 | QCustomPlot m_CustomPlot; 58 | QColor m_backgroundColor; 59 | ListInfo m_listInfo; 60 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlScatterStyle.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlScatterStyle.h" 2 | 3 | QCPScatterStyle::ScatterShape qmlScatterStyle::getType() const 4 | { 5 | return style.shape(); 6 | } 7 | 8 | void qmlScatterStyle::setType(QCPScatterStyle::ScatterShape t) 9 | { 10 | style.setShape(t); 11 | } 12 | 13 | qmlPen* qmlScatterStyle::getPen() const 14 | { 15 | return nullptr; 16 | } 17 | 18 | void qmlScatterStyle::setPen(qmlPen* p) 19 | { 20 | style.setPen(p->getPen()); 21 | } 22 | 23 | qreal qmlScatterStyle::getSize() const 24 | { 25 | return style.size(); 26 | } 27 | 28 | void qmlScatterStyle::setSize(qreal w) 29 | { 30 | style.setSize(w); 31 | } 32 | 33 | QColor qmlScatterStyle::getBrush() const 34 | { 35 | return style.brush().color(); 36 | } 37 | 38 | void qmlScatterStyle::setBrush(const QColor& c) 39 | { 40 | style.setBrush(c); 41 | } 42 | 43 | const QCPScatterStyle& qmlScatterStyle::getStyle() const 44 | { 45 | return style; 46 | } 47 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlScatterStyle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "qcustomplot.h" 4 | #include "qmlPen.h" 5 | 6 | class qmlScatterStyle : public QObject 7 | { 8 | Q_OBJECT 9 | Q_PROPERTY(QCPScatterStyle::ScatterShape type READ getType WRITE setType) 10 | Q_PROPERTY(qreal size READ getSize WRITE setSize) 11 | Q_PROPERTY(qmlPen* pen READ getPen WRITE setPen) 12 | Q_PROPERTY(QColor brush READ getBrush WRITE setBrush) 13 | public: 14 | QCPScatterStyle::ScatterShape getType() const; 15 | void setType(QCPScatterStyle::ScatterShape); 16 | 17 | qmlPen* getPen() const; 18 | void setPen(qmlPen* p); 19 | 20 | qreal getSize() const; 21 | void setSize(qreal w); 22 | 23 | QColor getBrush() const; 24 | void setBrush(const QColor& c); 25 | 26 | const QCPScatterStyle& getStyle() const; 27 | private: 28 | QCPScatterStyle style; 29 | }; -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlTick.cpp: -------------------------------------------------------------------------------- 1 | #include "qmlTick.h" 2 | #include "Utils.h" 3 | 4 | const QString& qmlTick::getFont() const 5 | { 6 | return font; 7 | } 8 | 9 | void qmlTick::setFont(const QString& f) 10 | { 11 | font = f; 12 | } 13 | 14 | QVariantList qmlTick::getVector() const 15 | { 16 | return ToVariant(vector).toList(); 17 | } 18 | 19 | void qmlTick::setVector(const QVariantList& varList) 20 | { 21 | vector = ToVector(varList); 22 | } 23 | 24 | QVariantList qmlTick::getLabels() const 25 | { 26 | QVariantList vals; vals.reserve(labels.size()); 27 | std::copy(labels.begin(), labels.end(), std::back_inserter(vals)); 28 | return vals; 29 | } 30 | 31 | void qmlTick::setLabels(const QVariantList& varList) 32 | { 33 | labels.clear(); labels.reserve(varList.size()); 34 | std::transform(varList.begin(), varList.end(), std::back_inserter(labels), [](const QVariant& v) { 35 | return v.toString(); 36 | }); 37 | } 38 | 39 | const QVector& qmlTick::getTickVector() const 40 | { 41 | return vector; 42 | } 43 | 44 | const QVector& qmlTick::getTickLabels() const 45 | { 46 | return labels; 47 | } 48 | -------------------------------------------------------------------------------- /Anvedi/qml-lib/qmlTick.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class qmlTick : public QObject 8 | { 9 | Q_OBJECT 10 | Q_PROPERTY(QString font READ getFont WRITE setFont) 11 | Q_PROPERTY(QVariantList vector READ getVector WRITE setVector) 12 | Q_PROPERTY(QVariantList labels READ getLabels WRITE setLabels) 13 | public: 14 | const QString& getFont() const; 15 | void setFont(const QString& f); 16 | QVariantList getVector() const; 17 | void setVector(const QVariantList& f); 18 | QVariantList getLabels() const; 19 | void setLabels(const QVariantList& f); 20 | 21 | const QVector& getTickVector() const; 22 | const QVector& getTickLabels() const; 23 | private: 24 | QString font; 25 | QVector vector; 26 | QVector labels; 27 | }; -------------------------------------------------------------------------------- /Anvedi/qml/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import CustomPlot 1.0 3 | 4 | Item { 5 | CustomPlotItem { 6 | 7 | id: customPlot 8 | anchors.fill: parent 9 | background : "white" 10 | 11 | graphs : [ 12 | Graph { 13 | name : "sin" 14 | pen : Pen { color : "blue"; width : 2.0 } 15 | scatter : ScatterStyle { 16 | brush : "white" 17 | type : Scatter.ssCircle 18 | size : 9 19 | pen : Pen { color : "black"; width: 1.5 } 20 | } 21 | xAxis : Axis { 22 | useDefault : true 23 | visible : false 24 | label : Label { 25 | text : "domain" 26 | font : "helvetica,-1,30,5,0,0,0,0,0,0" 27 | color: "blue" 28 | } 29 | } 30 | yAxis : Axis 31 | { 32 | label : Label { 33 | text : "sin" 34 | font : "helvetica,-1,30,5,0,0,0,0,0,0" 35 | color : "red" 36 | } 37 | } 38 | }, 39 | Graph { 40 | name : "parabola" 41 | pen : Pen { color : "red"; width : 2.0 } 42 | xAxis : Axis { 43 | useDefault : true 44 | } 45 | yAxis : Axis 46 | { 47 | useDefault : false 48 | } 49 | } 50 | ] 51 | 52 | // Timer { 53 | // interval: 150; running: true; repeat: true 54 | 55 | // property double xCurr: 0.0 56 | 57 | // onTriggered: { 58 | 59 | // var newX = []; 60 | // var newY = []; 61 | // for (var i = 0; i < 5; i++) 62 | // { 63 | // newX[i] = xCurr; 64 | // newY[i] = Math.sin(xCurr*Math.PI/100.0); 65 | // xCurr += i; 66 | // } 67 | // customPlot.addData(0, newX, newY); 68 | // customPlot.addData(1, newX, newY); 69 | // } 70 | // } 71 | 72 | Component.onCompleted: { 73 | var domain = []; 74 | var y = []; 75 | var y2 = []; 76 | 77 | for (var i=0; i<20; ++i) 78 | { 79 | domain[i] = i/20.0*10.0; 80 | y[i] = Math.cos(domain[i]*0.8+Math.sin(domain[i]*0.16+1.0))*Math.sin(domain[i]*0.54)+1.4; 81 | y2[i] = domain[i]*domain[i]; 82 | } 83 | 84 | addData(0, domain, y); 85 | addData(1, domain, y2); 86 | setYRange(1, {"lo":-10}); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Anvedi/rtConfig.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 336 10 | 186 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Real Time options 21 | 22 | 23 | 24 | 25 | 26 | Packets Count 27 | 28 | 29 | 30 | 31 | 32 | 33 | Timer Interval 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | File to Replay 44 | 45 | 46 | 47 | 48 | 49 | 50 | 99999 51 | 52 | 53 | 54 | 55 | 56 | 57 | Qt::Horizontal 58 | 59 | 60 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 61 | 62 | 63 | 64 | 65 | 66 | 67 | 5000 68 | 69 | 70 | 10 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 0 79 | 0 80 | 81 | 82 | 83 | 84 | 20 85 | 0 86 | 87 | 88 | 89 | 90 | 20 91 | 16777215 92 | 93 | 94 | 95 | ... 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | buttonBox 105 | accepted() 106 | Dialog 107 | accept() 108 | 109 | 110 | 248 111 | 254 112 | 113 | 114 | 157 115 | 274 116 | 117 | 118 | 119 | 120 | buttonBox 121 | rejected() 122 | Dialog 123 | reject() 124 | 125 | 126 | 316 127 | 260 128 | 129 | 130 | 286 131 | 274 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /Anvedi/signalInfo.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SignalInfoDlg 4 | 5 | 6 | 7 | 0 8 | 0 9 | 235 10 | 222 11 | 12 | 13 | 14 | Edit Signal Info 15 | 16 | 17 | 18 | 19 | 20 | Max Range 21 | 22 | 23 | 24 | 25 | 26 | 27 | 99999999999.000000000000000 28 | 29 | 30 | 31 | 32 | 33 | 34 | Ticks 35 | 36 | 37 | 38 | 39 | 40 | 41 | 2 42 | 43 | 44 | -99999999999.000000000000000 45 | 46 | 47 | 99999999999.000000000000000 48 | 49 | 50 | 0.500000000000000 51 | 52 | 53 | 54 | 55 | 56 | 57 | Min Range 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Tick Labels 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Qt::Horizontal 78 | 79 | 80 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 81 | 82 | 83 | 84 | 85 | 86 | 87 | Name 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | buttonBox 100 | accepted() 101 | SignalInfoDlg 102 | accept() 103 | 104 | 105 | 248 106 | 254 107 | 108 | 109 | 157 110 | 274 111 | 112 | 113 | 114 | 115 | buttonBox 116 | rejected() 117 | SignalInfoDlg 118 | reject() 119 | 120 | 121 | 316 122 | 260 123 | 124 | 125 | 286 126 | 274 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /AnvediCheExe/AnvediCheExe.pri: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | SOURCES += ./main.cpp 11 | -------------------------------------------------------------------------------- /AnvediCheExe/AnvediCheExe.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | TEMPLATE = app 6 | TARGET = AnvediCheExe 7 | DESTDIR = ../Win32/Debug 8 | QT += core script widgets gui qml printsupport quick 9 | CONFIG += debug 10 | DEFINES += WIN64 QT_DLL QT_PRINTSUPPORT_LIB QT_SCRIPT_LIB QT_WIDGETS_LIB QT_QML_LIB QT_QUICK_LIB 11 | INCLUDEPATH += ./GeneratedFiles \ 12 | . \ 13 | ./GeneratedFiles/Debug 14 | DEPENDPATH += . 15 | MOC_DIR += ./GeneratedFiles/debug 16 | OBJECTS_DIR += debug 17 | UI_DIR += ./GeneratedFiles 18 | RCC_DIR += ./GeneratedFiles 19 | include(AnvediCheExe.pri) 20 | -------------------------------------------------------------------------------- /AnvediCheExe/AnvediCheExe.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;cxx;c;def 7 | 8 | 9 | 10 | 11 | Source Files 12 | 13 | 14 | -------------------------------------------------------------------------------- /AnvediCheExe/AnvediCheExe.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | C:\Qt-5.2\5.5\msvc2013_64 6 | PATH=$(QTDIR)\bin%3b$(PATH) 7 | 8 | 9 | C:\Qt-5.2\5.5\msvc2013_64 10 | PATH=$(QTDIR)\bin%3b$(PATH) 11 | 12 | -------------------------------------------------------------------------------- /AnvediCheExe/anvedicheexe.ui: -------------------------------------------------------------------------------- 1 | 2 | AnvediCheExeClass 3 | 4 | 5 | AnvediCheExeClass 6 | 7 | 8 | 9 | 0 10 | 0 11 | 600 12 | 400 13 | 14 | 15 | 16 | AnvediCheExe 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /AnvediCheExe/main.cpp: -------------------------------------------------------------------------------- 1 | #include "..\Anvedi\anvedi.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main(int argc, char *argv []) 7 | { 8 | QApplication a(argc, argv); 9 | 10 | Anvedi w; 11 | w.show(); 12 | 13 | try 14 | { 15 | return a.exec(); 16 | } 17 | catch (const std::exception& ex) 18 | { 19 | QMessageBox msg; 20 | msg.setWindowTitle("Unexpected error"); 21 | msg.setInformativeText("Anfame!!!"); 22 | msg.setFont(QFont("serif", 15)); 23 | msg.setIconPixmap(QPixmap("mario.jpg")); 24 | msg.setDefaultButton(QMessageBox::Ok); 25 | msg.show(); 26 | msg.exec(); 27 | return -1; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /AnvediCheExe/mario.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilpropheta/anvedi/abd9af841faf56a7521d7d2e7cae1d6ec8d51116/AnvediCheExe/mario.jpg -------------------------------------------------------------------------------- /AnvediCheQml/AnvediCheQml.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;cxx;c;def 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | -------------------------------------------------------------------------------- /AnvediCheQml/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../Anvedi/qml-lib/RegisterAll.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | QmlModulesInstaller::Install(); 9 | QQuickView view(QUrl("file:///C:/Users/Marco/Source/Repos/anvedi/Anvedi/qml/main.qml")); 10 | view.setResizeMode(QQuickView::SizeRootObjectToView); 11 | view.resize(800, 600); 12 | view.show(); 13 | return a.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /AnvediCheTests/AnvediCheTests.pri: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | HEADERS += ./TestRunner.h \ 11 | ./SignalDataTests.h \ 12 | ./SignalListPresenterTests.h 13 | SOURCES += ./main.cpp \ 14 | ./TestRunner.cpp \ 15 | ./SignalDataTests.cpp \ 16 | ./SignalListPresenterTests.cpp 17 | -------------------------------------------------------------------------------- /AnvediCheTests/AnvediCheTests.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | TEMPLATE = app 6 | TARGET = AnvediCheTests 7 | DESTDIR = ../Win32/Debug 8 | QT += core script widgets gui printsupport 9 | CONFIG += qtestlib debug console 10 | DEFINES += WIN64 QT_DLL QT_TESTLIB_LIB QT_SCRIPT_LIB QT_WIDGETS_LIB QT_PRINTSUPPORT_LIB 11 | INCLUDEPATH += . \ 12 | ./GeneratedFiles/Debug 13 | DEPENDPATH += . 14 | MOC_DIR += ./GeneratedFiles/debug 15 | OBJECTS_DIR += debug 16 | UI_DIR += ./GeneratedFiles 17 | RCC_DIR += ./GeneratedFiles 18 | include(AnvediCheTests.pri) 19 | -------------------------------------------------------------------------------- /AnvediCheTests/AnvediCheTests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} 6 | moc;h;cpp 7 | False 8 | 9 | 10 | {149caa85-6a1c-432c-913a-4cf16db2cfb9} 11 | 12 | 13 | {b63e547a-21fa-413a-a0f9-2dde5849909e} 14 | 15 | 16 | {ac25b7ff-100d-4e86-96b2-4062669b7a47} 17 | cpp;moc 18 | False 19 | 20 | 21 | {d5c4ddf5-744d-4e4d-a36a-98b7b73e87ed} 22 | cpp;moc 23 | False 24 | 25 | 26 | {851bc0b5-0a23-436a-b0dd-d3679e770cd8} 27 | 28 | 29 | 30 | 31 | Utils 32 | 33 | 34 | 35 | 36 | Utils 37 | 38 | 39 | Utils 40 | 41 | 42 | Tests 43 | 44 | 45 | Generated Files\Debug 46 | 47 | 48 | Generated Files\Release 49 | 50 | 51 | Tests 52 | 53 | 54 | Generated Files\Debug 55 | 56 | 57 | Generated Files\Release 58 | 59 | 60 | Tests 61 | 62 | 63 | Generated Files\Debug 64 | 65 | 66 | Generated Files\Release 67 | 68 | 69 | Generated Files 70 | 71 | 72 | Tests 73 | 74 | 75 | Generated Files\Debug 76 | 77 | 78 | Generated Files\Release 79 | 80 | 81 | Tests 82 | 83 | 84 | Generated Files\Debug 85 | 86 | 87 | Generated Files\Release 88 | 89 | 90 | 91 | 92 | Tests 93 | 94 | 95 | Tests 96 | 97 | 98 | Tests 99 | 100 | 101 | Resource Files 102 | 103 | 104 | Tests 105 | 106 | 107 | Tests 108 | 109 | 110 | -------------------------------------------------------------------------------- /AnvediCheTests/GraphPresenterTests.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphPresenterTests.h" 2 | #include "..\qcustomplot.h" 3 | #include "..\SignalData.h" 4 | #include "..\GraphPresenter.h" 5 | #include 6 | #include "..\PlotInfo.h" 7 | 8 | template 9 | void DoGraphPresenterTest(TestCode t) 10 | { 11 | QCustomPlot plot; 12 | SignalData data; 13 | PlotInfo info; 14 | QScrollBar rangeScrollBar; 15 | GraphPresenter presenter(&plot, &rangeScrollBar, data, info); 16 | t(presenter, plot, data); 17 | } 18 | 19 | void GraphPresenterTests::On_DataAdded_Should_CreateGraphs() 20 | { 21 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 22 | data.add({ 23 | { "line", { "line", { 1, 2, 3 }, { "red", true } } }, 24 | { "cubic", { "cubic", { 1, 8, 27 }, { "blue", false } } }, 25 | }); 26 | 27 | QCOMPARE(plot.graphCount(), 2); 28 | QCOMPARE(plot.graph(1)->pen().color(), QColor("red")); 29 | QCOMPARE(plot.graph(1)->name(), QString("line")); 30 | QCOMPARE(plot.graph(1)->visible(), true); 31 | QCOMPARE(plot.graph(1)->data()->size(), 0); // because domain is not set yet 32 | }); 33 | } 34 | 35 | void GraphPresenterTests::On_DomainChanged_Should_SetDataToGraphs_And_ScaleXAxis() 36 | { 37 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 38 | data.add({ 39 | { "cubic", { "cubic", { 1, 8, 27 }, { "blue", true }, } }, 40 | { "line", { "line", { 1, 2, 3 }, { "red", true } } } 41 | }); 42 | data.setAsDomain("line"); 43 | 44 | // line 45 | QCOMPARE(plot.graph(1)->data()->size(), 3); 46 | const auto lineData = plot.graph(1)->data()->values(); 47 | QCOMPARE(lineData[0].key, 1.0); 48 | QCOMPARE(lineData[0].value, 1.0); 49 | QCOMPARE(lineData[1].key, 2.0); 50 | QCOMPARE(lineData[1].value, 2.0); 51 | QCOMPARE(lineData[2].key, 3.0); 52 | QCOMPARE(lineData[2].value, 3.0); 53 | 54 | // cubic 55 | QCOMPARE(plot.graph(0)->data()->size(), 3); 56 | const auto cubicData = plot.graph(0)->data()->values(); 57 | QCOMPARE(cubicData[0].key, 1.0); 58 | QCOMPARE(cubicData[0].value, 1.0); 59 | QCOMPARE(cubicData[1].key, 2.0); 60 | QCOMPARE(cubicData[1].value, 8.0); 61 | QCOMPARE(cubicData[2].key, 3.0); 62 | QCOMPARE(cubicData[2].value, 27.0); 63 | 64 | // x range [1.0 - 3.0] 65 | QCOMPARE(plot.xAxis->range().lower, 1.0); 66 | QCOMPARE(plot.xAxis->range().upper, 3.0); 67 | }); 68 | } 69 | 70 | void GraphPresenterTests::On_DataCleared_Should_RemoveGraphs() 71 | { 72 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 73 | data.add({ 74 | { "cubic", { "cubic", { 1, 8, 27 }, { "blue", true } } }, 75 | { "line", { "line", { 1, 2, 3 }, {"red", true } } } 76 | }); 77 | data.setAsDomain("line"); 78 | 79 | QCOMPARE(plot.graphCount(), 2); 80 | 81 | data.clear(); 82 | QCOMPARE(plot.graphCount(), 0); 83 | }); 84 | } 85 | 86 | void GraphPresenterTests::On_SignalVisibilityChanged_Should_UpdateGraphVisibility() 87 | { 88 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 89 | data.add({ 90 | { "cubic", { "cubic", { 1, 8, 27 }, { "blue", false} } }, 91 | { "line", { "line", { 1, 2, 3 }, { "red", true } } } 92 | }); 93 | 94 | QCOMPARE(plot.graph(0)->visible(), false); 95 | 96 | data.setVisible("cubic", true); 97 | QCOMPARE(plot.graph(0)->name(), QString("cubic")); 98 | QCOMPARE(plot.graph(0)->visible(), true); 99 | }); 100 | } 101 | 102 | void GraphPresenterTests::On_SignalColorChanged_Should_UpdateGraphColor() 103 | { 104 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 105 | data.add({ 106 | { "line", { "line", { 1, 2, 3 }, { "red", true } } } 107 | }); 108 | 109 | QCOMPARE(plot.graph(0)->pen().color(), QColor("red")); 110 | 111 | data.setColor("line", "blue"); 112 | 113 | QCOMPARE(plot.graph(0)->pen().color(), QColor("blue")); 114 | }); 115 | } 116 | 117 | void GraphPresenterTests::On_CursorValueChanged_Should_UpdateXRange() 118 | { 119 | DoGraphPresenterTest([](GraphPresenter& p, QCustomPlot& plot, SignalData& data){ 120 | data.add({ 121 | { "line", { "line", { 1, 2, 3, 4, 5 }, { "red", true } } } 122 | }); 123 | data.setAsDomain("line"); 124 | plot.xAxis->setRange(2, 4); 125 | 126 | p.OnCursorValueChanged(1.0, {}); // more left than current range 127 | QCOMPARE(plot.xAxis->range(), QCPRange(1.0, 3.0)); 128 | 129 | p.OnCursorValueChanged(4.0, {}); // more right than current range 130 | QCOMPARE(plot.xAxis->range(), QCPRange(2.0, 4.0)); 131 | 132 | p.OnCursorValueChanged(3.0, {}); // in current range 133 | QCOMPARE(plot.xAxis->range(), QCPRange(2.0, 4.0)); 134 | }); 135 | } 136 | -------------------------------------------------------------------------------- /AnvediCheTests/GraphPresenterTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TestRunner.h" 4 | 5 | class GraphPresenterTests : public QObject 6 | { 7 | Q_OBJECT 8 | private slots: 9 | // model -> (signal) -> presenter 10 | void On_DataAdded_Should_CreateGraphs(); 11 | void On_DomainChanged_Should_SetDataToGraphs_And_ScaleXAxis(); 12 | void On_DataCleared_Should_RemoveGraphs(); 13 | void On_SignalVisibilityChanged_Should_UpdateGraphVisibility(); 14 | void On_SignalColorChanged_Should_UpdateGraphColor(); 15 | void On_CursorValueChanged_Should_UpdateXRange(); 16 | }; 17 | 18 | DECLARE_TEST(GraphPresenterTests) -------------------------------------------------------------------------------- /AnvediCheTests/PerformanceTests.cpp: -------------------------------------------------------------------------------- 1 | #include "PerformanceTests.h" 2 | #include 3 | #include "..\qcustomplot.h" 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | static QVector IncreasingVector(size_t count) 10 | { 11 | QVector x; x.resize(count); 12 | iota(x.begin(), x.end(), 0.0); 13 | return x;; 14 | } 15 | 16 | static QVector RandomVector(size_t count) 17 | { 18 | QVector x; x.resize(count); 19 | default_random_engine generator; 20 | uniform_real_distribution distribution(0.0, 1.0); 21 | std::generate(x.begin(), x.end(), [&]{ 22 | return distribution(generator); 23 | }); 24 | return x; 25 | } 26 | 27 | void PerformanceTests::Measure_SetDataGraph_500kPoints() 28 | { 29 | QCustomPlot plot; 30 | auto graph = plot.addGraph(); 31 | 32 | QBENCHMARK { 33 | graph->setData(IncreasingVector(500000), IncreasingVector(500000)); 34 | } 35 | } 36 | 37 | void PerformanceTests::Measure_SetDataCurve_500kPoints() 38 | { 39 | QCustomPlot plot; 40 | auto curve = new QCPCurve(plot.xAxis, plot.yAxis); 41 | 42 | QBENCHMARK{ 43 | curve->setData(IncreasingVector(500000), IncreasingVector(500000)); 44 | } 45 | } 46 | 47 | void PerformanceTests::Measure_PlotSingleGraph_500kPoints_data() 48 | { 49 | QTest::addColumn>("x"); 50 | QTest::addColumn>("y"); 51 | 52 | QTest::newRow("Increasing X and Increasing Y") 53 | << IncreasingVector(500000) 54 | << IncreasingVector(500000); 55 | 56 | QTest::newRow("Increasing X and Random Y") 57 | << IncreasingVector(500000) 58 | << RandomVector(500000); 59 | } 60 | 61 | void PerformanceTests::Measure_PlotSingleCurve_500kPoints_data() 62 | { 63 | QTest::addColumn>("x"); 64 | QTest::addColumn>("y"); 65 | 66 | QTest::newRow("Increasing X and Increasing Y") 67 | << IncreasingVector(500000) 68 | << IncreasingVector(500000); 69 | 70 | QTest::newRow("Increasing X and Random Y") 71 | << IncreasingVector(500000) 72 | << RandomVector(500000); 73 | } 74 | 75 | void PerformanceTests::Measure_PlotSingleGraph_500kPoints() 76 | { 77 | QCustomPlot plot; 78 | QFETCH(QVector, x); 79 | QFETCH(QVector, y); 80 | 81 | auto graph = plot.addGraph(); 82 | graph->setData(x, y); 83 | graph->rescaleAxes(); 84 | 85 | QBENCHMARK{ 86 | plot.replot(); 87 | }; 88 | } 89 | 90 | void PerformanceTests::Measure_PlotSingleCurve_500kPoints() 91 | { 92 | QCustomPlot plot; 93 | QFETCH(QVector, x); 94 | QFETCH(QVector, y); 95 | 96 | auto curve = new QCPCurve(plot.xAxis, plot.yAxis); 97 | curve->setData(x, y); 98 | auto graph = plot.addPlottable(curve); 99 | curve->rescaleAxes(); 100 | 101 | QBENCHMARK{ 102 | plot.replot(); 103 | }; 104 | } 105 | 106 | void PerformanceTests::Measure_Plot10Graphs_SameX_data() 107 | { 108 | QTest::addColumn>("x"); 109 | 110 | QTest::newRow("100k") 111 | << IncreasingVector(100000); 112 | 113 | QTest::newRow("200k") 114 | << IncreasingVector(200000); 115 | 116 | QTest::newRow("500k") 117 | << IncreasingVector(500000); 118 | 119 | QTest::newRow("700k") 120 | << IncreasingVector(700000); 121 | 122 | QTest::newRow("1M") 123 | << IncreasingVector(1000000); 124 | } 125 | 126 | void PerformanceTests::Measure_Plot10Graphs_SameX() 127 | { 128 | QCustomPlot plot; 129 | QFETCH(QVector, x); 130 | 131 | for (auto i = 0u; i < 10u; ++i) 132 | { 133 | auto graph = plot.addGraph(plot.xAxis, new QCPAxis(plot.axisRect(), QCPAxis::atLeft)); 134 | graph->setData(x, RandomVector(x.size())); 135 | graph->rescaleValueAxis(); 136 | } 137 | plot.xAxis->rescale(); 138 | 139 | QBENCHMARK{ 140 | plot.replot(); 141 | }; 142 | } 143 | 144 | void PerformanceTests::Measure_MultipleGraphs_10kPoints_1_1_PenWidth() 145 | { 146 | QSKIP("This test is just to demonstrate that increasing pen width negatively affects performance..."); 147 | 148 | QCustomPlot plot; 149 | auto x = IncreasingVector(10000); 150 | auto y = RandomVector(10000); 151 | 152 | for (auto i = 0u; i < 5; ++i) 153 | { 154 | auto graph = plot.addGraph(plot.xAxis, new QCPAxis(plot.axisRect(), QCPAxis::atLeft)); 155 | graph->setData(x, y); 156 | auto p = graph->pen(); 157 | p.setWidthF(1.1); 158 | graph->setPen(p); 159 | graph->rescaleValueAxis(); 160 | } 161 | plot.xAxis->rescale(); 162 | 163 | QBENCHMARK{ 164 | plot.replot(); 165 | }; 166 | } 167 | 168 | -------------------------------------------------------------------------------- /AnvediCheTests/PerformanceTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TestRunner.h" 4 | 5 | class PerformanceTests : public QObject 6 | { 7 | Q_OBJECT 8 | private slots: 9 | void Measure_SetDataGraph_500kPoints(); 10 | void Measure_SetDataCurve_500kPoints(); 11 | 12 | void Measure_PlotSingleGraph_500kPoints_data(); 13 | void Measure_PlotSingleCurve_500kPoints_data(); 14 | void Measure_PlotSingleGraph_500kPoints(); 15 | void Measure_PlotSingleCurve_500kPoints(); 16 | 17 | void Measure_Plot10Graphs_SameX_data(); 18 | void Measure_Plot10Graphs_SameX(); 19 | void Measure_MultipleGraphs_10kPoints_1_1_PenWidth(); 20 | }; 21 | 22 | //DECLARE_TEST(PerformanceTests) // not running PerfTests -------------------------------------------------------------------------------- /AnvediCheTests/SignalDataTests.cpp: -------------------------------------------------------------------------------- 1 | #include "SignalDataTests.h" 2 | #include 3 | #include "..\SignalData.h" 4 | #include 5 | 6 | using namespace std; 7 | 8 | void SignalDataTests::On_empty_add_ShouldNotEmit_DataAdded() 9 | { 10 | SignalData data; 11 | QSignalSpy spy(&data, SIGNAL(DataAdded(const DataMap&))); 12 | data.add({}); 13 | 14 | QCOMPARE(spy.count(), 0); 15 | } 16 | 17 | void SignalDataTests::On_add_ShouldEmit_DataAdded() 18 | { 19 | SignalData data; 20 | QSignalSpy spy(&data, SIGNAL(DataAdded(const DataMap&))); 21 | data.add({ { "signal", { "signal", { 1, 2, 3 } } } }); 22 | 23 | QCOMPARE(spy.count(), 1); 24 | auto receivedMap = spy.takeFirst().takeFirst().value(); 25 | DataMap expectedMap{ { "signal", { "signal", { 1, 2, 3 }, { {}, {}, 1, 3 } } } }; 26 | QCOMPARE(receivedMap, expectedMap); 27 | } 28 | 29 | void SignalDataTests::On_add_Should_AllowOverwritingElements() 30 | { 31 | SignalData data; 32 | QSignalSpy spy(&data, SIGNAL(DataAdded(const DataMap&))); 33 | data.add({ 34 | { "signal", { "signal", {}, { {}, true } } }, 35 | { "line", { "line", {}, { {}, false } } } 36 | }); 37 | QCOMPARE(data.get("signal").graphic.visible, true); 38 | QCOMPARE(data.get("line").graphic.visible, false); 39 | 40 | data.add({ 41 | { "signal", { "signal", {}, { {}, false } } }, 42 | { "other", { "other", {}, { {}, true } } } 43 | }); 44 | QCOMPARE(data.get("signal").graphic.visible, false); 45 | QCOMPARE(data.get("other").graphic.visible, true); 46 | 47 | QCOMPARE(spy.count(), 2); 48 | auto finalMap = spy.takeAt(1).takeFirst().value(); 49 | DataMap expectedMap{ 50 | { "signal", { "signal", {}, { {}, false } } }, 51 | { "other", { "other", {}, { {}, true } } }, 52 | { "line", { "line", {}, { {}, false } } } 53 | }; 54 | QCOMPARE(finalMap, expectedMap); 55 | } 56 | 57 | void SignalDataTests::On_remove_ShouldEmit_SignalRemoved() 58 | { 59 | SignalData data; 60 | data.add({ 61 | { "signal", { "signal", {}, { {}, true } } }, 62 | { "line", { "line", {}, { {}, false } } } 63 | }); 64 | 65 | QSignalSpy spy(&data, SIGNAL(SignalRemoved(const QString&))); 66 | data.remove("signal"); 67 | QCOMPARE(spy.count(), 1); 68 | 69 | auto removedName = spy.takeFirst().takeFirst().value(); 70 | QCOMPARE(removedName, QString("signal")); 71 | } 72 | 73 | void SignalDataTests::On_rename_ShouldEmit_SignalRenamed() 74 | { 75 | SignalData data; 76 | data.add({ 77 | { "signal", { "signal", {}, { {}, true } } }, 78 | { "line", { "line", {}, { {}, false } } } 79 | }); 80 | 81 | QSignalSpy spy(&data, SIGNAL(SignalRenamed(const QString&, const Signal&))); 82 | data.rename("signal", "signal2"); 83 | QCOMPARE(spy.count(), 1); 84 | 85 | auto signalParams = spy.takeFirst(); 86 | auto oldName = signalParams.at(0).value(); 87 | auto newSignal = signalParams.at(1).value(); 88 | QCOMPARE(oldName, QString("signal")); 89 | Signal expectedSignal{ "signal2", {}, { {}, true } }; 90 | QCOMPARE(newSignal, expectedSignal); 91 | } 92 | 93 | void SignalDataTests::On_clear_ShouldEmit_DataCleared() 94 | { 95 | SignalData data; 96 | QSignalSpy spy(&data, SIGNAL(DataCleared())); 97 | data.clear(); 98 | 99 | QCOMPARE(spy.count(), 1); 100 | } 101 | 102 | void SignalDataTests::On_setValues_ShouldEmit_SignalValuesChanged() 103 | { 104 | SignalData data; 105 | data.add({ { "signal", {"signal"} } }); 106 | QSignalSpy spy(&data, SIGNAL(SignalValuesChanged(const Signal&))); 107 | data.setValues("signal", {1,2,3}); 108 | 109 | QCOMPARE(spy.count(), 1); 110 | auto receivedSignal = spy.takeFirst().takeFirst().value(); 111 | Signal actualSignal{ "signal", { 1, 2, 3 }, { {}, {}, 1, 3 } }; 112 | QCOMPARE(receivedSignal, actualSignal); 113 | } 114 | 115 | void SignalDataTests::On_setGraphic_ShouldEmit_SignalGraphicChanged() 116 | { 117 | SignalData data; 118 | data.add({ { "signal", { "signal" } } }); 119 | QSignalSpy spy(&data, SIGNAL(SignalGraphicChanged(const Signal&))); 120 | data.setSignalGraphic("signal", {"red", true}); 121 | 122 | QCOMPARE(spy.count(), 1); 123 | auto receivedSignal = spy.takeFirst().takeFirst().value(); 124 | Signal actualSignal{ "signal", {}, { "red", true } }; 125 | QCOMPARE(receivedSignal, actualSignal); 126 | } 127 | 128 | void SignalDataTests::On_setAsDomain_ShouldEmit_DomainChanged() 129 | { 130 | SignalData data; 131 | QSignalSpy spy(&data, SIGNAL(DomainChanged(const Signal&))); 132 | data.add({ { "signal", {"signal"} } }); 133 | data.setAsDomain("signal"); 134 | 135 | QCOMPARE(spy.count(), 1); 136 | auto actualAdded = spy.takeFirst().takeFirst().value(); 137 | 138 | QCOMPARE(actualAdded.name, QString("signal")); 139 | } 140 | 141 | void SignalDataTests::On_setAsDomainWithWrongSignal_ShouldThrowException() 142 | { 143 | SignalData data; 144 | QVERIFY_EXCEPTION_THROWN(data.setAsDomain("unknown"), std::exception); 145 | } 146 | 147 | void SignalDataTests::On_domainLowerBound() 148 | { 149 | SignalData data; 150 | data.add({ { "domain", { "domain", { 1, 2, 3, 4, 5 } } } }); 151 | data.setAsDomain("domain"); 152 | QVERIFY(std::make_pair(2.0, 1ull) == data.domainLowerBound(1.5)); 153 | QVERIFY(std::make_pair(1.0, 0ull) == data.domainLowerBound(0.5)); 154 | QVERIFY(std::make_pair(5.0, 4ull) == data.domainLowerBound(6.0)); 155 | QVERIFY(std::make_pair(4.0, 3ull) == data.domainLowerBound(4.0)); 156 | } 157 | 158 | void SignalDataTests::On_domainNextValue() 159 | { 160 | SignalData data; 161 | data.add({ { "domain", { "domain", { 1, 2, 3, 4, 5 } } } }); 162 | data.setAsDomain("domain"); 163 | QVERIFY(1.0 == data.domainNextValue(0.0)); // next non-existent left 164 | QVERIFY(2.0 == data.domainNextValue(1.0)); 165 | QVERIFY(2.0 == data.domainNextValue(1.5)); 166 | QVERIFY(5.0 == data.domainNextValue(5.0)); 167 | QVERIFY(5.0 == data.domainNextValue(6.0)); // next non-existent right 168 | } 169 | 170 | void SignalDataTests::On_domainPrevValue() 171 | { 172 | SignalData data; 173 | data.add({ { "domain", { "domain", { 1, 2, 3, 4, 5 } } } }); 174 | data.setAsDomain("domain"); 175 | QVERIFY(1.0 == data.domainPrevValue(0.0)); // prev non-existent left 176 | QVERIFY(1.0 == data.domainPrevValue(1.0)); 177 | QVERIFY(1.0 == data.domainPrevValue(2.0)); 178 | QVERIFY(1.0 == data.domainPrevValue(1.5)); 179 | QVERIFY(5.0 == data.domainPrevValue(6.0)); // prev non-existent right 180 | } -------------------------------------------------------------------------------- /AnvediCheTests/SignalDataTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TestRunner.h" 4 | 5 | class SignalDataTests : public QObject 6 | { 7 | Q_OBJECT 8 | private slots: 9 | void On_empty_add_ShouldNotEmit_DataAdded(); 10 | void On_add_ShouldEmit_DataAdded(); 11 | void On_add_Should_AllowOverwritingElements(); 12 | void On_remove_ShouldEmit_SignalRemoved(); 13 | void On_rename_ShouldEmit_SignalRenamed(); 14 | void On_clear_ShouldEmit_DataCleared(); 15 | void On_setValues_ShouldEmit_SignalValuesChanged(); 16 | void On_setAsDomain_ShouldEmit_DomainChanged(); 17 | void On_setAsDomainWithWrongSignal_ShouldThrowException(); 18 | void On_setGraphic_ShouldEmit_SignalGraphicChanged(); 19 | void On_domainLowerBound(); 20 | void On_domainNextValue(); 21 | void On_domainPrevValue(); 22 | }; 23 | 24 | DECLARE_TEST(SignalDataTests) -------------------------------------------------------------------------------- /AnvediCheTests/SignalListPresenterTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "..\SignalListPresenter.h" 6 | #include "..\SignalData.h" 7 | #include "..\PlotCursor.h" 8 | #include "..\qcustomplot.h" 9 | 10 | using namespace std; 11 | 12 | void SignalListPresenterTests::initTestCase() 13 | { 14 | // currently just copy/paste from ui_anvedi.h 15 | signalList.reset(new QTableWidget()); 16 | if (signalList->columnCount() < 4) 17 | signalList->setColumnCount(4); 18 | QTableWidgetItem *__qtablewidgetitem = new QTableWidgetItem(); 19 | signalList->setHorizontalHeaderItem(0, __qtablewidgetitem); 20 | QTableWidgetItem *__qtablewidgetitem1 = new QTableWidgetItem(); 21 | signalList->setHorizontalHeaderItem(1, __qtablewidgetitem1); 22 | QTableWidgetItem *__qtablewidgetitem2 = new QTableWidgetItem(); 23 | signalList->setHorizontalHeaderItem(2, __qtablewidgetitem2); 24 | QTableWidgetItem *__qtablewidgetitem3 = new QTableWidgetItem(); 25 | signalList->setHorizontalHeaderItem(3, __qtablewidgetitem3); 26 | signalList->setObjectName(QStringLiteral("signalList")); 27 | signalList->setEditTriggers(QAbstractItemView::NoEditTriggers); 28 | signalList->setDragDropOverwriteMode(false); 29 | } 30 | 31 | void SignalListPresenterTests::OnNewData_Should_PopulateTableWidget() 32 | { 33 | PrepareTest(); 34 | data.add({ 35 | { "domain", { "domain"} }, 36 | { "signal", { "signal"} }, 37 | }); 38 | 39 | QCOMPARE(2, signalList->rowCount()); 40 | } 41 | 42 | void SignalListPresenterTests::OnClear_Should_EmptyTableWidget() 43 | { 44 | PrepareTest(); 45 | data.add({ 46 | { "domain", { "domain" } }, 47 | { "signal", { "signal" } }, 48 | }); 49 | data.clear(); 50 | 51 | QCOMPARE(signalList->rowCount(), 0); 52 | } 53 | 54 | void SignalListPresenterTests::OnSignalVisibilityChanged_Should_ChangeCorrespondingTableEntry() 55 | { 56 | PrepareTest(); 57 | data.add({ 58 | { "domain", { "domain"} }, 59 | }); 60 | 61 | QCOMPARE(signalList->item(0, 0)->checkState(), Qt::Unchecked); 62 | data.setVisible("domain", true); 63 | QCOMPARE(signalList->item(0, 0)->checkState(), Qt::Checked); 64 | } 65 | 66 | void SignalListPresenterTests::OnDomainChanged_Should_BoldCorrespondingTableEntry() 67 | { 68 | PrepareTest(); 69 | data.add({ 70 | { "domain", { "domain" } }, 71 | { "signal", { "signal" } }, 72 | }); 73 | 74 | QCOMPARE(signalList->item(0, 0)->font().bold(), false); 75 | data.setAsDomain("domain"); 76 | QCOMPARE(signalList->item(0, 0)->font().bold(), true); 77 | } 78 | 79 | // filter -> presenter 80 | 81 | void SignalListPresenterTests::OnSignalFilterEdited_Should_HideNonMatchingRows() 82 | { 83 | PrepareTest(); 84 | data.add({ 85 | { "domain", { "domain" } }, 86 | { "signal", { "signal" } }, 87 | }); 88 | 89 | QTest::keyClicks(filterEdit.get(), "doma"); 90 | 91 | QVERIFY(!signalList->isRowHidden(0)); 92 | QVERIFY(signalList->isRowHidden(1)); 93 | } 94 | 95 | void SignalListPresenterTests::PrepareTest() 96 | { 97 | data.clear(); 98 | filterEdit.reset(new QLineEdit()); 99 | signalCntLabel.reset(new QLabel()); 100 | domainLabel.reset(new QLabel()); 101 | presenter = make_unique( 102 | signalList.get(), 103 | filterEdit.get(), 104 | signalCntLabel.get(), 105 | domainLabel.get(), 106 | data); 107 | } 108 | 109 | void SignalListPresenterTests::OnCursorChanged_Should_DisplaySignalValuesAtDomain() 110 | { 111 | PrepareTest(); 112 | data.add({ 113 | { "domain", { "domain", {1,2,3} } }, 114 | { "signal", { "signal", {1,4,9} } }, 115 | }); 116 | data.setAsDomain("domain"); 117 | 118 | QCustomPlot plot; 119 | PlotCursor cursor(&plot, data); 120 | QObject::connect(&cursor, SIGNAL(CursorChanged(qreal, size_t)), presenter.get(), SLOT(OnCursorValueChanged(qreal, size_t))); 121 | 122 | cursor.set(1.0); 123 | QCOMPARE(signalList->item(0, 1)->text(), QString("1")); 124 | QCOMPARE(signalList->item(1, 1)->text(), QString("1")); 125 | 126 | cursor.set(2.0); 127 | QCOMPARE(signalList->item(0, 1)->text(), QString("2")); 128 | QCOMPARE(signalList->item(1, 1)->text(), QString("4")); 129 | 130 | cursor.set(3.0); 131 | QCOMPARE(signalList->item(0, 1)->text(), QString("3")); 132 | QCOMPARE(signalList->item(1, 1)->text(), QString("9")); 133 | 134 | cursor.set(5.0); 135 | QCOMPARE(signalList->item(0, 1)->text(), QString("3")); 136 | QCOMPARE(signalList->item(1, 1)->text(), QString("9")); 137 | } 138 | -------------------------------------------------------------------------------- /AnvediCheTests/SignalListPresenterTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TestRunner.h" 4 | #include 5 | #include 6 | #include 7 | #include "..\SignalData.h" 8 | #include "..\SignalListPresenter.h" 9 | 10 | class SignalListPresenterTests : public QObject 11 | { 12 | Q_OBJECT 13 | private slots: 14 | void initTestCase(); 15 | // model -> (signal) -> presenter 16 | void OnNewData_Should_PopulateTableWidget(); 17 | void OnClear_Should_EmptyTableWidget(); 18 | void OnSignalVisibilityChanged_Should_ChangeCorrespondingTableEntry(); 19 | void OnDomainChanged_Should_BoldCorrespondingTableEntry(); 20 | // filter -> presenter 21 | void OnSignalFilterEdited_Should_HideNonMatchingRows(); 22 | // cursor -> presenter 23 | void OnCursorChanged_Should_DisplaySignalValuesAtDomain(); 24 | private: 25 | void PrepareTest(); 26 | 27 | std::unique_ptr signalList; 28 | std::unique_ptr filterEdit; 29 | std::unique_ptr signalCntLabel; 30 | std::unique_ptr domainLabel; 31 | SignalData data; 32 | 33 | std::unique_ptr presenter; 34 | }; 35 | 36 | DECLARE_TEST(SignalListPresenterTests) -------------------------------------------------------------------------------- /AnvediCheTests/TestRunner.cpp: -------------------------------------------------------------------------------- 1 | #include "TestRunner.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | QStringList FromCmdArgs(int argc, char** argv) 10 | { 11 | QStringList cmd; 12 | for (auto i = 0; i < argc; ++i) 13 | { 14 | cmd.push_back(argv[i]); 15 | } 16 | return cmd; 17 | } 18 | 19 | int TestRunner::RunAll(int argc, char** argv) 20 | { 21 | return RunAll(FromCmdArgs(argc, argv)); 22 | } 23 | 24 | int TestRunner::RunAll(const QStringList& cmd, std::function fn) 25 | { 26 | int errorCode = 0; 27 | for (auto test : m_tests) 28 | { 29 | errorCode |= QTest::qExec(test, cmd); 30 | fn(test); 31 | }; 32 | if (m_tests.empty()) 33 | cout << "Sad test project with no tests... :(" << endl; 34 | 35 | return errorCode; 36 | } 37 | 38 | enum Color { blue = 1, green, cyan, red, purple, yellow, grey, dgrey, hblue, hgreen, hcyan, hred, hpurple, hyellow, hwhite }; 39 | 40 | void SetConsoleColor(Color color) 41 | { 42 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (WORD) color); 43 | } 44 | 45 | WORD GetConsoleColor() 46 | { 47 | auto consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); 48 | CONSOLE_SCREEN_BUFFER_INFO con_info; 49 | GetConsoleScreenBufferInfo(consoleHandle, &con_info); 50 | return con_info.wAttributes; 51 | } 52 | 53 | class ColorGuard 54 | { 55 | public: 56 | ColorGuard() : m_color(GetConsoleColor()) {} 57 | 58 | ~ColorGuard() 59 | { 60 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), m_color); 61 | } 62 | 63 | private: 64 | WORD m_color; 65 | }; 66 | 67 | int TestRunner::RunAllColorized(int argc, char** argv) 68 | { 69 | auto cmd = FromCmdArgs(argc, argv); 70 | cmd.push_back("-o"); 71 | cmd.push_back("tmpLog,txt"); 72 | 73 | auto cnt = m_tests.size(); 74 | auto exitCode = RunAll(cmd, [&cnt](QObject*){ 75 | ifstream file("tmpLog"); 76 | string line; 77 | const regex fail("(FAIL.*)"); 78 | const regex pass("(PASS.*)"); 79 | const regex benchmark("(RESULT.*)"); 80 | const regex skip("(SKIP.*)"); 81 | 82 | while (getline(file, line)) 83 | { 84 | ColorGuard guard; 85 | if (regex_match(line, fail)) 86 | { 87 | SetConsoleColor(hred); 88 | } 89 | if (regex_match(line, pass)) 90 | { 91 | SetConsoleColor(hgreen); 92 | } 93 | if (regex_match(line, benchmark)) 94 | { 95 | SetConsoleColor(hcyan); 96 | } 97 | if (regex_match(line, skip)) 98 | { 99 | SetConsoleColor(hyellow); 100 | } 101 | cout << line << endl; 102 | } 103 | --cnt; 104 | }); 105 | 106 | if (exitCode) 107 | { 108 | ColorGuard guard; 109 | SetConsoleColor(hred); 110 | cout << ">>>>> TESTS FAILED!!!" << endl; 111 | } 112 | 113 | if (!cnt && !exitCode) 114 | { 115 | ColorGuard guard; 116 | SetConsoleColor(hgreen); 117 | cout << ":) :) :) :) TESTS OK!!!" << endl; 118 | } 119 | 120 | return exitCode; 121 | } 122 | 123 | void TestRunner::RegisterTest(QObject* test) 124 | { 125 | auto testName = test->objectName(); 126 | 127 | if (!std::count_if(begin(m_tests), end(m_tests), [&testName](QObject* elem) { 128 | return elem->objectName() == testName; 129 | })) 130 | { 131 | m_tests.push_back(test); 132 | } 133 | } 134 | 135 | TestRunner& TestRunner::Instance() 136 | { 137 | static TestRunner instance; 138 | return instance; 139 | } -------------------------------------------------------------------------------- /AnvediCheTests/TestRunner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class QStringList; 11 | 12 | ////////////////////////////////////////////////////////////////////////// 13 | // Test Runner allows automatic execution of tests 14 | class TestRunner 15 | { 16 | public: 17 | static TestRunner& Instance(); 18 | void RegisterTest(QObject* test); 19 | int RunAll(int argc, char** argv); 20 | int RunAllColorized(int argc, char** argv); 21 | private: 22 | int RunAll(const QStringList& cmd, std::function fn = [](QObject*){ std::cout << std::endl; }); 23 | std::list m_tests; 24 | }; 25 | 26 | ////////////////////////////////////////////////////////////////////////// 27 | //Support structure 28 | template 29 | class Test 30 | { 31 | public: 32 | QSharedPointer child; 33 | 34 | Test(const QString& name) : child(new T) 35 | { 36 | child->setObjectName(name); 37 | TestRunner::Instance().RegisterTest(child.data()); 38 | } 39 | }; 40 | 41 | 42 | // Use this macro after your test declaration (in the header file) 43 | #define DECLARE_TEST(className) static Test t(#className); 44 | 45 | // Use this macro to execute all tests 46 | #define RUN_ALL_TESTS(argc, argv) TestRunner::Instance().RunAll(argc, argv) 47 | 48 | // Use this macro to execute all tests (colored out) 49 | #define RUN_ALL_TESTS_COLOR(argc, argv) TestRunner::Instance().RunAllColorized(argc, argv) -------------------------------------------------------------------------------- /AnvediCheTests/WorkspaceSerializerTests.cpp: -------------------------------------------------------------------------------- 1 | #pragma warning (disable : 4996) 2 | #include "WorkspaceSerializerTests.h" 3 | #include "..\SignalData.h" 4 | #include "..\PlotInfo.h" 5 | #include "..\WorkspaceSerializer.h" 6 | #include 7 | #include 8 | #include 9 | 10 | void WorkspaceSerializerTests::VerifyActualDataAndPersistedFile(const QString& file, const DataMap& expectedDataMap, const QString& expectedDomain, const QColor& expectedBackground) 11 | { 12 | SignalData actualSignalData; 13 | PlotInfo actualPlotInfo; 14 | QSignalSpy dataSpy(&actualSignalData, SIGNAL(DataAdded(const DataMap&))); 15 | QSignalSpy plotSpy(&actualPlotInfo, SIGNAL(BackgroundColorChanged(const QColor&))); 16 | 17 | WorkspaceSerializer::Read(file, actualSignalData, actualPlotInfo); 18 | 19 | QCOMPARE(1, dataSpy.count()); 20 | QCOMPARE(1, plotSpy.count()); 21 | 22 | auto actualdata = dataSpy.takeFirst().takeFirst().value(); 23 | QCOMPARE(actualdata, expectedDataMap); 24 | QCOMPARE(actualSignalData.getDomain()->name, expectedDomain); 25 | 26 | auto actualColor = plotSpy.takeFirst().takeFirst().value(); 27 | QCOMPARE(actualColor, expectedBackground); 28 | } 29 | 30 | 31 | void WorkspaceSerializerTests::ReadTest() 32 | { 33 | DataMap expectedData = { 34 | { "cubic", 35 | { 36 | "cubic", { 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000 }, { "#008000", true, 1, 1000 } 37 | } 38 | }, 39 | { "line", 40 | { 41 | "line", { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { "#ffff00", false, 1, 10 } 42 | } 43 | } 44 | }; 45 | 46 | VerifyActualDataAndPersistedFile(":/AnvediCheTests/test-data/cubic.json", expectedData, "line", "#000000"); 47 | } 48 | 49 | void WorkspaceSerializerTests::WriteTest() 50 | { 51 | DataMap dataMap = { 52 | { "cubic", 53 | { 54 | "cubic", { 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000 }, { "#008000", true, 1, 1000 } 55 | } 56 | }, 57 | { "line", 58 | { 59 | "line", { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { "#ffff00", false, 1, 10 } 60 | } 61 | } 62 | }; 63 | PlotInfo plot; 64 | plot.setBackgroundColor("red"); 65 | SignalData data; 66 | data.add(dataMap); 67 | data.setAsDomain("line"); 68 | 69 | QString tmpFileName = std::tmpnam(nullptr); 70 | struct guard 71 | { 72 | guard(const QString& fileName) 73 | : fileName(fileName) 74 | { 75 | } 76 | ~guard() 77 | { 78 | QFile f(fileName); 79 | f.remove(); 80 | } 81 | const QString& fileName; 82 | } fileDeleter(tmpFileName); 83 | 84 | WorkspaceSerializer::Write(tmpFileName, data, plot); 85 | 86 | VerifyActualDataAndPersistedFile(tmpFileName, dataMap, "line", "red"); 87 | } 88 | -------------------------------------------------------------------------------- /AnvediCheTests/WorkspaceSerializerTests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "TestRunner.h" 4 | #include "..\SignalData.h" 5 | 6 | class WorkspaceSerializerTests : public QObject 7 | { 8 | Q_OBJECT 9 | private slots: 10 | void ReadTest(); 11 | void WriteTest(); 12 | private: 13 | void VerifyActualDataAndPersistedFile(const QString& file, const DataMap& expectedDataMap, const QString& expectedDomain, const QColor& expectedBackground); 14 | }; 15 | 16 | DECLARE_TEST(WorkspaceSerializerTests) -------------------------------------------------------------------------------- /AnvediCheTests/anvedichetests.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | test-data/cubic.json 4 | 5 | 6 | -------------------------------------------------------------------------------- /AnvediCheTests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TestRunner.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | return RUN_ALL_TESTS_COLOR(argc, argv); 8 | } 9 | -------------------------------------------------------------------------------- /AnvediCheTests/test-data/cubic.json: -------------------------------------------------------------------------------- 1 | { 2 | "background": "#000000", 3 | "domain": "line", 4 | "signals": [ 5 | { 6 | "color": "#008000", 7 | "name": "cubic", 8 | "values": [ 9 | 1, 10 | 8, 11 | 27, 12 | 64, 13 | 125, 14 | 216, 15 | 343, 16 | 512, 17 | 729, 18 | 1000 19 | ], 20 | "visible": true 21 | }, 22 | { 23 | "color": "#ffff00", 24 | "name": "line", 25 | "values": [ 26 | 1, 27 | 2, 28 | 3, 29 | 4, 30 | 5, 31 | 6, 32 | 7, 33 | 8, 34 | 9, 35 | 10 36 | ], 37 | "visible": false 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /HelloQCustomPlot/HelloQCustomPlot.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;cxx;c;def 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h 11 | 12 | 13 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} 14 | qrc;* 15 | false 16 | 17 | 18 | {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} 19 | moc;h;cpp 20 | False 21 | 22 | 23 | {ea7b98e5-0656-4a92-9c3a-adc7da6ea440} 24 | cpp;moc 25 | False 26 | 27 | 28 | {d7feb52e-39ee-47e9-bd4f-fb0169499ee5} 29 | cpp;moc 30 | False 31 | 32 | 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | Generated Files\Debug 42 | 43 | 44 | Generated Files\Release 45 | 46 | 47 | 48 | 49 | Header Files 50 | 51 | 52 | -------------------------------------------------------------------------------- /HelloQCustomPlot/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "../Anvedi/qcustomplot.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | QCustomPlot plot; plot.setGeometry(40, 40, 800, 600); 9 | 10 | // generate some data: 11 | QVector x(101), y(101); for (int i = 0; i < 101; ++i) 12 | { 13 | x[i] = i / 50.0 - 1; // x goes from -1 to 1 14 | y[i] = x[i] * x[i]; // let's plot a parabola 15 | } 16 | // create graph and assign data to it: 17 | auto graph = plot.addGraph(); 18 | graph->setData(x, y); 19 | // give the axes some labels: 20 | plot.xAxis->setLabel("x"); 21 | plot.yAxis->setLabel("y"); 22 | plot.legend->setVisible(true); 23 | // autoscale: 24 | graph->rescaleAxes(); 25 | 26 | // interactions 27 | plot.setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); 28 | 29 | plot.replot(); 30 | 31 | plot.show(); 32 | return a.exec(); 33 | } 34 | -------------------------------------------------------------------------------- /QShell/ClearConsole.cpp: -------------------------------------------------------------------------------- 1 | #include "ClearConsole.h" 2 | 3 | QString ClearConsole::toString() 4 | { 5 | emit ClcCalled(); // -> will be received to clear the console 6 | return QString(); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /QShell/ClearConsole.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* this hack merits consideration: 6 | you know when you evaluate an object in Js, its description gets printed 7 | In QtScript this means: invoking the toString public slot (if any), or a default description. 8 | Suppose I want a "clc" Matlab-like command, which clears the console: 9 | > clc 10 | We can declare a global "clc" object such that its toString emits a signal... 11 | */ 12 | 13 | class ClearConsole : public QObject 14 | { 15 | Q_OBJECT 16 | signals: 17 | void ClcCalled(); 18 | public slots: 19 | QString toString(); 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /QShell/CommandProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "CommandProvider.h" 2 | #include "qshell.h" 3 | #include "QtCore\qmimedata.h" 4 | #include "QShellUtils.h" 5 | #include "QtWidgets\qfiledialog.h" 6 | #include "QShellSyntaxHighlighter.h" 7 | #include 8 | #include "HistoryKeyHandler.h" 9 | #include "MiscKeyHandler.h" 10 | #include "ScriptEvaluatorKeyHandler.h" 11 | #include "CompleterKeyHandler.h" 12 | #include "DefaultKeyHandler.h" 13 | #include "ClearConsole.h" 14 | 15 | using namespace std; 16 | 17 | 18 | CommandProvider::CommandProvider(QShell& pShell) 19 | : shell(pShell), menuBuilder(pShell) 20 | { 21 | connect(&menuBuilder, SIGNAL(ClearActionTriggered()), this, SLOT(onClearAction())); 22 | connect(&menuBuilder, SIGNAL(LoadScriptActionTriggered()), this, SLOT(onLoadScriptAction())); 23 | 24 | new QShellSyntaxHighlighter(shell.document()); // probably to move 25 | } 26 | 27 | void CommandProvider::SetHandlers( HandlersVec h ) 28 | { 29 | handlers = move(h); 30 | } 31 | 32 | void CommandProvider::ChangeFontSize(int points) 33 | { 34 | auto currentFont = shell.font(); 35 | currentFont.setPointSize(currentFont.pointSize()+points); 36 | shell.setFont(currentFont); 37 | } 38 | 39 | void CommandProvider::onKeyPress( QKeyEvent* key ) 40 | { 41 | shell.setReadOnly(false); 42 | auto it = find_if(begin(handlers), end(handlers), 43 | [key, this](IKeyPressHandler* handler){ 44 | return handler->onKeyPressed(shell, key); 45 | }); 46 | } 47 | 48 | void CommandProvider::onMouseWheelEvent( QWheelEvent* e) 49 | { 50 | if (false == e->modifiers().testFlag(Qt::ControlModifier)) 51 | return shell.processWheelEvent(e); 52 | 53 | auto points = e->delta()>0 ? 1 : -1; 54 | ChangeFontSize(points); 55 | AcceptEvent(shell, e); 56 | } 57 | 58 | void CommandProvider::onContextMenuEvent( QContextMenuEvent *event ) 59 | { 60 | shell.setReadOnly(!CursorCanWrite(shell)); 61 | auto contextMenu = menuBuilder.CreateMenu(); 62 | contextMenu->exec(event->globalPos()); 63 | } 64 | 65 | void CommandProvider::onInsertFromMimeData( const QMimeData * source ) 66 | { 67 | auto&& text = source->text(); 68 | auto tokens = text.split("\n"); 69 | 70 | if (tokens.empty()) 71 | return; 72 | 73 | if (false == CursorCanWrite(shell)) 74 | MoveCursorAtEnd(shell); 75 | 76 | shell.insertPlainText(tokens[0]); 77 | for(auto i=1; i 4 | #include "QtGui\qevent.h" 5 | #include "QShellContextMenuBuilder.h" 6 | #include "KeyPressHandler.h" 7 | 8 | class QShell; 9 | class ClearConsole; 10 | 11 | typedef std::vector HandlersVec; 12 | 13 | class CommandProvider : public QObject 14 | { 15 | Q_OBJECT 16 | public: 17 | CommandProvider(QShell& pShell); 18 | 19 | void onKeyPress(QKeyEvent* key); 20 | void onMouseWheelEvent(QWheelEvent* key); 21 | void onContextMenuEvent(QContextMenuEvent *event); 22 | void onInsertFromMimeData ( const QMimeData * source ); 23 | void SetHandlers(HandlersVec h); 24 | void SetClc(ClearConsole& clc); 25 | private slots: 26 | void onClearAction(); 27 | void onLoadScriptAction(); 28 | signals: 29 | void ScriptLoadRequested(const QString&); 30 | private: 31 | void ChangeFontSize(int points); 32 | 33 | QShell& shell; 34 | QShellContextMenuBuilder menuBuilder; 35 | HandlersVec handlers; 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /QShell/CompleterKeyHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "CompleterKeyHandler.h" 2 | #include "QShellCompleter.h" 3 | #include "qshell.h" 4 | #include "QShellUtils.h" 5 | #include "QAbstractItemView.h" 6 | #include "qstringlistmodel.h" 7 | #include "qtwidgets\qscrollbar.h" 8 | 9 | CompleterKeyHandler::CompleterKeyHandler( QShell& s ) 10 | : shell(s) 11 | { 12 | completer = new QShellCompleter(&shell); // potrei passargli direttamente il completer 13 | completer->setWidget(&shell); 14 | completer->setCompletionMode(QCompleter::PopupCompletion); 15 | completer->setCaseSensitivity(Qt::CaseInsensitive); 16 | completer->setSeparator(QLatin1String(".")); 17 | 18 | QObject::connect(completer, SIGNAL(activated(QString)), 19 | this, SLOT(onInsertSuggestion(const QString&))); 20 | } 21 | 22 | bool CompleterKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 23 | { 24 | if (e->key() == Qt::Key_Tab) 25 | { 26 | MoveCursorAtEndIfBadPosition(shell); 27 | if (e->modifiers().testFlag(Qt::ShiftModifier)) 28 | { 29 | shell.processKeyEvent(e); 30 | return true; 31 | } 32 | if (completer->popup()->isVisible()) 33 | { 34 | completer->popup()->hide(); 35 | IgnoreEvent(shell, e); 36 | return true; 37 | } 38 | ShowCompletion(shell); 39 | return true; 40 | } 41 | if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) 42 | { 43 | if (completer->popup()->isVisible()) 44 | { 45 | IgnoreEvent(shell, e); 46 | return true; 47 | } 48 | } 49 | if (e->key() == Qt::Key_Backspace) 50 | { 51 | if (CursorCanWrite(shell) && (shell.textCursor().hasSelection() || CursorBlockDifference(shell) > 0)) 52 | { 53 | shell.processKeyEvent(e); 54 | if (completer->popup()->isVisible()) 55 | { 56 | ShowCompletion(shell); 57 | } 58 | } 59 | AcceptEvent(shell, e); 60 | return true; 61 | } 62 | if (e->key() == Qt::Key_Escape) 63 | { 64 | if (completer->popup()->isVisible()) 65 | { 66 | completer->popup()->hide(); 67 | AcceptEvent(shell, e); 68 | return true; 69 | } 70 | } 71 | 72 | return false; 73 | } 74 | 75 | void CompleterKeyHandler::onUnknownKeyProcessed( QShell& shell, QKeyEvent* e ) 76 | { 77 | if (completer->popup()->isVisible()) 78 | ShowCompletion(shell); 79 | } 80 | 81 | // not should be here 82 | QAbstractItemModel* CompleterKeyHandler::intelliNaiveModel(const QString& rootObj) 83 | { 84 | return new QStringListModel(engine->Suggest(rootObj), completer); 85 | } 86 | 87 | void CompleterKeyHandler::ShowCompletion(QShell& shell) 88 | { 89 | auto completionPrefix = TextUnderCursor(shell); 90 | auto toSuggest = completionPrefix; 91 | if (completionPrefix.isEmpty()) 92 | toSuggest = "this"; 93 | else 94 | { 95 | auto lastDot = completionPrefix.lastIndexOf('.'); 96 | if (lastDot>0) 97 | { 98 | toSuggest = completionPrefix.mid(0, lastDot); 99 | if (completionPrefix.endsWith('.')) 100 | completionPrefix = ""; 101 | else completionPrefix = completionPrefix.mid(lastDot+1); 102 | } 103 | else 104 | { 105 | toSuggest = "this"; 106 | } 107 | } 108 | 109 | completer->setModel(intelliNaiveModel(toSuggest)); 110 | 111 | // 112 | if (completionPrefix != completer->completionPrefix()) { 113 | completer->setCompletionPrefix(completionPrefix); 114 | completer->popup()->setCurrentIndex(completer->completionModel()->index(0, 0)); 115 | } 116 | QRect cr = shell.cursorRect(); 117 | cr.setWidth(completer->popup()->sizeHintForColumn(0) 118 | + completer->popup()->verticalScrollBar()->sizeHint().width()); 119 | auto popup = completer->popup(); 120 | completer->popup()->setVisible(true); 121 | completer->complete(cr); // popup it up! 122 | } 123 | 124 | void CompleterKeyHandler::onNewEngine( std::shared_ptr e) 125 | { 126 | engine = e; 127 | completer->setModel(intelliNaiveModel("this")); 128 | } 129 | 130 | void CompleterKeyHandler::onInsertSuggestion( const QString& suggestion ) 131 | { 132 | // possible refactoring: handle this with a SIGNAL 133 | // (handled by the CommandProvider or someone else) 134 | if (CursorCanWrite(shell) && completer) 135 | { 136 | auto tc = shell.textCursor(); 137 | auto prefixLen = completer->completionPrefix().length(); 138 | if (prefixLen > 0) 139 | { 140 | tc.selectionStart(); 141 | tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, prefixLen); 142 | tc.selectionEnd(); 143 | tc.removeSelectedText(); 144 | } 145 | tc.insertText(suggestion); 146 | shell.setTextCursor(tc); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /QShell/CompleterKeyHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyPressHandler.h" 3 | #include "qobjectdefs.h" 4 | #include "QShellEngine.h" 5 | 6 | class QAbstractItemModel; 7 | class QShellCompleter; 8 | 9 | class CompleterKeyHandler : public IKeyPressHandler 10 | { 11 | Q_OBJECT 12 | public: 13 | CompleterKeyHandler(QShell& shell); 14 | 15 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 16 | public slots: 17 | void onUnknownKeyProcessed(QShell& shell, QKeyEvent* e); 18 | void onNewEngine(std::shared_ptr); 19 | void onInsertSuggestion(const QString& suggestion); 20 | private: 21 | void ShowCompletion(QShell& shell); 22 | QAbstractItemModel* intelliNaiveModel(const QString& rootObj); 23 | 24 | QShellCompleter* completer; 25 | QShell& shell; 26 | std::shared_ptr engine; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /QShell/DefaultKeyHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "DefaultKeyHandler.h" 2 | #include "QShellUtils.h" 3 | #include "qtextcursor.h" 4 | #include "qshell.h" 5 | 6 | bool DefaultKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 7 | { 8 | MoveCursorAtEndIfBadPosition(shell); 9 | shell.processKeyEvent(e); 10 | emit UnknownKeyProcessed(shell, e); 11 | return true; 12 | } 13 | -------------------------------------------------------------------------------- /QShell/DefaultKeyHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyPressHandler.h" 3 | 4 | class DefaultKeyHandler : public IKeyPressHandler 5 | { 6 | Q_OBJECT 7 | public: 8 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 9 | signals: 10 | void UnknownKeyProcessed(QShell&, QKeyEvent*); 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /QShell/Forms/ShellWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | QShell 25 | QPlainTextEdit 26 |
qshell.h
27 |
28 |
29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /QShell/HistoryKeyHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "HistoryKeyHandler.h" 2 | #include "QShellUtils.h" 3 | #include "QShellEngineResult.h" 4 | 5 | 6 | bool HistoryKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 7 | { 8 | auto key = e->key(); 9 | if ( (key == Qt::Key_Up || key == Qt::Key_Down) && !IsMultiLine(shell)) 10 | { 11 | DeleteLastBlockContentKeepingPromptString(shell); 12 | auto historyFn = (key == Qt::Key_Up) ? &HistoryRecorder::Next : &HistoryRecorder::Previous; 13 | shell.insertPlainText( (history.*historyFn)() ); 14 | AcceptEvent(shell, e); 15 | return true; 16 | } 17 | return false; 18 | } 19 | 20 | void HistoryKeyHandler::onTextEvaluated( const QString& text, QShellEngineResult& ) 21 | { 22 | history.Add(text); 23 | } 24 | -------------------------------------------------------------------------------- /QShell/HistoryKeyHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyPressHandler.h" 3 | #include "HistoryRecorder.h" 4 | #include "qshell.h" 5 | 6 | class QShellEngineResult; 7 | 8 | class HistoryKeyHandler : public IKeyPressHandler 9 | { 10 | Q_OBJECT 11 | public: 12 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 13 | public slots: 14 | void onTextEvaluated(const QString& text, QShellEngineResult& res); 15 | private: 16 | HistoryRecorder history; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /QShell/HistoryRecorder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "qshell_global.h" 6 | 7 | template 8 | class HistoryRecorder 9 | { 10 | public: 11 | 12 | HistoryRecorder() : m_current(history.end()) {} 13 | 14 | bool IsEmpty() 15 | { 16 | return history.empty(); 17 | } 18 | 19 | void Add(const Command& command) 20 | { 21 | history.push_front(command); 22 | m_current = history.end(); 23 | } 24 | 25 | void Add(Command&& command) 26 | { 27 | history.push_front(std::move(command)); 28 | m_current = history.end(); 29 | } 30 | 31 | Command Next() 32 | { 33 | if (history.empty()) 34 | return Command(); 35 | 36 | if (m_current == history.end()) 37 | { 38 | m_current = history.begin(); 39 | return *m_current; 40 | } 41 | 42 | auto next = m_current; 43 | ++next; 44 | return next==history.end() ? *m_current : *(++m_current); 45 | } 46 | 47 | Command Previous() 48 | { 49 | if (history.empty() || m_current == history.end()) 50 | return Command(); 51 | 52 | return m_current==history.begin() ? *m_current : *(--m_current); 53 | } 54 | 55 | void Clear() 56 | { 57 | history.clear(); 58 | m_current = history.end(); 59 | } 60 | 61 | private: 62 | std::list history; 63 | typename std::list::iterator m_current; 64 | }; -------------------------------------------------------------------------------- /QShell/KeyHandlersManager.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyHandlersManager.h" 2 | #include "CompleterKeyHandler.h" 3 | #include "MiscKeyHandler.h" 4 | #include "ScriptEvaluatorKeyHandler.h" 5 | #include "HistoryKeyHandler.h" 6 | #include "DefaultKeyHandler.h" 7 | #include "qshell.h" 8 | #include "qobject.h" 9 | #include "QShellEngineResult.h" 10 | 11 | using namespace std; 12 | 13 | KeyHandlersManager::KeyHandlersManager(QShell& shell) 14 | { 15 | unique_ptr completerHandler (new CompleterKeyHandler(shell)); 16 | unique_ptr defaultHandler (new DefaultKeyHandler()); 17 | unique_ptr evaluatorHandler (new ScriptEvaluatorKeyHandler(shell)); 18 | unique_ptr historyHandler (new HistoryKeyHandler()); 19 | 20 | // engine-interested handlers -> can't do this (probably because of shared_ptr 21 | //QObject::connect(&shell, SIGNAL(NewEngine(shared_ptr)), completerHandler.get(), SLOT(onNewEngine(shared_ptr))); 22 | //QObject::connect(&shell, SIGNAL(NewEngine(shared_ptr)), evaluatorHandler.get(), SLOT(onNewEngine(shared_ptr))); 23 | 24 | // other handlers 25 | QObject::connect(defaultHandler.get(), SIGNAL(UnknownKeyProcessed(QShell&, QKeyEvent*)), completerHandler.get(), SLOT(onUnknownKeyProcessed(QShell&, QKeyEvent*))); 26 | QObject::connect(evaluatorHandler.get(), SIGNAL(TextEvaluated(const QString&, QShellEngineResult&)), historyHandler.get(), SLOT(onTextEvaluated(const QString&, QShellEngineResult&))); 27 | QObject::connect(&shell, SIGNAL(LoadScriptRequested(const QString&)), evaluatorHandler.get(), SLOT(onLoadScript(const QString&))); 28 | 29 | handlers[HandlerId::CompleterHandler] = move(completerHandler); 30 | handlers[HandlerId::HistoryRecorderHandler] = move(historyHandler); 31 | handlers[HandlerId::MiscHandler].reset(new MiscKeyHandler()); 32 | handlers[HandlerId::CursorDependantHandler].reset(new CursorDependantKeyHandler()); 33 | handlers[HandlerId::EvaluatorHandler] = move(evaluatorHandler); 34 | handlers[HandlerId::DefaultHandler] = move(defaultHandler); 35 | } 36 | 37 | std::vector KeyHandlersManager::GetHandlers() const 38 | { 39 | vector res; 40 | res.reserve(handlers.size()); 41 | transform(begin(handlers), end(handlers), back_inserter(res), 42 | [](const pair>& handler){ 43 | return handler.second.get(); 44 | }); 45 | 46 | return res; 47 | } 48 | 49 | void KeyHandlersManager::SetEngine( std::shared_ptr engine ) 50 | { 51 | // I'd like to do that with a SIGNAL 52 | auto evalHandler = dynamic_cast(handlers[HandlerId::EvaluatorHandler].get()); 53 | if (evalHandler) 54 | evalHandler->onNewEngine(engine); 55 | 56 | auto complHandler = dynamic_cast(handlers[HandlerId::CompleterHandler].get()); 57 | if (complHandler) 58 | complHandler->onNewEngine(engine); 59 | } -------------------------------------------------------------------------------- /QShell/KeyHandlersManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "KeyPressHandler.h" 5 | #include 6 | #include "QShellEngine.h" 7 | 8 | class QShell; 9 | 10 | class KeyHandlersManager 11 | { 12 | public: 13 | enum class HandlerId 14 | { 15 | CompleterHandler=0, HistoryRecorderHandler, MiscHandler, CursorDependantHandler, 16 | EvaluatorHandler, DefaultHandler 17 | }; 18 | 19 | KeyHandlersManager(QShell& shell); 20 | 21 | std::vector GetHandlers() const; 22 | void SetEngine( std::shared_ptr engine ); 23 | 24 | private: 25 | std::map> handlers; 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /QShell/KeyPressHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "qobjectdefs.h" 3 | #include "qobject.h" 4 | 5 | class QKeyEvent; 6 | class QShell; 7 | 8 | class IKeyPressHandler : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | ~IKeyPressHandler() {} 13 | virtual bool onKeyPressed(QShell& shell, QKeyEvent* e) = 0; 14 | }; -------------------------------------------------------------------------------- /QShell/MiscKeyHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "MiscKeyHandler.h" 2 | #include "qevent.h" 3 | #include "qshell.h" 4 | #include "QShellUtils.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | typedef map> CommandMap; 13 | 14 | #define REGISTER_FUNCTION(key, code)\ 15 | fns.insert(make_pair(key, code)) 16 | 17 | CommandMap CreateCursorIndependantFunctions() 18 | { 19 | CommandMap fns; 20 | REGISTER_FUNCTION(Qt::Key_Escape, [](QShell& shell, QKeyEvent* e){ 21 | DeleteLastBlockContentKeepingPromptString(shell); 22 | return true; 23 | }); 24 | REGISTER_FUNCTION(Qt::Key_Home, [](QShell& shell, QKeyEvent* e){ 25 | auto currentCursor = shell.textCursor(); 26 | auto moveMode = e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor; 27 | currentCursor.setPosition(shell.document()->lastBlock().position() + shell.Prompt().size(), moveMode); 28 | shell.setTextCursor(currentCursor); 29 | return true; 30 | }); 31 | REGISTER_FUNCTION(Qt::Key_PageUp, [](QShell& shell, QKeyEvent* e){ 32 | auto moveMode = e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor; 33 | shell.moveCursor(QTextCursor::Up, moveMode); 34 | return true; 35 | }); 36 | REGISTER_FUNCTION(Qt::Key_PageDown, [](QShell& shell, QKeyEvent* e){ 37 | auto moveMode = e->modifiers().testFlag(Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor; 38 | shell.moveCursor(QTextCursor::Down, moveMode); 39 | return true; 40 | }); 41 | REGISTER_FUNCTION(Qt::Key_Escape, [](QShell& shell, QKeyEvent* e){ 42 | DeleteLastBlockContentKeepingPromptString(shell); 43 | return true; 44 | }); 45 | 46 | return fns; 47 | } 48 | 49 | vector SpecialKeys() 50 | { 51 | vector vect(8); 52 | vect.push_back(Qt::Key_Shift); vect.push_back(Qt::Key_Control); 53 | vect.push_back(Qt::Key_Alt);vect.push_back(Qt::Key_Left); 54 | vect.push_back(Qt::Key_Right); vect.push_back(Qt::Key_End); 55 | vect.push_back(Qt::Key_Up); vect.push_back(Qt::Key_Down); 56 | return vect; 57 | } 58 | 59 | bool MiscKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 60 | { 61 | static vector specKeys = SpecialKeys(); 62 | static CommandMap fns = CreateCursorIndependantFunctions(); 63 | auto key = e->key(); 64 | 65 | if (e->matches(QKeySequence::Copy) || e->matches(QKeySequence::Undo) || 66 | e->matches(QKeySequence::Redo) || find(begin(specKeys), end(specKeys), key) != end(specKeys)) 67 | { 68 | shell.processKeyEvent(e); 69 | return true; 70 | } 71 | 72 | auto it = fns.find(key); 73 | if (it != end(fns)) 74 | { 75 | auto res = it->second(shell, e); 76 | AcceptEvent(shell, e); 77 | return res; 78 | } 79 | 80 | return false; 81 | } 82 | 83 | ////////////////////////////////////////////////////////////////////////// 84 | 85 | CommandMap CreateCursorDependantFunctions() 86 | { 87 | CommandMap fns; 88 | REGISTER_FUNCTION(Qt::Key_Backspace, [](QShell& shell, QKeyEvent* e){ 89 | if (CursorCanWrite(shell) && (shell.textCursor().hasSelection() || CursorBlockDifference(shell) > 0)) 90 | { 91 | shell.processKeyEvent(e); 92 | } 93 | return true; 94 | }); 95 | REGISTER_FUNCTION(Qt::Key_Enter, [](QShell& shell, QKeyEvent* e){ 96 | if (e->modifiers().testFlag(Qt::ShiftModifier)) 97 | { 98 | shell.processKeyEvent(e); 99 | return true; 100 | } 101 | return false; 102 | }); 103 | fns.insert(make_pair(Qt::Key_Return, fns[Qt::Key_Enter])); 104 | 105 | return fns; 106 | } 107 | 108 | bool CursorDependantKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 109 | { 110 | static CommandMap fns = CreateCursorDependantFunctions(); 111 | auto it = fns.find(e->key()); 112 | if (it != end(fns)) 113 | { 114 | MoveCursorAtEndIfBadPosition(shell); 115 | return it->second(shell, e); 116 | } 117 | return false; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /QShell/MiscKeyHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyPressHandler.h" 3 | 4 | class MiscKeyHandler : public IKeyPressHandler 5 | { 6 | public: 7 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 8 | }; 9 | 10 | class CursorDependantKeyHandler : public IKeyPressHandler 11 | { 12 | public: 13 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 14 | }; -------------------------------------------------------------------------------- /QShell/QShell.pri: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | HEADERS += ./qshell.h \ 11 | ./MiscKeyHandler.h \ 12 | ./ScriptEvaluatorKeyHandler.h \ 13 | ./HistoryKeyHandler.h \ 14 | ./CompleterKeyHandler.h \ 15 | ./DefaultKeyHandler.h \ 16 | ./KeyPressHandler.h \ 17 | ./QShellScriptUtilis.h \ 18 | ./QShellEngine.h \ 19 | ./QShellEngineResult.h \ 20 | ./QShellEngineResult_qt.h \ 21 | ./QShellEngine_Qt.h \ 22 | ./ClearConsole.h \ 23 | ./resource.h \ 24 | ./HistoryRecorder.h \ 25 | ./QShellUtils.h \ 26 | ./qshell_global.h \ 27 | ./QShellContextMenuBuilder.h \ 28 | ./KeyHandlersManager.h \ 29 | ./QShellSyntaxHighlighter.h \ 30 | ./QShellCompleter.h \ 31 | ./CommandProvider.h 32 | SOURCES += ./qshell.cpp \ 33 | ./CompleterKeyHandler.cpp \ 34 | ./DefaultKeyHandler.cpp \ 35 | ./HistoryKeyHandler.cpp \ 36 | ./MiscKeyHandler.cpp \ 37 | ./ScriptEvaluatorKeyHandler.cpp \ 38 | ./ClearConsole.cpp \ 39 | ./QShellEngineResult_qt.cpp \ 40 | ./QShellEngine_Qt.cpp \ 41 | ./QShellScriptUtilis.cpp \ 42 | ./QShellContextMenuBuilder.cpp \ 43 | ./QShellUtils.cpp \ 44 | ./CommandProvider.cpp \ 45 | ./KeyHandlersManager.cpp \ 46 | ./QShellCompleter.cpp \ 47 | ./QShellSyntaxHighlighter.cpp 48 | FORMS += ./Forms/ShellWidget.ui 49 | -------------------------------------------------------------------------------- /QShell/QShell.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | TEMPLATE = lib 6 | TARGET = QShell 7 | DESTDIR = ../Win32/Debug 8 | QT += core script widgets gui printsupport 9 | CONFIG += debug 10 | DEFINES += WIN64 QT_DLL QT_SCRIPT_LIB QT_WIDGETS_LIB QSHELL_LIB QT_PRINTSUPPORT_LIB 11 | INCLUDEPATH += ./GeneratedFiles \ 12 | . \ 13 | ./GeneratedFiles/Debug 14 | DEPENDPATH += . 15 | MOC_DIR += ./GeneratedFiles/debug 16 | OBJECTS_DIR += debug 17 | UI_DIR += ./GeneratedFiles 18 | RCC_DIR += ./GeneratedFiles 19 | include(QShell.pri) 20 | -------------------------------------------------------------------------------- /QShell/QShell.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | C:\Qt-5.2\5.5\msvc2013_64 6 | PATH=$(QTDIR)\bin%3b$(PATH) 7 | 8 | 9 | C:\Qt-5.2\5.5\msvc2013_64 10 | PATH=$(QTDIR)\bin%3b$(PATH) 11 | 12 | -------------------------------------------------------------------------------- /QShell/QShellCompleter.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellCompleter.h" 2 | #include 3 | #include "QtWidgets\qlistview.h" 4 | #include "qevent.h" 5 | 6 | QShellCompleter::QShellCompleter(QWidget *parent) 7 | : QCompleter(parent) 8 | { 9 | } 10 | 11 | QShellCompleter::QShellCompleter(QAbstractItemModel *model, QWidget *parent) 12 | : QCompleter(model, parent) 13 | { 14 | } 15 | 16 | void QShellCompleter::setSeparator(const QString &separator) 17 | { 18 | sep = separator; 19 | } 20 | 21 | QString QShellCompleter::separator() const 22 | { 23 | return sep; 24 | } 25 | 26 | QStringList QShellCompleter::splitPath(const QString &path) const 27 | { 28 | if (sep.isNull()) { 29 | return QCompleter::splitPath(path); 30 | } 31 | 32 | return path.split(sep); 33 | } 34 | 35 | QString QShellCompleter::pathFromIndex(const QModelIndex &index) const 36 | { 37 | if (sep.isNull()) { 38 | return QCompleter::pathFromIndex(index); 39 | } 40 | 41 | // navigate up and accumulate data 42 | QStringList dataList; 43 | for (QModelIndex i = index; i.isValid(); i = i.parent()) { 44 | dataList.prepend(model()->data(i, completionRole()).toString()); 45 | } 46 | 47 | return dataList.join(sep); 48 | } -------------------------------------------------------------------------------- /QShell/QShellCompleter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QtWidgets\qcompleter.h" 4 | 5 | class QShellCompleter : public QCompleter 6 | { 7 | Q_OBJECT 8 | Q_PROPERTY(QString separator READ separator WRITE setSeparator) 9 | 10 | public: 11 | explicit QShellCompleter(QWidget *parent = 0); 12 | explicit QShellCompleter(QAbstractItemModel *model, QWidget *parent = 0); 13 | 14 | QString separator() const; 15 | public slots: 16 | void setSeparator(const QString &separator); 17 | protected: 18 | QStringList splitPath(const QString &path) const; 19 | QString pathFromIndex(const QModelIndex &index) const; 20 | private: 21 | QString sep; 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /QShell/QShellContextMenuBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellContextMenuBuilder.h" 2 | #include "qshell.h" 3 | #include "QtCore\qobject.h" 4 | 5 | QShellContextMenuBuilder::QShellContextMenuBuilder(QShell& pShell) 6 | : shell(pShell) 7 | { 8 | // Clear Action 9 | m_ClearAction = new QAction(tr("Clear"), &shell); 10 | m_ClearAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Delete)); 11 | QObject::connect(m_ClearAction, SIGNAL(triggered()), this, SIGNAL(ClearActionTriggered())); 12 | shell.addAction(m_ClearAction); 13 | 14 | // LoadScript Action 15 | m_LoadScriptAction = new QAction(tr("Load Script"), &shell); 16 | m_LoadScriptAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); 17 | QObject::connect(m_LoadScriptAction, SIGNAL(triggered()), this, SIGNAL(LoadScriptActionTriggered())); 18 | shell.addAction(m_LoadScriptAction); 19 | } 20 | 21 | std::unique_ptr QShellContextMenuBuilder::CreateMenu() 22 | { 23 | std::unique_ptr menu (shell.createStandardContextMenu()); 24 | menu->addAction(m_ClearAction); 25 | menu->addAction(m_LoadScriptAction); 26 | return menu; 27 | } 28 | -------------------------------------------------------------------------------- /QShell/QShellContextMenuBuilder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "QtWidgets\qmenu.h" 3 | #include 4 | 5 | class QShell; 6 | 7 | class QShellContextMenuBuilder : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | QShellContextMenuBuilder(QShell& shell); 12 | 13 | std::unique_ptr CreateMenu(); 14 | 15 | signals: 16 | void ClearActionTriggered(); 17 | void LoadScriptActionTriggered(); 18 | private: 19 | QAction* m_ClearAction; 20 | QAction* m_LoadScriptAction; 21 | QShell& shell; 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /QShell/QShellEngine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "QShellEngineResult.h" 5 | 6 | class QString; 7 | class QStringList; 8 | class QPlainTextEdit; 9 | 10 | class QShellEngine 11 | { 12 | public: 13 | virtual ~QShellEngine() = default; 14 | virtual void RegisterPrintFunction(QPlainTextEdit& console) = 0; 15 | virtual std::unique_ptr Evaluate(const QString& toEval) = 0; 16 | virtual std::unique_ptr MakeEmptyResult() = 0; 17 | virtual QStringList Suggest(const QString& value) = 0; 18 | virtual std::unique_ptr LoadScript(const QString& toEval) = 0; 19 | }; 20 | -------------------------------------------------------------------------------- /QShell/QShellEngineResult.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | class QShellEngineResult 6 | { 7 | public: 8 | virtual ~QShellEngineResult() = default; 9 | virtual QString ToQString() const = 0; 10 | virtual bool IsPrintable() const = 0; 11 | virtual bool IsError() const = 0; 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /QShell/QShellEngineResult_qt.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellEngineResult_Qt.h" 2 | #include 3 | 4 | QShellEngineResult_Qt::QShellEngineResult_Qt( QScriptValue value ) : m_value(value) 5 | { 6 | 7 | } 8 | 9 | QString QShellEngineResult_Qt::ToQString() const 10 | { 11 | return m_value.toString(); 12 | } 13 | 14 | bool QShellEngineResult_Qt::IsPrintable() const 15 | { 16 | return false == (m_value.isNull() || m_value.isUndefined()); 17 | } 18 | 19 | bool QShellEngineResult_Qt::IsError() const 20 | { 21 | return m_value.isError(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /QShell/QShellEngineResult_qt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "qscriptvalue.h" 3 | #include "QShellEngineResult.h" 4 | 5 | class QString; 6 | 7 | class QShellEngineResult_Qt : public QShellEngineResult 8 | { 9 | public: 10 | QShellEngineResult_Qt(QScriptValue value); 11 | QString ToQString() const ; 12 | bool IsPrintable() const ; 13 | bool IsError() const ; 14 | 15 | QScriptValue m_value; 16 | }; -------------------------------------------------------------------------------- /QShell/QShellEngine_Qt.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellEngine_Qt.h" 2 | #include "qscriptvalueiterator.h" 3 | #include "qshell.h" 4 | #include "QShellEngineResult_qt.h" 5 | #include "QShellScriptUtilis.h" 6 | #include "ClearConsole.h" 7 | 8 | QShellEngine_Qt::QShellEngine_Qt() 9 | { 10 | m_engine.globalObject().setProperty("exit", m_engine.newFunction(QtShellScriptUtils::ExitFunction)); 11 | m_engine.globalObject().setProperty("load", m_engine.newFunction(QtShellScriptUtils::LoadScript, 1)); 12 | } 13 | 14 | ClearConsole& QShellEngine_Qt::RegisterAndGetClc() 15 | { 16 | static ClearConsole clc; 17 | m_engine.globalObject().setProperty("clc", m_engine.newQObject(&clc)); 18 | return clc; 19 | } 20 | 21 | void QShellEngine_Qt::RegisterPrintFunction(QPlainTextEdit& console) 22 | { 23 | QtShellScriptUtils::RegisterPrintFunctionTo(console, m_engine); 24 | } 25 | 26 | std::unique_ptr QShellEngine_Qt::Evaluate( const QString& toEval ) 27 | { 28 | auto result = m_engine.evaluate(toEval); 29 | return std::unique_ptr(new QShellEngineResult_Qt(result)); 30 | } 31 | 32 | QScriptValue ScriptValueSuggestion(QScriptEngine& engine, const QString& value) 33 | { 34 | static const QString intelliFn = "({ Intelli: function(something) { var local=new Array(); var j=0; for (var i in something) local[j++]=i; return local; } }).Intelli"; 35 | return engine.evaluate(QString("%1(%2)").arg(intelliFn).arg(value)); 36 | } 37 | 38 | QStringList QShellEngine_Qt::Suggest( const QString& value ) 39 | { 40 | QStringList queryResult; 41 | auto scriptResult = ScriptValueSuggestion(m_engine, value); 42 | if (false == scriptResult.isError()) 43 | { 44 | QScriptValueIterator scriptValueIterator(scriptResult); 45 | scriptValueIterator.toBack(); 46 | scriptValueIterator.previous(); 47 | while (scriptValueIterator.hasPrevious()) 48 | { 49 | scriptValueIterator.previous(); 50 | queryResult.append(scriptValueIterator.value().toString()); 51 | } 52 | queryResult.sort(); 53 | } 54 | 55 | return queryResult; 56 | } 57 | 58 | std::unique_ptr QShellEngine_Qt::LoadScript( const QString& toEval ) 59 | { 60 | return Evaluate("load('" + toEval + "')"); 61 | } 62 | 63 | std::unique_ptr QShellEngine_Qt::MakeEmptyResult() 64 | { 65 | return std::make_unique(m_engine.undefinedValue()); 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /QShell/QShellEngine_Qt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "QShellEngine.h" 5 | #include "qshell_global.h" 6 | 7 | class QStringList; 8 | class QString; 9 | class QShell; 10 | class ClearConsole; 11 | 12 | class QSHELL_EXPORT QShellEngine_Qt : public QShellEngine 13 | { 14 | public: 15 | QShellEngine_Qt(); 16 | 17 | std::unique_ptr Evaluate(const QString& toEval); 18 | std::unique_ptr MakeEmptyResult() override; 19 | QStringList Suggest(const QString& value); 20 | std::unique_ptr LoadScript(const QString& toEval); 21 | void RegisterPrintFunction(QPlainTextEdit& console); 22 | ClearConsole& RegisterAndGetClc(); 23 | protected: 24 | // just a sort of decoration of this engine 25 | QScriptEngine m_engine; 26 | }; -------------------------------------------------------------------------------- /QShell/QShellScriptUtilis.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellScriptUtilis.h" 2 | #include "qplaintextedit.h" 3 | #include "QShellEngine.h" 4 | #include "qscriptengine.h" 5 | #include "qscriptcontext.h" 6 | #include "qapplication.h" 7 | #include "qtextstream.h" 8 | 9 | using namespace QtShellScriptUtils; 10 | 11 | void QtShellScriptUtils::RegisterPrintFunctionTo( QPlainTextEdit& edit, QScriptEngine& engine ) 12 | { 13 | QScriptValue printFunc = engine.newFunction(ConsolePrintFunction); 14 | printFunc.setData(engine.newQObject(&edit)); 15 | engine.globalObject().setProperty("print", printFunc); 16 | } 17 | 18 | QScriptValue QtShellScriptUtils::ConsolePrintFunction( QScriptContext *context, QScriptEngine *engine ) 19 | { 20 | QString result; 21 | for (int i = 0; i < context->argumentCount(); ++i) { 22 | if (i > 0) 23 | result.append(" "); 24 | result.append(context->argument(i).toString()); 25 | } 26 | 27 | QScriptValue calleeData = context->callee().data(); 28 | QPlainTextEdit *edit = qobject_cast(calleeData.toQObject()); 29 | edit->insertPlainText("\n"+result); 30 | 31 | return engine->undefinedValue(); 32 | } 33 | 34 | QScriptValue QtShellScriptUtils::ExitFunction( QScriptContext *context, QScriptEngine *engine ) 35 | { 36 | QApplication::exit(0); 37 | return engine->undefinedValue(); 38 | } 39 | 40 | QScriptValue QtShellScriptUtils::LoadScript( QScriptContext *context, QScriptEngine *engine ) 41 | { 42 | QString fileName = context->argument(0).toString(); 43 | 44 | for (int i = 0; i < context->argumentCount(); ++i) { 45 | QFile file(fileName); 46 | if (!file.open(QIODevice::ReadOnly)) 47 | return context->throwError(QString::fromLatin1("Could not open %0 for reading...").arg(fileName)); 48 | 49 | QTextStream ts(&file); 50 | QString contents = ts.readAll(); 51 | file.close(); 52 | QScriptContext *pc = context->parentContext(); 53 | context->setActivationObject(pc->activationObject()); 54 | context->setThisObject(pc->thisObject()); 55 | QScriptValue ret = engine->evaluate(contents); 56 | if (engine->hasUncaughtException()) 57 | return ret; 58 | } 59 | return QString::fromLatin1("Script %0 loaded correctly!").arg(fileName); 60 | } 61 | -------------------------------------------------------------------------------- /QShell/QShellScriptUtilis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QPlainTextEdit; 4 | class QScriptEngine; 5 | class QScriptValue; 6 | class QScriptContext; 7 | 8 | namespace QtShellScriptUtils 9 | { 10 | void RegisterPrintFunctionTo(QPlainTextEdit& edit, QScriptEngine& engine); 11 | 12 | QScriptValue ConsolePrintFunction(QScriptContext *context, QScriptEngine *engine); 13 | QScriptValue ExitFunction(QScriptContext *context, QScriptEngine *engine); 14 | QScriptValue LoadScript(QScriptContext *context, QScriptEngine *engine); 15 | } 16 | -------------------------------------------------------------------------------- /QShell/QShellSyntaxHighlighter.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellSyntaxHighlighter.h" 2 | 3 | QShellSyntaxHighlighter::QShellSyntaxHighlighter(QTextDocument *parent) 4 | : QSyntaxHighlighter(parent) 5 | { 6 | HighlightingRule rule; 7 | 8 | keywordFormat.setForeground(Qt::darkBlue); 9 | keywordFormat.setFontWeight(QFont::Bold); 10 | QStringList keywordPatterns; 11 | keywordPatterns << "\\bvar\\b" << "\\bArray\\b" << "\\bfunction\\b" 12 | << "\\breturn\\b" << "\\barguments\\b" << "\\bif\\b" 13 | << "\\belse\\b" << "\\bfor\\b" << "\\bswitch\\b" 14 | << "\\bcase\\b" << "\\bbreak\\b" << "\\bwhile\\b" << "\\bnumber\\b" 15 | << "\\bstring\\b" << "\\bobject\\b" << "\\bboolean\\b"; 16 | foreach (const QString &pattern, keywordPatterns) { 17 | rule.pattern = QRegExp(pattern); 18 | rule.format = keywordFormat; 19 | highlightingRules.append(rule); 20 | } 21 | 22 | classFormat.setFontWeight(QFont::Bold); 23 | classFormat.setForeground(Qt::darkMagenta); 24 | rule.pattern = QRegExp("\\bQ[A-Za-z]+\\b"); 25 | rule.format = classFormat; 26 | highlightingRules.append(rule); 27 | 28 | QTextCharFormat trueFalseFormat; 29 | trueFalseFormat.setForeground(Qt::blue); 30 | rule.pattern = QRegExp("\\b(true|false|this)\\b"); 31 | rule.format = trueFalseFormat; 32 | highlightingRules.append(rule); 33 | 34 | quotationFormat.setForeground(Qt::darkGreen); 35 | rule.pattern = QRegExp("\".*\""); 36 | rule.format = quotationFormat; 37 | highlightingRules.append(rule); 38 | 39 | singleQuotationFormat.setForeground(Qt::darkGreen); 40 | rule.pattern = QRegExp("'.*'"); 41 | rule.format = singleQuotationFormat; 42 | highlightingRules.append(rule); 43 | 44 | functionFormat.setFontItalic(true); 45 | functionFormat.setForeground(Qt::blue); 46 | rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()"); 47 | rule.format = functionFormat; 48 | highlightingRules.append(rule); 49 | 50 | singleLineCommentFormat.setForeground(Qt::red); 51 | rule.pattern = QRegExp("//[^\n]*"); 52 | rule.format = singleLineCommentFormat; 53 | highlightingRules.append(rule); 54 | 55 | multiLineCommentFormat.setForeground(Qt::red); 56 | 57 | commentStartExpression = QRegExp("/\\*"); 58 | commentEndExpression = QRegExp("\\*/"); 59 | } 60 | 61 | void QShellSyntaxHighlighter::highlightBlock(const QString &text) 62 | { 63 | foreach (const HighlightingRule &rule, highlightingRules) { 64 | QRegExp expression(rule.pattern); 65 | int index = expression.indexIn(text); 66 | while (index >= 0) { 67 | int length = expression.matchedLength(); 68 | setFormat(index, length, rule.format); 69 | index = expression.indexIn(text, index + length); 70 | } 71 | } 72 | setCurrentBlockState(0); 73 | 74 | int startIndex = 0; 75 | if (previousBlockState() != 1) 76 | startIndex = commentStartExpression.indexIn(text); 77 | 78 | while (startIndex >= 0) { 79 | int endIndex = commentEndExpression.indexIn(text, startIndex); 80 | int commentLength; 81 | if (endIndex == -1) { 82 | setCurrentBlockState(1); 83 | commentLength = text.length() - startIndex; 84 | } else { 85 | commentLength = endIndex - startIndex 86 | + commentEndExpression.matchedLength(); 87 | } 88 | setFormat(startIndex, commentLength, multiLineCommentFormat); 89 | startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); 90 | } 91 | } -------------------------------------------------------------------------------- /QShell/QShellSyntaxHighlighter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "qsyntaxhighlighter.h" 4 | #include "qstring.h" 5 | #include "QtWidgets\qplaintextedit.h" 6 | 7 | class QShellSyntaxHighlighter : public QSyntaxHighlighter 8 | { 9 | Q_OBJECT 10 | public: 11 | QShellSyntaxHighlighter(QTextDocument *parent = 0); 12 | 13 | protected: 14 | void highlightBlock(const QString &text); 15 | 16 | private: 17 | struct HighlightingRule 18 | { 19 | QRegExp pattern; 20 | QTextCharFormat format; 21 | }; 22 | QVector highlightingRules; 23 | 24 | QRegExp commentStartExpression; 25 | QRegExp commentEndExpression; 26 | 27 | QTextCharFormat keywordFormat; 28 | QTextCharFormat classFormat; 29 | QTextCharFormat singleLineCommentFormat; 30 | QTextCharFormat multiLineCommentFormat; 31 | QTextCharFormat quotationFormat; 32 | QTextCharFormat singleQuotationFormat; 33 | QTextCharFormat functionFormat; 34 | }; 35 | -------------------------------------------------------------------------------- /QShell/QShellUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "QShellUtils.h" 2 | #include "qshell.h" 3 | #include 4 | #include 5 | #include 6 | #include "QShellEngineResult.h" 7 | 8 | 9 | QString StringToEvaluate( QShell& console ) 10 | { 11 | auto currentLine = console.document()->lastBlock().text(); 12 | return currentLine.trimmed().remove(0, console.Prompt().size()); 13 | } 14 | 15 | void MoveCursorAtEndIfBadPosition( QShell& console ) 16 | { 17 | if (!CursorCanWrite(console)) 18 | MoveCursorAtEnd(console); 19 | } 20 | 21 | bool CursorCanWrite( QShell& console ) 22 | { 23 | auto minValidPosition = console.document()->lastBlock().position() + console.Prompt().size(); 24 | auto start = console.textCursor().selectionStart(); 25 | auto end = console.textCursor().selectionEnd(); 26 | 27 | return start >= minValidPosition && end >= minValidPosition; 28 | } 29 | 30 | int CursorBlockDifference( QShell& console ) 31 | { 32 | auto currentCursorPosition = console.textCursor().position(); 33 | auto lastBlockPosition = console.document()->lastBlock().position(); 34 | lastBlockPosition += console.Prompt().size(); 35 | return currentCursorPosition - lastBlockPosition; 36 | } 37 | 38 | void MoveCursorAtEnd( QShell& console ) 39 | { 40 | auto cursor = console.textCursor(); 41 | cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); 42 | console.setTextCursor(cursor); 43 | } 44 | 45 | void DeleteLastBlockContentKeepingPromptString( QShell& console ) 46 | { 47 | MoveCursorAtEnd(console); 48 | console.moveCursor( QTextCursor::EndOfBlock, QTextCursor::MoveAnchor ); 49 | console.moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); 50 | 51 | for (int i=0; ilastBlock().lineCount()>1; 60 | } 61 | 62 | void InsertEvaluationResult( QShell& console, const QShellEngineResult& result ) 63 | { 64 | // this stuff should be done by caring the syntax highlighting rules... 65 | QString html; 66 | if (result.IsPrintable()) 67 | { 68 | html += QString("

%1

") 69 | .arg( 70 | result.IsError() ? 71 | QString("%1").arg(result.ToQString()) : 72 | result.ToQString() 73 | ); 74 | } 75 | console.appendHtml(html); 76 | console.insertPlainText(console.Prompt()); 77 | console.document()->clearUndoRedoStacks(); 78 | } 79 | 80 | QString TextUnderCursor(QShell& console) 81 | { 82 | auto pos = console.textCursor().positionInBlock(); 83 | return console.document()->lastBlock() 84 | .text().left(pos) 85 | .split(QChar(8232)).last() 86 | .split(" ").last(); 87 | } 88 | 89 | 90 | -------------------------------------------------------------------------------- /QShell/QShellUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QShell; 4 | class QString; 5 | class QShellEngineResult; 6 | 7 | template 8 | void AcceptEvent(QShell& shell, EventType* e) 9 | { 10 | e->accept(); 11 | shell.ensureCursorVisible(); 12 | } 13 | 14 | template 15 | void IgnoreEvent(QShell& shell, EventType* e) 16 | { 17 | e->ignore(); 18 | } 19 | 20 | QString StringToEvaluate(QShell& console); 21 | int CursorBlockDifference( QShell& console ); 22 | void MoveCursorAtEndIfBadPosition( QShell& console ); 23 | bool CursorCanWrite( QShell& console ); 24 | void MoveCursorAtEnd( QShell& console ); 25 | void DeleteLastBlockContentKeepingPromptString(QShell& console); 26 | bool IsMultiLine( const QShell& console ); 27 | void InsertEvaluationResult( QShell& console, const QShellEngineResult& result ); 28 | QString TextUnderCursor(QShell& console); 29 | -------------------------------------------------------------------------------- /QShell/README.md: -------------------------------------------------------------------------------- 1 | # QShell 2 | 3 | A shell-like plug and play widget made with Qt, supporting QtScript by default. My requirement was to have a reusable widget ready to 4 | be promoted from QPlainTextEdit. 5 | 6 | Some features: 7 | 8 | * JS-like syntax highlighting 9 | 10 | * QtScript integration by default: 11 | * evaluation 12 | * print function 13 | * load script function 14 | * exit function 15 | 16 | [![https://gyazo.com/e3e7caf0483b197eb40d760348e5184c](https://i.gyazo.com/e3e7caf0483b197eb40d760348e5184c.gif)](https://gyazo.com/e3e7caf0483b197eb40d760348e5184c) 17 | 18 | * Naive intellisense (by default is by JS reflection) 19 | 20 | [![https://gyazo.com/82ab80a90e473b4ebe6a6536acb8e4c3](https://i.gyazo.com/82ab80a90e473b4ebe6a6536acb8e4c3.gif)](https://gyazo.com/82ab80a90e473b4ebe6a6536acb8e4c3) 21 | 22 | * traditional shell behavior: 23 | * history recording 24 | * customizable prompt string 25 | * multi-line edit 26 | * Matlab-like clc command 27 | * zoom-in/zoom-out 28 | * drag-and-drop 29 | 30 | # Disclaimer 31 | 32 | I coded this project in a couple of weeks during 2011. It does the job. 33 | Now I realize it's over designed and it could be done with much less effort and a few classes, 34 | but I don't have time to redo this component. 35 | 36 | At that time I also wrote both unit tests and end-to-end tests (e.g. GUI interaction) by using the QTestLib module. 37 | I'll make them available on a repo fully dedicated to QShell. 38 | -------------------------------------------------------------------------------- /QShell/ScriptEvaluatorKeyHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptEvaluatorKeyHandler.h" 2 | #include "qevent.h" 3 | #include "qshell.h" 4 | #include "QShellUtils.h" 5 | #include 6 | 7 | ScriptEvaluatorKeyHandler::ScriptEvaluatorKeyHandler( QShell& s ) 8 | : shell(s) 9 | { 10 | 11 | } 12 | 13 | bool ScriptEvaluatorKeyHandler::onKeyPressed( QShell& shell, QKeyEvent* e ) 14 | { 15 | if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) 16 | { 17 | auto textToEval = StringToEvaluate(shell); 18 | if (textToEval.isEmpty()) 19 | { 20 | AcceptEvent(shell, e); 21 | return true; 22 | } 23 | 24 | MoveCursorAtEnd(shell); 25 | auto result = engine->Evaluate(textToEval); 26 | emit TextEvaluated(textToEval, *result); 27 | 28 | InsertEvaluationResult(shell, textToEval.endsWith(";") ? *engine->MakeEmptyResult() : *result); 29 | AcceptEvent(shell, e); 30 | return true; 31 | } 32 | 33 | return false; 34 | } 35 | 36 | void ScriptEvaluatorKeyHandler::onNewEngine( std::shared_ptr e ) 37 | { 38 | engine = e; 39 | } 40 | 41 | void ScriptEvaluatorKeyHandler::onLoadScript( const QString& file ) 42 | { 43 | // possible refactoring: handle this with a SIGNAL 44 | // (handled by the CommandProvider or someone else) 45 | auto result = engine->LoadScript(file); 46 | InsertEvaluationResult(shell, *result); 47 | shell.ensureCursorVisible(); 48 | } 49 | -------------------------------------------------------------------------------- /QShell/ScriptEvaluatorKeyHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "KeyPressHandler.h" 3 | #include 4 | #include "QShellEngine.h" 5 | #include "QtCore\qobjectdefs.h" 6 | 7 | class QShell; 8 | 9 | class ScriptEvaluatorKeyHandler : public IKeyPressHandler 10 | { 11 | Q_OBJECT 12 | public: 13 | ScriptEvaluatorKeyHandler(QShell& s); 14 | virtual bool onKeyPressed( QShell& shell, QKeyEvent* e ); 15 | public slots: 16 | void onNewEngine(std::shared_ptr); 17 | void onLoadScript(const QString& file); 18 | signals: 19 | void TextEvaluated(const QString&, QShellEngineResult&); 20 | private: 21 | std::shared_ptr engine; 22 | QShell& shell; 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /QShell/qshell.cpp: -------------------------------------------------------------------------------- 1 | #include "qshell.h" 2 | #include "QShellUtils.h" 3 | #include "QShellEngine_Qt.h" 4 | 5 | 6 | QShell::QShell( QWidget* parent, const QString& promptString /*= "Q> "*/ ) 7 | : QPlainTextEdit(parent), prompt(promptString), commands(*this), handlersMananger(*this) 8 | { 9 | setPlainText(prompt); 10 | MoveCursorAtEnd(*this); 11 | commands.SetHandlers(handlersMananger.GetHandlers()); 12 | QObject::connect(&commands, SIGNAL(ScriptLoadRequested(const QString&)), this, SIGNAL(LoadScriptRequested(const QString&))); 13 | auto engine = std::make_shared(); 14 | engine->RegisterPrintFunction(*this); 15 | commands.SetClc(engine->RegisterAndGetClc()); 16 | SetEngine(engine); 17 | } 18 | 19 | void QShell::SetPrompt( const QString& newPrompt ) 20 | { 21 | prompt = newPrompt; 22 | } 23 | 24 | void QShell::SetHandlers( HandlersVec handlers ) 25 | { 26 | commands.SetHandlers(std::move(handlers)); 27 | } 28 | 29 | const QString& QShell::Prompt() const 30 | { 31 | return prompt; 32 | } 33 | 34 | void QShell::SetEngine( std::shared_ptr engine ) 35 | { 36 | handlersMananger.SetEngine(engine); 37 | } 38 | 39 | void QShell::SetEngine(std::shared_ptr engine) 40 | { 41 | engine->RegisterPrintFunction(*this); 42 | commands.SetClc(engine->RegisterAndGetClc()); 43 | handlersMananger.SetEngine(engine); 44 | } 45 | 46 | // *************************************************************** handlers 47 | void QShell::keyPressEvent( QKeyEvent *e ) 48 | { 49 | commands.onKeyPress(e); 50 | } 51 | 52 | void QShell::wheelEvent( QWheelEvent *e ) 53 | { 54 | commands.onMouseWheelEvent(e); 55 | } 56 | 57 | void QShell::contextMenuEvent( QContextMenuEvent *e ) 58 | { 59 | commands.onContextMenuEvent(e); 60 | } 61 | 62 | void QShell::insertFromMimeData( const QMimeData * source ) 63 | { 64 | commands.onInsertFromMimeData(source); 65 | } 66 | // *************************************************************** handlers 67 | 68 | // *************************************************************** QPlainTextEdit forwards 69 | void QShell::processKeyEvent( QKeyEvent* e ) 70 | { 71 | QPlainTextEdit::keyPressEvent(e); 72 | } 73 | 74 | void QShell::processWheelEvent( QWheelEvent* e ) 75 | { 76 | QPlainTextEdit::wheelEvent(e); 77 | } 78 | 79 | // *************************************************************** QPlainTextEdit forwards -------------------------------------------------------------------------------- /QShell/qshell.h: -------------------------------------------------------------------------------- 1 | #ifndef QSHELL_H 2 | #define QSHELL_H 3 | 4 | #include "qshell_global.h" 5 | #include "QtWidgets\qplaintextedit.h" 6 | #include "CommandProvider.h" 7 | #include "QShellEngine.h" 8 | #include "KeyHandlersManager.h" 9 | #include "QShellEngine_Qt.h" 10 | 11 | class QSHELL_EXPORT QShell : public QPlainTextEdit 12 | { 13 | Q_OBJECT 14 | public: 15 | QShell(QWidget* parent, const QString& promptString = "Q> "); 16 | 17 | // setters 18 | void SetEngine(std::shared_ptr engine); 19 | void SetEngine(std::shared_ptr engine); 20 | void SetPrompt(const QString& newPrompt); 21 | void SetHandlers(HandlersVec handlers); 22 | 23 | const QString& Prompt() const; 24 | 25 | void processKeyEvent(QKeyEvent* e); 26 | void processWheelEvent(QWheelEvent* e); 27 | signals: 28 | void LoadScriptRequested(const QString&); 29 | protected: 30 | void keyPressEvent(QKeyEvent *e); 31 | void wheelEvent(QWheelEvent *e); 32 | 33 | void contextMenuEvent(QContextMenuEvent *e); 34 | void insertFromMimeData ( const QMimeData * source ); 35 | private: 36 | QString prompt; 37 | CommandProvider commands; 38 | KeyHandlersManager handlersMananger; 39 | }; 40 | 41 | #endif // QSHELL_H 42 | -------------------------------------------------------------------------------- /QShell/qshell_global.h: -------------------------------------------------------------------------------- 1 | #ifndef QSHELL_GLOBAL_H 2 | #define QSHELL_GLOBAL_H 3 | 4 | #include 5 | 6 | #ifdef QSHELL_LIB 7 | # define QSHELL_EXPORT Q_DECL_EXPORT 8 | #else 9 | # define QSHELL_EXPORT Q_DECL_IMPORT 10 | #endif 11 | 12 | #endif // QSHELL_GLOBAL_H 13 | -------------------------------------------------------------------------------- /QShell/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by QShell.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # anvedi 2 | 3 | A data viewer prototype made with Qt and [QCustomPlot](http://qcustomplot.com). 4 | 5 | ![alt text](https://github.com/ilpropheta/anvedi/blob/master/pics/main-view.png "anvedi") 6 | 7 | Replaying some data in Real Time mode: 8 | 9 | ![alt text](https://github.com/ilpropheta/anvedi/blob/master/pics/real-time.gif "anvedi Real Time") 10 | 11 | ## In a nutshell 12 | 13 | anvedi is a little project I've shaped to play with [QCustomPlot](http://qcustomplot.com), an awesome Qt library for plotting. Note it's a prototype, this means I allowed myself some design shortcuts and I've worked on edge cases sparingly. 14 | 15 | You can use anvedi both via GUI or via scripting. I usually integrate my Qt projects with QtScript by using another project I developed in 2011: [QShell](https://github.com/ilpropheta/anvedi/tree/master/QShell). 16 | 17 | I have also ported some QCustomPlot bricks to *QML*, since it's not provided by default. [This part](https://github.com/ilpropheta/anvedi/tree/master/Anvedi/qml-lib) of the repo is devoted to QML porting. More details to be added at some point. 18 | 19 | Last but not least, anvedi is partially tested with QTestLib. Refer to [AnvediCheTests](https://github.com/ilpropheta/anvedi/tree/master/AnvediCheTests) for test cases. 20 | 21 | What anvedi does: 22 | 23 | * The main purpose of anvedi is plotting data sharing a common domain (e.g. time) 24 | * Import/Export of the workspace data (JSON files for simplicity) 25 | * Import/Export of just plot - graphical - information (JSON files for simplicity) 26 | * Scripting integration 27 | * Per-curve customization 28 | * Pen color 29 | * Filtering and hiding 30 | * Ticks and labels 31 | * Ranges (auto and manual) 32 | * Viewer capabilities 33 | * Customizable background color 34 | * Rect zoom 35 | * Vertical cursor 36 | * Common plot browsing (horizontal scrollbar, keyboard, etc). 37 | * Real Time 38 | * replay of workspace files 39 | * simple acquisition protocol via PIPES (QLocalSocket) 40 | * interactive Real Time generation by using the console 41 | 42 | ## Building anvedi 43 | 44 | Apart from Qt, anvedi is completely self-contained. [QCustomPlot](http://qcustomplot.com) .h and .cpp (just two files) are added to the project for simplicity. 45 | 46 | I have been using Visual Studio 2013 Community Edition. .pro files are provided as well - automatically generated by the Qt Visual Studio add-in. 47 | 48 | ## Why "anvedi"? 49 | 50 | Don't bother to find an acronym of "anvedi". 51 | This is Roman slang, basically an exclamation of surprise after seeing something impressive, so it roughly means "Look at this!". 52 | -------------------------------------------------------------------------------- /pics/main-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilpropheta/anvedi/abd9af841faf56a7521d7d2e7cae1d6ec8d51116/pics/main-view.png -------------------------------------------------------------------------------- /pics/qml-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilpropheta/anvedi/abd9af841faf56a7521d7d2e7cae1d6ec8d51116/pics/qml-example.png -------------------------------------------------------------------------------- /pics/qml-real-time.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilpropheta/anvedi/abd9af841faf56a7521d7d2e7cae1d6ec8d51116/pics/qml-real-time.gif -------------------------------------------------------------------------------- /pics/real-time.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilpropheta/anvedi/abd9af841faf56a7521d7d2e7cae1d6ec8d51116/pics/real-time.gif --------------------------------------------------------------------------------