Repository: knarfS/smuview Branch: master Commit: a5ffb66287b7 Files: 850 Total size: 6.5 MB Directory structure: gitextract_dlxxxgec/ ├── .clang-tidy ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── workflows/ │ ├── build.yml │ └── documentation.yml ├── .gitignore ├── CMake/ │ ├── CheckSigrokFeatures.cmake │ ├── FindQwt.cmake │ ├── GetGitRevisionDescription.cmake │ ├── GetGitRevisionDescription.cmake.in │ └── memaccess.cmake ├── CMakeLists.txt ├── COPYING ├── Doxyfile ├── INSTALL ├── NEWS ├── README ├── TODO ├── appimagecraft.yml ├── config.h.in ├── contrib/ │ ├── config_version.sh.in │ ├── org.sigrok.SmuView.appdata.xml │ ├── org.sigrok.SmuView.desktop │ ├── smuview.spec │ └── smuview_cross.nsi.in ├── cppcheck-suppressions.xml ├── doc/ │ ├── smuview.1 │ ├── smuview_python_bindings.html │ └── smuview_python_bindings.txt ├── extdef.h ├── external/ │ ├── .clang-tidy │ ├── QCodeEditor/ │ │ ├── .clang-format │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── LICENSE.MIT │ │ ├── README.md │ │ ├── include/ │ │ │ ├── QCXXHighlighter │ │ │ ├── QCodeEditor │ │ │ ├── QGLSLCompleter │ │ │ ├── QGLSLHighlighter │ │ │ ├── QHighlightBlockRule │ │ │ ├── QHighlightRule │ │ │ ├── QJSHighlighter │ │ │ ├── QJSONHighlighter │ │ │ ├── QJavaHighlighter │ │ │ ├── QLanguage │ │ │ ├── QLineNumberArea │ │ │ ├── QLuaCompleter │ │ │ ├── QLuaHighlighter │ │ │ ├── QPythonCompleter │ │ │ ├── QPythonHighlighter │ │ │ ├── QStyleSyntaxHighlighter │ │ │ ├── QSyntaxStyle │ │ │ ├── QXMLHighlighter │ │ │ └── internal/ │ │ │ ├── QCXXHighlighter.hpp │ │ │ ├── QCodeEditor.hpp │ │ │ ├── QGLSLCompleter.hpp │ │ │ ├── QGLSLHighlighter.hpp │ │ │ ├── QHighlightBlockRule.hpp │ │ │ ├── QHighlightRule.hpp │ │ │ ├── QJSHighlighter.hpp │ │ │ ├── QJSONHighlighter.hpp │ │ │ ├── QJavaHighlighter.hpp │ │ │ ├── QLanguage.hpp │ │ │ ├── QLineNumberArea.hpp │ │ │ ├── QLuaCompleter.hpp │ │ │ ├── QLuaHighlighter.hpp │ │ │ ├── QPythonCompleter.hpp │ │ │ ├── QPythonHighlighter.hpp │ │ │ ├── QStyleSyntaxHighlighter.hpp │ │ │ ├── QSyntaxStyle.hpp │ │ │ └── QXMLHighlighter.hpp │ │ ├── resources/ │ │ │ ├── default_style.xml │ │ │ ├── languages/ │ │ │ │ ├── cpp.xml │ │ │ │ ├── glsl.xml │ │ │ │ ├── java.xml │ │ │ │ ├── js.xml │ │ │ │ ├── lua.xml │ │ │ │ └── python.xml │ │ │ └── qcodeeditor_resources.qrc │ │ └── src/ │ │ └── internal/ │ │ ├── QCXXHighlighter.cpp │ │ ├── QCodeEditor.cpp │ │ ├── QGLSLCompleter.cpp │ │ ├── QGLSLHighlighter.cpp │ │ ├── QJSHighlighter.cpp │ │ ├── QJSONHighlighter.cpp │ │ ├── QJavaHighlighter.cpp │ │ ├── QLanguage.cpp │ │ ├── QLineNumberArea.cpp │ │ ├── QLuaCompleter.cpp │ │ ├── QLuaHighlighter.cpp │ │ ├── QPythonCompleter.cpp │ │ ├── QPythonHighlighter.cpp │ │ ├── QStyleSyntaxHighlighter.cpp │ │ ├── QSyntaxStyle.cpp │ │ └── QXMLHighlighter.cpp │ ├── QtFindReplaceDialog/ │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── COPYING │ │ ├── README.txt │ │ ├── TODO │ │ ├── dialogs/ │ │ │ ├── CMakeLists.txt │ │ │ ├── dialogs.pro │ │ │ ├── finddialog.cpp │ │ │ ├── finddialog.h │ │ │ ├── findform.cpp │ │ │ ├── findform.h │ │ │ ├── findreplace_global.h │ │ │ ├── findreplacedialog.cpp │ │ │ ├── findreplacedialog.h │ │ │ ├── findreplacedialog.ui │ │ │ ├── findreplaceform.cpp │ │ │ ├── findreplaceform.h │ │ │ └── findreplaceform.ui │ │ ├── doc/ │ │ │ ├── .gitignore │ │ │ ├── Doxyfile │ │ │ ├── README.txt │ │ │ ├── doc.pro │ │ │ └── qtfindreplacedialog.dox │ │ ├── make-dist.sh │ │ ├── qtfindreplacedialog.pri │ │ └── qtfindreplacedialog.pro │ ├── pybind11_2.11_dev1/ │ │ ├── .appveyor.yml │ │ ├── .clang-format │ │ ├── .clang-tidy │ │ ├── .cmake-format.yaml │ │ ├── .codespell-ignore-lines │ │ ├── .gitignore │ │ ├── .readthedocs.yml │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── MANIFEST.in │ │ ├── README.rst │ │ ├── include/ │ │ │ └── pybind11/ │ │ │ ├── attr.h │ │ │ ├── buffer_info.h │ │ │ ├── cast.h │ │ │ ├── chrono.h │ │ │ ├── common.h │ │ │ ├── complex.h │ │ │ ├── detail/ │ │ │ │ ├── class.h │ │ │ │ ├── common.h │ │ │ │ ├── descr.h │ │ │ │ ├── init.h │ │ │ │ ├── internals.h │ │ │ │ ├── type_caster_base.h │ │ │ │ └── typeid.h │ │ │ ├── eigen/ │ │ │ │ ├── matrix.h │ │ │ │ └── tensor.h │ │ │ ├── eigen.h │ │ │ ├── embed.h │ │ │ ├── eval.h │ │ │ ├── functional.h │ │ │ ├── gil.h │ │ │ ├── iostream.h │ │ │ ├── numpy.h │ │ │ ├── operators.h │ │ │ ├── options.h │ │ │ ├── pybind11.h │ │ │ ├── pytypes.h │ │ │ ├── stl/ │ │ │ │ └── filesystem.h │ │ │ ├── stl.h │ │ │ └── stl_bind.h │ │ ├── noxfile.py │ │ ├── pybind11/ │ │ │ ├── __init__.py │ │ │ ├── __main__.py │ │ │ ├── _version.py │ │ │ ├── commands.py │ │ │ ├── py.typed │ │ │ └── setup_helpers.py │ │ ├── pyproject.toml │ │ ├── setup.cfg │ │ ├── setup.py │ │ ├── tests/ │ │ │ ├── CMakeLists.txt │ │ │ ├── catch/ │ │ │ │ └── catch.hpp │ │ │ ├── conftest.py │ │ │ ├── constructor_stats.h │ │ │ ├── cross_module_gil_utils.cpp │ │ │ ├── cross_module_interleaved_error_already_set.cpp │ │ │ ├── env.py │ │ │ ├── extra_python_package/ │ │ │ │ ├── pytest.ini │ │ │ │ └── test_files.py │ │ │ ├── extra_setuptools/ │ │ │ │ ├── pytest.ini │ │ │ │ └── test_setuphelper.py │ │ │ ├── local_bindings.h │ │ │ ├── object.h │ │ │ ├── pybind11_cross_module_tests.cpp │ │ │ ├── pybind11_tests.cpp │ │ │ ├── pybind11_tests.h │ │ │ ├── pytest.ini │ │ │ ├── requirements.txt │ │ │ ├── test_async.cpp │ │ │ ├── test_async.py │ │ │ ├── test_buffers.cpp │ │ │ ├── test_buffers.py │ │ │ ├── test_builtin_casters.cpp │ │ │ ├── test_builtin_casters.py │ │ │ ├── test_call_policies.cpp │ │ │ ├── test_call_policies.py │ │ │ ├── test_callbacks.cpp │ │ │ ├── test_callbacks.py │ │ │ ├── test_chrono.cpp │ │ │ ├── test_chrono.py │ │ │ ├── test_class.cpp │ │ │ ├── test_class.py │ │ │ ├── test_cmake_build/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── embed.cpp │ │ │ │ ├── installed_embed/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ ├── installed_function/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ ├── installed_target/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ ├── main.cpp │ │ │ │ ├── subdirectory_embed/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ ├── subdirectory_function/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ ├── subdirectory_target/ │ │ │ │ │ └── CMakeLists.txt │ │ │ │ └── test.py │ │ │ ├── test_const_name.cpp │ │ │ ├── test_const_name.py │ │ │ ├── test_constants_and_functions.cpp │ │ │ ├── test_constants_and_functions.py │ │ │ ├── test_copy_move.cpp │ │ │ ├── test_copy_move.py │ │ │ ├── test_custom_type_casters.cpp │ │ │ ├── test_custom_type_casters.py │ │ │ ├── test_custom_type_setup.cpp │ │ │ ├── test_custom_type_setup.py │ │ │ ├── test_docstring_options.cpp │ │ │ ├── test_docstring_options.py │ │ │ ├── test_eigen_matrix.cpp │ │ │ ├── test_eigen_matrix.py │ │ │ ├── test_eigen_tensor.cpp │ │ │ ├── test_eigen_tensor.inl │ │ │ ├── test_eigen_tensor.py │ │ │ ├── test_eigen_tensor_avoid_stl_array.cpp │ │ │ ├── test_embed/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── catch.cpp │ │ │ │ ├── external_module.cpp │ │ │ │ ├── test_interpreter.cpp │ │ │ │ ├── test_interpreter.py │ │ │ │ └── test_trampoline.py │ │ │ ├── test_enum.cpp │ │ │ ├── test_enum.py │ │ │ ├── test_eval.cpp │ │ │ ├── test_eval.py │ │ │ ├── test_eval_call.py │ │ │ ├── test_exceptions.cpp │ │ │ ├── test_exceptions.h │ │ │ ├── test_exceptions.py │ │ │ ├── test_factory_constructors.cpp │ │ │ ├── test_factory_constructors.py │ │ │ ├── test_gil_scoped.cpp │ │ │ ├── test_gil_scoped.py │ │ │ ├── test_iostream.cpp │ │ │ ├── test_iostream.py │ │ │ ├── test_kwargs_and_defaults.cpp │ │ │ ├── test_kwargs_and_defaults.py │ │ │ ├── test_local_bindings.cpp │ │ │ ├── test_local_bindings.py │ │ │ ├── test_methods_and_attributes.cpp │ │ │ ├── test_methods_and_attributes.py │ │ │ ├── test_modules.cpp │ │ │ ├── test_modules.py │ │ │ ├── test_multiple_inheritance.cpp │ │ │ ├── test_multiple_inheritance.py │ │ │ ├── test_numpy_array.cpp │ │ │ ├── test_numpy_array.py │ │ │ ├── test_numpy_dtypes.cpp │ │ │ ├── test_numpy_dtypes.py │ │ │ ├── test_numpy_vectorize.cpp │ │ │ ├── test_numpy_vectorize.py │ │ │ ├── test_opaque_types.cpp │ │ │ ├── test_opaque_types.py │ │ │ ├── test_operator_overloading.cpp │ │ │ ├── test_operator_overloading.py │ │ │ ├── test_pickling.cpp │ │ │ ├── test_pickling.py │ │ │ ├── test_pytypes.cpp │ │ │ ├── test_pytypes.py │ │ │ ├── test_sequences_and_iterators.cpp │ │ │ ├── test_sequences_and_iterators.py │ │ │ ├── test_smart_ptr.cpp │ │ │ ├── test_smart_ptr.py │ │ │ ├── test_stl.cpp │ │ │ ├── test_stl.py │ │ │ ├── test_stl_binders.cpp │ │ │ ├── test_stl_binders.py │ │ │ ├── test_tagbased_polymorphic.cpp │ │ │ ├── test_tagbased_polymorphic.py │ │ │ ├── test_thread.cpp │ │ │ ├── test_thread.py │ │ │ ├── test_union.cpp │ │ │ ├── test_union.py │ │ │ ├── test_virtual_functions.cpp │ │ │ ├── test_virtual_functions.py │ │ │ ├── valgrind-numpy-scipy.supp │ │ │ └── valgrind-python.supp │ │ └── tools/ │ │ ├── FindCatch.cmake │ │ ├── FindEigen3.cmake │ │ ├── FindPythonLibsNew.cmake │ │ ├── JoinPaths.cmake │ │ ├── check-style.sh │ │ ├── cmake_uninstall.cmake.in │ │ ├── codespell_ignore_lines_from_errors.py │ │ ├── libsize.py │ │ ├── make_changelog.py │ │ ├── pybind11.pc.in │ │ ├── pybind11Common.cmake │ │ ├── pybind11Config.cmake.in │ │ ├── pybind11NewTools.cmake │ │ ├── pybind11Tools.cmake │ │ ├── pyproject.toml │ │ ├── setup_global.py.in │ │ └── setup_main.py.in │ └── pybind11_2.9.2/ │ ├── .appveyor.yml │ ├── .clang-format │ ├── .clang-tidy │ ├── .cmake-format.yaml │ ├── .gitignore │ ├── .readthedocs.yml │ ├── CMakeLists.txt │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.rst │ ├── include/ │ │ └── pybind11/ │ │ ├── attr.h │ │ ├── buffer_info.h │ │ ├── cast.h │ │ ├── chrono.h │ │ ├── common.h │ │ ├── complex.h │ │ ├── detail/ │ │ │ ├── class.h │ │ │ ├── common.h │ │ │ ├── descr.h │ │ │ ├── init.h │ │ │ ├── internals.h │ │ │ ├── type_caster_base.h │ │ │ └── typeid.h │ │ ├── eigen.h │ │ ├── embed.h │ │ ├── eval.h │ │ ├── functional.h │ │ ├── gil.h │ │ ├── iostream.h │ │ ├── numpy.h │ │ ├── operators.h │ │ ├── options.h │ │ ├── pybind11.h │ │ ├── pytypes.h │ │ ├── stl/ │ │ │ └── filesystem.h │ │ ├── stl.h │ │ └── stl_bind.h │ ├── noxfile.py │ ├── pybind11/ │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── _version.py │ │ ├── _version.pyi │ │ ├── commands.py │ │ ├── py.typed │ │ ├── setup_helpers.py │ │ └── setup_helpers.pyi │ ├── pyproject.toml │ ├── setup.cfg │ ├── setup.py │ ├── tests/ │ │ ├── CMakeLists.txt │ │ ├── catch/ │ │ │ └── catch.hpp │ │ ├── conftest.py │ │ ├── constructor_stats.h │ │ ├── cross_module_gil_utils.cpp │ │ ├── env.py │ │ ├── extra_python_package/ │ │ │ ├── pytest.ini │ │ │ └── test_files.py │ │ ├── extra_setuptools/ │ │ │ ├── pytest.ini │ │ │ └── test_setuphelper.py │ │ ├── local_bindings.h │ │ ├── object.h │ │ ├── pybind11_cross_module_tests.cpp │ │ ├── pybind11_tests.cpp │ │ ├── pybind11_tests.h │ │ ├── pytest.ini │ │ ├── requirements.txt │ │ ├── test_async.cpp │ │ ├── test_async.py │ │ ├── test_buffers.cpp │ │ ├── test_buffers.py │ │ ├── test_builtin_casters.cpp │ │ ├── test_builtin_casters.py │ │ ├── test_call_policies.cpp │ │ ├── test_call_policies.py │ │ ├── test_callbacks.cpp │ │ ├── test_callbacks.py │ │ ├── test_chrono.cpp │ │ ├── test_chrono.py │ │ ├── test_class.cpp │ │ ├── test_class.py │ │ ├── test_cmake_build/ │ │ │ ├── CMakeLists.txt │ │ │ ├── embed.cpp │ │ │ ├── installed_embed/ │ │ │ │ └── CMakeLists.txt │ │ │ ├── installed_function/ │ │ │ │ └── CMakeLists.txt │ │ │ ├── installed_target/ │ │ │ │ └── CMakeLists.txt │ │ │ ├── main.cpp │ │ │ ├── subdirectory_embed/ │ │ │ │ └── CMakeLists.txt │ │ │ ├── subdirectory_function/ │ │ │ │ └── CMakeLists.txt │ │ │ ├── subdirectory_target/ │ │ │ │ └── CMakeLists.txt │ │ │ └── test.py │ │ ├── test_const_name.cpp │ │ ├── test_const_name.py │ │ ├── test_constants_and_functions.cpp │ │ ├── test_constants_and_functions.py │ │ ├── test_copy_move.cpp │ │ ├── test_copy_move.py │ │ ├── test_custom_type_casters.cpp │ │ ├── test_custom_type_casters.py │ │ ├── test_custom_type_setup.cpp │ │ ├── test_custom_type_setup.py │ │ ├── test_docstring_options.cpp │ │ ├── test_docstring_options.py │ │ ├── test_eigen.cpp │ │ ├── test_eigen.py │ │ ├── test_embed/ │ │ │ ├── CMakeLists.txt │ │ │ ├── catch.cpp │ │ │ ├── external_module.cpp │ │ │ ├── test_interpreter.cpp │ │ │ ├── test_interpreter.py │ │ │ └── test_trampoline.py │ │ ├── test_enum.cpp │ │ ├── test_enum.py │ │ ├── test_eval.cpp │ │ ├── test_eval.py │ │ ├── test_eval_call.py │ │ ├── test_exceptions.cpp │ │ ├── test_exceptions.h │ │ ├── test_exceptions.py │ │ ├── test_factory_constructors.cpp │ │ ├── test_factory_constructors.py │ │ ├── test_gil_scoped.cpp │ │ ├── test_gil_scoped.py │ │ ├── test_iostream.cpp │ │ ├── test_iostream.py │ │ ├── test_kwargs_and_defaults.cpp │ │ ├── test_kwargs_and_defaults.py │ │ ├── test_local_bindings.cpp │ │ ├── test_local_bindings.py │ │ ├── test_methods_and_attributes.cpp │ │ ├── test_methods_and_attributes.py │ │ ├── test_modules.cpp │ │ ├── test_modules.py │ │ ├── test_multiple_inheritance.cpp │ │ ├── test_multiple_inheritance.py │ │ ├── test_numpy_array.cpp │ │ ├── test_numpy_array.py │ │ ├── test_numpy_dtypes.cpp │ │ ├── test_numpy_dtypes.py │ │ ├── test_numpy_vectorize.cpp │ │ ├── test_numpy_vectorize.py │ │ ├── test_opaque_types.cpp │ │ ├── test_opaque_types.py │ │ ├── test_operator_overloading.cpp │ │ ├── test_operator_overloading.py │ │ ├── test_pickling.cpp │ │ ├── test_pickling.py │ │ ├── test_pytypes.cpp │ │ ├── test_pytypes.py │ │ ├── test_sequences_and_iterators.cpp │ │ ├── test_sequences_and_iterators.py │ │ ├── test_smart_ptr.cpp │ │ ├── test_smart_ptr.py │ │ ├── test_stl.cpp │ │ ├── test_stl.py │ │ ├── test_stl_binders.cpp │ │ ├── test_stl_binders.py │ │ ├── test_tagbased_polymorphic.cpp │ │ ├── test_tagbased_polymorphic.py │ │ ├── test_thread.cpp │ │ ├── test_thread.py │ │ ├── test_union.cpp │ │ ├── test_union.py │ │ ├── test_virtual_functions.cpp │ │ ├── test_virtual_functions.py │ │ ├── valgrind-numpy-scipy.supp │ │ └── valgrind-python.supp │ └── tools/ │ ├── FindCatch.cmake │ ├── FindEigen3.cmake │ ├── FindPythonLibsNew.cmake │ ├── check-style.sh │ ├── cmake_uninstall.cmake.in │ ├── libsize.py │ ├── make_changelog.py │ ├── pybind11Common.cmake │ ├── pybind11Config.cmake.in │ ├── pybind11NewTools.cmake │ ├── pybind11Tools.cmake │ ├── pyproject.toml │ ├── setup_global.py.in │ └── setup_main.py.in ├── fonts/ │ └── LICENSE ├── main.cpp ├── manual/ │ ├── CMakeLists.txt │ ├── asciidoctor-stylesheet-factory/ │ │ ├── LICENSE │ │ └── stylesheets/ │ │ └── readthedocs.css │ ├── cli.adoc │ ├── data_processing.adoc │ ├── data_tables/ │ │ ├── combine.csv │ │ ├── combine_30ms.csv │ │ ├── combine_absolute.csv │ │ └── simple.csv │ ├── data_visualisation.adoc │ ├── device_control.adoc │ ├── devices.adoc │ ├── images/ │ │ ├── AddMathChannelDialog.xcf │ │ ├── ConnectDeviceDialog.xcf │ │ ├── DataTableView.xcf │ │ ├── PowerPanelView.xcf │ │ ├── SaveSignalsDialog.xcf │ │ ├── SequenceOutputView.xcf │ │ ├── SmuScript.xcf │ │ ├── SourceControlView.xcf │ │ ├── TimePlotView_2.xcf │ │ ├── UserDevice.xcf │ │ ├── ValuePanelView.xcf │ │ ├── Welcome.xcf │ │ └── numbers/ │ │ ├── 1.xcf │ │ ├── 10.xcf │ │ ├── 11.xcf │ │ ├── 12.xcf │ │ ├── 13.xcf │ │ ├── 14.xcf │ │ ├── 15.xcf │ │ ├── 16.xcf │ │ ├── 17.xcf │ │ ├── 18.xcf │ │ ├── 19.xcf │ │ ├── 2.xcf │ │ ├── 20.xcf │ │ ├── 3.xcf │ │ ├── 4.xcf │ │ ├── 5.xcf │ │ ├── 6.xcf │ │ ├── 7.xcf │ │ ├── 8.xcf │ │ └── 9.xcf │ ├── installation.adoc │ ├── license.adoc │ ├── manual.adoc │ ├── overview.adoc │ └── smuscript.adoc ├── signalhandler.cpp ├── signalhandler.hpp ├── smuscript/ │ ├── example_characterize_battery.py │ ├── example_characterize_psu.py │ ├── example_characterize_psu_2.py │ ├── example_device_properties.py │ ├── example_multiplexer.py │ ├── example_ui.py │ ├── example_user_channel.py │ ├── generate_documentation.py │ ├── python_version.py │ ├── test_combine_signals.py │ ├── test_fixed_channel.py │ └── test_rnd_crashes.py ├── smuview.kdev4 ├── smuview.qrc ├── smuviewico.rc ├── src/ │ ├── application.cpp │ ├── application.hpp │ ├── channels/ │ │ ├── addscchannel.cpp │ │ ├── addscchannel.hpp │ │ ├── basechannel.cpp │ │ ├── basechannel.hpp │ │ ├── dividechannel.cpp │ │ ├── dividechannel.hpp │ │ ├── hardwarechannel.cpp │ │ ├── hardwarechannel.hpp │ │ ├── integratechannel.cpp │ │ ├── integratechannel.hpp │ │ ├── mathchannel.cpp │ │ ├── mathchannel.hpp │ │ ├── movingavgchannel.cpp │ │ ├── movingavgchannel.hpp │ │ ├── multiplysfchannel.cpp │ │ ├── multiplysfchannel.hpp │ │ ├── multiplysschannel.cpp │ │ ├── multiplysschannel.hpp │ │ ├── userchannel.cpp │ │ └── userchannel.hpp │ ├── data/ │ │ ├── analogbasesignal.cpp │ │ ├── analogbasesignal.hpp │ │ ├── analogsamplesignal.cpp │ │ ├── analogsamplesignal.hpp │ │ ├── analogtimesignal.cpp │ │ ├── analogtimesignal.hpp │ │ ├── basesignal.cpp │ │ ├── basesignal.hpp │ │ ├── datautil.cpp │ │ ├── datautil.hpp │ │ └── properties/ │ │ ├── baseproperty.cpp │ │ ├── baseproperty.hpp │ │ ├── boolproperty.cpp │ │ ├── boolproperty.hpp │ │ ├── doubleproperty.cpp │ │ ├── doubleproperty.hpp │ │ ├── doublerangeproperty.cpp │ │ ├── doublerangeproperty.hpp │ │ ├── int32property.cpp │ │ ├── int32property.hpp │ │ ├── measuredquantityproperty.cpp │ │ ├── measuredquantityproperty.hpp │ │ ├── rationalproperty.cpp │ │ ├── rationalproperty.hpp │ │ ├── stringproperty.cpp │ │ ├── stringproperty.hpp │ │ ├── uint64property.cpp │ │ ├── uint64property.hpp │ │ ├── uint64rangeproperty.cpp │ │ └── uint64rangeproperty.hpp │ ├── devicemanager.cpp │ ├── devicemanager.hpp │ ├── devices/ │ │ ├── basedevice.cpp │ │ ├── basedevice.hpp │ │ ├── configurable.cpp │ │ ├── configurable.hpp │ │ ├── deviceutil.cpp │ │ ├── deviceutil.hpp │ │ ├── hardwaredevice.cpp │ │ ├── hardwaredevice.hpp │ │ ├── measurementdevice.cpp │ │ ├── measurementdevice.hpp │ │ ├── oscilloscopedevice.cpp │ │ ├── oscilloscopedevice.hpp │ │ ├── sourcesinkdevice.cpp │ │ ├── sourcesinkdevice.hpp │ │ ├── userdevice.cpp │ │ └── userdevice.hpp │ ├── mainwindow.cpp │ ├── mainwindow.hpp │ ├── python/ │ │ ├── bindings.cpp │ │ ├── bindings.hpp │ │ ├── pystreambuf.cpp │ │ ├── pystreambuf.hpp │ │ ├── pystreamredirect.hpp │ │ ├── smuscriptrunner.cpp │ │ ├── smuscriptrunner.hpp │ │ ├── uihelper.cpp │ │ ├── uihelper.hpp │ │ ├── uiproxy.cpp │ │ └── uiproxy.hpp │ ├── session.cpp │ ├── session.hpp │ ├── settingsmanager.cpp │ ├── settingsmanager.hpp │ ├── ui/ │ │ ├── data/ │ │ │ ├── quantitycombobox.cpp │ │ │ ├── quantitycombobox.hpp │ │ │ ├── quantityflagslist.cpp │ │ │ ├── quantityflagslist.hpp │ │ │ ├── unitcombobox.cpp │ │ │ └── unitcombobox.hpp │ │ ├── datatypes/ │ │ │ ├── basewidget.cpp │ │ │ ├── basewidget.hpp │ │ │ ├── boolbutton.cpp │ │ │ ├── boolbutton.hpp │ │ │ ├── boolcheckbox.cpp │ │ │ ├── boolcheckbox.hpp │ │ │ ├── boolled.cpp │ │ │ ├── boolled.hpp │ │ │ ├── datatypehelper.cpp │ │ │ ├── datatypehelper.hpp │ │ │ ├── doublecontrol.cpp │ │ │ ├── doublecontrol.hpp │ │ │ ├── doubledisplay.cpp │ │ │ ├── doubledisplay.hpp │ │ │ ├── doubleknob.cpp │ │ │ ├── doubleknob.hpp │ │ │ ├── doublerangecombobox.cpp │ │ │ ├── doublerangecombobox.hpp │ │ │ ├── doubleslider.cpp │ │ │ ├── doubleslider.hpp │ │ │ ├── doublesmallcontrol.cpp │ │ │ ├── doublesmallcontrol.hpp │ │ │ ├── doublespinbox.cpp │ │ │ ├── doublespinbox.hpp │ │ │ ├── int32spinbox.cpp │ │ │ ├── int32spinbox.hpp │ │ │ ├── measuredquantitycombobox.cpp │ │ │ ├── measuredquantitycombobox.hpp │ │ │ ├── rationalcombobox.cpp │ │ │ ├── rationalcombobox.hpp │ │ │ ├── stringcombobox.cpp │ │ │ ├── stringcombobox.hpp │ │ │ ├── stringlabel.cpp │ │ │ ├── stringlabel.hpp │ │ │ ├── stringled.cpp │ │ │ ├── stringled.hpp │ │ │ ├── thresholdcontrol.cpp │ │ │ ├── thresholdcontrol.hpp │ │ │ ├── uint64combobox.cpp │ │ │ ├── uint64combobox.hpp │ │ │ ├── uint64label.cpp │ │ │ ├── uint64label.hpp │ │ │ ├── uint64rangecombobox.cpp │ │ │ ├── uint64rangecombobox.hpp │ │ │ ├── uint64spinbox.cpp │ │ │ └── uint64spinbox.hpp │ │ ├── devices/ │ │ │ ├── channelcombobox.cpp │ │ │ ├── channelcombobox.hpp │ │ │ ├── channelgroupcombobox.cpp │ │ │ ├── channelgroupcombobox.hpp │ │ │ ├── configkeycombobox.cpp │ │ │ ├── configkeycombobox.hpp │ │ │ ├── configurablecombobox.cpp │ │ │ ├── configurablecombobox.hpp │ │ │ ├── devicecombobox.cpp │ │ │ ├── devicecombobox.hpp │ │ │ ├── devicetree/ │ │ │ │ ├── devicetreemodel.cpp │ │ │ │ ├── devicetreemodel.hpp │ │ │ │ ├── devicetreeview.cpp │ │ │ │ ├── devicetreeview.hpp │ │ │ │ ├── treeitem.cpp │ │ │ │ └── treeitem.hpp │ │ │ ├── selectconfigurableform.cpp │ │ │ ├── selectconfigurableform.hpp │ │ │ ├── selectpropertyform.cpp │ │ │ ├── selectpropertyform.hpp │ │ │ ├── selectsignalwidget.cpp │ │ │ ├── selectsignalwidget.hpp │ │ │ ├── signalcombobox.cpp │ │ │ └── signalcombobox.hpp │ │ ├── dialogs/ │ │ │ ├── aboutdialog.cpp │ │ │ ├── aboutdialog.hpp │ │ │ ├── addmathchanneldialog.cpp │ │ │ ├── addmathchanneldialog.hpp │ │ │ ├── adduserchanneldialog.cpp │ │ │ ├── adduserchanneldialog.hpp │ │ │ ├── addviewdialog.cpp │ │ │ ├── addviewdialog.hpp │ │ │ ├── connectdialog.cpp │ │ │ ├── connectdialog.hpp │ │ │ ├── generatewaveformdialog.cpp │ │ │ ├── generatewaveformdialog.hpp │ │ │ ├── plotconfigdialog.cpp │ │ │ ├── plotconfigdialog.hpp │ │ │ ├── plotcurveconfigdialog.cpp │ │ │ ├── plotcurveconfigdialog.hpp │ │ │ ├── plotdiffmarkerdialog.cpp │ │ │ ├── plotdiffmarkerdialog.hpp │ │ │ ├── selectsignaldialog.cpp │ │ │ ├── selectsignaldialog.hpp │ │ │ ├── selectxysignalsdialog.cpp │ │ │ ├── selectxysignalsdialog.hpp │ │ │ ├── signalsavedialog.cpp │ │ │ └── signalsavedialog.hpp │ │ ├── tabs/ │ │ │ ├── basetab.cpp │ │ │ ├── basetab.hpp │ │ │ ├── devicetab.cpp │ │ │ ├── devicetab.hpp │ │ │ ├── measurementtab.cpp │ │ │ ├── measurementtab.hpp │ │ │ ├── oscilloscopetab.cpp │ │ │ ├── oscilloscopetab.hpp │ │ │ ├── smuscripttab.cpp │ │ │ ├── smuscripttab.hpp │ │ │ ├── sourcesinktab.cpp │ │ │ ├── sourcesinktab.hpp │ │ │ ├── tabdockwidget.cpp │ │ │ ├── tabdockwidget.hpp │ │ │ ├── tabhelper.cpp │ │ │ ├── tabhelper.hpp │ │ │ ├── usertab.cpp │ │ │ ├── usertab.hpp │ │ │ ├── welcometab.cpp │ │ │ └── welcometab.hpp │ │ ├── views/ │ │ │ ├── baseplotview.cpp │ │ │ ├── baseplotview.hpp │ │ │ ├── baseview.cpp │ │ │ ├── baseview.hpp │ │ │ ├── dataview.cpp │ │ │ ├── dataview.hpp │ │ │ ├── democontrolview.cpp │ │ │ ├── democontrolview.hpp │ │ │ ├── devicesview.cpp │ │ │ ├── devicesview.hpp │ │ │ ├── genericcontrolview.cpp │ │ │ ├── genericcontrolview.hpp │ │ │ ├── measurementcontrolview.cpp │ │ │ ├── measurementcontrolview.hpp │ │ │ ├── powerpanelview.cpp │ │ │ ├── powerpanelview.hpp │ │ │ ├── scopehorizontalcontrolview.cpp │ │ │ ├── scopehorizontalcontrolview.hpp │ │ │ ├── scopetriggercontrolview.cpp │ │ │ ├── scopetriggercontrolview.hpp │ │ │ ├── scopeverticalcontrolview.cpp │ │ │ ├── scopeverticalcontrolview.hpp │ │ │ ├── sequenceoutputview.cpp │ │ │ ├── sequenceoutputview.hpp │ │ │ ├── smuscriptoutputview.cpp │ │ │ ├── smuscriptoutputview.hpp │ │ │ ├── smuscripttreeview.cpp │ │ │ ├── smuscripttreeview.hpp │ │ │ ├── smuscriptview.cpp │ │ │ ├── smuscriptview.hpp │ │ │ ├── sourcesinkcontrolview.cpp │ │ │ ├── sourcesinkcontrolview.hpp │ │ │ ├── timeplotview.cpp │ │ │ ├── timeplotview.hpp │ │ │ ├── valuepanelview.cpp │ │ │ ├── valuepanelview.hpp │ │ │ ├── viewhelper.cpp │ │ │ ├── viewhelper.hpp │ │ │ ├── xyplotview.cpp │ │ │ └── xyplotview.hpp │ │ └── widgets/ │ │ ├── clickablelabel.cpp │ │ ├── clickablelabel.hpp │ │ ├── colorbutton.cpp │ │ ├── colorbutton.hpp │ │ ├── monofontdisplay.cpp │ │ ├── monofontdisplay.hpp │ │ ├── plot/ │ │ │ ├── axislocklabel.cpp │ │ │ ├── axislocklabel.hpp │ │ │ ├── axispopup.cpp │ │ │ ├── axispopup.hpp │ │ │ ├── basecurvedata.cpp │ │ │ ├── basecurvedata.hpp │ │ │ ├── curve.cpp │ │ │ ├── curve.hpp │ │ │ ├── plot.cpp │ │ │ ├── plot.hpp │ │ │ ├── plotmagnifier.cpp │ │ │ ├── plotmagnifier.hpp │ │ │ ├── plotscalepicker.cpp │ │ │ ├── plotscalepicker.hpp │ │ │ ├── timecurvedata.cpp │ │ │ ├── timecurvedata.hpp │ │ │ ├── xycurvedata.cpp │ │ │ └── xycurvedata.hpp │ │ ├── popup.cpp │ │ └── popup.hpp │ ├── util.cpp │ └── util.hpp ├── stuff/ │ ├── gstreamer.txt │ ├── linuxgpib_build.txt │ ├── loads.txt │ ├── mxe.txt │ ├── parameter.txt │ ├── pps.txt │ ├── python.txt │ ├── relays/ │ │ └── relays.txt │ ├── release.txt │ ├── scpi-use.txt │ └── snippets.txt └── test/ ├── CMakeLists.txt ├── test.cpp ├── test.hpp └── util.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-tidy ================================================ # Disabled checks: # - bugprone-easily-swappable-parameters # Not needed # - bugprone-suspicious-include # Too many false positives in "build/smuview_autogen". Maybe activate again # when HeaderFilterRegex is working correctly. # - misc-definitions-in-headers # TODO: Used in deviceutil.hpp and datautil.hpp. Maybe to const static? # - misc-non-private-member-variables-in-classes # protected member variables are used all over the place... # - misc-static-assert # TODO: Replace all `assert("ex")` with `throw Ex()` and activate check! # - misc-no-recursion # We do use recursions # - performance-unnecessary-value-param # TODO: ! # - readability-avoid-const-params-in-decls # TODO: Keep Decl and Def signatures the same! Recheck after # performance-unnecessary-value-param solved! Maybe `const type` makes # no sense: # https://stackoverflow.com/questions/52916410/why-is-const-allowed-in-function-declarations # - readability-braces-around-statements # Even with `ShortStatementLines` = 3 there are too many false positives. # - readability-convert-member-functions-to-static, # TODO: Could this be useful in some cases? # - readability-inconsistent-declaration-parameter-name # This check shows to many false positives for Qt signal declarations. We can # use CppChecks "funcArgNamesDifferent" instead. # - readability-magic-numbers # Magic numbers are ok. Maybe replace with macros(?) in the future? # - readability-redundant-access-specifiers # Used to often in header files, also not complatible with Qt access specifiers. # public Q_SLOTS: # public: # - readability-function-cognitive-complexity # TODO: Enable! # - readability-use-anyofallof # Foreach loops are ok for now # # TODO: # - google-*, # -google-readability-braces-around-statements, # -google-readability-todo, # - modernize-*, # -modernize-raw-string-literal # Checks: >- bugprone-*, -bugprone-easily-swappable-parameters, -bugprone-suspicious-include, clang-diagnostic-*, clang-analyzer-*, google-explicit-constructor, misc-*, -misc-definitions-in-headers, -misc-non-private-member-variables-in-classes, -misc-static-assert, -misc-no-recursion, performance-*, -performance-unnecessary-value-param, readability-*, -readability-avoid-const-params-in-decls, -readability-braces-around-statements, -readability-convert-member-functions-to-static, -readability-inconsistent-declaration-parameter-name, -readability-magic-numbers, -readability-redundant-access-specifiers, -readability-function-cognitive-complexity, -readability-use-anyofallof, WarningsAsErrors: '' HeaderFilterRegex: '^src/.*$' AnalyzeTemporaryDtors: false FormatStyle: none User: frank CheckOptions: - key: cert-dcl16-c.NewSuffixes value: 'L;LL;LU;LLU' - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic value: '1' - key: google-readability-braces-around-statements.ShortStatementLines value: '1' - key: google-readability-function-size.StatementThreshold value: '800' - key: google-readability-namespace-comments.ShortNamespaceLines value: '10' - key: google-readability-namespace-comments.SpacesBeforeComments value: '2' - key: modernize-loop-convert.MaxCopySize value: '16' - key: modernize-loop-convert.MinConfidence value: reasonable - key: modernize-loop-convert.NamingStyle value: CamelCase - key: modernize-pass-by-value.IncludeStyle value: llvm - key: modernize-replace-auto-ptr.IncludeStyle value: llvm - key: modernize-use-nullptr.NullMacros value: 'NULL' - key: readability-braces-around-statements.ShortStatementLines value: '3' - key: readability-implicit-bool-conversion.AllowPointerConditions value: '1' - key: readability-implicit-bool-conversion.AllowIntegerConditions value: '1' - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors value: '1' - key: readability-identifier-length.MinimumVariableNameLength value: '2' - key: readability-identifier-length.MinimumParameterNameLength value: '2' - key: readability-identifier-length.MinimumLoopCounterNameLength value: '1' - key: readability-identifier-length.MinimumExceptionNameLength value: '1' - key: readability-qualified-auto.AddConstToQualified value: '1' ================================================ FILE: .editorconfig ================================================ # EditorConfig file (https://editorconfig.org) for the SmuView project root = true [*] end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true # SmuView C++ and C files [**.{c,h,cpp,hpp}] indent_style = tab indent_size = 4 # SmuScript python files [smuscript/**.py] indent_style = space indent_size = 4 # SmuScript XML files [**.xml] indent_style = tab indent_size = 4 [**.qrc] indent_style = tab indent_size = 4 # SmuScript CMake files [CMakeLists.txt] indent_style = tab indent_size = 2 [**.cmake] indent_style = space indent_size = 2 # SmuView yml files [**.yml] indent_style = space indent_size = 2 # external: QCodeEditor C++ files [external/QCodeEditor/**.{cpp,hpp}] indent_style = space indent_size = 4 # external: QCodeEditor XML files [external/QCodeEditor/**.xml] indent_style = space indent_size = 4 [external/QCodeEditor/**.qrc] indent_style = space indent_size = 4 # external: QCodeEditor CMake files [external/QCodeEditor/CMakeLists.txt] indent_style = space indent_size = 4 # external: pybind11 C++ header files [external/pybind11*/**.h] indent_style = space indent_size = 4 # external: pybind11 CMake files [external/pybind11*/CMakeLists.txt] indent_style = space indent_size = 2 [external/pybind11*/**.cmake] indent_style = space indent_size = 2 ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots or log** If applicable, add screenshots or logs to help explain your problem. **Enviroment (please complete the following information):** - SmuView version / git commit - AppImage, Windows installer or self compiled [gcc version, ...] - OS: [e.g. Debian 9, openSUSE 15.1, Windows 10, ...] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/workflows/build.yml ================================================ name: SmuView Build on: push: paths-ignore: - "doxy/**" - "manual/**" - "stuff/**" pull_request: paths-ignore: - "doxy/**" - "manual/**" - "stuff/**" workflow_dispatch: defaults: run: shell: bash env: # The path where the compiled packages will be installed. INSTALL_DIR: "${{ github.workspace }}/sr" # Git URL for the libserialport dependency LIBSERIALPORT_REPO: "git://sigrok.org/libserialport" # Git URL for the libsigrok dependency LIBSIGROK_REPO: "git://sigrok.org/libsigrok" # Git branch for the libsigrok dependency LIBSIGROK_BRANCH: "master" # Build type for SmuView (Debug, Release, RelWithDebInfo, MinSizeRel) BUILD_TYPE: "Release" # Misc commands WGET: "wget -c --quiet" GIT_CLONE: "git clone --depth=1" # AppImage related properties are set as container ENVs if needed. jobs: build_linux: name: SmuView Linux build (${{ matrix.target }}, ${{ matrix.compiler.compiler }}, Qt ${{ matrix.qt.version }}) runs-on: ubuntu-latest strategy: matrix: # i686 build is disabled for now! target: ["x86_64"] compiler: - { compiler: GCC, cc: "gcc", cxx: "g++" } - { compiler: LLVM, cc: "clang", cxx: "clang++" } qt: - { version: 5.12.11, system_qwt: false } - { version: 5.13.2, system_qwt: false } - { version: 5.14.2, system_qwt: false } - { version: 5.15.2, system_qwt: true } env: BUILD_TYPE: "Release" CC: ${{ matrix.compiler.cc }} CXX: ${{ matrix.compiler.cxx }} steps: - name: Set up Qt environment uses: jurplel/install-qt-action@v3 with: version: ${{ matrix.qt.version }} - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ git-core gcc make autoconf automake libtool \ g++ autoconf-archive pkg-config libglib2.0-dev libglibmm-2.4-dev \ libzip-dev libusb-1.0-0-dev libftdi1-dev libieee1284-3-dev \ libvisa-dev nettle-dev libavahi-client-dev libhidapi-dev check \ doxygen swig \ cmake libboost-dev libboost-test-dev \ python3-dev python-gi-dev python-setuptools - name: Checkout sigrok-utils uses: actions/checkout@v3 with: repository: knarfS/sigrok-util path: sigrok-util ref: github - name: Install system Qwt if: ${{ matrix.qt.system_qwt }} run: | sudo apt-get install -y libqwt-qt5-dev - name: Build custom Qwt if: ${{ !matrix.qt.system_qwt }} run: | sudo apt-get install -y mesa-common-dev libgl1-mesa-dev cd sigrok-util/cross-compile/github-actions source sigrok-linux-init-toolchain.sh ./sigrok-linux-build-qwt.sh - name: Build dependencies run: | cd sigrok-util/cross-compile/github-actions source sigrok-linux-init-toolchain.sh ./sigrok-linux-build-dependencies.sh - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build smuview run: | source sigrok-util/cross-compile/github-actions/sigrok-linux-init-toolchain.sh mkdir -p smuview/build cd smuview/build PKG_CONFIG_PATH=$P cmake \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_DIR \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DDISABLE_WERROR=FALSE \ -DENABLE_TESTS=TRUE \ .. make $PARALLEL $V build_appimage: name: SmuView AppImage build (${{ matrix.target.target }}) runs-on: ubuntu-latest container: image: ghcr.io/knarfs/sigrok-appimage-${{ matrix.target.container }}:latest strategy: matrix: target: - target: "i386" container: "x86_64-i386" cc: "gcc -m32" cxx: "g++ -m32" ld: "ld -melf_i386" ldflags: "-m32" - target: "x86_64" container: "x86_64" cc: "gcc" cxx: "g++" ld: "ld" ldflags: "" env: CC: ${{ matrix.target.cc }} CXX: ${{ matrix.target.cxx }} LD: ${{ matrix.target.ld }} LDFLAGS: ${{ matrix.target.ldflags }} steps: - name: Update dependencies run: | sudo apt-get update sudo apt-get upgrade -y - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build AppImage run: | wget https://github.com/knarfS/appimagecraft/releases/download/continuous/appimagecraft-${{ matrix.target.target }}.AppImage #wget https://github.com/TheAssassin/appimagecraft/releases/download/continuous/appimagecraft-${{ matrix.target.target }}.AppImage chmod ug+x appimagecraft-${{ matrix.target.target }}.AppImage export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig:$PKG_CONFIG_PATH" cd smuview ../appimagecraft-${{ matrix.target.target }}.AppImage build -d build/ - name: Upload artifact uses: actions/upload-artifact@v3 with: name: SmuView ${{ matrix.target.target }} AppImage path: smuview/SmuView*.AppImage* build_mxe: name: SmuView MXE build (${{ matrix.target.target }}) runs-on: ubuntu-latest container: image: ghcr.io/knarfs/sigrok-mxe:latest strategy: matrix: target: - { target: "i686", nsis_param: "" } - { target: "x86_64", nsis_param: "-DPE64=1" } env: TARGET: ${{ matrix.target.target }} DEBUG: 0 # Download python from sigrok.org and smth is wrong with the cert WGET: "wget -c --quiet --no-check-certificate" steps: - name: Checkout sigrok-utils uses: actions/checkout@v3 with: repository: knarfS/sigrok-util path: sigrok-util ref: github - name: Build dependencies run: | cd sigrok-util/cross-compile/github-actions source sigrok-mxe-init-toolchain.sh ./sigrok-mxe-build-dependencies.sh - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build smuview run: | source sigrok-util/cross-compile/github-actions/sigrok-mxe-init-toolchain.sh mkdir -p smuview/build cd smuview/build $CMAKE \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_DIR \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DDISABLE_WERROR=FALSE \ -DENABLE_TESTS=TRUE \ .. make $PARALLEL $V make install/strip $V - name: Build NSIS installer run: | source sigrok-util/cross-compile/github-actions/sigrok-mxe-init-toolchain.sh # Zadig (we ship this with frontends for easy driver switching). $WGET https://github.com/pbatard/libwdi/releases/download/b721/zadig-2.4.exe -O $INSTALL_DIR/zadig.exe $WGET https://github.com/pbatard/libwdi/releases/download/v1.2.5/zadig_xp-2.2.exe -O $INSTALL_DIR/zadig_xp.exe cp sigrok-util/cross-compile/github-actions/contrib-mxe/FileAssociation.nsh smuview/build/contrib makensis ${{ matrix.target.nsis_param }} smuview/build/contrib/smuview_cross.nsi - name: Upload artifact uses: actions/upload-artifact@v3 with: name: SmuView NSIS ${{ matrix.target.target }} installer path: smuview/build/contrib/SmuView*installer.exe build_macos: name: SmuView MacOS build (${{ matrix.compiler.compiler }}) runs-on: macos-11 strategy: matrix: compiler: - { compiler: GCC, cc: "gcc", cxx: "g++" } - { compiler: LLVM, cc: "llvm-gcc", cxx: "llvm-g++" } env: CC: ${{ matrix.compiler.cc }} CXX: ${{ matrix.compiler.cxx }} # We use Homebrew Qt 5.15.x (current) HB_QTVER: "qt@5" # We use Homebrew Python 3.x (current) HB_PYVER: "python@3" steps: - name: Install dependencies run: | brew install autoconf automake autoconf-archive pkg-config \ libtool libzip libusb libftdi hidapi nettle check doxygen swig \ glib glibmm@2.66 cmake "$HB_PYVER" boost "$HB_QTVER" qwt-qt5 - name: Checkout sigrok-utils uses: actions/checkout@v3 with: repository: knarfS/sigrok-util path: sigrok-util ref: github - name: Build dependencies run: | cd sigrok-util/cross-compile/github-actions source sigrok-macos-init-toolchain.sh ./sigrok-macos-build-dependencies.sh - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build smuview run: | source sigrok-util/cross-compile/github-actions/sigrok-macos-init-toolchain.sh mkdir -p smuview/build cd smuview/build PKG_CONFIG_PATH=$P cmake \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_DIR \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DDISABLE_WERROR=FALSE \ -DENABLE_TESTS=TRUE \ .. make $PARALLEL $V make install $V - name: Build DMG run: | # Source SmuView versions and names source smuview/build/contrib/config_version.sh cd sigrok-util/cross-compile/github-actions source sigrok-macos-init-toolchain.sh ./sigrok-macos-create-dmg.sh - name: Upload artifact # Only upload artifact from the LLVM build! if: startsWith(matrix.compiler.compiler, 'LLVM') uses: actions/upload-artifact@v3 with: name: SmuView DMG path: sigrok-util/cross-compile/github-actions/SmuView*.dmg publish: name: SmuView publish runs-on: ubuntu-latest if: | (github.event_name == 'push' && github.ref == 'refs/heads/master') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/master') needs: - build_linux - build_appimage - build_mxe - build_macos steps: - name: Install dependencies run: | # AppImage needs libfuse2 to start sudo apt-get update sudo apt-get install -y libfuse2 - name: Download artifacts uses: actions/download-artifact@v2 - name: Inspect directory after downloading artifacts run: ls -alFR - name: Upload artifacts and create (continuous) release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | wget -q https://github.com/TheAssassin/pyuploadtool/releases/download/continuous/pyuploadtool-x86_64.AppImage chmod +x pyuploadtool-x86_64.AppImage ./pyuploadtool-x86_64.AppImage **/SmuView-*.* ================================================ FILE: .github/workflows/documentation.yml ================================================ name: SmuView Documentation on: push: branches: - master paths: - "manual/**" - "src/python/bindings.cpp" workflow_dispatch: defaults: run: shell: bash env: # The path where the compiled packages will be installed. INSTALL_DIR: "${{ github.workspace }}/sr" # Git URL for the sigrok dependencies SIGROK_REPO_BASE: "https://github.com/sigrokproject" # Build type for SmuView (Debug, Release, RelWithDebInfo, MinSizeRel) BUILD_TYPE: "Release" # Misc commands WGET: "wget -c --quiet" GIT_CLONE: "git clone --depth=1" jobs: build_manual: name: SmuView Manual runs-on: ubuntu-latest env: BUILD_TYPE: "Release" steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ git-core gcc make autoconf automake libtool \ g++ autoconf-archive pkg-config libglib2.0-dev libglibmm-2.4-dev \ libzip-dev check doxygen swig \ cmake libboost-dev libqt5svg5-dev qtbase5-dev libqwt-qt5-dev \ python3-dev python-gi-dev python-setuptools \ asciidoctor ruby-asciidoctor-pdf coderay - name: Checkout sigrok-utils uses: actions/checkout@v3 with: repository: knarfS/sigrok-util path: sigrok-util ref: github - name: Build dependencies run: | cd sigrok-util/cross-compile/github-actions source sigrok-linux-init-toolchain.sh ./sigrok-linux-build-dependencies.sh - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build manual run: | source sigrok-util/cross-compile/github-actions/sigrok-linux-init-toolchain.sh mkdir -p smuview/build cd smuview/build PKG_CONFIG_PATH=$P cmake \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_DIR \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DDISABLE_WERROR=TRUE \ -DENABLE_TESTS=FALSE \ .. make manual $V make manual-publish $V - name: Upload artifact uses: actions/upload-artifact@v3 with: name: SmuView_manual path: smuview/build/manual_publish/ build_python_doc: name: SmuView Python API runs-on: ubuntu-latest env: BUILD_TYPE: "Release" steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ git-core gcc make autoconf automake libtool \ g++ autoconf-archive pkg-config libglib2.0-dev libglibmm-2.4-dev \ libzip-dev check doxygen swig \ cmake libboost-dev libqt5svg5-dev qtbase5-dev libqwt-qt5-dev \ python3-dev python-gi-dev python-setuptools coreutils - name: Checkout sigrok-utils uses: actions/checkout@v3 with: repository: knarfS/sigrok-util path: sigrok-util ref: github - name: Build dependencies run: | cd sigrok-util/cross-compile/github-actions source sigrok-linux-init-toolchain.sh ./sigrok-linux-build-dependencies.sh - name: Checkout smuview uses: actions/checkout@v3 with: path: smuview - name: Build smuview run: | source sigrok-util/cross-compile/github-actions/sigrok-linux-init-toolchain.sh mkdir -p smuview/build cd smuview/build PKG_CONFIG_PATH=$P cmake \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_DIR \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DDISABLE_WERROR=y \ -DENABLE_TESTS=FALSE \ .. make $PARALLEL $V make install $V - name: Build API doc run: | pip3 install pdoc3 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$INSTALL_DIR/lib" # Source versions and create destination folders source smuview/build/contrib/config_version.sh mkdir -p api_doc/"$SV_MANUAL_VERSION" # Create script for generating the API doc cat > gen_api.py << EOF import smuview import pdoc html_str = pdoc.html("smuview", show_type_annotations=True) f = open('./api_doc/$SV_MANUAL_VERSION/python_bindings_api.html', 'w') print(html_str, file=f) f.close() EOF # Generating the API doc timeout --preserve-status 60 $INSTALL_DIR/bin/smuview -s gen_api.py -platform offscreen - name: Upload artifact uses: actions/upload-artifact@v3 with: name: SmuView_Python_API path: api_doc/ publish: name: SmuView documentation publish runs-on: ubuntu-latest needs: - build_manual - build_python_doc steps: - name: Download artifacts uses: actions/download-artifact@v2 - name: Inspect directory after downloading artifacts run: ls -alFR - name: Checkout knarfS.github.io uses: actions/checkout@v3 with: repository: knarfS/knarfS.github.io path: knarfS.github.io persist-credentials: false - name: Copy documentation run: | cp -r SmuView_manual/* knarfS.github.io/doc/smuview cp -r SmuView_Python_API/* knarfS.github.io/doc/smuview - name: Publish documentation uses: cpina/github-action-push-to-another-repository@main env: SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }} with: source-directory: "knarfS.github.io" destination-github-username: "knarfS" destination-repository-name: "knarfS.github.io" user-email: "frank-stettner@gmx.net" target-branch: "master" commit-message: "Automated update for ORIGIN_COMMIT" ================================================ FILE: .gitignore ================================================ # Unwanted smuview files TOASK doc/linuxgpib_build.txt doc/parameter.txt doc/pps-wiki.txt doc/relays/ testing/ # CMake CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake build/ # clangd cache .cache/ # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.lib *.dylib *.exe *.out *.app # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* *build-* !build-*.yml # Qt unit tests target_wrapper.* # doxygen doxy/ # QtCreator *.autosave # QtCtreator Qml *.qmlproject.user *.qmlproject.user.* # QtCtreator CMake CMakeLists.txt.user* # KDEvelop .kdev4/ # JetBrains .idea/ # Editor backups *.save *.swp # Build artifacts *.AppImage ================================================ FILE: CMake/CheckSigrokFeatures.cmake ================================================ ## ## This file is part of the SmuView project. ## ## Copyright (C) 2021 Frank Stettner ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## # Check if the installed libsigrok has the required features. function(check_libsigrok_features additional_header additional_lib) include(CheckCXXSourceCompiles) include(CMakePushCheckState) cmake_push_check_state() set(CMAKE_REQUIRED_FLAGS "-std=c++11") set(CMAKE_REQUIRED_INCLUDES "${additional_header}") set(CMAKE_REQUIRED_LIBRARIES "${additional_lib}") set(CMAKE_REQUIRED_QUIET 1) check_cxx_source_compiles(" #include const sigrok::ConfigKey *config_key; int main() { config_key = sigrok::ConfigKey::MULTIPLEXER; return 0; } " HAS_FEATURES) cmake_pop_check_state() if (NOT HAS_FEATURES) message(FATAL_ERROR "libsigrok is too old, minimum required version is 0.6.0-git-522381a3") endif() endfunction(check_libsigrok_features) ================================================ FILE: CMake/FindQwt.cmake ================================================ # Find Qwt # ~~~~~~~~ # Copyright (c) 2010, Tim Sutton # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Once run this will define: # # QWT_FOUND = system has QWT lib # QWT_LIBRARY = full path to the QWT library # QWT_INCLUDE_DIR = where to find headers # set(QWT_LIBRARY_NAMES qwt-qt5 qwt6-qt5 qwt) set(QWT_HOMEBREW_INSTALL_PATH /usr/local/opt/qwt-qt5) file(GLOB QWT_CUSTOM_INSTALL_PATH /usr/local/qwt-6.1.[0-9]) find_library(QWT_LIBRARY NAMES ${QWT_LIBRARY_NAMES} PATHS /usr/lib /usr/local/lib "${QWT_HOMEBREW_INSTALL_PATH}/lib" "${QWT_CUSTOM_INSTALL_PATH}/lib" "$ENV{LIB_DIR}/lib" "$ENV{LIB}" ) set(_qwt_fw) if(QWT_LIBRARY MATCHES "/qwt.*\\.framework") string(REGEX REPLACE "^(.*/qwt.*\\.framework).*$" "\\1" _qwt_fw "${QWT_LIBRARY}") endif() FIND_PATH(QWT_INCLUDE_DIR NAMES qwt.h PATHS "${_qwt_fw}/Headers" /usr/include /usr/local/include "${QWT_HOMEBREW_INSTALL_PATH}/include" "${QWT_CUSTOM_INSTALL_PATH}/include" "$ENV{LIB_DIR}/include" "$ENV{INCLUDE}" PATH_SUFFIXES qwt-qt5 qt5/qwt qwt qwt6 ) IF (QWT_INCLUDE_DIR AND QWT_LIBRARY) SET(QWT_FOUND TRUE) ENDIF (QWT_INCLUDE_DIR AND QWT_LIBRARY) IF (QWT_FOUND) FILE(READ ${QWT_INCLUDE_DIR}/qwt_global.h qwt_header) STRING(REGEX REPLACE "^.*QWT_VERSION_STR +\"([^\"]+)\".*$" "\\1" QWT_VERSION_STR "${qwt_header}") IF (NOT QWT_FIND_QUIETLY) MESSAGE(STATUS "Found Qwt: ${QWT_LIBRARY} (${QWT_VERSION_STR})") ENDIF (NOT QWT_FIND_QUIETLY) ELSE (QWT_FOUND) IF (QWT_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find Qwt") ENDIF (QWT_FIND_REQUIRED) ENDIF (QWT_FOUND) ================================================ FILE: CMake/GetGitRevisionDescription.cmake ================================================ # - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() # check if this is a submodule if(NOT IS_DIRECTORY ${GIT_DIR}) file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() ================================================ FILE: CMake/GetGitRevisionDescription.cmake.in ================================================ # # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) else() configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() ================================================ FILE: CMake/memaccess.cmake ================================================ ## ## This file is part of the SmuView project. ## ## Copyright (C) 2014 Marcus Comstedt ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## include(CheckCSourceRuns) function(memaccess_check_unaligned_le _var) if(NOT CMAKE_CROSSCOMPILING) CHECK_C_SOURCE_RUNS(" #include int main() { int i; union { uint64_t u64; uint8_t u8[16]; } d; uint64_t v; for (i=0; i<16; i++) d.u8[i] = i; v = *(uint64_t *)(d.u8+1); if (v != 0x0807060504030201ULL) return 1; return 0; }" ${_var}) endif() if(CMAKE_CROSSCOMPILING) message(STATUS "Cross compiling - using portable code for memory access") endif() endfunction() ================================================ FILE: CMakeLists.txt ================================================ ## ## This file is part of the SmuView project. ## ## Copyright (C) 2012 Joel Holdsworth ## Copyright (C) 2012-2013 Alexandru Gagniuc ## Copyright (C) 2017-2022 Frank Stettner ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## cmake_minimum_required(VERSION 3.6) project(smuview C CXX) include(GNUInstallDirs) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake") #=============================================================================== #= User Options #------------------------------------------------------------------------------- option(DISABLE_WERROR "Build without -Werror" FALSE) option(ENABLE_SIGNALS "Build with UNIX signals" TRUE) option(ENABLE_TESTS "Enable unit tests" TRUE) option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE) # Let AUTOMOC and AUTOUIC process GENERATED files. if(POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif() # Only interpret if() arguments as variables or keywords when unquoted. if(POLICY CMP0054) cmake_policy(SET CMP0054 NEW) endif() # SmuView, QCodeEditor and pybind11 only need C++11, but the upcoming Boost.Math # (Boost 1.82 release) library requires C++14 as minimum language standard. set(CMAKE_CXX_STANDARD 14) if(WIN32) # On Windows/MinGW we need to statically link to libraries. # This option is user configurable, but enable it by default on win32. set(STATIC_PKGDEPS_LIBS TRUE) # Windows does not support UNIX signals. set(ENABLE_SIGNALS FALSE) # When cross compiling this is needed for pkg-config set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build (None, Debug, Release, RelWithDebInfo, MinSizeRel)." FORCE) endif() # Generate compile_commands.json in build/ for analyzers like clang-tidy. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) message(STATUS "DISABLE_WERROR: ${DISABLE_WERROR}") message(STATUS "ENABLE_SIGNALS: ${ENABLE_SIGNALS}") message(STATUS "ENABLE_TESTS: ${ENABLE_TESTS}") message(STATUS "STATIC_PKGDEPS_LIBS: ${STATIC_PKGDEPS_LIBS}") #=============================================================================== #= Dependencies #------------------------------------------------------------------------------- list(APPEND PKGDEPS glib-2.0>=2.28.0) list(APPEND PKGDEPS glibmm-2.4>=2.28.0) set(LIBSR_CXX_BINDING "libsigrokcxx>=0.5.2") list(APPEND PKGDEPS "${LIBSR_CXX_BINDING}") find_package(PkgConfig) pkg_check_modules(LIBSRCXX ${LIBSR_CXX_BINDING} IMPORTED_TARGET) if(NOT LIBSRCXX_FOUND OR NOT LIBSRCXX_VERSION) message(FATAL_ERROR "libsigrok C++ bindings missing, check libsigrok's 'configure' output (missing dependencies?)") endif() pkg_check_modules(PKGDEPS REQUIRED IMPORTED_TARGET ${PKGDEPS}) include(CheckSigrokFeatures) if(STATIC_PKGDEPS_LIBS) check_libsigrok_features("${LIBSRCXX_STATIC_INCLUDE_DIRS}" PkgConfig::PKGDEPS) else() check_libsigrok_features("${LIBSRCXX_INCLUDE_DIRS}" PkgConfig::PKGDEPS) endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) find_package(Qt5 5.7 COMPONENTS Core Gui Widgets Svg REQUIRED) if(MINGW) # MXE workaround: Use pkg-config to find Qt5 libs. # https://github.com/mxe/mxe/issues/1642 # Not required (and doesn't work) on MSYS2. if(NOT DEFINED ENV{MSYSTEM}) pkg_check_modules(QT5ALL REQUIRED Qt5Widgets Qt5Gui Qt5Svg) endif() endif() set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg) find_package(Qwt 6.1.2 REQUIRED) # boost::multiprecision is required, but it's header only. So no need to specify set(BOOSTCOMPS) if(ENABLE_TESTS) list(APPEND BOOSTCOMPS unit_test_framework) endif() find_package(Boost 1.54 COMPONENTS ${BOOSTCOMPS} REQUIRED) # Find the platform's thread library (needed for C++11 threads). # This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value. find_package(Threads REQUIRED) if(MINGW) # MXE workaround: Use PkgConfig to find the supplied Python 3.4 (see MXE build # script sigrok-cross-mingw-smuview in sigrok-util) and disable the find # python functionality in pybind11 set(PYBIND11_NOPYTHON ON) # This is not needed atm, but might come in handy in the future: # Python 3.8 no longer links to libpython, but it provides a python3-embed.pc # now, so let's try using that first and only fall back on the normal case if # that fails. # See: https://docs.python.org/3.8/whatsnew/3.8.html#debug-build-uses-the-same-abi-as-release-build pkg_check_modules(PYTHON3 python3-embed) if(NOT PYTHON3_FOUND) pkg_check_modules(PYTHON3 python3) endif() endif() # SmuView has its own copy of pybind11 if(MINGW) # Use pybind11 2.9.2 with the emum_docstring patch for the MXE build. 2.9.2 # is the last pybind11 version that supports Python 3.4.4 which is needed for # static linking in the MXE cross build set(PYBIND11_SUBDIRECTORY external/pybind11_2.9.2) else() # Use pybind11 2.11 dev1 with the emum_docstring patch for all other builds set(PYBIND11_SUBDIRECTORY external/pybind11_2.11_dev1) endif() add_subdirectory(${PYBIND11_SUBDIRECTORY}) # SmuView has its own copy of QCodeEditor add_subdirectory(external/QCodeEditor) # SmuView has its own copy of QtFindReplaceDialog add_subdirectory(external/QtFindReplaceDialog/dialogs) #=============================================================================== #= System Introspection #------------------------------------------------------------------------------- include(memaccess) memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS) #=============================================================================== #= Config Header #------------------------------------------------------------------------------- set(SV_TITLE SmuView) set(SV_VERSION_STRING "0.0.6") # Append the revision hash unless we are exactly on a tagged release. include(GetGitRevisionDescription) git_describe(SV_TAG_VERSION_STRING --match "v${SV_VERSION_STRING}" --exact-match) if(NOT SV_TAG_VERSION_STRING) get_git_head_revision(SV_REVSPEC SV_HASH) if(SV_HASH) string(SUBSTRING "${SV_HASH}" 0 7 SV_SHORTHASH) set(SV_VERSION_STRING "${SV_VERSION_STRING}-git-${SV_SHORTHASH}") endif() # Non-tagged releases use the continuous manual set(SV_MANUAL_VERSION "continuous") else() # Tagged releases use a fixed manual version set(SV_MANUAL_VERSION ${SV_VERSION_STRING}) endif() if(SV_VERSION_STRING MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[-0-9a-z]*)?$") set(SV_VERSION_MAJOR ${CMAKE_MATCH_1}) set(SV_VERSION_MINOR ${CMAKE_MATCH_2}) set(SV_VERSION_MICRO ${CMAKE_MATCH_3}) set(SV_VERSION_SUFFIX ${CMAKE_MATCH_4}) endif() message(STATUS "${SV_TITLE} version: ${SV_VERSION_STRING}") # Library versions set(SV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION}) get_directory_property(pybind11_VERSION DIRECTORY ${PYBIND11_SUBDIRECTORY} DEFINITION pybind11_VERSION) get_directory_property(pybind11_VERSION_TYPE DIRECTORY ${PYBIND11_SUBDIRECTORY} DEFINITION pybind11_VERSION_TYPE) set(SV_PYBIND11_VERSION "${pybind11_VERSION} ${pybind11_VERSION_TYPE}") if(MINGW) # MXE workaround: Use PkgConfig to find Python set(SV_PYTHON_VERSION ${PYTHON3_VERSION}) else() set(SV_PYTHON_VERSION ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}) endif() configure_file ( ${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h ) configure_file ( ${PROJECT_SOURCE_DIR}/contrib/config_version.sh.in ${PROJECT_BINARY_DIR}/contrib/config_version.sh ) #=============================================================================== #= Sources #------------------------------------------------------------------------------- set(smuview_SOURCES main.cpp src/application.cpp src/devicemanager.cpp src/mainwindow.cpp src/session.cpp src/settingsmanager.cpp src/util.cpp src/channels/addscchannel.cpp src/channels/basechannel.cpp src/channels/dividechannel.cpp src/channels/hardwarechannel.cpp src/channels/integratechannel.cpp src/channels/mathchannel.cpp src/channels/movingavgchannel.cpp src/channels/multiplysfchannel.cpp src/channels/multiplysschannel.cpp src/channels/userchannel.cpp src/data/analogbasesignal.cpp src/data/analogsamplesignal.cpp src/data/analogtimesignal.cpp src/data/basesignal.cpp src/data/datautil.cpp src/data/properties/baseproperty.cpp src/data/properties/boolproperty.cpp src/data/properties/doubleproperty.cpp src/data/properties/doublerangeproperty.cpp src/data/properties/int32property.cpp src/data/properties/measuredquantityproperty.cpp src/data/properties/rationalproperty.cpp src/data/properties/stringproperty.cpp src/data/properties/uint64property.cpp src/data/properties/uint64rangeproperty.cpp src/devices/basedevice.cpp src/devices/configurable.cpp src/devices/deviceutil.cpp src/devices/hardwaredevice.cpp src/devices/measurementdevice.cpp src/devices/oscilloscopedevice.cpp src/devices/sourcesinkdevice.cpp src/devices/userdevice.cpp src/python/bindings.cpp src/python/pystreambuf.cpp src/python/pystreamredirect.hpp src/python/smuscriptrunner.cpp src/python/uihelper.cpp src/python/uiproxy.cpp src/ui/data/quantitycombobox.cpp src/ui/data/quantityflagslist.cpp src/ui/data/unitcombobox.cpp src/ui/datatypes/basewidget.cpp src/ui/datatypes/boolbutton.cpp src/ui/datatypes/boolcheckbox.cpp src/ui/datatypes/boolled.cpp src/ui/datatypes/datatypehelper.cpp src/ui/datatypes/doublecontrol.cpp src/ui/datatypes/doubledisplay.cpp src/ui/datatypes/doubleknob.cpp src/ui/datatypes/doublerangecombobox.cpp src/ui/datatypes/doubleslider.cpp src/ui/datatypes/doublesmallcontrol.cpp src/ui/datatypes/doublespinbox.cpp src/ui/datatypes/int32spinbox.cpp src/ui/datatypes/measuredquantitycombobox.cpp src/ui/datatypes/rationalcombobox.cpp src/ui/datatypes/stringcombobox.cpp src/ui/datatypes/stringlabel.cpp src/ui/datatypes/stringled.cpp src/ui/datatypes/thresholdcontrol.cpp src/ui/datatypes/uint64combobox.cpp src/ui/datatypes/uint64label.cpp src/ui/datatypes/uint64rangecombobox.cpp src/ui/datatypes/uint64spinbox.cpp src/ui/devices/channelcombobox.cpp src/ui/devices/channelgroupcombobox.cpp src/ui/devices/configkeycombobox.cpp src/ui/devices/configurablecombobox.cpp src/ui/devices/devicecombobox.cpp src/ui/devices/selectconfigurableform.cpp src/ui/devices/selectpropertyform.cpp src/ui/devices/selectsignalwidget.cpp src/ui/devices/signalcombobox.cpp src/ui/devices/devicetree/devicetreemodel.cpp src/ui/devices/devicetree/devicetreeview.cpp src/ui/devices/devicetree/treeitem.cpp src/ui/dialogs/aboutdialog.cpp src/ui/dialogs/addmathchanneldialog.cpp src/ui/dialogs/adduserchanneldialog.cpp src/ui/dialogs/addviewdialog.cpp src/ui/dialogs/addviewdialog.cpp src/ui/dialogs/connectdialog.cpp src/ui/dialogs/generatewaveformdialog.cpp src/ui/dialogs/plotconfigdialog.cpp src/ui/dialogs/plotcurveconfigdialog.cpp src/ui/dialogs/plotdiffmarkerdialog.cpp src/ui/dialogs/selectsignaldialog.cpp src/ui/dialogs/selectxysignalsdialog.cpp src/ui/dialogs/signalsavedialog.cpp src/ui/tabs/basetab.cpp src/ui/tabs/devicetab.cpp src/ui/tabs/measurementtab.cpp src/ui/tabs/oscilloscopetab.cpp src/ui/tabs/smuscripttab.cpp src/ui/tabs/sourcesinktab.cpp src/ui/tabs/tabdockwidget.cpp src/ui/tabs/tabhelper.cpp src/ui/tabs/usertab.cpp src/ui/tabs/welcometab.cpp src/ui/views/baseplotview.cpp src/ui/views/baseview.cpp src/ui/views/dataview.cpp src/ui/views/devicesview.cpp src/ui/views/democontrolview.cpp src/ui/views/genericcontrolview.cpp src/ui/views/measurementcontrolview.cpp src/ui/views/powerpanelview.cpp src/ui/views/scopehorizontalcontrolview.cpp src/ui/views/scopetriggercontrolview.cpp src/ui/views/scopeverticalcontrolview.cpp src/ui/views/sequenceoutputview.cpp src/ui/views/smuscriptoutputview.cpp src/ui/views/smuscripttreeview.cpp src/ui/views/smuscriptview.cpp src/ui/views/sourcesinkcontrolview.cpp src/ui/views/timeplotview.cpp src/ui/views/valuepanelview.cpp src/ui/views/viewhelper.cpp src/ui/views/xyplotview.cpp src/ui/widgets/clickablelabel.cpp src/ui/widgets/colorbutton.cpp src/ui/widgets/monofontdisplay.cpp src/ui/widgets/popup.cpp src/ui/widgets/plot/axislocklabel.cpp src/ui/widgets/plot/axispopup.cpp src/ui/widgets/plot/basecurvedata.cpp src/ui/widgets/plot/curve.cpp src/ui/widgets/plot/plot.cpp src/ui/widgets/plot/plotmagnifier.cpp src/ui/widgets/plot/plotscalepicker.cpp src/ui/widgets/plot/timecurvedata.cpp src/ui/widgets/plot/xycurvedata.cpp ) if(ENABLE_SIGNALS) list(APPEND smuview_SOURCES signalhandler.cpp) endif() set(smuview_RESOURCES smuview.qrc ) if(WIN32) # Use the sigrok icon for the smuview.exe executable. set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_RC_COMPILER} -O coff -I${CMAKE_CURRENT_SOURCE_DIR} ") enable_language(RC) list(APPEND smuview_SOURCES smuviewico.rc) endif() qt5_add_resources(smuview_RESOURCES_RCC ${smuview_RESOURCES}) #=============================================================================== #= Global Definitions #------------------------------------------------------------------------------- add_definitions(-DQT_NO_KEYWORDS) add_definitions(-D__STDC_LIMIT_MACROS) add_definitions(-Wall -Wextra -Woverloaded-virtual -Wdeprecated-declarations) # -Weffc++ -Wconversion -Wsign-conversion) add_definitions(-std=c++14) add_definitions(-DBOOST_MATH_DISABLE_FLOAT128=1) if(NOT DISABLE_WERROR) #add_definitions(-Werror -pedantic-errors) add_definitions(-Werror=pedantic -pedantic-errors) endif() if(ENABLE_SIGNALS) add_definitions(-DENABLE_SIGNALS) endif() if(MINGW) # MXE workaround: Prevents compile error: # mxe-git-x86_64/usr/lib/gcc/x86_64-w64-mingw32.static.posix/5.5.0/include/c++/cmath:1147:11: error: '::hypot' has not been declared # using ::hypot; # Alternativ solution: # Add "#include " before the pybind11 includes in bindings.cpp and smuscriptrunner.cpp add_compile_options(-D_hypot=hypot) endif() if(MINGW AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") # Fix error "too many sections (37653)" and "file too big" for mingw/MXE when building for Debug add_definitions(-Wa,-mbig-obj) endif() #=============================================================================== #= Global Include Directories #------------------------------------------------------------------------------- include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${QWT_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ) if(MINGW) # MXE workaround: Use PkgConfig to find Python include_directories(${PYTHON3_INCLUDE_DIRS}) endif() if(STATIC_PKGDEPS_LIBS) include_directories(${PKGDEPS_STATIC_INCLUDE_DIRS}) else() include_directories(${PKGDEPS_INCLUDE_DIRS}) endif() #=============================================================================== #= Linker Configuration #------------------------------------------------------------------------------- link_directories(${Boost_LIBRARY_DIRS}) set(SMUVIEW_LINK_LIBS ${Boost_LIBRARIES} ${QT_LIBRARIES} ${QWT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} #${LIBATOMIC_LIBRARY} PkgConfig::PKGDEPS pybind11::embed QCodeEditor QtFindReplaceDialog ) if(MINGW) # MXE workaround: Use PkgConfig to find Python if(PYTHON3_LINK_LIBRARIES) # Try to use the fully qualified name for cross compiling list(APPEND SMUVIEW_LINK_LIBS ${PYTHON3_LINK_LIBRARIES}) else() list(APPEND SMUVIEW_LINK_LIBS ${PYTHON3_LIBRARIES}) endif() endif() if(WIN32) # On Windows we need to statically link the libqsvg imageformat # plugin (and the QtSvg component) for SVG graphics/icons to work. # We also need QWindowsIntegrationPlugin, Qt5PlatformSupport, and all # Qt libs and their dependencies. add_definitions(-DQT_STATICPLUGIN) list(APPEND SMUVIEW_LINK_LIBS Qt5::QSvgPlugin) list(APPEND SMUVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin) # Form Qt 5.8 on, Qt5PlatformSupport is split into several plugins: # QtAccessibilitySupport QtCliboardSupport QtEventDispatcherSupport # QtFontDatabaseSupport QtGraphicsSupport QtThemeSupport # TODO: Some of the plugins are wrong? if(Qt5Core_VERSION VERSION_LESS "5.8.0") list(APPEND SMUVIEW_LINK_LIBS -lQt5PlatformSupport ${QT5ALL_LDFLAGS}) else() list(APPEND SMUVIEW_LINK_LIBS -lQt5AccessibilitySupport) list(APPEND SMUVIEW_LINK_LIBS -lQt5EventDispatcherSupport) list(APPEND SMUVIEW_LINK_LIBS -lQt5FontDatabaseSupport) list(APPEND SMUVIEW_LINK_LIBS -lQt5ThemeSupport) #list(APPEND SMUVIEW_LINK_LIBS -lQt5ClipboardSupport) #list(APPEND SMUVIEW_LINK_LIBS -lQt5GraphicsSupport) list(APPEND SMUVIEW_LINK_LIBS ${QT5ALL_LDFLAGS}) endif() endif() add_executable(${PROJECT_NAME} ${smuview_SOURCES} ${smuview_RESOURCES_RCC}) target_link_libraries(${PROJECT_NAME} ${SMUVIEW_LINK_LIBS}) if(WIN32 AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") # Pass -mwindows so that no "DOS box" opens when SmuView is started. set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mwindows") endif() #=============================================================================== #= Installation #------------------------------------------------------------------------------- # Install the executable. install(TARGETS ${PROJECT_NAME} DESTINATION bin/) # Install the manpage. install(FILES doc/smuview.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc) # Install the desktop file. install(FILES contrib/org.sigrok.SmuView.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) # Install the AppData/AppStream file. install(FILES contrib/org.sigrok.SmuView.appdata.xml DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo) # Install the SmuView icons. install(FILES icons/smuview.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps) install(FILES icons/smuview.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) # Install the SmuScript examples. install(DIRECTORY smuscript/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/smuscript) # Generate Windows installer script. configure_file(contrib/smuview_cross.nsi.in ${CMAKE_CURRENT_BINARY_DIR}/contrib/smuview_cross.nsi @ONLY) #=============================================================================== #= Documentation #------------------------------------------------------------------------------- add_subdirectory(manual) #=============================================================================== #= Packaging (handled by CPack) #------------------------------------------------------------------------------- #=============================================================================== #= Tests #------------------------------------------------------------------------------- if(ENABLE_TESTS) add_subdirectory(test) enable_testing() add_test(test ${CMAKE_CURRENT_BINARY_DIR}/test/smuview-test) endif() ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: Doxyfile ================================================ # Doxyfile 1.8.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "SmuView" PROJECT_NUMBER = "unreleased development snapshot" PROJECT_BRIEF = "A Qt-based sigrok GUI" PROJECT_LOGO = icons/smuview.png OUTPUT_DIRECTORY = doxy CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = YES SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = YES TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = YES SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = NO GENERATE_TESTLIST = YES GENERATE_BUGLIST = NO GENERATE_DEPRECATEDLIST= NO ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = . INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = moc_*.c* */external/* EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html-api HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 DISABLE_INDEX = NO GENERATE_TREEVIEW = YES ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the Latex output #--------------------------------------------------------------------------- GENERATE_LATEX = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = NO HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = YES UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = svg INTERACTIVE_SVG = YES DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES DOT_CLEANUP = YES ================================================ FILE: INSTALL ================================================ ------------------------------------------------------------------------------- INSTALL ------------------------------------------------------------------------------- Requirements ------------ - git (only needed when building from git) - A C++ compiler with C++11 support (-std=c++11 option), e.g. - g++ (>= 4.8.1) - clang++ (>= 3.3) - make - libtool (only needed when building from git) - pkg-config (>= 0.22) - cmake (>= 2.8.12) - libglib (>= 2.28.0) - glibmm-2.4 (>= 2.28.0) - Qt5 >= 5.7 (including the following components): - Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg - Boost (>= 1.55) - Qwt (>= 6.1.2) - Python (>= 3) - libsigrokcxx (>= 0.5.2) (libsigrok C++ bindings) - Asciidoctor (optional, only needed to build the manual) Building and installing ----------------------- In order to get the SmuView source code and build it, run: $ git clone https://github.com/knarfS/smuview.git $ cd smuview $ mkdir build $ cd build $ cmake ../ $ make For installing SmuView: $ sudo make install Creating a source distribution package -------------------------------------- In order to build a source package begin with an unconfigured source tree. $ cd smuview $ mkdir dist $ cd dist $ cmake ../ $ make package_source ================================================ FILE: NEWS ================================================ 0.0.5 (2021-01-09) ------------------ For a detailed list of changes, please see the git commit history. * Save and restore settings: - Save/restore the size and position of the main window. - Save/restore the shown views and their size/position for a specific device. - Add a tab for curve color settings in the plot config dialog. - Add -c cmd line parameter for don't restoring the settings. - Save/restore the settings of the find and replace dialog. - Save/restore last directory of the SmuScript tree. - Save/restore the settings of the save signals dialog. * Enhance the Python API: - Message Boxes and input dialoges for various data types. - Return the view id when adding a new view. - Improve bindings for time and x/y plots. - Add function to set/change a curve color. - Add function to set a custom signal name. - Add function to set a custom plot curve name. - Add functions for getting the available config keys. * More verbose device info in the about dialog. * Plot view: - Make curve name editable and curve removable. - Fix the axis lock labels. * Many improvements for the python editor. * Add a find and replace dialog for the python editor. * Add a time window option to combine analog values when saving signals (Thanks to Cymaphore). * Support for the new "force_detect" connection parameter of libsigrok. * Update libsigrok for new devices and bugfixes. * Update manual for SmuView. * Many more bug fixes and improvements. 0.0.4 (2020-02-18) ------------------ For a detailed list of changes, please see the git commit history. * Embed a Python interpreter to automate measurements and device control. * Add an editor to create and modify Python scripts. * Add a command line parameter to load and execute Python scripts. * Countless layout and GUI improvements, for example: - Improve device control views. - Replace the knob in the control views with a slider widget. - Don't freeze the connection dialog when loading bluetooth LE devices. - Ability to add a single signal to a XY-plot. - Improve changing plot modes. - Make the plot markers box position configurable. - Nicer looking plot markers and better initial plot marker position. - Support multiple signals per data table view. - Add plot curve colors for various quantities and use random color for unknown quantities. - Use embedded mono space font for the value displays. - Stack views to save space. - Settings for line and symbol style in the plot config dialog. - Don't mess up the plot axis, when the signal value overflows. - Add function to save plots as image. * Add class AnalogSampleSignal to store sample via Python. * Add a view to apply sequences to (device) controls like the output voltage. * Create a manual for SmuView. * Many bug fixes. 0.0.3 (2019-04-04) ------------------ For a detailed list of changes, please see the git commit history. * Various layout improvements * Revert "feature" that only one signal can be added to fixed channels * Plot view: - Improve axis locking - Improve axis scale updates - Add the same signal only once * Commit spin box values only when enter is pressed or widget is leaved * Use a monospace font instead of the LCD font for various views * Add ConfigKey OFFSET 0.0.2 (2019-03-21) ------------------ For a detailed list of changes, please see the git commit history. * Add manpage * Add Fedora support (Thanks to gvegidy) * Plot view: - Curve colors - Better legend titles - Add AC/DC to curve unit names (axis labels) - Use one axis for data with the same quantity / quantity flags - Fix non working mouse wheel over axis * Use linear interpolation to combine signals with different time intervals: - Used in math channles - Used in x/y-plot view * Use session start timestamp as start timestamp for channels and signals * Fix bug when listing ConfigKey SAMPLERATE * Fix bugs in ui datatype widgets * Add GenericControlView * Add support for the demo device * Initially populate serial connections in the connection dialog * Add "addition with constant" math channel * Add "moving average" math channel * New device tree * Move "add device" and "add user device" to new device tree * Add start/stop aquisition to device tab * Add disconnect device in device tree * Add clear signal to device tree * Add configurabel and config keys to device tree * Add support for interleaved samples (demo device and oscilloscopes) * Auto resize (device) combo boxes 0.0.1 (2019-01-21) ------------------ * Initial release. ================================================ FILE: README ================================================ ------------------------------------------------------------------------------- README ------------------------------------------------------------------------------- The sigrok project aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types (such as logic analyzers, oscilloscopes, multimeters, and more). SmuView is a Qt-based source measurement unit GUI for sigrok. Status ------ SmuView is in a development state. Copyright and license --------------------- SmuView is licensed under the terms of the GNU General Public License (GPL), version 3 or later. While some individual source code files are licensed under the GPLv2+, and some files are licensed under the GPLv3+, this doesn't change the fact that the program as a whole is licensed under the terms of the GPLv3+ (e.g. also due to the fact that it links against GPLv3+ libraries). Please see the individual source files for the full list of copyright holders. Copyright notices ----------------- A copyright notice indicating a range of years, must be interpreted as having had copyrightable material added in each of those years. Example: Copyright (C) 2010-2013 Contributor Name is to be interpreted as Copyright (C) 2010,2011,2012,2013 Contributor Name Icons authors and licenses -------------------------- icons/information.svg: Bobarino https://en.wikipedia.org/wiki/File:Information.svg All other icons: Oxygen Icon Theme, GNU LGPLv3 Fonts licenses -------------- DejaVue Sana Mono: Bitstream Vera Fonts Copyright and Arev Fonts Copyright Mailing list ------------ https://lists.sourceforge.net/lists/listinfo/sigrok-devel IRC --- You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat. Website ------- https://github.com/knarfS/smuview ================================================ FILE: TODO ================================================ SmuView ======= ToDo clang-tidy --------------- - Unify readability-implicit-bool-conversion (AllowPointerConditions) -> " == nullptr" and " != nullptr" - readability-redundant-member-init.IgnoreBaseInCopyConstructors = 1 not always working! - src/ui/datatypes/doubledisplay.hpp void setup_ui(); // TODO: widgets::MonoFontDisplay also has setup_ui() - src/ui/datatypes/basewidget.hpp Q_SLOTS redesign? - src/ui/datatypes/doubledisplay.hpp: setup_ui() override, b/c of widgets::MonoFontDisplay. - bugprone-narrowing-conversions: Maybe use boost convert_to<>() -> see util.cpp, format_time_minutes() ToDo Release ------------ - Python binding for save CSV ToDo clazy ---------- - Disabled due to false positives: - connect-non-signal - incorrect-emit ToDo Scope ---------- - Fix ConfigKey sample_rate in UInt64Property::change_value(const QVariant qvar) ToDo ---- - Use util::Timestamp? - last_value_, min_value_, max_value_ -> std::atomic! - Plot: Sampling - Mutex for aquisition_state_? - Save all data (gnuplot, octave, ...) - add a saveutil.cpp - Configurable: - default check for getable/setable in get/set functions without assert(false)? - add button to sink/source/demodmm/measurement-ControlViews to control non default config keys. - Check getable/setable/listable also in the properties - Enhance ThresholdControl widget (e.g for re:load pro). Maybe a dedicated property? - Dedicated SvDoubleSpinBox and SvBoolButton? - Breeze icons action/labplot-* for plots? - Get fixed quantity signals from channel(/channel group) (q/qf/u is set in ch.meaning at the beginning). Implement in sigrok! - Implement something like ch.meanings for config keys in sigrok, so smuview known the quantity/unit of config keys. - Start timestamp on_update() -> device ctor -> timestamp = .0 - Start timestamp to session - Use new connect/disconnet variant whenever possible. - Use connect(obj, SIGNAL, SIGNAL) when possible (UIProxy/UIHelper) - Lookup: std::atomic, std::condition_variable, std::mutex - Remove "const Session &session" form ui::devices::* ctors and more?? - Maybe: In all ctors "Session &session" -> "const Session &session" - Resize the DockWidgets/Views to their best sizes (value panels minimum, plot views max, ...). Maybe with QMainWindow->resizeDocks, SizePolicy in QDockWidget does not work.... - boost::stacktrace, see pv - BaseDevice open()/close(): move aquisition stuff to HardwareDevice, move VirtualDevice stuff to BaseDevice - util.cpp: use cmath instead of math.h, but Timestamp is boost and only works with non std::? - Use AnalogBaseSignal in all(?) Channel classes, in View classes and in Plot classes - Remove Configurable and ConfigKey from data::properties::* - Session: shared_ptr or reference? - Missing msvcr100.dll (Windows Server 2016) SmuView/PulseView - DeviceTree: Button for expand/close all - Plot: Add gradient (dY/dX) to diff marker in plot marker box - Plot: Movable polt marker box. Maybe not possible b/c of missing stuff in qwt? - Plot: Highlight polt marker and plot marker box when mouse over (near by for marker) - Plot: Class for marker -> qwt curve tracker playground - SequenceView: Handle headers (order of columns, time abs/rel) in CSV - SequenceView: Handle units (ms, s, h, ...) in CSV - PlotView: Make resolution and dpi configurable when saving as image - Implement AnalogXYDataSignal (for python automatisation)? See scope support? - MonoFontDisplay/PowerPanelView: To small for neg. values (esp. W/Ah/Wh with 6632B). B/c of the decimal point? - Remove LCDDisplay? - Reduce/Simplyfy BaseDevice name methods - Bindings: Implement add_math_channel() - Can we inherit MathChannel from UserChannel? - Unify name of static (const) variables - Remove devicetab.hpp time_unit_ - Cycle: replace BaseChannel::parent_device_ with device_id_, and so on.... - main.cpp: Remove while loop. - AnalogTimeSignal: Move signal_start_timestamp_ to BaseSignal - *Signal: Clean up Signal classes - Add a way to change a channel/signal name in the GUI. Connect BaseSignal::name_changed() signal. - Add a way to change the plot curves that have a default color to the new default color configured in the PlotConfigDialog. E.g. with a singleton to connect a dialog to multiple curves from different plots. - Use singleton for SettingsManager instead of static methods? - Use deleteLater() instead of delete - Add AddPlotCurveDialog with channel and signals. - Manage axes in plot.cpp: Per Quantity and not per curve! Test: Remove all curves and add a new one.... - UiHelper/UiProxy: Remove tab_id? - SettingsManager: Method for getting the QSettings object and maybe load an user defined QSettings-file. - example_multiplexer.py: Checkboxes in control views don't change. - Keyboard shortcuts (Ctrl-Q -> Close App, Ctrl-W -> Close tab) Done ---- - Check in connect.cpp if GPIB is part of build info (libsigrok -> backend.c) - Everey device must have its own session: 1. Performance: With one sessions the aquisition functions are processed in sequence. The HP 3478A f.e. takes for ever, and so the other devices to. There are NO parallel requests. 2. Adding new device to a running session: Chicken/Egg problem! Dev->open() tries to start aquisition, but device is not added to session yet (cxx binding). session->add_device() tries to start aquisition, but device is not opened yet... 3. Solves the sr_session_source_add_internal() dublicate key problem. 4. Propably use the multidevice API functions from linux gpib in scpi_libgpib.c - Better detection of common_time_data_ (when in frame). Who is this possible when in init_signal() and not afterwards when in feed_in() - QIcon (On/Off/Dis) -> Led: State(On/Off), Mode(En/Dis) - ValueControl implement is_value_setable_/is_value_getable_ and disable/enable controls - Save signals as CSV - Math channels (for Power, resistance, Wh, Ah) - Correlation between time bases (common, same count+fixed diff, "random") in (Analog)Data - Configurable: - list_config - default check for listable in list function without assert(false) - Remove unused specific functions - min/max/step in views when not listable - Add functions for datatypes -> determin what list_* function to use - make the sink/source/demodmm/measurement-ControlViews more generic - signals for controls (views) to work with processing - Cache stuff - Missing sigok::ConfigKey::OFFSET - get_mq / set_mq problem with template? - Move is_controllable() to tab/view? Make more generic... -> Generic is_controllable() function, and specialized function in viewhelper.cpp - namespace src/data/datautil.hpp - Refactor ui class members CamelCase -> _ _ _ - Replace all " Q_OBJECT" with " Q_OBJECT" (4 spaces -> 1 tab) - Replace other " " -> " " (4 spaces -> 1 tab) - Autoscale plot. Plot doesn't always auto-update scale (rlp without load) - Remove "hp3478a" from gpib_libgpib_name_ and list all available names. - DataView: Add toolbar with "StayAtBottom" switchable action. - Remove all items in combo box when parent has changed and setup_ui() is recalled? see ConfigKeyComboBox and ConfigurableComboBox - Refactore: - src/data/datautil.cpp -> src/data/ - src/devices/deviceutil.cpp -> src/data/ - src/devices/properties/ -> src/data/ - DataView: add signal (into this table) action - static unsigned int device_counter; is changing - License headers are not the same - Session: move init to main.cpp - Bindings: Binding for setting the color of a plot curve - Bindings: Set name for new signal? e.g. f(x, y, name=nullptr) - Add "E" for detecting energy (Wh) channels - ValueDisplay SLOTS: CallByRef &? - Connection dialog thread - BaseChannel::add_signal(), L. 156, Korad PSU TODO - Save session - Segfault when closing/disconnet devices via UI/DevicesView Won't fix --------- - QWT Multiaxis - Refactore: - src/data -> src/signals - src/data/properties/* on_value_changed(gvar) -> on_value_changed(const gvar) - *.hpp: virtual f_name() = 0; -> virtual f_name() = nullptr; libsigrok ========= - Add GPIB address to libgpib:conn as paramenter - Re:load Pro: when acquisition is running and we send a command (e.g. "set xxx"), there are 2 listeners (1st send_cmd() (<-serial_write_blocking) and 2nd handle_new_data() (<-serial_read_nonblocking)) - Make devc->acquisition_running thread safe, bool is NOT thread safe! - Re:load Pro: when 2 responses are send fast afer each other, serial_read_nonblocking reads them both and the \r\n gets in beetween - Re:load Pro: sometimes only a ^M is send from the rlp - Re:load Pro: Sometimes the responses are mixed up (2 listeners??, Re:load Pro not keeping up??) - Re:load Pro: how to clear otp_active and uvc_active? - hp3478a: device isn't closed after stopping acquisition - scpi-pps: protocol.c -> scpi_pps_receive_data() uses double, but output (e.g. analog) and DF_ANALOG receivers use hardcoded float. Using sr_analog_encoding.unitsize and .is_float to define the datatype (and size) (.unitsize = sizeof(double) (or by lurchi_: .unitsize=8 + float is double)) in the receiver functions! - dev_acquisition_start() has to do first measurement (3478a, scpi-pps, reload(?)), because 1. DF_HEADER, 2. trigger measurement - If not: 3378a use scpi_cmd_resp() for use of mutex (wrt+read); - Can't add 2x scpi_libgpib devices: scpi_libgpib.c -> scpi_gpib_source_add() calls session.c -> sr_session_source_add() with fd = -1. This calls session.c -> sr_session_fd_source_add() with key = GINT_TO_POINTER(fd) and finally throws an error in session.c -> sr_session_source_add_internal() because "Event source with key %p already exists." - scpi-pps: when in acquisition send one write ":MEAS:VOLT?;:MEAS:CURR?" instead of 2 writes when possible - Add channel.meaning.mq, channel.meaning.mqflags, channel.meaning.unit (+channel.fixed) to PPU and load channels, to make it easy to init signals in smuview - Add missing units: sigrok::Unit::DECIBEL - scpi-pps/hp66xxx: Return always TRUE for OVP_ENABLED - hp3478a: spec_digits and enc_digits are used in the wrong way! - Fix UserDevice::open() and ::close() -> virtual in BaseDevice and empty in UserDevice WIP --- - Change key naming (see load + psu wiki pages) Done ---- - Re:load Pro: send "monitor 0" when device is scaned to stop eventually running monitoring and version can be read properly - Re:load Pro: send "monitor 0" when device is closed - Wrong string to float/double conversion (because of locale settings): - dmm/m2110.c: 2x sscanf((const char *)buf, "%f", &val) == 1 -> sr_atof_ascii((const char *)buf, &val) == SR_OK - motech-lps-30x/protocol.c: sr_atod(devc->buf, &dbl) - dmm/metex14.c: sscanf((const char *)&valstr, "%f", result) -> sr_atof_ascii((const char *)&valstr, result) - scpi/scpi.c: sr_atod(response, scpi_response) -> sr_atod_ascii(response, scpi_response) - hp3478a: acquisition doesn't stop (sigrok-cli/smuview must be killed) - Tests for convertion (st_ascii_tod) for locale "bugs" - use mutex around ibwrt() and ibrd() (scpi_libgpib.c) per device, so they are thread safe - use mutex around scpi_cmd_resp() and scpi_cmd() (helpers.c), so when a request has a response, they are thread safe. - Mutex of scpi/helpers.c: - find all ocurrence of (sr_scpi_send_variadic(), sr_scpi_send(), scpi_cmd(), scpi_cmd_resp(), rigol_ds_config_set(), ....) and check if thread safe - Any functions missing, that need the mutex - Valid for SCPI over something something? - Mutex of scpi_libgpib: Add to other transport variants (serial, tcp, ...), if so is the mutex in scpi/helpers.c enough? - scpi-pps: add otp/ovp/ocp to pps_profiles[] (or to channel(group)) for HP 663xx - HP3478A: Implement SPoll, - Add missing confg key sigok::ConfigKey::OFFSET - Provied SR_DF_META packets for the Re:load Pro and the HP3478A for changed values/states (e.g. output switched on/off, ovp, otp, ...) - Add mqflags (AC/DC) to channel_group_spec for scpi-pps - scpi-pps/hp66xxB: Don't send SCPI_CMD_REMOTE and SCPI_CMD_LOCAL when GPIB is used. Will result in error 602. - scpi-pps/hp66xxx: Check if SPoll is faster than sending *STB? / FAULT?, if so implement and use SPoll (or SRQ) when using GPIB. - scpi-pps's have channel groups but options/keys/_lists_ (voltage_target, ...) apply to device - Add missing units: sigrok::Unit::JOULE, sigrok::Unit::AMPEREHOUR, sigrok::Unit::COULOMB - HP-3478A: R4W -> R2W not working Won't fix --------- - Re:load Pro: Don't use "monitor 200", Instead "read" values every aquisition cycle with serial "read line blocking" and use a mutex around every read, write and write+read - Add missing units: sigrok::Unit::(HECTO)PASCAL Doku ==== - Link API to the psu and load wiki pages Re:load Pro Firmware ==================== - Add "output" command (returns "on" / "off") to determin in wich state the output is (git commit 448cf71, 558bc0e (part)) - Send "on" / "off" when output state was changed at the device directly - Send "uvlo xxx" when undervoltage threshold was changed at the device directly (git commit 224ab62) ================================================ FILE: appimagecraft.yml ================================================ version: 1 project: name: com.github.knarfs.smuview #version_command: git describe --tags build: cmake: extra_variables: # Taken from some other script - DISABLE_WERROR=y - ENABLE_TESTS=n - CMAKE_EXPORT_COMPILE_COMMANDS=y - PYBIND11_FINDPYTHON=ON raw_environment: # We have to pre-build some dependencies, and also want to ship our own custom Python environment # the CMake build system has been instructed to load the dependencies via pkg-config - PKG_CONFIG_PATH="$BUILD_DIR"/deps/lib/pkgconfig:"$BUILD_DIR"/AppDir/usr/conda/lib/pkgconfig:"$PKG_CONFIG_PATH" # Also we want to use a newer CMake version - PATH="$BUILD_DIR"/deps/bin:"$PATH" script: - |2 . AppDir/usr/conda/bin/activate echo "PKG_CONFIG_PATH (cmake): $PKG_CONFIG_PATH" scripts: pre_build: - pushd "$BUILD_DIR" - |2 # We need the Python environment before the build already, therefore we have to run the conda # plugin manually before the build #wget https://github.com/linuxdeploy/linuxdeploy-plugin-conda/raw/master/linuxdeploy-plugin-conda.sh wget https://github.com/knarfS/linuxdeploy-plugin-conda/raw/master/linuxdeploy-plugin-conda.sh # Skip the built-in cleanup as the build needs the development files (headers, libs, ...) export CONDA_SKIP_CLEANUP=1 export CONDA_SKIP_ADJUST_PATHS=1 chmod +x linuxdeploy-plugin-conda.sh ./linuxdeploy-plugin-conda.sh --appdir AppDir . AppDir/usr/conda/bin/activate - popd - |2 # Build libserialport and libsigrok for i in libserialport libsigrok; do git clone --depth=1 git://sigrok.org/"$i" pushd "$i" ./autogen.sh # Once libserialport is built, libsigrok can be linked against it export PKG_CONFIG_PATH="$BUILD_DIR"/deps/lib/pkgconfig:"$PKG_CONFIG_PATH" echo "PKG_CONFIG_PATH (pre_build): $PKG_CONFIG_PATH" ./configure --prefix="$BUILD_DIR"/deps make -j$(nprocs) make install popd done post_build: - |2 # Cleanup conda export CONDA_SKIP_INSTALL=1 export CONDA_SKIP_ADJUST_PATHS=0 ./linuxdeploy-plugin-conda.sh --appdir AppDir - |2 # Generate AppRun.sh scrip cat > "$BUILD_DIR"/AppRun.sh <<\EOF #! /bin/bash # Load miniconda environment . "$APPDIR"/usr/conda/etc/profile.d/conda.sh conda activate # Run SmuView exec "$APPDIR"/usr/bin/smuview "$@" EOF - chmod +x "$BUILD_DIR"/AppRun.sh - |2 # Set VERSION from CMake generated shell script source cmake-build/contrib/config_version.sh export VERSION=${SV_VERSION_STRING} appimage: linuxdeploy: plugins: - qt extra_args: --custom-apprun "$BUILD_DIR"/AppRun.sh raw_environment: - LD_LIBRARY_PATH="$LD_LIBRARY_PATH":"$BUILD_DIR"/deps/lib:"$BUILD_DIR"/AppDir/usr/conda/lib ================================================ FILE: config.h.in ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Alexandru Gagniuc * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONFIG_H #define CONFIG_H /* Application details */ #define SV_TITLE "@SV_TITLE@" #define SV_BIN_NAME "@PROJECT_NAME@" /* SmuView version information */ #define SV_VERSION_MAJOR @SV_VERSION_MAJOR@ #define SV_VERSION_MINOR @SV_VERSION_MINOR@ #define SV_VERSION_MICRO @SV_VERSION_MICRO@ #define SV_VERSION_SUFFIX "@SV_VERSION_SUFFIX@" #define SV_VERSION_STRING "@SV_VERSION_STRING@" #define SV_MANUAL_VERSION "@SV_MANUAL_VERSION@" /* Platform properties */ #cmakedefine HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS #define SV_GLIBMM_VERSION "@SV_GLIBMM_VERSION@" #define SV_PYBIND11_VERSION "@SV_PYBIND11_VERSION@" #define SV_PYTHON_VERSION "@SV_PYTHON_VERSION@" #endif // CONFIG_H ================================================ FILE: contrib/config_version.sh.in ================================================ #!/bin/sh ## ## This file is part of the SmuView project. ## ## Copyright (C) 2021 Frank Stettner ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## # Application details export SV_TITLE="@SV_TITLE@" export SV_BIN_NAME="@PROJECT_NAME@" # SmuView version information export SV_VERSION_MAJOR=@SV_VERSION_MAJOR@ export SV_VERSION_MINOR=@SV_VERSION_MINOR@ export SV_VERSION_MICRO=@SV_VERSION_MICRO@ export SV_VERSION_SUFFIX="@SV_VERSION_SUFFIX@" export SV_VERSION_STRING="@SV_VERSION_STRING@" export SV_MANUAL_VERSION="@SV_MANUAL_VERSION@" # Platform properties export SV_GLIBMM_VERSION="@SV_GLIBMM_VERSION@" export SV_PYBIND11_VERSION="@SV_PYBIND11_VERSION@" export SV_PYTHON_VERSION="@SV_PYTHON_VERSION@" ================================================ FILE: contrib/org.sigrok.SmuView.appdata.xml ================================================ org.sigrok.SmuView.desktop CC0-1.0 GPL-3.0+ SmuView Power supplies, electronic loads, multimeters and more

SmuView is a Qt based source measure unit GUI for sigrok.

Features:

  • Remote control devices
  • Data acquisition
  • Multiple devices at the same time
  • Math channels
  • Data export as CSV
https://sigrok.org/wiki/SmuView https://github.com/knarfS/smuview/issues https://sigrok.org/wiki/FAQ https://sigrok.org/wimg/8/88/Sv_with_psu.png smuview org.sigrok.SmuView.desktop
================================================ FILE: contrib/org.sigrok.SmuView.desktop ================================================ [Desktop Entry] # Creative Commons CC0 1.0 Universal (CC0-1.0, Public Domain Dedication). Name=SmuView GenericName=A sigrok GUI for power supplies, loads and measurement devices Categories=Development;Electronics; Comment=SmuView is a Qt based source measure unit GUI for sigrok. Exec=smuview Icon=smuview Type=Application ================================================ FILE: contrib/smuview.spec ================================================ # .SPEC-file to package RPMs for Fedora # download and build like this # (you need an rpmbuild dir, can be created with rpmdev-setuptree) # # cp smuview.spec $HOME/rpmbuild/SPECS # cd $HOME/rpmbuild # spectool -g -R SPECS/smuview.spec # rpmbuild -ba SPECS/smuview.spec Summary: SmuView is a Qt based source measure unit GUI for sigrok. Name: smuview Version: 0.0.1 Release: 1%{?dist} License: GPLv3 URL: https://github.com/knarfS/smuview Source: https://github.com/knarfS/%{name}/archive/%{name}-%{version}.tar.gz BuildRequires: libsigrok-cxx-devel BuildRequires: qwt-qt5-devel BuildRequires: qt5-devel BuildRequires: qt5-qtsvg-devel BuildRequires: qt5-qtbase-devel BuildRequires: glibmm24-devel BuildRequires: boost-devel BuildRequires: rubygem-asciidoctor %description The sigrok project aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types (such as logic analyzers, oscilloscopes, multimeters, and more). SmuView is a Qt-based source measurement unit GUI for sigrok. %prep %autosetup -n %{name}-%{name}-%{version} %build %cmake . %make_build make manual-html %install %make_install %files %{!?_licensedir:%global license %%doc} %license COPYING %doc README INSTALL stuff %{_docdir}/%{name}/images/sv_with_load.png %{_docdir}/%{name}/manual.html %{_bindir}/smuview %{_mandir}/man1/%{name}.* %{_datadir}/applications/org.sigrok.SmuView.desktop %{_datadir}/metainfo/org.sigrok.SmuView.appdata.xml %{_datadir}/icons/hicolor/48x48/apps/smuview.png %{_datadir}/icons/hicolor/scalable/apps/smuview.svg ================================================ FILE: contrib/smuview_cross.nsi.in ================================================ ## ## This file is part of the SmuView project. ## ## Copyright (C) 2013-2014 Uwe Hermann ## Copyright (C) 2019-2021 Frank Stettner ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## # # This file is used to create the Smuiew Windows installer via NSIS. # It is meant for use in a cross-compile setup (not for native builds). # See the 'sigrok-cross-mingw-smuview' script in the sigrok-util repo for details. # # NSIS documentation: # http://nsis.sourceforge.net/Docs/ # http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html # # Include the "Modern UI" header, which gives us the usual Windows look-n-feel. !include "MUI2.nsh" # --- Global stuff ------------------------------------------------------------ # Installer/product name. Name "SmuView" # Filename of the installer executable. !if "$%TARGET%" != "" !define TARGET "-$%TARGET%" !else !define TARGET "" !endif OutFile "SmuView-@SV_VERSION_STRING@${TARGET}-installer.exe" # Where to install the application. !ifdef PE64 InstallDir "$PROGRAMFILES64\sigrok\SmuView" !else InstallDir "$PROGRAMFILES\sigrok\SmuView" !endif # Request admin privileges for Windows Vista and Windows 7. # http://nsis.sourceforge.net/Docs/Chapter4.html RequestExecutionLevel admin # Local helper definitions. !define REGSTR "Software\Microsoft\Windows\CurrentVersion\Uninstall\SmuView" # --- MUI interface configuration --------------------------------------------- # Use the following icon for the installer EXE file. !define MUI_ICON "@PROJECT_SOURCE_DIR@/icons/smuview.ico" # Show a nice image at the top of each installer page. !define MUI_HEADERIMAGE # Don't automatically go to the Finish page so the user can check the log. !define MUI_FINISHPAGE_NOAUTOCLOSE # Upon "cancel", ask the user if he really wants to abort the installer. !define MUI_ABORTWARNING # Don't force the user to accept the license, just show it. # Details: http://trac.videolan.org/vlc/ticket/3124 !define MUI_LICENSEPAGE_BUTTON $(^NextBtn) !define MUI_LICENSEPAGE_TEXT_BOTTOM "Click Next to continue." # Path where the cross-compiled sigrok tools and libraries are located. # Change this to where-ever you installed libsigrok.a and so on. !define CROSS "@CMAKE_INSTALL_PREFIX@" # Defines for WinAPI SHChangeNotify call. !define SHCNE_ASSOCCHANGED 0x8000000 !define SHCNF_IDLIST 0 # --- Functions/Macros -------------------------------------------------------- # Inspired by http://nsis.sourceforge.net/Create_Internet_Shorcuts_during_installation !Macro "CreateURL" "URLFile" "URLSite" "URLDesc" WriteINIStr "$INSTDIR\${URLFile}.URL" "InternetShortcut" "URL" "${URLSite}" CreateShortCut "$SMPROGRAMS\sigrok\SmuView\${URLFile}.lnk" "$INSTDIR\${URLFile}.url" "" \ "$INSTDIR\smuview.exe" 0 "SW_SHOWNORMAL" "" "${URLDesc}" !MacroEnd # --- MUI pages --------------------------------------------------------------- # Show a nice "Welcome to the ... Setup Wizard" page. !insertmacro MUI_PAGE_WELCOME # Show the license of the project. !insertmacro MUI_PAGE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING" # Show a screen which allows the user to select which components to install. !insertmacro MUI_PAGE_COMPONENTS # Allow the user to select a different install directory. !insertmacro MUI_PAGE_DIRECTORY # Perform the actual installation, i.e. install the files. !insertmacro MUI_PAGE_INSTFILES # Show a final "We're done, click Finish to close this wizard" message. !insertmacro MUI_PAGE_FINISH # Pages used for the uninstaller. !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH # --- MUI language files ------------------------------------------------------ # Select an installer language (required!). !insertmacro MUI_LANGUAGE "English" # --- Default section --------------------------------------------------------- Section "SmuView (required)" Section1 # This section is gray (can't be disabled) in the component list. SectionIn RO # Install the file(s) specified below into the specified directory. SetOutPath "$INSTDIR" # License file. File "@PROJECT_SOURCE_DIR@/COPYING" # SmuView (statically linked, includes all libs). File "${CROSS}/bin/smuview.exe" # Zadig (used for installing WinUSB drivers). File "${CROSS}/zadig.exe" File "${CROSS}/zadig_xp.exe" # Python File "${CROSS}/python34.dll" File "${CROSS}/python34.zip" SetOutPath "$INSTDIR\share" # Generate the uninstaller executable. WriteUninstaller "$INSTDIR\Uninstall.exe" # Create a sub-directory in the start menu. CreateDirectory "$SMPROGRAMS\sigrok" CreateDirectory "$SMPROGRAMS\sigrok\SmuView" # Create a shortcut for the SmuView application. SetOutPath "$INSTDIR" CreateShortCut "$SMPROGRAMS\sigrok\SmuView\SmuView.lnk" \ "$INSTDIR\smuview.exe" "" "$INSTDIR\smuview.exe" \ 0 SW_SHOWNORMAL \ "" "Open-source, portable sigrok GUI" # Create a shortcut for the SmuView application running in debug mode. CreateShortCut "$SMPROGRAMS\sigrok\SmuView\SmuView (Debug).lnk" \ "$INSTDIR\smuview.exe" "-l 5" "$INSTDIR\smuview.exe" \ 0 SW_SHOWNORMAL \ "" "Open-source, portable sigrok GUI (debug log level)" # Create a shortcut for the uninstaller. CreateShortCut "$SMPROGRAMS\sigrok\SmuView\Uninstall SmuView.lnk" \ "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 \ SW_SHOWNORMAL "" "Uninstall SmuView" # Create a shortcut for the Zadig executable. CreateShortCut "$SMPROGRAMS\sigrok\SmuView\Zadig (SmuView).lnk" \ "$INSTDIR\zadig.exe" "" "$INSTDIR\zadig.exe" 0 \ SW_SHOWNORMAL "" "Zadig (SmuView)" # Create a shortcut for the Zadig executable (for Win XP). CreateShortCut "$SMPROGRAMS\sigrok\SmuView\Zadig (SmuView, Win XP).lnk" \ "$INSTDIR\zadig_xp.exe" "" "$INSTDIR\zadig_xp.exe" 0 \ SW_SHOWNORMAL "" "Zadig (SmuView, Win XP)" # Create shortcuts to the HTML and PDF manuals, respectively. !InsertMacro "CreateURL" "SmuView HTML manual" "https://knarfs.github.io/doc/smuview/@SV_MANUAL_VERSION@/manual.html" "SmuView HTML manual" !InsertMacro "CreateURL" "SmuView PDF manual" "https://knarfs.github.io/doc/smuview/@SV_MANUAL_VERSION@/manual.pdf" "SmuView PDF manual" !InsertMacro "CreateURL" "SmuView Python Bindings API" "https://knarfs.github.io/doc/smuview/@SV_MANUAL_VERSION@/python_bindings_api.html" "SmuView Python Bindings API" # Create registry keys for "Add/remove programs" in the control panel. WriteRegStr HKLM "${REGSTR}" "DisplayName" "SmuView" WriteRegStr HKLM "${REGSTR}" "UninstallString" \ "$\"$INSTDIR\Uninstall.exe$\"" WriteRegStr HKLM "${REGSTR}" "InstallLocation" "$\"$INSTDIR$\"" WriteRegStr HKLM "${REGSTR}" "DisplayIcon" \ "$\"$INSTDIR\smuview.ico$\"" WriteRegStr HKLM "${REGSTR}" "Publisher" "sigrok" WriteRegStr HKLM "${REGSTR}" "HelpLink" \ "http://sigrok.org/wiki/SmuView" WriteRegStr HKLM "${REGSTR}" "URLUpdateInfo" \ "https://github.com/knarfS/smuview/releases" WriteRegStr HKLM "${REGSTR}" "URLInfoAbout" "https://github.com/knarfS/smuview" WriteRegStr HKLM "${REGSTR}" "DisplayVersion" "@SV_VERSION_STRING@" WriteRegStr HKLM "${REGSTR}" "Contact" \ "sigrok-devel@lists.sourceforge.org" WriteRegStr HKLM "${REGSTR}" "Comments" \ "This is a Qt based sigrok GUI." # Display "Remove" instead of "Modify/Remove" in the control panel. WriteRegDWORD HKLM "${REGSTR}" "NoModify" 1 WriteRegDWORD HKLM "${REGSTR}" "NoRepair" 1 SectionEnd Section "Example scripts" Section2 # Example smuscript *.py files. SetOutPath "$INSTDIR\examples" File "${CROSS}/share/smuscript/example_characterize_battery.py" File "${CROSS}/share/smuscript/example_characterize_psu.py" File "${CROSS}/share/smuscript/example_characterize_psu_2.py" File "${CROSS}/share/smuscript/example_device_properties.py" File "${CROSS}/share/smuscript/example_ui.py" File "${CROSS}/share/smuscript/example_user_channel.py" File "${CROSS}/share/smuscript/generate_documentation.py" File "${CROSS}/share/smuscript/python_version.py" # Create a shortcut for the example scripts folder. CreateShortCut "$SMPROGRAMS\sigrok\SmuView\Examples (SmuScript).lnk" \ "$INSTDIR\examples" "" "$INSTDIR\examples" 0 \ SW_SHOWNORMAL "" "" SectionEnd # --- Uninstaller section ----------------------------------------------------- Section "Uninstall" # Always delete the uninstaller first (yes, this really works). Delete "$INSTDIR\Uninstall.exe" # Delete the application, the application data, and related libs. Delete "$INSTDIR\COPYING" Delete "$INSTDIR\smuview.exe" Delete "$INSTDIR\zadig.exe" Delete "$INSTDIR\zadig_xp.exe" Delete "$INSTDIR\python34.dll" Delete "$INSTDIR\python34.zip" # Delete the URL files for the manual. #Delete "$INSTDIR\SmuView HTML manual.url" #Delete "$INSTDIR\SmuView PDF manual.url" # Delete the example *.py files. RMDir /r "$INSTDIR\examples\*" # Delete the install directory and its sub-directories. RMDir "$INSTDIR\share" RMDir "$INSTDIR\examples" RMDir "$INSTDIR" # Delete the links from the start menu. Delete "$SMPROGRAMS\sigrok\SmuView\SmuView.lnk" Delete "$SMPROGRAMS\sigrok\SmuView\SmuView (Debug).lnk" Delete "$SMPROGRAMS\sigrok\SmuView\Uninstall SmuView.lnk" Delete "$SMPROGRAMS\sigrok\SmuView\Zadig (SmuView).lnk" Delete "$SMPROGRAMS\sigrok\SmuView\Zadig (SmuView, Win XP).lnk" Delete "$SMPROGRAMS\sigrok\SmuView\Examples (SmuScript).lnk" # Delete the links to the manual. Delete "$SMPROGRAMS\sigrok\SmuView\SmuView HTML manual.lnk" Delete "$SMPROGRAMS\sigrok\SmuView\SmuView PDF manual.lnk" Delete "$SMPROGRAMS\sigrok\SmuView\SmuView Python Bindings API.lnk" # Delete the sub-directory in the start menu. RMDir "$SMPROGRAMS\sigrok\SmuView" RMDir "$SMPROGRAMS\sigrok" # Delete the registry key(s). DeleteRegKey HKLM "${REGSTR}" SectionEnd # --- Component selection section descriptions -------------------------------- LangString DESC_Section1 ${LANG_ENGLISH} "This installs the SmuView sigrok GUI and all required libraries." LangString DESC_Section2 ${LANG_ENGLISH} "This installs some example script files (SmuScript) that you can use to try out the features SmuView has to offer." !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${Section1} $(DESC_Section1) !insertmacro MUI_DESCRIPTION_TEXT ${Section2} $(DESC_Section2) !insertmacro MUI_FUNCTION_DESCRIPTION_END ================================================ FILE: cppcheck-suppressions.xml ================================================ * */build/* * */external/pybind11_2.11_dev1/* * */external/pybind11_2.9.2/* * */external/QCodeEditor/* * */external/QtFindReplaceDialog/* unsafeClassCanLeak nullPointer */devices/configurable.hpp configurable unusedFunction useStlAlgorithm useInitializationList uninitMemberVar ================================================ FILE: doc/smuview.1 ================================================ .TH SMUVIEW 1 "Januray 6, 2021" .SH "NAME" SmuView \- Qt-based source measure unit GUI for sigrok .SH "SYNOPSIS" .B smuview \fR[\fBOPTIONS\fR] .SH "DESCRIPTION" .B SmuView is a cross-platform Qt-based GUI for the .B sigrok software suite for test and measurement equipment such as power supplies, electronic loads, multimeters and more. .SH "OPTIONS" .B SmuView has very few command line options, as most configuration elements are available from the GUI itself. .TP .BR "\-l, \-\-loglevel " Set the libsigrok loglevel. The higher the number, the more debug output will be printed. The dafault loglevel is 2 (Warnings), valid loglevels are: .sp \fB0\fP None .br \fB1\fP Error .br \fB2\fP Warnings .br \fB3\fP Informational .br \fB4\fP Debug .br \fB5\fP Spew .TP .B "\-h, \-?, \-\-help" Show a help text and exit. .TP .B "\-V, \-\-version" Show version information and exit. .TP .BR "\-d, \-\-driver " Specify the capture device to connect to. .TP .BR "\-D, \-\-dont\-scan " Usually SmuView automatically scans all drivers to find suitable devices during program startup. This option disables the auto-scan. .TP .B "\-c, \-\-clean" Don't restore previous settings (like views, position and size) on startup. .TP .B \-\-driver Users can either specify the option to pick a device at startup, or interactively scan for devices after SmuView has finished starting up. .TP .BR "\-s, \-\-script " Specify the SmuScript to load and execute. .SH "EXIT STATUS" .B SmuView exits with 0 on success, 1 on most failures. .SH "SEE ALSO" \fBsigrok\-cli\fP(1) .SH "BUGS" Please report any bugs via Github .RB "(" https://github.com/knarfS/smuview/issues ")" or on the sigrok\-devel mailing list .RB "(" sigrok\-devel@lists.souceforge.net ")." .SH "LICENSE" .B SmuView is covered by the GNU General Public License (GPL), version 3 or later. .SH "AUTHORS" Please see the individual source code files. .PP This manual page was written by Frank Stettner . It is licensed under the terms of the GNU GPL (version 3 or later). ================================================ FILE: doc/smuview_python_bindings.html ================================================ smuview API documentation

Module smuview

The SmuView 0.0.5-git-64e7b9d Python bindings.

The Python bindings are a scripting extension for SmuView to automate, setup and control complex or repetitive measurements, to process the incoming data and to create a standardized user interface for those measurements.

The smuview module offers two default object instances: Session and UiProxy. The Session object gives access to already connected devices or connects new devices. The returned device object can then be used to read data from the device or control the device. The UiProxy object instance is used to modify the user interface, for example adding tabs or views.

Here is a short example that connects the HP 3378A DMM via GPIB, reads a sample and creates the default tab for the device:

import smuview
import time

# Connect device.
dmm_dev = Session.connect_device("hp-3478a:conn=libgpib/hp3478a")[0]
# Sleep 1s to give the devices the chance to create signals.
time.sleep(1)
# Get last sample from channel P1.
sample = dmm_dev.channels()["P1"].actual_signal().get_last_sample(True)
print(sample)

# Add default tab for the DMM device.
UiProxy.add_device_tab(dmm_dev)

For more example scripts, please have a look into the smuscript folder.

Classes

class AnalogSampleSignal (*args, **kwargs)

A signal with key-value pairs.

Ancestors

Methods

def get_sample(self: AnalogSampleSignal, pos: int) ‑> Tuple[int, float]

Return the sample for the given position.

Parameters

pos : int
The position/number of the sample.

Returns

Tuple[int, float]
The sample with 1. the key and 2. the sample value.
def push_sample(self: AnalogSampleSignal, sample: None, pos: int, unit_size: int, digits: int, decimal_places: int)

Push a new sample to the signal.

Parameters

sample : float or double
The sample value.
pos : int
The key (position) of the new sample.
unit_size : int
The size of the floating point data type (float=4, double=8) for the sample argument.
digits : int
The total number of digits.
decimal_places : int
The number of decimal places.

Inherited members

class AnalogTimeSignal (*args, **kwargs)

A signal with time-value pairs.

Ancestors

Methods

def get_last_sample(self: AnalogTimeSignal, relative_time: bool) ‑> Tuple[float, float]

Return the last sample of the signal.

Parameters

relative_time : bool
When True, the returned timestamp is relative to the start of the SmuView session.

Returns

Tuple[float, float]
The sample with 1. timestamp in milliseconds and 2. the sample value.
def get_sample(self: AnalogTimeSignal, pos: int, relative_time: bool) ‑> Tuple[float, float]

Return the sample at the given position.

Parameters

pos : int
The position/number of the sample.
relative_time : bool
When True, the returned timestamp is relative to the start of the SmuView session.

Returns

Tuple[float, float]
The sample with 1. timestamp in milliseconds and 2. the sample value.
def push_sample(self: AnalogTimeSignal, sample: None, timestamp: float, unit_size: int, digits: int, decimal_places: int)

Push a new sample to the signal.

Parameters

sample : float or double
The sample value.
timestamp : float
The absolute timestamp in milliseconds.
unit_size : int
The size of the floating point data type (float=4, double=8) for the sample argument.
digits : int
The total number of digits.
decimal_places : int
The number of decimal places.

Inherited members

class BaseChannel (*args, **kwargs)

The base class for all channel types.

Ancestors

  • pybind11_builtins.pybind11_object

Subclasses

Methods

def actual_signal(self: BaseChannel) ‑> BaseSignal

Return the actual signal of the channel.

Returns

BaseSignal
The actual signal object.
def add_signal(self: BaseChannel, quantity: Quantity, quantity_flags: Set[QuantityFlag], unit: Unit, custom_name: str = '') ‑> BaseSignal

Add a new signal to the channel.

Parameters

quantity : Quantity
The Quantity of the new signal.
quantity_flags : Set[QuantityFlag]
The QuantityFlags of the new signal.
unit : Unit
The Unit of the new signal.
custom_name : str
A custom name for the new signal. If empty (default), the signal name will be automatically generated.

Returns

BaseSignal
The new signal object.
def name(self: BaseChannel) ‑> str

Return the name of the channel.

Returns

str
The name of the channel.
def signals(self: BaseChannel) ‑> List[BaseSignal]

Return all signals of the channel.

Returns

List[BaseSignal]
All signals of the channel.
class BaseDevice (*args, **kwargs)

The base class for all device types.

Ancestors

  • pybind11_builtins.pybind11_object

Subclasses

Methods

def add_user_channel(self: BaseDevice, channel_name: str, channel_group_name: str) ‑> UserChannel

Add a new user channel to the device.

Parameters

channel_name : str
The name of the new user channel.
channel_group_name : str
The name of the channel group where to create the user channel. Can be empty.

Returns

UserChannel
The new user channel object.
def channels(self: BaseDevice) ‑> Dict[str, BaseChannel]

Return all channels of the device.

Returns

Dict[str, BaseChannel]
A Dict where the key is the id of the channel and the value is the channel object.
def configurables(self: BaseDevice) ‑> Dict[str, Configurable]

Return all configurables of the device.

Returns

Dict[str, Configurable]
A Dict where the key is the id of the Configurable and the value is the Configurable object.
def id(self: BaseDevice) ‑> str

Return the unique id of the device.

Returns

str
The id of the device.
def name(self: BaseDevice) ‑> str

Return the name of the device.

Returns

str
The name of the device.
class BaseSignal (*args, **kwargs)

The base class for all signal types.

Ancestors

  • pybind11_builtins.pybind11_object

Subclasses

Methods

def name(self: BaseSignal) ‑> str

Return the name of the signal.

Returns

str
The name of the signal.
def sample_count(self: BaseSignal) ‑> int

Return the number of samples of the signal.

Returns

int
The number of samples.
def set_name(self: BaseSignal, arg0: str)

Set a custom name for the signal.

Parameters

custom_name : str
A custom name for the signal. If empty, the signal name will be automatically generated.
class ConfigKey (...)

Enum of all available config keys for controlling a device.

init(self: smuview.ConfigKey, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var ADCPowerlineCycles

Number of powerline cycles for ADC integration time.

var Amplitude

Amplitude of a source without strictly-defined ConfigKey.MeasuredQuantity.

var Averaging

Averaging.

var AvgSamples

The number of samples to be averaged over.

var BufferSize

Buffer size.

var CaptureFile

The capturefile to inject.

var CaptureRatio

The pre/post-trigger capture ratio.

var CaptureUnitSize

The capturefile unit size.

var CenterFrequency

Center frequency.

var ChannelConfig

Channel configuration.

var ClockEdge

Choice of clock edge for external clock (r or f).

var Coupling

Coupling.

var Current

Current current.

var CurrentLimit

Current limit.

var DataLog

The device has internal storage, into which data is logged.

var DataSource

Data source for acquisition.

var DeviceMode

Device mode for multi-function devices.

var Digits

The number of digits (e.g. for a DMM).

var Enabled

Enabling/disabling a channel (group).

var EquivCircuitModel

Equivalent circuit model.

var ExternalClock

Using an external clock.

var ExternalClockSource

Which external clock source to use if the device supports multiple external clock channels.

var Filter

Filter.

var HighResolution

High resolution mode.

var HoldMax

Max hold mode.

var HoldMin

Min hold mode.

var HorizTriggerPos

Horizontal trigger position.

var LogicThreshold

Logic threshold: predefined levels (TTL, ECL, CMOS, etc).

var LogicThresholdCustom

Logic threshold: custom numerical value.

var MeasuredQuantity

Measured quantity.

var NumAnalogChannels

The number of analog channels.

var NumHDiv

Number of horizontal divisions, as related to ConfigKey.TimeBase.

var NumLogicChannels

The number of logic channels.

var NumVDiv

Number of vertical divisions, as related to ConfigKey.VDiv.

var Offset

Offset of a source without strictly-defined ConfigKey.MeasuredQuantity.

var OutputFrequency

Output frequency in Hz.

var OutputFrequencyTarget

Output frequency target in Hz.

var OverCurrentProtectionActive

Status of over current protection (OCP).

var OverCurrentProtectionEnabled

Enabling/disable over current protection (OCP) feature.

var OverCurrentProtectionThreshold

Over current protection (OCP) threshold.

var OverTemperatureProtectionActive

Status of over temperature protection (OTP).

var OverTemperatureProtectionEnabled

Enabling/disable over temperature protection (OTP) feature.

var OverVoltageProtectionActive

Status of over voltage protection (OVP).

var OverVoltageProtectionEnabled

Enabling/disable over voltage protection (OVP) feature.

var OverVoltageProtectionThreshold

Over voltage protection (OVP) threshold.

var PatternMode

A pattern (pattern generator mode).

var PeakDetection

Peak detection.

var PowerOff

Power off the device.

var ProbeFactor

The probe factor.

var RLE

Run-length encoding (RLE).

var Range

The measurement range of a DMM or the output range of a power supply.

var Regulation

Channel regulation. CV, CC or UR, denoting constant voltage, constant current or unregulated. CC- denotes a power supply in current sink mode (e.g. HP 66xxB). An empty string is used when there is no regulation, e.g. the output is disabled.

var SampleInterval

The sample interval, in ms.

var Samplerate

The samplerate, in Hz.

var SessionFile

Session filename.

var SplMeasurementRange

Sound pressure level measurement range.

var SplWeightFreq

Sound pressure level frequency weighting.

var SplWeightTime

Sound pressure level time weighting.

var Swap

Swapping channels.

var TestMode

Self test mode.

var TimeBase

Time base.

var TriggerLevel

Trigger level.

var TriggerMatch

Trigger matches.

var TriggerPattern

The pattern for the logic trigger.

var TriggerSlope

The trigger slope.

var TriggerSource

Trigger source.

var UnderVoltageConditionActive

Status of under voltage condition (UVC).

var UnderVoltageConditionEnabled

Enabling/disable under voltage condition (UVC) feature.

var UnderVoltageConditionThreshold

Under voltage condition threshold (UVC).

var Unknown

Unknown config key.

var VDiv

Volts/div.

var Voltage

Current voltage.

var VoltageTarget

Maximum target voltage.

var VoltageThreshold

Logic low-high threshold range.

Static methods

def get_data_type(config_key: ConfigKey) ‑> DataType

Helper function to get the data type for a config key.

Parameters

config_key : ConfigKey
The config key.

Returns

DataType
The data type of the config key.

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.ConfigKey) -> int

class Configurable (*args, **kwargs)

A configurable for controlling a device with config keys.

Ancestors

  • pybind11_builtins.pybind11_object

Methods

def get_bool_config(self: Configurable, config_key: ConfigKey) ‑> bool

Return a boolean value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

bool
The bool value of the config key.
def get_double_config(self: Configurable, config_key: ConfigKey) ‑> float

Return a double value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

float
The float value of the config key.
def get_int_config(self: Configurable, config_key: ConfigKey) ‑> int

Return an integer value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

int
The int value of the config key.
def get_measured_quantity_config(self: Configurable, config_key: ConfigKey) ‑> Tuple[Quantity, Set[QuantityFlag]]

Return a measured quantity value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

Tuple[Quantity, Set[QuantityFlag]]
The measured quantity value of the config key.
def get_string_config(self: Configurable, config_key: ConfigKey) ‑> str

Return a string value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

str
The string value of the config key.
def get_uint_config(self: Configurable, config_key: ConfigKey) ‑> int

Return an unsigned integer value from the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to get.

Returns

int
The (unsigned) int value of the config key.
def getable_configs(self: Configurable) ‑> Set[ConfigKey]

Return all getable config keys.

Returns

List[ConfigKey]
All getable config keys.
def listable_configs(self: Configurable) ‑> Set[ConfigKey]

Return all listable config keys.

Returns

List[ConfigKey]
All listable config keys.
def name(self: Configurable) ‑> str

Return the name of the configurable.

Returns

str
The name of the configurable.
def set_config(*args, **kwargs)

Overloaded function.

  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: bool) -> None

Set a boolean value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : bool
The bool value to set.
  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: int) -> None

Set an integer value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : int
The int value to set.
  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: int) -> None

Set an unsigned integer value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : int
The (unsigned) int value to set.
  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: float) -> None

Set a double value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : float
The float value to set.
  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: str) -> None

Set a string value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : str
The string value to set.
  1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: Tuple[smuview.Quantity, Set[smuview.QuantityFlag]]) -> None

Set a measured quantity value to the given config key.

Parameters

config_key : ConfigKey
The ConfigKey to set.
value : Tuple[Quantity, Set[QuantityFlag]]
The measured quantity value to set.
def setable_configs(self: Configurable) ‑> Set[ConfigKey]

Return all setable config keys.

Returns

List[ConfigKey]
All setable config keys.
class DataType (...)

Enum of all available data types.

init(self: smuview.DataType, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var Bool

Bool

var Double

Double

var DoubleRange

DoubleRange

var Int32

Int32

var KeyValue

KeyValue

var MQ

MQ

var RationalPeriod

RationalPeriod

var RationalVolt

RationalVolt

var String

String

var UInt64

UInt64

var UInt64Range

UInt64Range

var Unknown

Unknown

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.DataType) -> int

class DockArea (...)

Enum of all possible docking locations for a view.

init(self: smuview.DockArea, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var BottomDockArea

Dock to the bottom dock area.

var LeftDocktArea

Dock to the left dock area.

var RightDockArea

Dock to the right dock area.

var TopDockArea

Dock to the top dock area.

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.DockArea) -> int

class HardwareChannel (*args, **kwargs)

An actual hardware channel

Ancestors

Inherited members

class HardwareDevice (*args, **kwargs)

An actual hardware device.

Ancestors

Inherited members

class PyStreamBuf (...)

Redirect all Python output to a SmuView console. This class is for internal SmuView use only!

init(self: smuview.PyStreamBuf, arg0: str, arg1: str) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Instance variables

var closed

True if the stream is closed.

var encoding

The name of the encoding that is used.

var errors

The error setting of the decoder or encoder.

Methods

def close(self: PyStreamBuf)

Flush and close this stream.

def fileno(self: PyStreamBuf) ‑> int

Raises an OSError, because PyStreamBuf doesn't use a file descriptor.

def flush(self: PyStreamBuf)

Flush the write buffers of the stream.

def isatty(self: PyStreamBuf) ‑> bool

Always returns False.

def read(self: PyStreamBuf, size: int) ‑> str

Raises an OSError, because PyStreamBuf is write only.

def readable(self: PyStreamBuf) ‑> bool

Always returns False.

def readline(self: PyStreamBuf, size: int) ‑> str

Raises an OSError, because PyStreamBuf is write only.

def readlines(self: PyStreamBuf, hint: int) ‑> List[str]

Raises an OSError, because PyStreamBuf is write only.

def seek(self: PyStreamBuf, offset: int, whence: int) ‑> int

Raises an OSError, because PyStreamBuf is not seekable.

def seekable(self: PyStreamBuf) ‑> bool

Always returns False. PyStreamBuf is not seekable atm.

def tell(self: PyStreamBuf) ‑> int

Raises an OSError, because PyStreamBuf is not seekable.

def truncate(self: PyStreamBuf, size: int) ‑> int

Raises an OSError, because PyStreamBuf is not seekable.

def writable(self: PyStreamBuf) ‑> bool

Always return True.

def write(self: PyStreamBuf, s: str) ‑> int

Write the string s to the stream and return the number of characters written.

def writelines(self: PyStreamBuf, lines: List[str])

Write a list of lines to the stream.

class Quantity (...)

Enum of all available quantities.

init(self: smuview.Quantity, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var ApparentPower

Apparent power

var Capacitance

Capacitance

var CarbonMonoxide

Carbon monoxide

var Conductance

Conductance

var Continuity

Continuity

var Count

Count

var Current

Current

var Difference

Difference from reference value.

var DissipationFactor

Dissipation factor

var DutyCyle

DutyCyle

var ElectricCharge

Electric charge

var Energy

Energy (also Work)

var Frequency

Frequency

var Gain

Gain (a transistor's gain, or hFE, for example).

var HarmonicRatio

Harmonic ratio

var Mass

Mass

var ParallelCapacitance

Parallel capacitance

var ParallelInductance

Parallel inductance

var ParallelResistance

Parallel resistance

var PhaseAngle

Phase angle

var Power

Electrical power, usually in W, or dBm.

var PowerFactor

Power factor

var Pressure

Pressure

var PulseWidth

PulseWidth

var QualityFactor

Quality factor

var RelativeHumidity

Relative humidity

var Resistance

Resistance

var SeriesCapacitance

Series capacitance

var SeriesInductance

Series inductance

var SeriesResistance

Series resistance

var SoundPressureLevel

Logarithmic representation of sound pressure relative to a reference value.

var Temperature

Temperature

var Time

Time

var Unknown

Unknown

var Voltage

Voltage

var WindSpeed

Wind speed

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.Quantity) -> int

class QuantityFlag (...)

Enum of all available quantity flags.

init(self: smuview.QuantityFlag, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var AC

Alternating current.

var Autorange

Device is in autoranging mode.

var Avg

Device is in average mode, averaging upon each new value.

var DC

Direct current.

var Diode

Value is voltage drop across a diode, or NAN.

var Duration

Time is duration (as opposed to epoch, …).

var FourWire

Device is in 4-wire mode.

var Hold

Device is in hold mode (repeating the last measurement).

var Max

Device is in max mode, only updating upon a new max value.

var Min

Device is in min mode, only updating upon a new min value.

var RMS

Root mean square (RMS).

var Reference

Reference value shown.

var Relative

Device is in relative mode.

var SplFreqWeightA

Sound pressure level is A-weighted in the frequency domain, according to IEC 61672:2003.

var SplFreqWeightC

Sound pressure level is C-weighted in the frequency domain, according to IEC 61672:2003.

var SplFreqWeightFlat

Sound pressure level is not weighted in the frequency domain, albeit without standards-defined low and high frequency limits.

var SplFreqWeightZ

Sound pressure level is Z-weighted.

var SplLAT

Sound pressure level is time-averaged (LAT), also known as Equivalent Continuous A-weighted Sound Level (LEQ).

var SplPctOverAlarm

Sound pressure level represented as a percentage of measurements that were over a preset alarm level.

var SplTimeWeightF

Sound pressure level measurement is F-weighted (125ms) in the time domain.

var SplTimeWeightS

Sound pressure level measurement is S-weighted (1s) in the time domain.

var Unknown

Unknown quantity flag.

var Unstable

Unstable value (hasn't settled yet).

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.QuantityFlag) -> int

class Session (*args, **kwargs)

The SmuView Session class for accessing the actual state of the application.

Ancestors

  • pybind11_builtins.pybind11_object

Methods

def add_user_device(self: Session) ‑> UserDevice

Create a new user device.

Returns

UserDevice
The created user device object.
def connect_device(self: Session, conn_str: str) ‑> List[HardwareDevice]

Connect a new device. For some devices (like DMMs) you may want to wait a fixed time, until the first sample has arrived and an AnalogSignal object has been created. Example:

import smuview
import time

# Connect device.
dmm_dev = Session.connect_device("hp-3478a:conn=libgpib/hp3478a")[0]
# Sleep 1s to give the devices the chance to create signals.
time.sleep(1)

Parameters

conn_str : str
The connection string. See https://sigrok.org/wiki/Connection_parameters

Returns

List[HardwareDevice]
A List with the newly connected device objects.
def devices(self: Session) ‑> Dict[str, BaseDevice]

Return all connected devices.

Returns

Dict[str, BaseDevice]
A Dict where the key is the device id and the value is the device object.
class UiProxy (*args, **kwargs)

Helper class for accessing the UI.

Ancestors

  • pybind11_builtins.pybind11_object

Methods

def add_control_view(self: UiProxy, tab_id: str, area: DockArea, configurable: Configurable) ‑> str

Add a control view for a configurable to the given tab.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.
configurable : Configurable
The Configurable object.

Returns

str
The id of the new view or empty if the view couldn't be added.
def add_curve_to_time_plot_view(self: UiProxy, tab_id: str, view_id: str, signal: AnalogTimeSignal) ‑> str

Add a signal to the given time plot view.

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the time plot view.
signal : AnalogTimeSignal
The signal object.

Returns

str
The id of the new curve or empty if the curve couldn't be added.
def add_curve_to_xy_plot_view(self: UiProxy, tab_id: str, view_id: str, x_signal: AnalogTimeSignal, y_signal: AnalogTimeSignal) ‑> str

Add x/y signals to the given x/y plot view.

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the x/y plot view.
x_signal : AnalogTimeSignal
The x signal object.
y_signal : AnalogTimeSignal
The y signal object.

Returns

str
The id of the new curve or empty if the curve couldn't be added.
def add_data_view(self: UiProxy, tab_id: str, area: DockArea, signal: AnalogTimeSignal) ‑> str

Add a data view for a signal to the given tab.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.
signal : AnalogTimeSignal
The signal object.

Returns

str
The id of the new view or empty if the view couldn't be added.
def add_device_tab(self: UiProxy, device: BaseDevice) ‑> str

Add a device tab with standard view for a device to the UI.

Parameters

device : BaseDevice
The device object.

Returns

str
The id of the new tab or empty if the tab couldn't be added.
def add_power_panel_view(self: UiProxy, tab_id: str, area: DockArea, voltage_signal: AnalogTimeSignal, current_signal: AnalogTimeSignal) ‑> str

Add a power panel view for a voltage and a current signal to the given tab.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.
voltage_signal : AnalogTimeSignal
The voltage signal object.
current_signal : AnalogTimeSignal
The current signal object.

Returns

str
The id of the new view or empty if the view couldn't be added.
def add_signal_to_data_view(self: UiProxy, tab_id: str, view_id: str, signal: AnalogTimeSignal)

Add a signal to the given data view.

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the data view.
signal : AnalogTimeSignal
The signal object.
def add_time_plot_view(self: UiProxy, tab_id: str, area: DockArea) ‑> str

Add a time plot view to the given tab. Use UiProxy.set_channel_to_time_plot_view() to set a channel to the plot view or use UiProxy.add_curve_to_time_plot_view() to set a signal to the plot view. When you have set a channel to the plot, new curves will be automatically created, when the channel changes (e.g. for multimeters when switching functions).

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.

Returns

str
The id of the new view or empty if the view couldn't be added.
def add_value_panel_view(*args, **kwargs)

Overloaded function.

  1. add_value_panel_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, channel: smuview.BaseChannel) -> str

Add a value panel view for a channel to the given tab.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.
channel : BaseChannel
The channel object.

Returns

str
The id of the new view or empty if the view couldn't be added.
2. add_value_panel_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, signal: smuview.AnalogTimeSignal) -> str
 

Add a value panel view for a signal to the given tab.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.
signal : AnalogTimeSignal
The signal object.

Returns

str
The id of the new view or empty if the view couldn't be added.
def add_xy_plot_view(self: UiProxy, tab_id: str, area: DockArea) ‑> str

Add a x/y plot view for two signals to the given tab. Use UiProxy.add_curve_to_xy_plot_view() to add a new curve (a set of two signals) to the plot view.

Parameters

tab_id : str
The id of the tab.
area : DockArea
Where to put the new view.

Returns

str
The id of the new view or empty if the view couldn't be added.
def set_channel_to_time_plot_view(self: UiProxy, tab_id: str, view_id: str, channel: BaseChannel)

Set a channel to the given time plot view. New curves will be automatically created, when the channel changes (e.g. for multimeters when switching functions).

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the time plot view.
channel : BaseChannel
The channel object.
def set_curve_color(self: UiProxy, tab_id: str, view_id: str, curve_id: str, color: Tuple[int, int, int])

Set the color of the given curve.

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the plot view.
curve_id : str
The id of the curve.
color : Tuple[int, int, int]
The color for the curve as a Tuple with the RGB values.
def set_curve_name(self: UiProxy, tab_id: str, view_id: str, curve_id: str, name: str)

Set the name of the given curve.

Parameters

tab_id : str
The id of the tab.
view_id : str
The id of the plot view.
curve_id : str
The id of the curve.
name : str
The name for the curve.
def show_double_input_dialog(self: UiProxy, title: str, label: str, value: float = 0.0, decimals: int = 1, step: float = 0.1, min: float = 2.2250738585072014e-308, max: float = 1.7976931348623157e+308) ‑> object

Show a dialog window to get a float value from the user. It returns the entered float value or None if the Cancel button was pressed.

Only has effect if the used Qt version is equal or greater than 5.10!

Parameters

title : str
The window title of the input dialog.
label : str
The label to display in the input dialog.
value : float
The default value of the float.
decimals : int
The maximum number of decimal places the number may have. Default is 1.
step : float
The amount by which the value can be incremented or decremented by the user. Default is 0.1. Only has effect for Qt versions >= 5.10!
min : float
The minimum value the user may choose.
max : float
The maximum value the user may choose.

Returns

float or None
The user entered float value or None when the Cancel button was pressed.
def show_int_input_dialog(self: UiProxy, title: str, label: str, value: int = 0, step: int = 1, min: int = -2147483648, max: int = 2147483647) ‑> object

Show a dialog window to get an integer value from the user. It returns the entered float value or None if the Cancel button was pressed.

Parameters

title : str
The window title of the input dialog.
label : str
The label to display in the input dialog.
value : int
The default value of the integer.
step : int
The amount by which the value can be incremented or decremented by the user. Default is 1.
min : int
The minimum value the user may choose.
max : int
The maximum value the user may choose.

Returns

int or None
The user entered integer value or None when the Cancel button was pressed.
def show_message_box(self: UiProxy, title: str, text: str) ‑> bool

Show a (info) message box with the given window title and text. Returns True when the Ok button was pressed.

Parameters

title : str
The window title of the message box.
text : str
The text to display in the message box.

Returns

bool
True when the Ok button was pressed, else False.
def show_string_input_dialog(self: UiProxy, title: str, label: str, value: str = '') ‑> object

Show a dialog window to get a string value from the user. It returns the entered string value or None if the Cancel button was pressed.

Parameters

title : str
The window title of the input dialog.
label : str
The label to display in the input dialog.
value : str
The default value of the string.

Returns

str or None
The user entered string value or None when the Cancel button was pressed.
class Unit (...)

Enum of all available units.

init(self: smuview.Unit, value: int) -> None

Ancestors

  • pybind11_builtins.pybind11_object

Class variables

var Ampere

Ampere

var AmpereHour

AmpereHour (Ah)

var Boolean

Boolean

var Carat

Weight in carat.

var Celsius

Celsius

var Concentration

Concentration

var Coulomb

Coulomb

var DecibelMW

Decibel milliWatt (dBm)

var DecibelSpl

Decibel sound pressure level

var DecibelVolt

Decibel Volt (dBV)

var Degree

Degree

var Fahrenheit

Fahrenheit

var Farad

Farad

var Grain

Weight in grain.

var Gram

Weight in gram (g).

var HectoPascal

HectoPascal (hPa)

var Henry

Henry

var Hertz

Hertz

var Humidity293K

Humidity at 293K

var Joule

Joule

var Kelvin

Kelvin

var MeterPerSecond

Meter per second (m/s)

var Momme

Weight in momme.

var Ohm

Ohm

var Ounce

Weight in avoirdupois ounce (oz).

var Pennyweight

Weight in pennyweight.

var Percentage

Percentage

var Piece

Piece

var Pound

Weight in avoirdupois pound (lb).

var RevolutionsPerMinute

Revolutions per minute (RPM)

var Second

Second

var Siemens

Siemens

var Tael

Weight in tael.

var Tola

Weight in tola.

var TroyOunce

Weight in troy ounce (oz t).

var Unitless

Unitless

var Unknown

Unknown

var Volt

Volt

var VoltAmpere

VoltAmpere (VA)

var Watt

Watt

var WattHour

WattHour (Wh)

Instance variables

var name

name(self: handle) -> str

var value

(arg0: smuview.Unit) -> int

class UserChannel (*args, **kwargs)

An user generated channel for storing custom data.

Ancestors

Methods

def push_sample(self: UserChannel, sample: float, timestamp: float, quantity: Quantity, quantity_flags: Set[QuantityFlag], unit: Unit, digits: int, decimal_places: int)

Push a single sample to the channel.

Parameters

sample : float
The sample value.
timestamp : float
The absolute timestamp in milliseconds.
quantity : Quantity
The Quantity of the new signal.
quantity_flags : Set[QuantityFlag]
The QuantityFlags of the new signal.
unit : Unit
The Unit of the new signal.
digits : int
The total number of digits.
decimal_places : int
The number of decimal places.

Inherited members

class UserDevice (*args, **kwargs)

An user generated (virtual) device for storing custom data and showing a custom tab.

Ancestors

Inherited members

================================================ FILE: doc/smuview_python_bindings.txt ================================================ Help on built-in module smuview: NAME smuview - The SmuView 0.0.5-git-64e7b9d Python bindings. DESCRIPTION The Python bindings are a scripting extension for SmuView to automate, setup and control complex or repetitive measurements, to process the incoming data and to create a standardized user interface for those measurements. The smuview module offers two default object instances: `Session` and `UiProxy`. The `Session` object gives access to already connected devices or connects new devices. The returned device object can then be used to read data from the device or control the device. The `UiProxy` object instance is used to modify the user interface, for example adding tabs or views. Here is a short example that connects the HP 3378A DMM via GPIB, reads a sample and creates the default tab for the device: ``` import smuview import time # Connect device. dmm_dev = Session.connect_device("hp-3478a:conn=libgpib/hp3478a")[0] # Sleep 1s to give the devices the chance to create signals. time.sleep(1) # Get last sample from channel P1. sample = dmm_dev.channels()["P1"].actual_signal().get_last_sample(True) print(sample) # Add default tab for the DMM device. UiProxy.add_device_tab(dmm_dev) ``` For more example scripts, please have a look into the `smuscript` folder. CLASSES pybind11_builtins.pybind11_object(builtins.object) BaseChannel HardwareChannel UserChannel BaseDevice HardwareDevice UserDevice BaseSignal AnalogSampleSignal AnalogTimeSignal ConfigKey Configurable DataType DockArea PyStreamBuf Quantity QuantityFlag Session UiProxy Unit class AnalogSampleSignal(BaseSignal) | A signal with key-value pairs. | | Method resolution order: | AnalogSampleSignal | BaseSignal | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | get_sample(...) | get_sample(self: smuview.AnalogSampleSignal, pos: int) -> Tuple[int, float] | | Return the sample for the given position. | | Parameters | ---------- | pos : int | The position/number of the sample. | | Returns | ------- | Tuple[int, float] | The sample with 1. the key and 2. the sample value. | | push_sample(...) | push_sample(self: smuview.AnalogSampleSignal, sample: capsule, pos: int, unit_size: int, digits: int, decimal_places: int) -> None | | Push a new sample to the signal. | | Parameters | ---------- | sample : float or double | The sample value. | pos : int | The key (position) of the new sample. | unit_size : int | The size of the floating point data type (float=4, double=8) for the `sample` argument. | digits : int | The total number of digits. | decimal_places : int | The number of decimal places. | | ---------------------------------------------------------------------- | Methods inherited from BaseSignal: | | name(...) | name(self: smuview.BaseSignal) -> str | | Return the name of the signal. | | Returns | ------- | str | The name of the signal. | | sample_count(...) | sample_count(self: smuview.BaseSignal) -> int | | Return the number of samples of the signal. | | Returns | ------- | int | The number of samples. | | set_name(...) | set_name(self: smuview.BaseSignal, arg0: str) -> None | | Set a custom name for the signal. | | Parameters | ---------- | custom_name : str | A custom name for the signal. If empty, the signal name will be automatically generated. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class AnalogTimeSignal(BaseSignal) | A signal with time-value pairs. | | Method resolution order: | AnalogTimeSignal | BaseSignal | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | get_last_sample(...) | get_last_sample(self: smuview.AnalogTimeSignal, relative_time: bool) -> Tuple[float, float] | | Return the last sample of the signal. | | Parameters | ---------- | relative_time : bool | When `True`, the returned timestamp is relative to the start of the SmuView session. | | Returns | ------- | Tuple[float, float] | The sample with 1. timestamp in milliseconds and 2. the sample value. | | get_sample(...) | get_sample(self: smuview.AnalogTimeSignal, pos: int, relative_time: bool) -> Tuple[float, float] | | Return the sample at the given position. | | Parameters | ---------- | pos : int | The position/number of the sample. | relative_time : bool | When `True`, the returned timestamp is relative to the start of the SmuView session. | | Returns | ------- | Tuple[float, float] | The sample with 1. timestamp in milliseconds and 2. the sample value. | | push_sample(...) | push_sample(self: smuview.AnalogTimeSignal, sample: capsule, timestamp: float, unit_size: int, digits: int, decimal_places: int) -> None | | Push a new sample to the signal. | | Parameters | ---------- | sample : float or double | The sample value. | timestamp : float | The absolute timestamp in milliseconds. | unit_size : int | The size of the floating point data type (float=4, double=8) for the `sample` argument. | digits : int | The total number of digits. | decimal_places : int | The number of decimal places. | | ---------------------------------------------------------------------- | Methods inherited from BaseSignal: | | name(...) | name(self: smuview.BaseSignal) -> str | | Return the name of the signal. | | Returns | ------- | str | The name of the signal. | | sample_count(...) | sample_count(self: smuview.BaseSignal) -> int | | Return the number of samples of the signal. | | Returns | ------- | int | The number of samples. | | set_name(...) | set_name(self: smuview.BaseSignal, arg0: str) -> None | | Set a custom name for the signal. | | Parameters | ---------- | custom_name : str | A custom name for the signal. If empty, the signal name will be automatically generated. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class BaseChannel(pybind11_builtins.pybind11_object) | The base class for all channel types. | | Method resolution order: | BaseChannel | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | actual_signal(...) | actual_signal(self: smuview.BaseChannel) -> smuview.BaseSignal | | Return the actual signal of the channel. | | Returns | ------- | BaseSignal | The actual signal object. | | add_signal(...) | add_signal(self: smuview.BaseChannel, quantity: smuview.Quantity, quantity_flags: Set[smuview.QuantityFlag], unit: smuview.Unit, custom_name: str = '') -> smuview.BaseSignal | | Add a new signal to the channel. | | Parameters | ---------- | quantity : Quantity | The `Quantity` of the new signal. | quantity_flags : Set[QuantityFlag] | The `QuantityFlag`s of the new signal. | unit : Unit | The `Unit` of the new signal. | custom_name: str | A custom name for the new signal. If empty (default), the signal name will be automatically generated. | | Returns | ------- | BaseSignal | The new signal object. | | name(...) | name(self: smuview.BaseChannel) -> str | | Return the name of the channel. | | Returns | ------- | str | The name of the channel. | | signals(...) | signals(self: smuview.BaseChannel) -> List[smuview.BaseSignal] | | Return all signals of the channel. | | Returns | ------- | List[BaseSignal] | All signals of the channel. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class BaseDevice(pybind11_builtins.pybind11_object) | The base class for all device types. | | Method resolution order: | BaseDevice | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | add_user_channel(...) | add_user_channel(self: smuview.BaseDevice, channel_name: str, channel_group_name: str) -> smuview.UserChannel | | Add a new user channel to the device. | | Parameters | ---------- | channel_name : str | The name of the new user channel. | channel_group_name : str | The name of the channel group where to create the user channel. Can be empty. | | Returns | ------- | UserChannel | The new user channel object. | | channels(...) | channels(self: smuview.BaseDevice) -> Dict[str, smuview.BaseChannel] | | Return all channels of the device. | | Returns | ------- | Dict[str, BaseChannel] | A Dict where the key is the id of the channel and the value is the channel object. | | configurables(...) | configurables(self: smuview.BaseDevice) -> Dict[str, smuview.Configurable] | | Return all configurables of the device. | | Returns | ------- | Dict[str, Configurable] | A Dict where the key is the id of the `Configurable` and the value is the `Configurable` object. | | id(...) | id(self: smuview.BaseDevice) -> str | | Return the unique id of the device. | | Returns | ------- | str | The id of the device. | | name(...) | name(self: smuview.BaseDevice) -> str | | Return the name of the device. | | Returns | ------- | str | The name of the device. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class BaseSignal(pybind11_builtins.pybind11_object) | The base class for all signal types. | | Method resolution order: | BaseSignal | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | name(...) | name(self: smuview.BaseSignal) -> str | | Return the name of the signal. | | Returns | ------- | str | The name of the signal. | | sample_count(...) | sample_count(self: smuview.BaseSignal) -> int | | Return the number of samples of the signal. | | Returns | ------- | int | The number of samples. | | set_name(...) | set_name(self: smuview.BaseSignal, arg0: str) -> None | | Set a custom name for the signal. | | Parameters | ---------- | custom_name : str | A custom name for the signal. If empty, the signal name will be automatically generated. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class ConfigKey(pybind11_builtins.pybind11_object) | Enum of all available config keys for controlling a device. | | Method resolution order: | ConfigKey | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.ConfigKey) -> int | | __init__(...) | __init__(self: smuview.ConfigKey, value: int) -> None | | __int__(...) | __int__(self: smuview.ConfigKey) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.ConfigKey, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Static methods defined here: | | get_data_type(...) from builtins.PyCapsule | get_data_type(config_key: smuview.ConfigKey) -> smuview.DataType | | Helper function to get the data type for a config key. | | Parameters | ---------- | config_key : ConfigKey | The config key. | | Returns | ------- | DataType | The data type of the config key. | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | ADCPowerlineCycles = | | Amplitude = | | Averaging = | | AvgSamples = | | BufferSize = | | CaptureFile = | | CaptureRatio = | | CaptureUnitSize = | | CenterFrequency = | | ChannelConfig = | | ClockEdge = | | Coupling = | | Current = | | CurrentLimit = | | DataLog = | | DataSource = | | DeviceMode = | | Digits = | | Enabled = | | EquivCircuitModel = | | ExternalClock = | | ExternalClockSource = | | Filter = | | HighResolution = | | HoldMax = | | HoldMin = | | HorizTriggerPos = | | LogicThreshold = | | LogicThresholdCustom = | | MeasuredQuantity = | | NumAnalogChannels = | | NumHDiv = | | NumLogicChannels = | | NumVDiv = | | Offset = | | OutputFrequency = | | OutputFrequencyTarget = | | OverCurrentProtectionActive = | | PeakDetection = | | PowerOff = | | ProbeFactor = | | RLE = | | Range = | | Regulation = | | SampleInterval = | | Samplerate = | | SessionFile = | | SplMeasurementRange = | | SplWeightFreq = | | SplWeightTime = | | Swap = | | TestMode = | | TimeBase = | | TriggerLevel = | | TriggerMatch = | | TriggerPattern = | | TriggerSlope = | | TriggerSource = | | UnderVoltageConditionActive = | | VDiv = | | Voltage = | | VoltageTarget = | | VoltageThreshold = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class Configurable(pybind11_builtins.pybind11_object) | A configurable for controlling a device with config keys. | | Method resolution order: | Configurable | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | get_bool_config(...) | get_bool_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> bool | | Return a boolean value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | bool | The bool value of the config key. | | get_double_config(...) | get_double_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> float | | Return a double value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | float | The float value of the config key. | | get_int_config(...) | get_int_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> int | | Return an integer value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | int | The int value of the config key. | | get_measured_quantity_config(...) | get_measured_quantity_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> Tuple[smuview.Quantity, Set[smuview.QuantityFlag]] | | Return a measured quantity value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | Tuple[Quantity, Set[QuantityFlag]] | The measured quantity value of the config key. | | get_string_config(...) | get_string_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> str | | Return a string value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | str | The string value of the config key. | | get_uint_config(...) | get_uint_config(self: smuview.Configurable, config_key: smuview.ConfigKey) -> int | | Return an unsigned integer value from the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to get. | | Returns | ------- | int | The (unsigned) int value of the config key. | | getable_configs(...) | getable_configs(self: smuview.Configurable) -> Set[smuview.ConfigKey] | | Return all getable config keys. | | Returns | ------- | List[ConfigKey] | All getable config keys. | | listable_configs(...) | listable_configs(self: smuview.Configurable) -> Set[smuview.ConfigKey] | | Return all listable config keys. | | Returns | ------- | List[ConfigKey] | All listable config keys. | | name(...) | name(self: smuview.Configurable) -> str | | Return the name of the configurable. | | Returns | ------- | str | The name of the configurable. | | set_config(...) | set_config(*args, **kwargs) | Overloaded function. | | 1. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: bool) -> None | | Set a boolean value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : bool | The bool value to set. | | 2. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: int) -> None | | Set an integer value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : int | The int value to set. | | 3. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: int) -> None | | Set an unsigned integer value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : int | The (unsigned) int value to set. | | 4. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: float) -> None | | Set a double value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : float | The float value to set. | | 5. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: str) -> None | | Set a string value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : str | The string value to set. | | 6. set_config(self: smuview.Configurable, config_key: smuview.ConfigKey, value: Tuple[smuview.Quantity, Set[smuview.QuantityFlag]]) -> None | | Set a measured quantity value to the given config key. | | Parameters | ---------- | config_key : ConfigKey | The `ConfigKey` to set. | value : Tuple[Quantity, Set[QuantityFlag]] | The measured quantity value to set. | | setable_configs(...) | setable_configs(self: smuview.Configurable) -> Set[smuview.ConfigKey] | | Return all setable config keys. | | Returns | ------- | List[ConfigKey] | All setable config keys. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class DataType(pybind11_builtins.pybind11_object) | Enum of all available data types. | | Method resolution order: | DataType | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.DataType) -> int | | __init__(...) | __init__(self: smuview.DataType, value: int) -> None | | __int__(...) | __int__(self: smuview.DataType) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.DataType, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | Bool = | | Double = | | DoubleRange = | | Int32 = | | KeyValue = | | MQ = | | RationalPeriod = | | RationalVolt = | | String = | | UInt64 = | | UInt64Range = | | Unknown = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class DockArea(pybind11_builtins.pybind11_object) | Enum of all possible docking locations for a view. | | Method resolution order: | DockArea | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.DockArea) -> int | | __init__(...) | __init__(self: smuview.DockArea, value: int) -> None | | __int__(...) | __int__(self: smuview.DockArea) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.DockArea, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | BottomDockArea = | | LeftDocktArea = | | RightDockArea = | | TopDockArea = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class HardwareChannel(BaseChannel) | An actual hardware channel | | Method resolution order: | HardwareChannel | BaseChannel | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | ---------------------------------------------------------------------- | Methods inherited from BaseChannel: | | actual_signal(...) | actual_signal(self: smuview.BaseChannel) -> smuview.BaseSignal | | Return the actual signal of the channel. | | Returns | ------- | BaseSignal | The actual signal object. | | add_signal(...) | add_signal(self: smuview.BaseChannel, quantity: smuview.Quantity, quantity_flags: Set[smuview.QuantityFlag], unit: smuview.Unit, custom_name: str = '') -> smuview.BaseSignal | | Add a new signal to the channel. | | Parameters | ---------- | quantity : Quantity | The `Quantity` of the new signal. | quantity_flags : Set[QuantityFlag] | The `QuantityFlag`s of the new signal. | unit : Unit | The `Unit` of the new signal. | custom_name: str | A custom name for the new signal. If empty (default), the signal name will be automatically generated. | | Returns | ------- | BaseSignal | The new signal object. | | name(...) | name(self: smuview.BaseChannel) -> str | | Return the name of the channel. | | Returns | ------- | str | The name of the channel. | | signals(...) | signals(self: smuview.BaseChannel) -> List[smuview.BaseSignal] | | Return all signals of the channel. | | Returns | ------- | List[BaseSignal] | All signals of the channel. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class HardwareDevice(BaseDevice) | An actual hardware device. | | Method resolution order: | HardwareDevice | BaseDevice | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | ---------------------------------------------------------------------- | Methods inherited from BaseDevice: | | add_user_channel(...) | add_user_channel(self: smuview.BaseDevice, channel_name: str, channel_group_name: str) -> smuview.UserChannel | | Add a new user channel to the device. | | Parameters | ---------- | channel_name : str | The name of the new user channel. | channel_group_name : str | The name of the channel group where to create the user channel. Can be empty. | | Returns | ------- | UserChannel | The new user channel object. | | channels(...) | channels(self: smuview.BaseDevice) -> Dict[str, smuview.BaseChannel] | | Return all channels of the device. | | Returns | ------- | Dict[str, BaseChannel] | A Dict where the key is the id of the channel and the value is the channel object. | | configurables(...) | configurables(self: smuview.BaseDevice) -> Dict[str, smuview.Configurable] | | Return all configurables of the device. | | Returns | ------- | Dict[str, Configurable] | A Dict where the key is the id of the `Configurable` and the value is the `Configurable` object. | | id(...) | id(self: smuview.BaseDevice) -> str | | Return the unique id of the device. | | Returns | ------- | str | The id of the device. | | name(...) | name(self: smuview.BaseDevice) -> str | | Return the name of the device. | | Returns | ------- | str | The name of the device. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class PyStreamBuf(pybind11_builtins.pybind11_object) | Redirect all Python output to a SmuView console. This class is for internal SmuView use only! | | Method resolution order: | PyStreamBuf | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __del__(...) | __del__(self: smuview.PyStreamBuf) -> None | | Prepare for object destruction. | | __init__(...) | __init__(self: smuview.PyStreamBuf, arg0: str, arg1: str) -> None | | close(...) | close(self: smuview.PyStreamBuf) -> None | | Flush and close this stream. | | fileno(...) | fileno(self: smuview.PyStreamBuf) -> int | | Raises an `OSError`, because `PyStreamBuf` doesn't use a file descriptor. | | flush(...) | flush(self: smuview.PyStreamBuf) -> None | | Flush the write buffers of the stream. | | isatty(...) | isatty(self: smuview.PyStreamBuf) -> bool | | Always returns `False`. | | read(...) | read(self: smuview.PyStreamBuf, size: int) -> str | | Raises an `OSError`, because `PyStreamBuf` is write only. | | readable(...) | readable(self: smuview.PyStreamBuf) -> bool | | Always returns `False`. | | readline(...) | readline(self: smuview.PyStreamBuf, size: int) -> str | | Raises an `OSError`, because `PyStreamBuf` is write only. | | readlines(...) | readlines(self: smuview.PyStreamBuf, hint: int) -> List[str] | | Raises an `OSError`, because `PyStreamBuf` is write only. | | seek(...) | seek(self: smuview.PyStreamBuf, offset: int, whence: int) -> int | | Raises an `OSError`, because `PyStreamBuf` is not seekable. | | seekable(...) | seekable(self: smuview.PyStreamBuf) -> bool | | Always returns `False`. `PyStreamBuf` is not seekable atm. | | tell(...) | tell(self: smuview.PyStreamBuf) -> int | | Raises an `OSError`, because `PyStreamBuf` is not seekable. | | truncate(...) | truncate(self: smuview.PyStreamBuf, size: int) -> int | | Raises an `OSError`, because `PyStreamBuf` is not seekable. | | writable(...) | writable(self: smuview.PyStreamBuf) -> bool | | Always return `True`. | | write(...) | write(self: smuview.PyStreamBuf, s: str) -> int | | Write the string `s` to the stream and return the number of characters written. | | writelines(...) | writelines(self: smuview.PyStreamBuf, lines: List[str]) -> None | | Write a list of lines to the stream. | | ---------------------------------------------------------------------- | Readonly properties defined here: | | closed | `True` if the stream is closed. | | encoding | The name of the encoding that is used. | | errors | The error setting of the decoder or encoder. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class Quantity(pybind11_builtins.pybind11_object) | Enum of all available quantities. | | Method resolution order: | Quantity | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.Quantity) -> int | | __init__(...) | __init__(self: smuview.Quantity, value: int) -> None | | __int__(...) | __int__(self: smuview.Quantity) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.Quantity, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | ApparentPower = | | Capacitance = | | CarbonMonoxide = | | Conductance = | | Continuity = | | Count = | | Current = | | Difference = | | DissipationFactor = | | DutyCyle = | | ElectricCharge = | | Energy = | | Frequency = | | Gain = | | HarmonicRatio = | | Mass = | | ParallelCapacitance = | | ParallelInductance = | | ParallelResistance = | | PhaseAngle = | | Power = | | PowerFactor = | | Pressure = | | PulseWidth = | | QualityFactor = | | RelativeHumidity = | | Resistance = | | SeriesCapacitance = | | SeriesInductance = | | SeriesResistance = | | SoundPressureLevel = | | Temperature = | | Time = | | Unknown = | | Voltage = | | WindSpeed = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class QuantityFlag(pybind11_builtins.pybind11_object) | Enum of all available quantity flags. | | Method resolution order: | QuantityFlag | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.QuantityFlag) -> int | | __init__(...) | __init__(self: smuview.QuantityFlag, value: int) -> None | | __int__(...) | __int__(self: smuview.QuantityFlag) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.QuantityFlag, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | AC = | | Autorange = | | Avg = | | DC = | | Diode = | | Duration = | | FourWire = | | Hold = | | Max = | | Min = | | RMS = | | Reference = | | Relative = | | SplFreqWeightA = | | SplFreqWeightC = | | SplFreqWeightFlat = | | SplFreqWeightZ = | | SplLAT = | | SplPctOverAlarm = | | SplTimeWeightF = | | SplTimeWeightS = | | Unknown = | | Unstable = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class Session(pybind11_builtins.pybind11_object) | The SmuView `Session` class for accessing the actual state of the application. | | Method resolution order: | Session | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | add_user_device(...) | add_user_device(self: smuview.Session) -> smuview.UserDevice | | Create a new user device. | | Returns | ------- | UserDevice | The created user device object. | | connect_device(...) | connect_device(self: smuview.Session, conn_str: str) -> List[smuview.HardwareDevice] | | Connect a new device. For some devices (like DMMs) you may want to wait a fixed time, until the first sample has arrived and an `AnalogSignal` object has been created. Example: | ``` | import smuview | import time | | # Connect device. | dmm_dev = Session.connect_device("hp-3478a:conn=libgpib/hp3478a")[0] | # Sleep 1s to give the devices the chance to create signals. | time.sleep(1) | ``` | | Parameters | ---------- | conn_str : str | The connection string. See https://sigrok.org/wiki/Connection_parameters | | Returns | ------- | List[HardwareDevice] | A List with the newly connected device objects. | | devices(...) | devices(self: smuview.Session) -> Dict[str, smuview.BaseDevice] | | Return all connected devices. | | Returns | ------- | Dict[str, BaseDevice] | A Dict where the key is the device id and the value is the device object. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class UiProxy(pybind11_builtins.pybind11_object) | Helper class for accessing the UI. | | Method resolution order: | UiProxy | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | add_control_view(...) | add_control_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, configurable: smuview.Configurable) -> str | | Add a control view for a configurable to the given tab. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | configurable : Configurable | The `Configurable` object. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | add_curve_to_time_plot_view(...) | add_curve_to_time_plot_view(self: smuview.UiProxy, tab_id: str, view_id: str, signal: smuview.AnalogTimeSignal) -> str | | Add a signal to the given time plot view. | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the time plot view. | signal : AnalogTimeSignal | The signal object. | | Returns | ------- | str | The id of the new curve or empty if the curve couldn't be added. | | add_curve_to_xy_plot_view(...) | add_curve_to_xy_plot_view(self: smuview.UiProxy, tab_id: str, view_id: str, x_signal: smuview.AnalogTimeSignal, y_signal: smuview.AnalogTimeSignal) -> str | | Add x/y signals to the given x/y plot view. | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the x/y plot view. | x_signal : AnalogTimeSignal | The x signal object. | y_signal : AnalogTimeSignal | The y signal object. | | Returns | ------- | str | The id of the new curve or empty if the curve couldn't be added. | | add_data_view(...) | add_data_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, signal: smuview.AnalogTimeSignal) -> str | | Add a data view for a signal to the given tab. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | signal : AnalogTimeSignal | The signal object. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | add_device_tab(...) | add_device_tab(self: smuview.UiProxy, device: smuview.BaseDevice) -> str | | Add a device tab with standard view for a device to the UI. | | Parameters | ---------- | device : BaseDevice | The device object. | | Returns | ------- | str | The id of the new tab or empty if the tab couldn't be added. | | add_power_panel_view(...) | add_power_panel_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, voltage_signal: smuview.AnalogTimeSignal, current_signal: smuview.AnalogTimeSignal) -> str | | Add a power panel view for a voltage and a current signal to the given tab. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | voltage_signal : AnalogTimeSignal | The voltage signal object. | current_signal : AnalogTimeSignal | The current signal object. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | add_signal_to_data_view(...) | add_signal_to_data_view(self: smuview.UiProxy, tab_id: str, view_id: str, signal: smuview.AnalogTimeSignal) -> None | | Add a signal to the given data view. | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the data view. | signal : AnalogTimeSignal | The signal object. | | add_time_plot_view(...) | add_time_plot_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea) -> str | | Add a time plot view to the given tab. Use [`UiProxy.set_channel_to_time_plot_view()`](UiProxy.set_channel_to_time_plot_view) to set a channel to the plot view or use [`UiProxy.add_curve_to_time_plot_view()`](UiProxy.add_curve_to_time_plot_view) to set a signal to the plot view. | When you have set a channel to the plot, new curves will be automatically created, when the channel changes (e.g. for multimeters when switching functions). | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | add_value_panel_view(...) | add_value_panel_view(*args, **kwargs) | Overloaded function. | | 1. add_value_panel_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, channel: smuview.BaseChannel) -> str | | Add a value panel view for a channel to the given tab. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | channel : BaseChannel | The channel object. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | 2. add_value_panel_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea, signal: smuview.AnalogTimeSignal) -> str | | Add a value panel view for a signal to the given tab. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | signal : AnalogTimeSignal | The signal object. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | add_xy_plot_view(...) | add_xy_plot_view(self: smuview.UiProxy, tab_id: str, area: smuview.DockArea) -> str | | Add a x/y plot view for two signals to the given tab. Use [`UiProxy.add_curve_to_xy_plot_view()`](UiProxy.add_curve_to_xy_plot_view) to add a new curve (a set of two signals) to the plot view. | | Parameters | ---------- | tab_id : str | The id of the tab. | area : DockArea | Where to put the new view. | | Returns | ------- | str | The id of the new view or empty if the view couldn't be added. | | set_channel_to_time_plot_view(...) | set_channel_to_time_plot_view(self: smuview.UiProxy, tab_id: str, view_id: str, channel: smuview.BaseChannel) -> None | | Set a channel to the given time plot view. New curves will be automatically created, when the channel changes (e.g. for multimeters when switching functions). | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the time plot view. | channel : BaseChannel | The channel object. | | set_curve_color(...) | set_curve_color(self: smuview.UiProxy, tab_id: str, view_id: str, curve_id: str, color: Tuple[int, int, int]) -> None | | Set the color of the given curve. | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the plot view. | curve_id : str | The id of the curve. | color : Tuple[int, int, int] | The color for the curve as a Tuple with the RGB values. | | set_curve_name(...) | set_curve_name(self: smuview.UiProxy, tab_id: str, view_id: str, curve_id: str, name: str) -> None | | Set the name of the given curve. | | Parameters | ---------- | tab_id : str | The id of the tab. | view_id : str | The id of the plot view. | curve_id : str | The id of the curve. | name : str | The name for the curve. | | show_double_input_dialog(...) | show_double_input_dialog(self: smuview.UiProxy, title: str, label: str, value: float = 0.0, decimals: int = 1, step: float = 0.1, min: float = 2.2250738585072014e-308, max: float = 1.7976931348623157e+308) -> object | | Show a dialog window to get a float value from the user. It returns the entered float value or `None` if the Cancel button was pressed. | | Only has effect if the used Qt version is equal or greater than 5.10! | | Parameters | ---------- | title : str | The window title of the input dialog. | label : str | The label to display in the input dialog. | value : float | The default value of the float. | decimals : int | The maximum number of decimal places the number may have. Default is 1. | step : float | The amount by which the value can be incremented or decremented by the user. Default is 0.1. | Only has effect for Qt versions >= 5.10! | min : float | The minimum value the user may choose. | max : float | The maximum value the user may choose. | | Returns | ------- | float or None | The user entered float value or `None` when the Cancel button was pressed. | | show_int_input_dialog(...) | show_int_input_dialog(self: smuview.UiProxy, title: str, label: str, value: int = 0, step: int = 1, min: int = -2147483648, max: int = 2147483647) -> object | | Show a dialog window to get an integer value from the user. It returns the entered float value or `None` if the Cancel button was pressed. | | Parameters | ---------- | title : str | The window title of the input dialog. | label : str | The label to display in the input dialog. | value : int | The default value of the integer. | step : int | The amount by which the value can be incremented or decremented by the user. Default is 1. | min : int | The minimum value the user may choose. | max : int | The maximum value the user may choose. | | Returns | ------- | int or None | The user entered integer value or `None` when the Cancel button was pressed. | | show_message_box(...) | show_message_box(self: smuview.UiProxy, title: str, text: str) -> bool | | Show a (info) message box with the given window title and text. Returns `True` when the Ok button was pressed. | | Parameters | ---------- | title : str | The window title of the message box. | text : str | The text to display in the message box. | | Returns | ------- | bool | `True` when the Ok button was pressed, else `False`. | | show_string_input_dialog(...) | show_string_input_dialog(self: smuview.UiProxy, title: str, label: str, value: str = '') -> object | | Show a dialog window to get a string value from the user. It returns the entered string value or `None` if the Cancel button was pressed. | | Parameters | ---------- | title : str | The window title of the input dialog. | label : str | The label to display in the input dialog. | value : str | The default value of the string. | | Returns | ------- | str or None | The user entered string value or `None` when the Cancel button was pressed. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class Unit(pybind11_builtins.pybind11_object) | Enum of all available units. | | Method resolution order: | Unit | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __eq__(...) | __eq__(self: object, other: object) -> bool | | __getstate__(...) | __getstate__(self: object) -> int | | __hash__(...) | __hash__(self: object) -> int | | __index__(...) | __index__(self: smuview.Unit) -> int | | __init__(...) | __init__(self: smuview.Unit, value: int) -> None | | __int__(...) | __int__(self: smuview.Unit) -> int | | __ne__(...) | __ne__(self: object, other: object) -> bool | | __repr__(...) | __repr__(self: object) -> str | | __setstate__(...) | __setstate__(self: smuview.Unit, state: int) -> None | | __str__ = name(...) | name(self: handle) -> str | | ---------------------------------------------------------------------- | Readonly properties defined here: | | __members__ | | name | name(self: handle) -> str | | value | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | Ampere = | | AmpereHour = | | Boolean = | | Carat = | | Celsius = | | Concentration = | | Coulomb = | | DecibelMW = | | DecibelSpl = | | DecibelVolt = | | Degree = | | Fahrenheit = | | Farad = | | Grain = | | Gram = | | HectoPascal = | | Henry = | | Hertz = | | Humidity293K = | | Joule = | | Kelvin = | | MeterPerSecond = | | Momme = | | Ohm = | | Ounce = | | Pennyweight = | | Percentage = | | Piece = | | Pound = | | RevolutionsPerMinute = | | Second = | | Siemens = | | Tael = | | Tola = | | TroyOunce = | | Unitless = | | Unknown = | | Volt = | | VoltAmpere = | | Watt = | | WattHour = | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class UserChannel(BaseChannel) | An user generated channel for storing custom data. | | Method resolution order: | UserChannel | BaseChannel | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | push_sample(...) | push_sample(self: smuview.UserChannel, sample: float, timestamp: float, quantity: smuview.Quantity, quantity_flags: Set[smuview.QuantityFlag], unit: smuview.Unit, digits: int, decimal_places: int) -> None | | Push a single sample to the channel. | | Parameters | ---------- | sample : float | The sample value. | timestamp : float | The absolute timestamp in milliseconds. | quantity : Quantity | The `Quantity` of the new signal. | quantity_flags : Set[QuantityFlag] | The `QuantityFlag`s of the new signal. | unit : Unit | The `Unit` of the new signal. | digits : int | The total number of digits. | decimal_places : int | The number of decimal places. | | ---------------------------------------------------------------------- | Methods inherited from BaseChannel: | | actual_signal(...) | actual_signal(self: smuview.BaseChannel) -> smuview.BaseSignal | | Return the actual signal of the channel. | | Returns | ------- | BaseSignal | The actual signal object. | | add_signal(...) | add_signal(self: smuview.BaseChannel, quantity: smuview.Quantity, quantity_flags: Set[smuview.QuantityFlag], unit: smuview.Unit, custom_name: str = '') -> smuview.BaseSignal | | Add a new signal to the channel. | | Parameters | ---------- | quantity : Quantity | The `Quantity` of the new signal. | quantity_flags : Set[QuantityFlag] | The `QuantityFlag`s of the new signal. | unit : Unit | The `Unit` of the new signal. | custom_name: str | A custom name for the new signal. If empty (default), the signal name will be automatically generated. | | Returns | ------- | BaseSignal | The new signal object. | | name(...) | name(self: smuview.BaseChannel) -> str | | Return the name of the channel. | | Returns | ------- | str | The name of the channel. | | signals(...) | signals(self: smuview.BaseChannel) -> List[smuview.BaseSignal] | | Return all signals of the channel. | | Returns | ------- | List[BaseSignal] | All signals of the channel. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. class UserDevice(BaseDevice) | An user generated (virtual) device for storing custom data and showing a custom tab. | | Method resolution order: | UserDevice | BaseDevice | pybind11_builtins.pybind11_object | builtins.object | | Methods defined here: | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | ---------------------------------------------------------------------- | Methods inherited from BaseDevice: | | add_user_channel(...) | add_user_channel(self: smuview.BaseDevice, channel_name: str, channel_group_name: str) -> smuview.UserChannel | | Add a new user channel to the device. | | Parameters | ---------- | channel_name : str | The name of the new user channel. | channel_group_name : str | The name of the channel group where to create the user channel. Can be empty. | | Returns | ------- | UserChannel | The new user channel object. | | channels(...) | channels(self: smuview.BaseDevice) -> Dict[str, smuview.BaseChannel] | | Return all channels of the device. | | Returns | ------- | Dict[str, BaseChannel] | A Dict where the key is the id of the channel and the value is the channel object. | | configurables(...) | configurables(self: smuview.BaseDevice) -> Dict[str, smuview.Configurable] | | Return all configurables of the device. | | Returns | ------- | Dict[str, Configurable] | A Dict where the key is the id of the `Configurable` and the value is the `Configurable` object. | | id(...) | id(self: smuview.BaseDevice) -> str | | Return the unique id of the device. | | Returns | ------- | str | The id of the device. | | name(...) | name(self: smuview.BaseDevice) -> str | | Return the name of the device. | | Returns | ------- | str | The name of the device. | | ---------------------------------------------------------------------- | Static methods inherited from pybind11_builtins.pybind11_object: | | __new__(*args, **kwargs) from pybind11_builtins.pybind11_type | Create and return a new object. See help(type) for accurate signature. DATA __pdoc__ = {'ConfigKey.ADCPowerlineCycles': 'Number of powerline cycle... FILE (built-in) ================================================ FILE: extdef.h ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SMUVIEW_EXTDEF_H #define SMUVIEW_EXTDEF_H #define countof(x) (sizeof(x) / sizeof(x[0])) #define begin_element(x) (&x[0]) #define end_element(x) (&x[countof(x)]) #endif // SMUVIEW_EXTDEF_H ================================================ FILE: external/.clang-tidy ================================================ --- Checks: 'nocheck-*' WarningsAsErrors: '' HeaderFilterRegex: '' ... ================================================ FILE: external/QCodeEditor/.clang-format ================================================ --- Language: Cpp # BasedOnStyle: Microsoft AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: false AfterExternBlock: true BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 1000 PointerAlignment: Right ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 4 UseTab: Never ... ================================================ FILE: external/QCodeEditor/.editorconfig ================================================ # EditorConfig file (https://editorconfig.org) for the QCodeEditor project root = true [*] end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true # QCodeEditor C++ files [**.cpp] indent_style = space indent_size = 4 [**.hpp] indent_style = space indent_size = 2 # QCodeEditor XML files [**.xml] indent_style = space indent_size = 4 [**.qrc] indent_style = space indent_size = 4 # QCodeEditor CMake files [CMakeLists.txt] indent_style = space indent_size = 4 ================================================ FILE: external/QCodeEditor/.gitignore ================================================ build/ cmake-build-debug/ cmake-build-release/ .idea/ .kdev4/ QCodeEditor.kdev4 *~ ================================================ FILE: external/QCodeEditor/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.6) project(QCodeEditor) set(CMAKE_CXX_STANDARD 17) option(BUILD_EXAMPLE "Example building required" Off) if (${BUILD_EXAMPLE}) message(STATUS "QCodeEditor example will be built.") add_subdirectory(example) endif() set(RESOURCES_FILE resources/qcodeeditor_resources.qrc ) set(INCLUDE_FILES include/QHighlightRule include/QHighlightBlockRule include/QCodeEditor include/QCXXHighlighter include/QLineNumberArea include/QStyleSyntaxHighlighter include/QSyntaxStyle include/QGLSLCompleter include/QGLSLHighlighter include/QJavaHighlighter include/QJSHighlighter include/QLanguage include/QXMLHighlighter include/QJSONHighlighter include/QLuaCompleter include/QLuaHighlighter include/QPythonHighlighter include/internal/QHighlightRule.hpp include/internal/QHighlightBlockRule.hpp include/internal/QCodeEditor.hpp include/internal/QCXXHighlighter.hpp include/internal/QJavaHighlighter.hpp include/internal/QJSHighlighter.hpp include/internal/QLineNumberArea.hpp include/internal/QStyleSyntaxHighlighter.hpp include/internal/QSyntaxStyle.hpp include/internal/QGLSLCompleter.hpp include/internal/QGLSLHighlighter.hpp include/internal/QLanguage.hpp include/internal/QXMLHighlighter.hpp include/internal/QJSONHighlighter.hpp include/internal/QLuaCompleter.hpp include/internal/QLuaHighlighter.hpp include/internal/QPythonCompleter.hpp include/internal/QPythonHighlighter.hpp ) set(SOURCE_FILES src/internal/QCodeEditor.cpp src/internal/QLineNumberArea.cpp src/internal/QCXXHighlighter.cpp src/internal/QSyntaxStyle.cpp src/internal/QStyleSyntaxHighlighter.cpp src/internal/QGLSLCompleter.cpp src/internal/QGLSLHighlighter.cpp src/internal/QJavaHighlighter.cpp src/internal/QJSHighlighter.cpp src/internal/QLanguage.cpp src/internal/QXMLHighlighter.cpp src/internal/QJSONHighlighter.cpp src/internal/QLuaCompleter.cpp src/internal/QLuaHighlighter.cpp src/internal/QPythonCompleter.cpp src/internal/QPythonHighlighter.cpp ) # Create code for QObjects set(CMAKE_AUTOMOC On) # Create code from resource files set(CMAKE_AUTORCC ON) # Generate compile_commands.json in build/ for analyzers like clang-tidy. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Find includes in corresponding build directories find_package(Qt5Core CONFIG REQUIRED) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5Gui CONFIG REQUIRED) add_library(QCodeEditor STATIC ${RESOURCES_FILE} ${SOURCE_FILES} ${INCLUDE_FILES} ) target_include_directories(QCodeEditor PUBLIC include ) if(CMAKE_COMPILER_IS_GNUCXX) target_compile_options(QCodeEditor PRIVATE -pedantic -Wall -Wextra -Weffc++ -Woverloaded-virtual -Winit-self -Wunreachable-code ) endif(CMAKE_COMPILER_IS_GNUCXX) target_link_libraries(QCodeEditor Qt5::Core Qt5::Widgets Qt5::Gui ) ================================================ FILE: external/QCodeEditor/LICENSE.MIT ================================================ MIT License Copyright (c) 2013-2019 Megaxela Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: external/QCodeEditor/README.md ================================================ # Qt Code Editor Widget [![GitHub Workflow Status](https://github.com/cpeditor/QCodeEditor/workflows/CI:%20Build%20Test/badge.svg?event=push)](https://github.com/cpeditor/QCodeEditor/actions?query=event%3Apush) It's a widget for editing/viewing code. This project uses a resource named `qcodeeditor_resources.qrc`. The main application must not use a resource file with the same name. (It's not a project from a Qt example.) ## Requirements 0. C++11 featured compiler. 0. Qt 5. ## Abilities 1. Auto parentheses. 1. Different highlight rules. 1. Auto indentation. 1. Replace tabs with spaces. 1. GLSL completion rules. 1. GLSL highlight rules. 1. C++ highlight rules. 1. XML highlight rules. 1. JSON highligh rules. 1. Java highligh rules. 1. JavaScript highligh rules. 1. Frame selection. 1. Qt Creator styles. ## Build It's a CMake-based library, so it can be used as a submodule (see the example). But here are the steps to build it as a static library (for external use for example). 1. Clone the repository: `git clone https://github.com/Megaxela/QCodeEditor` 1. Go into the repository: `cd QCodeEditor` 1. Create a build folder: `mkdir build` 1. Go into the build folder: `cd build` 1. Generate a build file for your compiler: `cmake ..` 1. If you need to build the example, specify `-DBUILD_EXAMPLE=On` on this step. 1. Build the library: `cmake --build .` ## Example By default, `QCodeEditor` uses the standard QtCreator theme. But you may specify your own by parsing it with `QSyntaxStyle`. The example uses [Dracula](https://draculatheme.com) theme. (See the example for more.) ## LICENSE Library is licensed under the [MIT License](https://opensource.org/licenses/MIT) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: external/QCodeEditor/include/QCXXHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QCodeEditor ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QGLSLCompleter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QGLSLHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QHighlightBlockRule ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QHighlightRule ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QJSHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QJSONHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QJavaHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QLanguage ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QLineNumberArea ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QLuaCompleter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QLuaHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QPythonCompleter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QPythonHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QStyleSyntaxHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QSyntaxStyle ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/QXMLHighlighter ================================================ #pragma once #include ================================================ FILE: external/QCodeEditor/include/internal/QCXXHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include // Required for inheritance // Qt #include #include class QSyntaxStyle; /** * @brief Class, that describes C++ code * highlighter. */ class QCXXHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QCXXHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QRegularExpression m_includePattern; QRegularExpression m_functionPattern; QRegularExpression m_defTypePattern; QRegularExpression m_commentStartPattern; QRegularExpression m_commentEndPattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QCodeEditor.hpp ================================================ #pragma once // Qt #include // Required for inheritance class QCompleter; class QLineNumberArea; class QSyntaxStyle; class QStyleSyntaxHighlighter; class QFramedTextAttribute; /** * @brief Class, that describes code editor. */ class QCodeEditor : public QTextEdit { Q_OBJECT public: /** * @brief The SeverityLevel enum * @note the order should be: the bigger the more important */ enum class SeverityLevel { Hint, Information, Warning, Error }; struct Parenthesis { QChar left, right; bool autoComplete, autoRemove, tabJumpOut; Parenthesis(QChar l = '(', QChar r = ')', bool complete = true, bool remove = true, bool jumpout = true) : left(l), right(r), autoComplete(complete), autoRemove(remove), tabJumpOut(jumpout) { } }; /** * @brief Constructor. * @param widget Pointer to parent widget. */ explicit QCodeEditor(QWidget *widget = nullptr); // Disable copying QCodeEditor(const QCodeEditor &) = delete; QCodeEditor &operator=(const QCodeEditor &) = delete; /** * @brief Method for getting first visible block * index. * @return Index. */ int getFirstVisibleBlock(); /** * @brief Method for setting highlighter. * @param highlighter Pointer to syntax highlighter. */ void setHighlighter(QStyleSyntaxHighlighter *highlighter); /** * @brief Method for setting syntax sty.e. * @param style Pointer to syntax style. */ void setSyntaxStyle(QSyntaxStyle *style); /** * @brief Method for setting tab replacing * enabled. */ void setTabReplace(bool enabled); /** * @brief Method for getting is tab replacing enabled. * Default value: true */ bool tabReplace() const; /** * @brief Method for setting amount of spaces, that will * replace tab. * @param val Number of spaces. */ void setTabReplaceSize(int val); /** * @brief Method for getting number of spaces, that will * replace tab if `tabReplace` is true. * Default: 4 */ int tabReplaceSize() const; /** * @brief Method for setting auto indentation enabled. */ void setAutoIndentation(bool enabled); /** * @brief Method for setting the parentheses. */ void setParentheses(const QVector &parentheses); /** * @brief Method for setting extra bottom margin enabled. */ void setExtraBottomMargin(bool enabled); /** * @brief Method for getting is auto indentation enabled. * Default: true */ bool autoIndentation() const; /** * @brief Method for setting completer. * @param completer Pointer to completer object. */ void setCompleter(QCompleter *completer); /** * @brief Method for getting completer. * @return Pointer to completer. */ QCompleter *completer() const; /** * @brief squiggle Puts a underline squiggle under text ranges in Editor * @param level defines the color of the underline depending upon the severity * @param tooltipMessage The tooltip hover message to show when over selection. * @note QPair: first -> Line number in 1-based indexing * second -> Character number in 0-based indexing */ void squiggle(SeverityLevel level, QPair, QPair, const QString &tooltipMessage); /** * @brief clearSquiggle, Clears complete squiggle from editor */ void clearSquiggle(); Q_SIGNALS: /** * @brief Signal, the font is changed by the wheel event. */ void fontChanged(const QFont &newFont); public Q_SLOTS: /** * @brief Slot, that performs insertion of * completion info into code. * @param s Data. */ void insertCompletion(const QString &s); /** * @brief Slot, that performs update of * internal editor viewport based on line * number area width. */ void updateLineNumberAreaWidth(int); /** * @brief Slot, that performs update of some * part of line number area. * @param rect Area that has to be updated. */ void updateLineNumberArea(QRect rect); /** * @brief Slot, that will proceed extra selection * for current cursor position. */ void updateExtraSelection1(); void updateExtraSelection2(); /** * @brief Slot, that will update editor style. */ void updateStyle(); /** * @brief Slot, that indent the selected lines. */ void indent(); /** * @brief Slot, that unindent the selected lines. */ void unindent(); /** * @brief Slot, that swap the selected lines up. */ void swapLineUp(); /** * @brief Slot, that swap the selected lines down. */ void swapLineDown(); /** * @brief Slot, that delete the selected lines. */ void deleteLine(); /** * @brief Slot, that duplicate the current line or the selection. */ void duplicate(); /** * @brief Slot, that toggle single line comment of the * selected lines. */ void toggleComment(); /** * @brief Slot, that toggle block comment of the selection. */ void toggleBlockComment(); protected: /** * @brief Method, that's called on any text insertion of * mimedata into editor. If it's text - it inserts text * as plain text. */ void insertFromMimeData(const QMimeData *source) override; /** * @brief Method, that's called on editor painting. This * method if overloaded for line number area redraw. */ void paintEvent(QPaintEvent *e) override; /** * @brief Method, that's called on any widget resize. * This method if overloaded for line number area * resizing. */ void resizeEvent(QResizeEvent *e) override; /** * @brief Method, update the bottom margin when the font changes. */ void changeEvent(QEvent *e) override; /** * @brief Method, update the font size when the wheel is rotated with Ctrl pressed */ void wheelEvent(QWheelEvent *e) override; /** * @brief Method, that's called on any key press, posted * into code editor widget. This method is overloaded for: * * 1. Completion * 2. Tab to spaces * 3. Low indentation * 4. Auto parenthesis */ void keyPressEvent(QKeyEvent *e) override; /** * @brief Method, that's called on focus into widget. * It's required for setting this widget to set * completer. */ void focusInEvent(QFocusEvent *e) override; /** * @brief Method for tooltip generation */ bool event(QEvent *e) override; private Q_SLOTS: /** * @brief Slot, that updates the bottom margin. */ void updateBottomMargin(); private: /** * @brief Method for initializing default * monospace font. */ void initFont(); /** * @brief Method for performing connection * of objects. */ void performConnections(); /** * @brief Method for updating geometry of line number area. */ void updateLineGeometry(); /** * @brief Method, that performs completer processing. * Returns true if event has to be dropped. * @param e Pointer to key event. * @return Shall event be dropped. */ bool proceedCompleterBegin(QKeyEvent *e); void proceedCompleterEnd(QKeyEvent *e); /** * @brief Method for getting character under * cursor. * @param offset Offset to cursor. */ QChar charUnderCursor(int offset = 0) const; /** * @brief Method for getting word under * cursor. * @return Word under cursor. */ QString wordUnderCursor() const; /** * @brief Method, that adds highlighting of * currently selected line to extra selection list. */ void highlightCurrentLine(); /** * @brief Method, that adds highlighting of * parenthesis if available. */ void highlightParenthesis(); void highlightOccurrences(); /** * @brief Method for remove the first group of regex * in each line of the selection. * @param regex remove its first group * @param force if true, remove regardless of whether * all lines are begun with regex; if false remove * only when all lines are begun with regex. * @return if regex is removed */ bool removeInEachLineOfSelection(const QRegularExpression ®ex, bool force); /** * @brief Method for add the str at the begin of regex * in each line of the selection. * @param regex add at the begin of its match * @param str string to add */ void addInEachLineOfSelection(const QRegularExpression ®ex, const QString &str); /** * @brief The SquiggleInformation struct, Line number will be index of vector+1; */ struct SquiggleInformation { SquiggleInformation() = default; SquiggleInformation(QPair start, QPair stop, const QString &text) : m_startPos(start), m_stopPos(stop), m_tooltipText(text) { } QPair m_startPos; QPair m_stopPos; QString m_tooltipText; }; QStyleSyntaxHighlighter *m_highlighter; QSyntaxStyle *m_syntaxStyle; QLineNumberArea *m_lineNumberArea; QCompleter *m_completer; bool m_autoIndentation; bool m_replaceTab; bool m_extraBottomMargin; QString m_tabReplace; QList extra1, extra2, extra_squiggles; QVector m_squiggler; QVector m_parentheses; }; ================================================ FILE: external/QCodeEditor/include/internal/QGLSLCompleter.hpp ================================================ #pragma once // Qt #include // Required for inheritance /** * @brief Class, that describes completer with * glsl specific types and functions. */ class QGLSLCompleter : public QCompleter { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QObject. */ explicit QGLSLCompleter(QObject *parent = nullptr); }; ================================================ FILE: external/QCodeEditor/include/internal/QGLSLHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include // Required for inheritance // Qt #include #include class QSyntaxStyle; /** * @brief Class, that describes Glsl code * highlighter. */ class QGLSLHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QGLSLHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QRegularExpression m_includePattern; QRegularExpression m_functionPattern; QRegularExpression m_defTypePattern; QRegularExpression m_commentStartPattern; QRegularExpression m_commentEndPattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QHighlightBlockRule.hpp ================================================ #pragma once // Qt #include #include struct QHighlightBlockRule { QHighlightBlockRule() : startPattern(), endPattern(), formatName() { } QHighlightBlockRule(QRegularExpression start, QRegularExpression end, QString format) : startPattern(std::move(start)), endPattern(std::move(end)), formatName(std::move(format)) { } QRegularExpression startPattern; QRegularExpression endPattern; QString formatName; }; ================================================ FILE: external/QCodeEditor/include/internal/QHighlightRule.hpp ================================================ #pragma once // Qt #include #include struct QHighlightRule { QHighlightRule() : pattern(), formatName() { } QHighlightRule(QRegularExpression p, QString f) : pattern(std::move(p)), formatName(std::move(f)) { } QRegularExpression pattern; QString formatName; }; ================================================ FILE: external/QCodeEditor/include/internal/QJSHighlighter.hpp ================================================ #pragma once #include #include // Required for inheritance // Qt #include #include class QSyntaxStyle; /** * @brief Derived to implement highlighting of JavaScript code. */ class QJSHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructs a new instance of a JavaScript highlighter. * @param document The text document to be highlighted. * This may be a null pointer. */ explicit QJSHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QRegularExpression m_commentStartPattern; QRegularExpression m_commentEndPattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QJSONHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include // Required for inheritance // Qt #include /** * @brief Class, that describes JSON code * highlighter. */ class QJSONHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QJSONHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QRegularExpression m_keyRegex; }; ================================================ FILE: external/QCodeEditor/include/internal/QJavaHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include // Required for inheritance // Qt #include #include class QSyntaxStyle; /** * @brief Derived to implement highlighting of Java code. */ class QJavaHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructs a new instance of a Java highlighter. * @param document The text document to be highlighted. * This may be a null pointer. */ explicit QJavaHighlighter(QTextDocument *document = nullptr); protected: /** * @brief Derived to highlight blocks of Java code. * @param text The block of text containing Java code. */ void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QRegularExpression m_commentStartPattern; QRegularExpression m_commentEndPattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QLanguage.hpp ================================================ #pragma once // Qt #include #include // Required for inheritance #include class QIODevice; /** * Class, that describes object for parsing * language file. */ class QLanguage : public QObject { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QObject. */ explicit QLanguage(QIODevice *device = nullptr, QObject *parent = nullptr); /** * @brief Method for parsing. * @param device Pointer to device. * @return Success. */ bool load(QIODevice *device); /** * @brief Method for getting available keys. */ QStringList keys(); /** * @brief Method for getting names * from key. * @param name * @return */ QStringList names(const QString &key); /** * @brief Method for getting is object loaded. */ bool isLoaded() const; private: bool m_loaded; QMap m_list; }; ================================================ FILE: external/QCodeEditor/include/internal/QLineNumberArea.hpp ================================================ #pragma once // Qt #include // Required for inheritance #include class QSyntaxStyle; /** * @brief Class, that describes line number area widget. */ class QLineNumberArea : public QWidget { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QTextEdit widget. */ explicit QLineNumberArea(QCodeEditor *parent = nullptr); // Disable copying QLineNumberArea(const QLineNumberArea &) = delete; QLineNumberArea &operator=(const QLineNumberArea &) = delete; /** * @brief Overridden method for getting line number area * size. */ QSize sizeHint() const override; /** * @brief Method for setting syntax style object. * @param style Pointer to syntax style. */ void setSyntaxStyle(QSyntaxStyle *style); /** * @brief Method for getting syntax style. * @return Pointer to syntax style. */ QSyntaxStyle *syntaxStyle() const; void lint(QCodeEditor::SeverityLevel level, int from, int to); void clearLint(); protected: void paintEvent(QPaintEvent *event) override; private: QSyntaxStyle *m_syntaxStyle; QCodeEditor *m_codeEditParent; QMap m_squiggles; }; ================================================ FILE: external/QCodeEditor/include/internal/QLuaCompleter.hpp ================================================ #pragma once // Qt #include // Required for inheritance /** * @brief Class, that describes completer with * glsl specific types and functions. */ class QLuaCompleter : public QCompleter { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QObject. */ explicit QLuaCompleter(QObject *parent = nullptr); }; ================================================ FILE: external/QCodeEditor/include/internal/QLuaHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include #include // Required for inheritance // Qt #include #include #include class QSyntaxStyle; /** * @brief Class, that describes C++ code * highlighter. */ class QLuaHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QLuaHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QVector m_highlightBlockRules; QRegularExpression m_requirePattern; QRegularExpression m_functionPattern; QRegularExpression m_defTypePattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QPythonCompleter.hpp ================================================ #pragma once // Qt #include // Required for inheritance /** * @brief Class, that describes completer with * glsl specific types and functions. */ class QPythonCompleter : public QCompleter { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QObject. */ explicit QPythonCompleter(QObject *parent = nullptr); }; ================================================ FILE: external/QCodeEditor/include/internal/QPythonHighlighter.hpp ================================================ #pragma once // QCodeEditor #include #include #include // Required for inheritance // Qt #include #include class QSyntaxStyle; /** * @brief Class, that describes Glsl code * highlighter. */ class QPythonHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QPythonHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: QVector m_highlightRules; QVector m_highlightBlockRules; QRegularExpression m_includePattern; QRegularExpression m_functionPattern; QRegularExpression m_defTypePattern; }; ================================================ FILE: external/QCodeEditor/include/internal/QStyleSyntaxHighlighter.hpp ================================================ #pragma once // Qt #include // Required for inheritance class QSyntaxStyle; /** * @brief Class, that descrubes highlighter with * syntax style. */ class QStyleSyntaxHighlighter : public QSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to text document. */ explicit QStyleSyntaxHighlighter(QTextDocument *document = nullptr); // Disable copying QStyleSyntaxHighlighter(const QStyleSyntaxHighlighter &) = delete; QStyleSyntaxHighlighter &operator=(const QStyleSyntaxHighlighter &) = delete; /** * @brief Method for setting syntax style. * @param style Pointer to syntax style. */ void setSyntaxStyle(QSyntaxStyle *style); /** * @brief Method for getting syntax style. * @return Pointer to syntax style. May be nullptr. */ QSyntaxStyle *syntaxStyle() const; /** * @brief Method for getting a sequence that marks a comment line. * @return QString containing a sequence that marks a comment line. * @details Returned value can be empty meaning that this language doesn't * support single-line comments */ QString commentLineSequence() const; /** * @brief Method to set a sequence that marks a comment line. * @param commentLineSequence a sequence that marks a comment line. Can be empty. */ void setCommentLineSequence(const QString &commentLineSequence); /** * @brief Method for getting a sequence that marks a start of a multi line comment block. * @return QString containing a sequence that marks a multi line comment block. * @details Returned value can be empty meaning that this language doesn't * support multi line comments */ QString startCommentBlockSequence() const; /** * @brief Method to set a sequence that marks a start of a multi line comment block. * @param commentLineSequence a sequence that marks a start of a multi line comment block. Can be empty. */ void setStartCommentBlockSequence(const QString &startCommentBlockSequence); /** * @brief Method for getting a sequence that marks a end of a multi line comment block. * @return QString containing a sequence that marks an end multi line comment block. * @details Returned value can be empty meaning that this language doesn't * support multi line comments. */ QString endCommentBlockSequence() const; /** * @brief Method to set a sequence that marks an end of a multi line comment block. * @param commentLineSequence a sequence that marks an end of a multi line comment block. Can be empty. */ void setEndCommentBlockSequence(const QString &endCommentBlockSequence); private: QSyntaxStyle *m_syntaxStyle; protected: QString m_commentLineSequence; QString m_startCommentBlockSequence; QString m_endCommentBlockSequence; }; ================================================ FILE: external/QCodeEditor/include/internal/QSyntaxStyle.hpp ================================================ #pragma once // Qt #include #include // Required for inheritance #include #include /** * @brief Class, that describes Qt style * parser for QCodeEditor. */ class QSyntaxStyle : public QObject { Q_OBJECT public: /** * @brief Constructor. * @param parent Pointer to parent QObject */ explicit QSyntaxStyle(QObject *parent = nullptr); /** * @brief Method for loading and parsing * style. * @param fl Style. * @return Success. */ bool load(const QString &fl); /** * @brief Method for getting style name. * @return Name. */ QString name() const; /** * @brief Method for checking is syntax style loaded. * @return Is loaded. */ bool isLoaded() const; /** * @brief Method for getting format for property * name. * @param name Property name. * @return Text char format. */ QTextCharFormat getFormat(const QString &name) const; /** * @brief Static method for getting default style. * @return Pointer to default style. */ static QSyntaxStyle *defaultStyle(); private: QString m_name; QMap m_data; bool m_loaded; }; ================================================ FILE: external/QCodeEditor/include/internal/QXMLHighlighter.hpp ================================================ #pragma once // QCodeEditor #include // Required for inheritance // Qt #include #include /** * @brief Class, that describes XML code * highlighter. */ class QXMLHighlighter : public QStyleSyntaxHighlighter { Q_OBJECT public: /** * @brief Constructor. * @param document Pointer to document. */ explicit QXMLHighlighter(QTextDocument *document = nullptr); protected: void highlightBlock(const QString &text) override; private: void highlightByRegex(const QTextCharFormat &format, const QRegularExpression ®ex, const QString &text); QVector m_xmlKeywordRegexes; QRegularExpression m_xmlElementRegex; QRegularExpression m_xmlAttributeRegex; QRegularExpression m_xmlValueRegex; QRegularExpression m_xmlCommentBeginRegex; QRegularExpression m_xmlCommentEndRegex; }; ================================================ FILE: external/QCodeEditor/resources/default_style.xml ================================================ "); html.append(""); /* Library info */ html.append(""); html.append(QString("") .arg(QString("Qt"), qVersion())); html.append(QString("") .arg(QString("Qwt"), QWT_VERSION_STR)); html.append(QString("") .arg(QString("glibmm"), SV_GLIBMM_VERSION)); html.append(QString("") .arg(QString("Boost"), BOOST_LIB_VERSION)); html.append(QString("") .arg(QString("pybind11"), SV_PYBIND11_VERSION)); html.append(QString("") .arg(QString("Python"), SV_PYTHON_VERSION)); html.append(QString("") .arg(QString("libsigrok"), SR_PACKAGE_VERSION_STRING, SR_LIB_VERSION_STRING, sr_package_version_string_get(), sr_lib_version_string_get())); GSList *libs_orig = sr_buildinfo_libs_get(); for (GSList *lib = libs_orig; lib; lib = lib->next) { GSList *lib_data = static_cast(lib->data); const char *name = static_cast(lib_data->data); const char *version = static_cast(lib_data->next->data); html.append(QString("") .arg(QString(name), QString(version))); g_slist_free_full(lib_data, g_free); } g_slist_free(libs_orig); char *host = sr_buildinfo_host_get(); html.append(QString("") .arg(QString(host))); g_free(host); char *scpi_backends = sr_buildinfo_scpi_backends_get(); html.append(QString("") .arg(QString(scpi_backends))); g_free(scpi_backends); /* Set up the supported field */ html.append(""); html.append(""); for (const auto &entry : context->drivers()) { html.append(QString("") .arg(QString::fromUtf8(entry.first.c_str()), QString::fromUtf8(entry.second->long_name().c_str()))); } // No need for input formats /* html.append(""); html.append(""); for (const auto &entry : context->input_formats()) { html.append(QString("") .arg(QString::fromUtf8(entry.first.c_str()), QString::fromUtf8(entry.second->description().c_str()))); } */ // No need for output formats /* html.append(""); html.append(""); for (const auto &entry : context->output_formats()) { html.append(QString("") .arg(QString::fromUtf8(entry.first.c_str()), QString::fromUtf8(entry.second->description().c_str()))); } */ html.append("
" + tr("Libraries and features:") + "
%1%2
%1%2
%1%2
%1%2
%1%2
%1%2
%1%2/%3 (rt: %4/%5)
- %1%2
- Host%1
- SCPI backends%1
" + tr("Supported hardware drivers:") + "
%1%2
" + tr("Supported input formats:") + "
%1%2
" + tr("Supported output formats:") + "
%1%2
"); QTextDocument *supported_doc = new QTextDocument(); supported_doc->setHtml(html); QTextBrowser *support_list = new QTextBrowser(); support_list->setDocument(supported_doc); QGridLayout *layout = new QGridLayout(); layout->addWidget(icon, 0, 0, 1, 1); layout->addWidget(version_info, 0, 1, 1, 1); layout->addWidget(support_list, 1, 1, 1, 1); QWidget *page = new QWidget(parent); page->setLayout(layout); return page; } QWidget *AboutDialog::get_device_page(QWidget *parent) const { QLabel *icon = new QLabel(); icon->setPixmap(QPixmap(QString::fromUtf8(":/icons/smuview.svg"))); // Device info auto sr_device = device_->sr_device(); auto hw_device = dynamic_pointer_cast(device_); shared_ptr sr_hw_device = nullptr; if (hw_device) sr_hw_device = hw_device->sr_hardware_device(); QString device_info_text(""); if (sr_device->vendor().length() > 0) { device_info_text.append(QString("%1 ").arg( QString::fromStdString(sr_device->vendor()))); } device_info_text.append(QString("%1").arg( QString::fromStdString(sr_device->model()))); if (sr_device->version().length() > 0) { device_info_text.append(QString(" (%1)").arg( QString::fromStdString(sr_device->version()))); } QString sn("-"); if (sr_device->serial_number().length() > 0) sn = QString::fromStdString(sr_device->serial_number()); device_info_text.append( QString("
" + tr("Serial Number") + ": %1").arg(sn)); QString conn_id("-"); if (sr_device->connection_id().length() > 0) conn_id = QString::fromStdString(sr_device->connection_id()); device_info_text.append( QString("
" + tr("Connection") + ": %1").arg(conn_id)); QString id("-"); if (device_->id().length() > 0) id = QString::fromStdString(device_->id()); device_info_text.append( QString("
" + tr("Device ID") + ": %1").arg(id)); QLabel *device_info = new QLabel(); device_info->setText(device_info_text); QString html; html.append(""); html.append(""); /* Device functions */ html.append(""); html.append(QString("")); html.append(""); html.append(QString("") .arg(devices::deviceutil::format_device_type(device_->type()))); html.append(""); /* SmuView device configurables and config keys */ if (hw_device) { html.append(""); for (const auto &c_pair : hw_device->configurable_map()) { auto configurable = c_pair.second; html.append(QString("") .arg(configurable->display_name())); html.append(QString("")); html.append(QString("")); auto props = configurable->property_map(); for (const auto &prop : props) { html.append(QString("")); html.append(QString("") .arg(devices::deviceutil::format_config_key(prop.first))); if (prop.second->is_getable()) { html.append(QString("")); //if (prop.second->value().canConvert()) // html.append(QString("").arg( // prop.second->value().toString())); //else html.append(QString("")); } else html.append(QString("")); if (prop.second->is_setable()) html.append(QString("")); else html.append(QString("")); if (prop.second->is_listable()) html.append(QString("")); else html.append(QString("")); html.append(QString("")); } } html.append(""); } html.append("
" + tr("Sigrok device functions:") + "
 ")); if (sr_hw_device) { const auto sr_keys = sr_hw_device->driver()->config_keys(); QString sep(""); for (const auto &sr_key : sr_keys) { html.append(sep).append( QString::fromStdString(sr_key->description())); sep = QString(", "); } } html.append(QString("
" + tr("SmuView device functions:") + "
 %1
 
" + tr("SmuView device configurables and properties:") + "
 %1GETValueSETLISTValues
 %1X%1?  X X   
 
"); QTextDocument *device_doc = new QTextDocument(); device_doc->setHtml(html); QTextBrowser *device_list = new QTextBrowser(); device_list->setDocument(device_doc); QGridLayout *layout = new QGridLayout(); layout->addWidget(icon, 0, 0, 1, 1); layout->addWidget(device_info, 0, 1, 1, 1); layout->addWidget(device_list, 1, 1, 1, 1); QWidget *page = new QWidget(parent); page->setLayout(layout); return page; } void AboutDialog::on_page_changed( QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; pages->setCurrentIndex(page_list->row(current)); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/aboutdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017 Soeren Apel * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_ABOUTDIALOG_HPP #define UI_DIALOGS_ABOUTDIALOG_HPP #include #include #include #include "src/devicemanager.hpp" namespace sv { namespace devices { class BaseDevice; } namespace ui { namespace dialogs { class AboutDialog : public QDialog { Q_OBJECT public: AboutDialog(DeviceManager &device_manager, shared_ptr device, QWidget *parent = nullptr); private: void create_pages(); QWidget *get_about_page(QWidget *parent) const; QWidget *get_device_page(QWidget *parent) const; DeviceManager &device_manager_; shared_ptr device_; QListWidget *page_list; QStackedWidget *pages; private Q_SLOTS: void on_page_changed(QListWidgetItem *current, QListWidgetItem *previous); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_ABOUTDIALOG_HPP ================================================ FILE: src/ui/dialogs/addmathchanneldialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "addmathchanneldialog.hpp" #include "src/channels/addscchannel.hpp" #include "src/channels/basechannel.hpp" #include "src/channels/dividechannel.hpp" #include "src/channels/integratechannel.hpp" #include "src/channels/mathchannel.hpp" #include "src/channels/movingavgchannel.hpp" #include "src/channels/multiplysfchannel.hpp" #include "src/channels/multiplysschannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/data/quantitycombobox.hpp" #include "src/ui/data/quantityflagslist.hpp" #include "src/ui/data/unitcombobox.hpp" #include "src/ui/devices/channelgroupcombobox.hpp" #include "src/ui/devices/devicecombobox.hpp" #include "src/ui/devices/selectsignalwidget.hpp" using std::make_shared; using std::set; using std::static_pointer_cast; using std::string; Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) namespace sv { namespace ui { namespace dialogs { AddMathChannelDialog::AddMathChannelDialog(const Session &session, shared_ptr device, QWidget *parent) : QDialog(parent), session_(session), device_(device) { assert(device); setup_ui(); } void AddMathChannelDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Add Math Channel")); this->setMinimumWidth(550); QVBoxLayout *main_layout = new QVBoxLayout(); // General stuff QFormLayout *form_layout = new QFormLayout(); name_edit_ = new QLineEdit(); form_layout->addRow(tr("Name"), name_edit_); main_layout->addLayout(form_layout); // Measured Quantity QGroupBox *mq_group = new QGroupBox(tr("Measured Quantity")); QFormLayout *mq_layout = new QFormLayout(); quantity_box_ = new ui::data::QuantityComboBox(); mq_layout->addRow(tr("Quantity"), quantity_box_); quantity_flags_list_ = new ui::data::QuantityFlagsList(); mq_layout->addRow(tr("Quantity Flags"), quantity_flags_list_); unit_box_ = new ui::data::UnitComboBox(); mq_layout->addRow(tr("Unit"), unit_box_); mq_group->setLayout(mq_layout); main_layout->addWidget(mq_group); // Add to... QGroupBox *add_to_group = new QGroupBox(tr("Add to...")); QFormLayout *add_to_layout = new QFormLayout(); device_box_ = new ui::devices::DeviceComboBox(session_); device_box_->select_device(device_); add_to_layout->addRow(tr("Device"), device_box_); channel_group_box_ = new ui::devices::ChannelGroupComboBox(device_); channel_group_box_->addItem(QString(tr("Math"))); connect(device_box_, &ui::devices::DeviceComboBox::device_changed, this, &AddMathChannelDialog::on_device_changed); add_to_layout->addRow(tr("Channel Group"), channel_group_box_); add_to_group->setLayout(add_to_layout); main_layout->addWidget(add_to_group); // Tabs tab_widget_ = new QTabWidget(); this->setup_ui_multiply_signals_tab(); this->setup_ui_multiply_signal_tab(); this->setup_ui_divide_signals_tab(); this->setup_ui_add_signal_tab(); this->setup_ui_integrate_signal_tab(); this->setup_ui_movingavg_signal_tab(); tab_widget_->setCurrentIndex(0); main_layout->addWidget(tab_widget_); // Buttons button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &AddMathChannelDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &AddMathChannelDialog::reject); this->setLayout(main_layout); } void AddMathChannelDialog::setup_ui_multiply_signals_tab() { QString title(tr("S\u2081(t) * S\u2082(t)")); QWidget *widget = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(); QGroupBox *signal1_group = new QGroupBox(tr("Signal 1")); QVBoxLayout *s1_layout = new QVBoxLayout(); m_ss_signal1_ = new ui::devices::SelectSignalWidget(session_); m_ss_signal1_->select_device(device_); s1_layout->addWidget(m_ss_signal1_); signal1_group->setLayout(s1_layout); layout->addWidget(signal1_group); QGroupBox *signal2_group = new QGroupBox(tr("Signal 2")); QVBoxLayout *s2_layout = new QVBoxLayout(); m_ss_signal2_ = new ui::devices::SelectSignalWidget(session_); m_ss_signal2_->select_device(device_); s2_layout->addWidget(m_ss_signal2_); signal2_group->setLayout(s2_layout); layout->addWidget(signal2_group); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void AddMathChannelDialog::setup_ui_multiply_signal_tab() { QString title(tr("S(t) * f")); QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); QGroupBox *signal_group = new QGroupBox(tr("Signal")); QVBoxLayout *s_layout = new QVBoxLayout(); m_sf_signal_ = new ui::devices::SelectSignalWidget(session_); m_sf_signal_->select_device(device_); s_layout->addWidget(m_sf_signal_); signal_group->setLayout(s_layout); layout->addWidget(signal_group); QFormLayout *f_layout = new QFormLayout(); m_sf_factor_edit_ = new QLineEdit(); f_layout->addRow(tr("Factor"), m_sf_factor_edit_); layout->addLayout(f_layout); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void AddMathChannelDialog::setup_ui_divide_signals_tab() { QString title(tr("S\u2081(t) / S\u2082(t)")); QWidget *widget = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(); QGroupBox *signal1_group = new QGroupBox(tr("Signal 1")); QVBoxLayout *s1_layout = new QVBoxLayout(); d_ss_signal1_ = new ui::devices::SelectSignalWidget(session_); d_ss_signal1_->select_device(device_); s1_layout->addWidget(d_ss_signal1_); signal1_group->setLayout(s1_layout); layout->addWidget(signal1_group); QGroupBox *signal2_group = new QGroupBox(tr("Signal 2")); QVBoxLayout *s2_layout = new QVBoxLayout(); d_ss_signal2_ = new ui::devices::SelectSignalWidget(session_); d_ss_signal2_->select_device(device_); s2_layout->addWidget(d_ss_signal2_); signal2_group->setLayout(s2_layout); layout->addWidget(signal2_group); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void AddMathChannelDialog::setup_ui_add_signal_tab() { QString title(tr("S(t) + c")); QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); QGroupBox *signal_group = new QGroupBox(tr("Signal")); QVBoxLayout *s_layout = new QVBoxLayout(); a_sc_signal_ = new ui::devices::SelectSignalWidget(session_); a_sc_signal_->select_device(device_); s_layout->addWidget(a_sc_signal_); signal_group->setLayout(s_layout); layout->addWidget(signal_group); QFormLayout *c_layout = new QFormLayout(); a_sc_constant_edit_ = new QLineEdit(); c_layout->addRow(tr("Constant"), a_sc_constant_edit_); layout->addLayout(c_layout); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void AddMathChannelDialog::setup_ui_integrate_signal_tab() { QString title(tr("\u222B S(t) * dt")); QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); QGroupBox *signal_group = new QGroupBox(tr("Signal")); QVBoxLayout *s_layout = new QVBoxLayout(); i_s_signal_ = new ui::devices::SelectSignalWidget(session_); i_s_signal_->select_device(device_); s_layout->addWidget(i_s_signal_); signal_group->setLayout(s_layout); layout->addWidget(signal_group); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void AddMathChannelDialog::setup_ui_movingavg_signal_tab() { QString title(tr("Moving Average")); QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); QGroupBox *signal_group = new QGroupBox(tr("Signal")); QVBoxLayout *s_layout = new QVBoxLayout(); ma_signal_ = new ui::devices::SelectSignalWidget(session_); ma_signal_->select_device(device_); s_layout->addWidget(ma_signal_); signal_group->setLayout(s_layout); layout->addWidget(signal_group); QFormLayout *ac_layout = new QFormLayout(); ma_num_samples_box_ = new QSpinBox(); ma_num_samples_box_->setMinimum(1); ac_layout->addRow(tr("Sample count"), ma_num_samples_box_); layout->addLayout(ac_layout); widget->setLayout(layout); tab_widget_->addTab(widget, title); } shared_ptr AddMathChannelDialog::channel() const { return channel_; } QString AddMathChannelDialog::channel_group_name() const { return channel_group_box_->selected_channel_group(); } void AddMathChannelDialog::accept() { if (name_edit_->text().size() == 0) { QMessageBox::warning(this, tr("Channel name missing"), tr("Please enter a name for the new channel."), QMessageBox::Ok); return; } auto device = device_box_->selected_device(); string chg_name = channel_group_name().toStdString(); set channel_group_names { chg_name }; auto quantity = quantity_box_->selected_quantity(); auto quantity_flags = quantity_flags_list_->selected_quantity_flags(); auto unit = unit_box_->selected_unit(); switch (tab_widget_->currentIndex()) { case 0: { if (m_ss_signal1_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose signal 1 for the signal multiplication."), QMessageBox::Ok); return; } auto signal_1 = static_pointer_cast( m_ss_signal1_->selected_signal()); if (m_ss_signal2_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose signal 2 for the signal multiplication."), QMessageBox::Ok); return; } auto signal_2 = static_pointer_cast( m_ss_signal2_->selected_signal()); double start_timestamp = signal_1->signal_start_timestamp(); if (signal_2->signal_start_timestamp() < start_timestamp) start_timestamp = signal_2->signal_start_timestamp(); channel_ = make_shared( quantity, quantity_flags, unit, signal_1, signal_2, device, channel_group_names, name_edit_->text().toStdString(), start_timestamp); } break; case 1: { if (m_sf_signal_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose a signal for the factor multiplication."), QMessageBox::Ok); return; } auto signal = static_pointer_cast( m_sf_signal_->selected_signal()); if (m_sf_factor_edit_->text().size() == 0) { QMessageBox::warning(this, tr("Factor missing"), tr("Please enter a factor for the factor multiplication."), QMessageBox::Ok); return; } bool ok; double factor = QString(m_sf_factor_edit_->text()).toDouble(&ok); if (!ok) { QMessageBox::warning(this, tr("Factor not a number"), tr("Please enter a number as factor for the factor multiplication."), QMessageBox::Ok); return; } channel_ = make_shared( quantity, quantity_flags, unit, signal, factor, device, channel_group_names, name_edit_->text().toStdString(), signal->signal_start_timestamp()); } break; case 2: { if (d_ss_signal1_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose signal 1 for the signal division."), QMessageBox::Ok); return; } auto signal1 = static_pointer_cast( d_ss_signal1_->selected_signal()); if (d_ss_signal2_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose signal 2 for the signal division."), QMessageBox::Ok); return; } auto signal2 = static_pointer_cast( d_ss_signal2_->selected_signal()); double start_timestamp = signal1->signal_start_timestamp(); if (signal2->signal_start_timestamp() < start_timestamp) start_timestamp = signal2->signal_start_timestamp(); channel_ = make_shared( quantity, quantity_flags, unit, signal1, signal2, device, channel_group_names, name_edit_->text().toStdString(), start_timestamp); } break; case 3: { if (a_sc_signal_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose a signal for the constant addition."), QMessageBox::Ok); return; } auto signal = static_pointer_cast( a_sc_signal_->selected_signal()); if (a_sc_constant_edit_->text().size() == 0) { QMessageBox::warning(this, tr("Constant missing"), tr("Please enter a constant for the constant addition."), QMessageBox::Ok); return; } bool ok; double constant = QString(a_sc_constant_edit_->text()).toDouble(&ok); if (!ok) { QMessageBox::warning(this, tr("Constant not a number"), tr("Please enter a number as constant for the constant addition."), QMessageBox::Ok); return; } channel_ = make_shared( quantity, quantity_flags, unit, signal, constant, device, channel_group_names, name_edit_->text().toStdString(), signal->signal_start_timestamp()); } break; case 4: { if (i_s_signal_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose a signal for the integration."), QMessageBox::Ok); return; } auto signal = static_pointer_cast( i_s_signal_->selected_signal()); channel_ = make_shared( quantity, quantity_flags, unit, signal, device, channel_group_names, name_edit_->text().toStdString(), signal->signal_start_timestamp()); } break; case 5: { if (ma_signal_->selected_signal() == nullptr) { QMessageBox::warning(this, tr("Signal missing"), tr("Please choose a signal for the moving average."), QMessageBox::Ok); return; } auto signal = static_pointer_cast( ma_signal_->selected_signal()); uint num_samples = ma_num_samples_box_->value(); channel_ = make_shared( quantity, quantity_flags, unit, signal, num_samples, device, channel_group_names, name_edit_->text().toStdString(), signal->signal_start_timestamp()); } break; default: break; } QDialog::accept(); } void AddMathChannelDialog::on_device_changed() { channel_group_box_->change_device(device_box_->selected_device()); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/addmathchanneldialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_ADDMATHCHANNELDIALOG_HPP #define UI_DIALOGS_ADDMATHCHANNELDIALOG_HPP #include #include #include #include #include #include #include "src/session.hpp" using std::shared_ptr; namespace sv { namespace devices { class BaseDevice; } namespace channels { class MathChannel; } namespace ui { namespace data { class QuantityComboBox; class QuantityFlagsList; class UnitComboBox; } namespace devices { class ChannelGroupComboBox; class DeviceComboBox; class SelectSignalWidget; } namespace dialogs { class AddMathChannelDialog : public QDialog { Q_OBJECT public: AddMathChannelDialog(const Session &session, shared_ptr device, QWidget *parent = nullptr); shared_ptr channel() const; QString channel_group_name() const; private: void setup_ui(); void setup_ui_multiply_signals_tab(); void setup_ui_multiply_signal_tab(); void setup_ui_divide_signals_tab(); void setup_ui_divide_signal_tab(); void setup_ui_add_signal_tab(); void setup_ui_integrate_signal_tab(); void setup_ui_movingavg_signal_tab(); const Session &session_; shared_ptr device_; shared_ptr channel_; QTabWidget *tab_widget_; QLineEdit *name_edit_; ui::data::QuantityComboBox *quantity_box_; ui::data::QuantityFlagsList *quantity_flags_list_; ui::data::UnitComboBox *unit_box_; ui::devices::DeviceComboBox *device_box_; ui::devices::ChannelGroupComboBox *channel_group_box_; ui::devices::SelectSignalWidget *m_ss_signal1_; ui::devices::SelectSignalWidget *m_ss_signal2_; ui::devices::SelectSignalWidget *m_sf_signal_; QLineEdit *m_sf_factor_edit_; ui::devices::SelectSignalWidget *d_ss_signal1_; ui::devices::SelectSignalWidget *d_ss_signal2_; ui::devices::SelectSignalWidget *a_sc_signal_; QLineEdit *a_sc_constant_edit_; ui::devices::SelectSignalWidget *i_s_signal_; ui::devices::SelectSignalWidget *ma_signal_; QSpinBox *ma_num_samples_box_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; private Q_SLOTS: void on_device_changed(); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_ADDMATHCHANNELDIALOG_HPP ================================================ FILE: src/ui/dialogs/adduserchanneldialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "adduserchanneldialog.hpp" #include "src/channels/userchannel.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/data/quantitycombobox.hpp" #include "src/ui/data/quantityflagslist.hpp" #include "src/ui/data/unitcombobox.hpp" #include "src/ui/devices/channelgroupcombobox.hpp" #include "src/ui/devices/devicecombobox.hpp" using std::shared_ptr; using std::string; Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) namespace sv { namespace ui { namespace dialogs { AddUserChannelDialog::AddUserChannelDialog(const Session &session, shared_ptr device, QWidget *parent) : QDialog(parent), session_(session), device_(device) { assert(device); setup_ui(); } void AddUserChannelDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Add User Channel")); this->setMinimumWidth(550); QVBoxLayout *main_layout = new QVBoxLayout(); // General stuff QFormLayout *form_layout = new QFormLayout(); name_edit_ = new QLineEdit(); form_layout->addRow(tr("Name"), name_edit_); main_layout->addLayout(form_layout); // Measured Quantity QGroupBox *mq_group = new QGroupBox(tr("Measured Quantity")); QFormLayout *mq_layout = new QFormLayout(); quantity_box_ = new ui::data::QuantityComboBox(); mq_layout->addRow(tr("Quantity"), quantity_box_); quantity_flags_list_ = new ui::data::QuantityFlagsList(); mq_layout->addRow(tr("Quantity Flags"), quantity_flags_list_); unit_box_ = new ui::data::UnitComboBox(); mq_layout->addRow(tr("Unit"), unit_box_); mq_group->setLayout(mq_layout); main_layout->addWidget(mq_group); // Add to... QGroupBox *add_to_group = new QGroupBox(tr("Add to...")); QFormLayout *add_to_layout = new QFormLayout(); device_box_ = new ui::devices::DeviceComboBox(session_); device_box_->select_device(device_); add_to_layout->addRow(tr("Device"), device_box_); channel_group_box_ = new ui::devices::ChannelGroupComboBox(device_); channel_group_box_->addItem(QString(tr("User"))); connect(device_box_, &ui::devices::DeviceComboBox::device_changed, this, &AddUserChannelDialog::on_device_changed); add_to_layout->addRow(tr("Channel Group"), channel_group_box_); add_to_group->setLayout(add_to_layout); main_layout->addWidget(add_to_group); // Buttons button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &AddUserChannelDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &AddUserChannelDialog::reject); this->setLayout(main_layout); } shared_ptr AddUserChannelDialog::channel() { return channel_; } void AddUserChannelDialog::accept() { if (name_edit_->text().size() == 0) { QMessageBox::warning(this, tr("Channel name missing"), tr("Please enter a name for the new channel."), QMessageBox::Ok); return; } auto device = device_box_->selected_device(); channel_ = device->add_user_channel( name_edit_->text().toStdString(), channel_group_box_->selected_channel_group().toStdString()); QDialog::accept(); } void AddUserChannelDialog::on_device_changed() { channel_group_box_->change_device(device_box_->selected_device()); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/adduserchanneldialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_ADDUSERCHANNELDIALOG_HPP #define UI_DIALOGS_ADDUSERCHANNELDIALOG_HPP #include #include #include #include #include "src/session.hpp" using std::shared_ptr; namespace sv { namespace devices { class BaseDevice; } namespace channels { class UserChannel; } namespace ui { namespace data { class QuantityComboBox; class QuantityFlagsList; class UnitComboBox; } namespace devices { class ChannelGroupComboBox; class DeviceComboBox; } namespace dialogs { class AddUserChannelDialog : public QDialog { Q_OBJECT public: AddUserChannelDialog(const Session &session, shared_ptr device, QWidget *parent = nullptr); shared_ptr channel(); private: void setup_ui(); const Session &session_; shared_ptr device_; shared_ptr channel_; QLineEdit *name_edit_; ui::data::QuantityComboBox *quantity_box_; ui::data::QuantityFlagsList *quantity_flags_list_; ui::data::UnitComboBox *unit_box_; ui::devices::DeviceComboBox *device_box_; ui::devices::ChannelGroupComboBox *channel_group_box_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; private Q_SLOTS: void on_device_changed(); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_ADDUSERCHANNELDIALOG_HPP ================================================ FILE: src/ui/dialogs/addviewdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "addviewdialog.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/properties/baseproperty.hpp" #include "src/data/properties/doubleproperty.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/devices/selectconfigurableform.hpp" #include "src/ui/devices/selectpropertyform.hpp" #include "src/ui/devices/selectsignalwidget.hpp" #include "src/ui/devices/devicetree/devicetreeview.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/dataview.hpp" #include "src/ui/views/powerpanelview.hpp" #include "src/ui/views/sequenceoutputview.hpp" #include "src/ui/views/timeplotview.hpp" #include "src/ui/views/valuepanelview.hpp" #include "src/ui/views/viewhelper.hpp" #include "src/ui/views/xyplotview.hpp" using std::set; using std::static_pointer_cast; Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) namespace sv { namespace ui { namespace dialogs { AddViewDialog::AddViewDialog(Session &session, const shared_ptr device, int selected_tab, QWidget *parent) : QDialog(parent), session_(session), device_(device), selected_tab_(selected_tab) { setup_ui(); } void AddViewDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Add View")); this->setMinimumWidth(500); QVBoxLayout *main_layout = new QVBoxLayout; tab_widget_ = new QTabWidget(); this->setup_ui_control_tab(); this->setup_ui_sequence_tab(); this->setup_ui_panel_tab(); this->setup_ui_time_plot_tab(); this->setup_ui_xy_plot_tab(); this->setup_ui_data_table_tab(); this->setup_ui_power_panel_tab(); tab_widget_->setCurrentIndex(selected_tab_); main_layout->addWidget(tab_widget_); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &AddViewDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &AddViewDialog::reject); this->setLayout(main_layout); } void AddViewDialog::setup_ui_control_tab() { QString title(tr("Control")); QWidget *control_widget = new QWidget(); configurable_configurable_form_ = new ui::devices::SelectConfigurableForm(session_); configurable_configurable_form_->select_device(device_); control_widget->setLayout(configurable_configurable_form_); tab_widget_->addTab(control_widget, title); } void AddViewDialog::setup_ui_sequence_tab() { QString title(tr("Sequence Output")); QWidget *sequence_widget = new QWidget(); sequence_property_form_ = new ui::devices::SelectPropertyForm(session_); sequence_property_form_->select_device(device_); sequence_property_form_->filter_config_keys(set{ sv::data::DataType::Double}); sequence_widget->setLayout(sequence_property_form_); tab_widget_->addTab(sequence_widget, title); } void AddViewDialog::setup_ui_panel_tab() { QString title(tr("Panel")); QWidget *panel_widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); panel_widget->setLayout(layout); panel_channel_tree_ = new ui::devices::devicetree::DeviceTreeView( session_, false, false, true, false, false, false, false, false); panel_channel_tree_->expand_device(device_); layout->addWidget(panel_channel_tree_); tab_widget_->addTab(panel_widget, title); } void AddViewDialog::setup_ui_time_plot_tab() { QString title(tr("Time Plot")); QWidget *plot_widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); plot_widget->setLayout(layout); time_plot_channel_tree_ = new ui::devices::devicetree::DeviceTreeView( session_, false, false, true, true, false, false, false, false); time_plot_channel_tree_->expand_device(device_); layout->addWidget(time_plot_channel_tree_); tab_widget_->addTab(plot_widget, title); } void AddViewDialog::setup_ui_xy_plot_tab() { QString title(tr("XY Plot")); QWidget *plot_widget = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(); plot_widget->setLayout(layout); QGroupBox *x_signal_group = new QGroupBox(tr("X Signal")); QVBoxLayout *x_layout = new QVBoxLayout(); xy_plot_x_signal_widget_ = new ui::devices::SelectSignalWidget(session_); xy_plot_x_signal_widget_->select_device(device_); x_layout->addWidget(xy_plot_x_signal_widget_); x_signal_group->setLayout(x_layout); layout->addWidget(x_signal_group); QGroupBox *y_signal_group = new QGroupBox(tr("Y Signal")); QVBoxLayout *y_layout = new QVBoxLayout(); xy_plot_y_signal_widget_ = new ui::devices::SelectSignalWidget(session_); xy_plot_y_signal_widget_->select_device(device_); y_layout->addWidget(xy_plot_y_signal_widget_); y_signal_group->setLayout(y_layout); layout->addWidget(y_signal_group); tab_widget_->addTab(plot_widget, title); } void AddViewDialog::setup_ui_data_table_tab() { QString title(tr("Data Table")); QWidget *table_widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); table_widget->setLayout(layout); data_table_signal_tree_ = new ui::devices::devicetree::DeviceTreeView( session_, false, false, false, true, false, false, false, false); data_table_signal_tree_->expand_device(device_); layout->addWidget(data_table_signal_tree_); tab_widget_->addTab(table_widget, title); } void AddViewDialog::setup_ui_power_panel_tab() { QString title(tr("Power Panel")); QWidget *pp_widget = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(); pp_widget->setLayout(layout); QGroupBox *voltage_signal_group = new QGroupBox(tr("Voltage Signal")); QVBoxLayout *voltage_layout = new QVBoxLayout(); ppanel_voltage_signal_widget_ = new ui::devices::SelectSignalWidget(session_); ppanel_voltage_signal_widget_->filter_quantity(data::Quantity::Voltage); ppanel_voltage_signal_widget_->select_device(device_); voltage_layout->addWidget(ppanel_voltage_signal_widget_); voltage_signal_group->setLayout(voltage_layout); layout->addWidget(voltage_signal_group); QGroupBox *current_signal_group = new QGroupBox(tr("Current Signal")); QVBoxLayout *current_layout = new QVBoxLayout(); ppanel_current_signal_widget_ = new ui::devices::SelectSignalWidget(session_); ppanel_current_signal_widget_->filter_quantity(data::Quantity::Current); ppanel_current_signal_widget_->select_device(device_); current_layout->addWidget(ppanel_current_signal_widget_); current_signal_group->setLayout(current_layout); layout->addWidget(current_signal_group); tab_widget_->addTab(pp_widget, title); } vector AddViewDialog::views() { return views_; } void AddViewDialog::accept() { int tab_index = tab_widget_->currentIndex(); switch (tab_index) { case 0: // Add control view for configurable { auto configurable = configurable_configurable_form_->selected_configurable(); auto conf_views = views::viewhelper::get_views_for_configurable( session_, configurable); for (const auto &view : conf_views) { views_.push_back(view); } } break; case 1: // Add sequence view for property { auto property = sequence_property_form_->selected_property(); auto *conf_views = new ui::views::SequenceOutputView(session_); conf_views->set_property( static_pointer_cast(property)); views_.push_back(conf_views); } break; case 2: // Add value panel view for (const auto &channel : panel_channel_tree_->checked_channels()) { auto *conf_views = new ui::views::ValuePanelView(session_); conf_views->set_channel(channel); views_.push_back(conf_views); } break; case 3: // Add time plot view for (const auto &channel : time_plot_channel_tree_->checked_channels()) { auto *conf_views = new ui::views::TimePlotView(session_); conf_views->set_channel(channel); views_.push_back(conf_views); } for (const auto &signal : time_plot_channel_tree_->checked_signals()) { auto *conf_views = new ui::views::TimePlotView(session_); conf_views->add_signal(static_pointer_cast(signal)); views_.push_back(conf_views); } break; case 4: // Add x/y plot view { auto x_signal = xy_plot_x_signal_widget_->selected_signal(); auto y_signal = xy_plot_y_signal_widget_->selected_signal(); if (x_signal != nullptr && y_signal != nullptr) { auto *view = new ui::views::XYPlotView(session_); view->add_signals( static_pointer_cast(x_signal), static_pointer_cast(y_signal)); views_.push_back(view); } } break; case 5: // Add data table view { auto signals = data_table_signal_tree_->checked_signals(); if (!signals.empty()) { auto *view = new ui::views::DataView(session_); for (const auto &signal : signals) { view->add_signal( static_pointer_cast(signal)); } views_.push_back(view); } } break; case 6: // Add power panel view { auto v_signal = ppanel_voltage_signal_widget_->selected_signal(); auto c_signal = ppanel_current_signal_widget_->selected_signal(); if (v_signal != nullptr && c_signal != nullptr) { auto *view = new ui::views::PowerPanelView(session_); view->set_signals( static_pointer_cast(v_signal), static_pointer_cast(c_signal)); views_.push_back(view); } } break; default: break; } QDialog::accept(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/addviewdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_ADDVIEWDIALOG_HPP #define UI_DIALOGS_ADDVIEWDIALOG_HPP #include #include #include #include #include "src/session.hpp" using std::shared_ptr; using std::vector; namespace sv { namespace devices { class BaseDevice; } namespace ui { namespace devices { class SelectConfigurableForm; class SelectPropertyForm; class SelectSignalWidget; namespace devicetree { class DeviceTreeView; } } namespace views { class BaseView; } namespace dialogs { class AddViewDialog : public QDialog { Q_OBJECT public: AddViewDialog(Session &session, const shared_ptr device, int selected_tab, QWidget *parent = nullptr); vector views(); private: void setup_ui(); void setup_ui_control_tab(); void setup_ui_sequence_tab(); void setup_ui_panel_tab(); void setup_ui_time_plot_tab(); void setup_ui_xy_plot_tab(); void setup_ui_data_table_tab(); void setup_ui_power_panel_tab(); Session &session_; const shared_ptr device_; int selected_tab_; vector views_; QTabWidget *tab_widget_; ui::devices::SelectConfigurableForm *configurable_configurable_form_; ui::devices::SelectPropertyForm *sequence_property_form_; ui::devices::devicetree::DeviceTreeView *panel_channel_tree_; ui::devices::devicetree::DeviceTreeView *time_plot_channel_tree_; ui::devices::SelectSignalWidget *xy_plot_x_signal_widget_; ui::devices::SelectSignalWidget *xy_plot_y_signal_widget_; ui::devices::devicetree::DeviceTreeView *data_table_signal_tree_; ui::devices::SelectSignalWidget *ppanel_voltage_signal_widget_; ui::devices::SelectSignalWidget *ppanel_current_signal_widget_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_ADDVIEWDIALOG_HPP ================================================ FILE: src/ui/dialogs/connectdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012-2013 Joel Holdsworth * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "connectdialog.hpp" #include "src/devicemanager.hpp" #include "src/devices/deviceutil.hpp" using std::list; using std::map; using std::shared_ptr; using std::string; using Glib::ustring; using Glib::Variant; using Glib::VariantBase; using sigrok::ConfigKey; using sigrok::Driver; using sv::devices::HardwareDevice; namespace sv { namespace ui { namespace dialogs { ConnectDialog::ConnectDialog(sv::DeviceManager &device_manager, QWidget *parent) : QDialog(parent), device_manager_(device_manager), layout_(this), form_(this), form_layout_(&form_), drivers_(&form_), scan_button_(tr("&Scan for devices using driver above"), this), device_list_(this), button_box_(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this) { qRegisterMetaType>("std::map"); setWindowTitle(tr("Connect to Device")); connect(&button_box_, &QDialogButtonBox::accepted, this, &ConnectDialog::accept); connect(&button_box_, &QDialogButtonBox::rejected, this, &ConnectDialog::reject); connect(this, &ConnectDialog::populate_serials_done, this, &ConnectDialog::populate_serials_finish); populate_drivers(); connect(&drivers_, QOverload::of(&QComboBox::activated), this, &ConnectDialog::driver_selected); form_.setLayout(&form_layout_); QVBoxLayout *vbox_drv = new QVBoxLayout; vbox_drv->addWidget(&drivers_); QGroupBox *groupbox_drv = new QGroupBox(tr("Step 1: Choose the driver")); groupbox_drv->setLayout(vbox_drv); form_layout_.addRow(groupbox_drv); radiobtn_usb_ = new QRadioButton(tr("&USB"), this); radiobtn_serial_ = new QRadioButton(tr("Serial &Port"), this); radiobtn_tcp_ = new QRadioButton(tr("&TCP/IP"), this); radiobtn_usb_->setChecked(true); serial_config_ = new QWidget(); QHBoxLayout *serial_config_layout = new QHBoxLayout(serial_config_); serial_devices_.setEditable(true); serial_devices_.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); serial_config_layout->addWidget(&serial_devices_); serial_baudrate_.setEditable(true); serial_baudrate_.addItem(""); serial_baudrate_.addItem("921600"); serial_baudrate_.addItem("115200"); serial_baudrate_.addItem("57600"); serial_baudrate_.addItem("19200"); serial_baudrate_.addItem("9600"); serial_config_layout->addWidget(&serial_baudrate_); serial_config_layout->addWidget(new QLabel("baud")); serial_config_->setEnabled(false); tcp_config_ = new QWidget(); QHBoxLayout *tcp_config_layout = new QHBoxLayout(tcp_config_); tcp_host_ = new QLineEdit; tcp_host_->setText("192.168.1.100"); tcp_config_layout->addWidget(tcp_host_); tcp_config_layout->addWidget(new QLabel(":")); tcp_port_ = new QSpinBox; tcp_port_->setRange(1, 65535); tcp_port_->setValue(5555); tcp_config_layout->addWidget(tcp_port_); tcp_config_layout->addSpacing(30); tcp_config_layout->addWidget(new QLabel(tr("Protocol:"))); tcp_protocol_ = new QComboBox(); tcp_protocol_->addItem("Raw TCP", QVariant("tcp-raw/%1/%2")); tcp_protocol_->addItem("VXI", QVariant("vxi/%1/%2")); tcp_config_layout->addWidget(tcp_protocol_); tcp_config_layout->setContentsMargins(0, 0, 0, 0); tcp_config_->setEnabled(false); check_available_libs(); QVBoxLayout *vbox_if = new QVBoxLayout; vbox_if->addWidget(radiobtn_usb_); vbox_if->addWidget(radiobtn_serial_); vbox_if->addWidget(serial_config_); vbox_if->addWidget(radiobtn_tcp_); vbox_if->addWidget(tcp_config_); QGroupBox *groupbox_if = new QGroupBox(tr("Step 2: Choose the interface")); groupbox_if->setLayout(vbox_if); form_layout_.addRow(groupbox_if); QVBoxLayout *vbox_scan = new QVBoxLayout; vbox_scan->addWidget(&scan_button_); QGroupBox *groupbox_scan = new QGroupBox(tr("Step 3: Scan for devices")); groupbox_scan->setLayout(vbox_scan); form_layout_.addRow(groupbox_scan); QVBoxLayout *vbox_select = new QVBoxLayout; // Let the device list occupy only the minimum space needed device_list_.setMaximumHeight(device_list_.minimumSizeHint().height()); vbox_select->addWidget(&device_list_); QGroupBox *groupbox_select = new QGroupBox(tr("Step 4: Select the device")); groupbox_select->setLayout(vbox_select); form_layout_.addRow(groupbox_select); unset_connection(); connect(radiobtn_serial_, &QRadioButton::toggled, this, &ConnectDialog::serial_toggled); connect(radiobtn_tcp_, &QRadioButton::toggled, this, &ConnectDialog::tcp_toggled); connect(&scan_button_, &QPushButton::pressed, this, &ConnectDialog::scan_pressed); if (gpib_avialable_) { radiobtn_gpib_ = new QRadioButton(tr("&GPIB"), this); /* * TODO: Replace with QComboBox and prefill with available GPIB * connection strings (like the serial box). * Must be implemented in libsigrok. */ gpib_libgpib_name_ = new QLineEdit; gpib_libgpib_name_->setEnabled(false); vbox_if->addWidget(radiobtn_gpib_); vbox_if->addWidget(gpib_libgpib_name_); connect(radiobtn_gpib_, &QRadioButton::toggled, this, &ConnectDialog::gpib_toggled); } setLayout(&layout_); layout_.addWidget(&form_); layout_.addWidget(&button_box_); // Initially populate serials for current selected device driver_selected(drivers_.currentIndex()); } ConnectDialog::~ConnectDialog() { /* * NOTE: Wait until a potentially running populate_serials_thread_ thread * has finished, otherwise sv will crash. * Waiting for the lock/mutex isn't strictly needed (empty d'tor is * sufficient), but better safe than sorry. :) */ std::lock_guard lock(populate_serials_mtx_); } shared_ptr ConnectDialog::get_selected_device() const { const QListWidgetItem *const item = device_list_.currentItem(); if (!item) return shared_ptr(); return item->data(Qt::UserRole).value>(); } void ConnectDialog::populate_drivers() { for (const auto &entry : device_manager_.context()->drivers()) { auto name = entry.first; auto sr_driver = entry.second; if (sv::devices::deviceutil::is_supported_driver(sr_driver)) { drivers_.addItem(QString("%1 (%2)").arg( sr_driver->long_name().c_str(), name.c_str()), QVariant::fromValue(sr_driver)); } } } void ConnectDialog::check_available_libs() { gpib_avialable_ = false; QString libgpib("libgpib"); GSList *libs_orig = sr_buildinfo_libs_get(); for (GSList *lib = libs_orig; lib; lib = lib->next) { GSList *lib_data = static_cast(lib->data); QString name(static_cast(lib_data->data)); if (QString::compare(name, libgpib, Qt::CaseInsensitive) == 0) { gpib_avialable_ = true; g_slist_free_full(lib_data, g_free); break; } g_slist_free_full(lib_data, g_free); } g_slist_free(libs_orig); } void ConnectDialog::populate_serials_start(shared_ptr driver) { serial_devices_.clear(); serial_devices_.addItem(tr("Loading...")); serial_config_->setDisabled(true); populate_serials_thread_ = std::thread(&ConnectDialog::populate_serials_thread_proc, this, driver); populate_serials_thread_.detach(); } void ConnectDialog::populate_serials_finish( const std::map &serials) { std::lock_guard lock(populate_serials_mtx_); serial_devices_.clear(); for (const auto &serial : serials) { serial_devices_.addItem(QString("%1 (%2)").arg( serial.first.c_str(), serial.second.c_str()), QString::fromStdString(serial.first)); } if (radiobtn_serial_->isChecked()) serial_config_->setDisabled(false); } void ConnectDialog::populate_serials_thread_proc(shared_ptr driver) { std::unique_lock lock(populate_serials_mtx_, std::try_to_lock); if (lock.owns_lock()) { map serials = device_manager_.context()->serials(driver); Q_EMIT populate_serials_done(serials); } } void ConnectDialog::unset_connection() { device_list_.clear(); button_box_.button(QDialogButtonBox::Ok)->setDisabled(true); } void ConnectDialog::serial_toggled(bool checked) { std::unique_lock lock(populate_serials_mtx_, std::try_to_lock); if (lock.owns_lock()) serial_config_->setEnabled(checked); } void ConnectDialog::tcp_toggled(bool checked) { tcp_config_->setEnabled(checked); } void ConnectDialog::gpib_toggled(bool checked) { gpib_libgpib_name_->setEnabled(checked); } void ConnectDialog::scan_pressed() { device_list_.clear(); const int d_index = drivers_.currentIndex(); if (d_index == -1) return; shared_ptr driver = drivers_.itemData(d_index).value>(); assert(driver); map drvopts; if (serial_config_->isEnabled()) { QString serial; const int s_index = serial_devices_.currentIndex(); if (s_index >= 0 && s_index < serial_devices_.count() && serial_devices_.currentText() == serial_devices_.itemText(s_index)) serial = serial_devices_.itemData(s_index).value(); else serial = serial_devices_.currentText(); drvopts[ConfigKey::CONN] = Variant::create( serial.toUtf8().constData()); // Set baud rate if specified if (serial_baudrate_.currentText().length() > 0) drvopts[ConfigKey::SERIALCOMM] = Variant::create( QString("%1/8n1").arg(serial_baudrate_.currentText()).toUtf8().constData()); } if (tcp_config_->isEnabled()) { QString host = tcp_host_->text(); QString port = tcp_port_->text(); if (!host.isEmpty()) { QString conn = tcp_protocol_-> itemData(tcp_protocol_->currentIndex()).toString(); conn = conn.arg(host, port); drvopts[ConfigKey::CONN] = Variant::create( conn.toUtf8().constData()); } } if (gpib_avialable_ && gpib_libgpib_name_->isEnabled()) { QString name = gpib_libgpib_name_->text(); QString conn = QString("libgpib/%1").arg(name); drvopts[ConfigKey::CONN] = Variant::create( conn.toUtf8().constData()); } const list> devices = device_manager_.driver_scan(driver, drvopts); for (const auto &device : devices) { assert(device); QString text = device->display_name(device_manager_); text += QString(" with %1 channels").arg( device->sr_device()->channels().size()); QListWidgetItem *const item = new QListWidgetItem(text, &device_list_); item->setData(Qt::UserRole, QVariant::fromValue(device)); device_list_.addItem(item); } device_list_.setCurrentRow(0); button_box_.button(QDialogButtonBox::Ok)->setDisabled(device_list_.count() == 0); } void ConnectDialog::driver_selected(int index) { shared_ptr driver = drivers_.itemData(index).value>(); unset_connection(); populate_serials_start(driver); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/connectdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012-2013 Joel Holdsworth * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_CONNECTDIALOG_HPP #define UI_DIALOGS_CONNECTDIALOG_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/devices/hardwaredevice.hpp" using std::shared_ptr; namespace sigrok { class Driver; } Q_DECLARE_METATYPE(shared_ptr) Q_DECLARE_METATYPE(shared_ptr) namespace sv { class DeviceManager; namespace devices { class HardwareDevice; } namespace ui { namespace dialogs { class ConnectDialog : public QDialog { Q_OBJECT public: explicit ConnectDialog(sv::DeviceManager &device_manager, QWidget *parent = nullptr); ~ConnectDialog(); shared_ptr get_selected_device() const; private: void populate_drivers(); void populate_serials_start(shared_ptr driver); void populate_serials_thread_proc(shared_ptr driver); void check_available_libs(); void unset_connection(); private Q_SLOTS: void driver_selected(int index); void serial_toggled(bool checked); void tcp_toggled(bool checked); void gpib_toggled(bool checked); void scan_pressed(); void populate_serials_finish( const std::map &serials); private: sv::DeviceManager &device_manager_; bool gpib_avialable_; QVBoxLayout layout_; QWidget form_; QFormLayout form_layout_; QComboBox drivers_; QRadioButton *radiobtn_usb_; QRadioButton *radiobtn_serial_; QRadioButton *radiobtn_tcp_; QRadioButton *radiobtn_gpib_; std::thread populate_serials_thread_; std::mutex populate_serials_mtx_; QWidget *serial_config_; QComboBox serial_devices_; QComboBox serial_baudrate_; QWidget *tcp_config_; QLineEdit *tcp_host_; QSpinBox *tcp_port_; QComboBox *tcp_protocol_; QLineEdit *gpib_libgpib_name_; QPushButton scan_button_; QListWidget device_list_; QDialogButtonBox button_box_; Q_SIGNALS: void populate_serials_done(std::map serials); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_CONNECTDIALOG_HPP ================================================ FILE: src/ui/dialogs/generatewaveformdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "generatewaveformdialog.hpp" #include "src/data/properties/doubleproperty.hpp" #include "src/data/datautil.hpp" using std::shared_ptr; using std::vector; Q_DECLARE_METATYPE(sv::ui::dialogs::WaveformType) namespace sv { namespace ui { namespace dialogs { GenerateWaveformDialog::GenerateWaveformDialog( double min_value, double max_value, double step, int decimals, const QString &unit, QWidget *parent) : QDialog(parent), min_value_(min_value), max_value_(max_value), step_(step), decimals_(decimals), unit_(unit) { setup_ui(); } GenerateWaveformDialog::GenerateWaveformDialog( shared_ptr property, QWidget *parent) : QDialog(parent) { min_value_ = property->min(); max_value_ = property->max(); step_ = property->step(); decimals_ = property->decimal_places(); sv::data::Unit unit = property->unit(); if (unit != sv::data::Unit::Unitless && unit != sv::data::Unit::Unknown) unit_ = " " + sv::data::datautil::format_unit(unit); else unit_ = ""; setup_ui(); } void GenerateWaveformDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Generate Waveform")); this->setMinimumWidth(500); QFormLayout *layout = new QFormLayout; waveform_box_ = new QComboBox(); waveform_box_->addItem(tr("Sine"), QVariant::fromValue(WaveformType::Sine)); waveform_box_->addItem(tr("Square"), QVariant::fromValue(WaveformType::Square)); waveform_box_->addItem(tr("Triangle"), QVariant::fromValue(WaveformType::Triangle)); waveform_box_->addItem(tr("Sawtooth"), QVariant::fromValue(WaveformType::Sawtooth)); waveform_box_->addItem(tr("Sawtooth inverted"), QVariant::fromValue(WaveformType::SawtoothInv)); connect(waveform_box_, QOverload::of(&QComboBox::currentIndexChanged), this, &GenerateWaveformDialog::on_waveform_changed); layout->addRow(tr("Waveform"), waveform_box_); QGroupBox *amp_group = new QGroupBox(tr("Min/Max - Amplitude")); QHBoxLayout *amp_layout = new QHBoxLayout; QFormLayout *ampmm_layout = new QFormLayout; QFormLayout *ampf_layout = new QFormLayout; min_value_box_ = new QDoubleSpinBox(); min_value_box_->setMinimum(min_value_); min_value_box_->setMaximum(max_value_); min_value_box_->setSingleStep(step_); min_value_box_->setDecimals(decimals_); min_value_box_->setSuffix(unit_); connect(min_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); ampmm_layout->addRow(tr("Min. value"), min_value_box_); max_value_box_ = new QDoubleSpinBox(); max_value_box_->setMinimum(min_value_); max_value_box_->setMaximum(max_value_); max_value_box_->setSingleStep(step_); max_value_box_->setDecimals(decimals_); max_value_box_->setSuffix(unit_); connect(max_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); ampmm_layout->addRow(tr("Max. value"), max_value_box_); amp_layout->addLayout(ampmm_layout); amplitude_box_ = new QDoubleSpinBox(); amplitude_box_->setMinimum(min_value_); amplitude_box_->setMaximum(max_value_); amplitude_box_->setSingleStep(step_); amplitude_box_->setDecimals(decimals_); amplitude_box_->setSuffix(unit_); connect(amplitude_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); ampf_layout->addRow(tr("Amplitude"), amplitude_box_); offset_box_ = new QDoubleSpinBox(); offset_box_->setMinimum(min_value_); offset_box_->setMaximum(max_value_); offset_box_->setSingleStep(step_); offset_box_->setDecimals(decimals_); offset_box_->setSuffix(unit_); connect(offset_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); ampf_layout->addRow(tr("Offset"), offset_box_); amp_layout->addLayout(ampf_layout); amp_group->setLayout(amp_layout); layout->addRow(amp_group); QGroupBox *freq_group = new QGroupBox(tr("Periode - Frequency")); QHBoxLayout *freq_layout = new QHBoxLayout; QFormLayout *freqp_layout = new QFormLayout; QFormLayout *freqf_layout = new QFormLayout; periode_box_ = new QDoubleSpinBox(); periode_box_->setMinimum(0); periode_box_->setMaximum(500000); periode_box_->setSingleStep(0.1); periode_box_->setDecimals(3); periode_box_->setSuffix(" s"); connect(periode_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_periode_changed); freqp_layout->addRow(tr("Periode"), periode_box_); freq_layout->addLayout(freqp_layout); frequency_box_ = new QDoubleSpinBox(); frequency_box_->setMinimum(0); frequency_box_->setMaximum(5000); frequency_box_->setSingleStep(0.001); frequency_box_->setDecimals(4); frequency_box_->setSuffix(" Hz"); connect(frequency_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_frequency_changed); freqf_layout->addRow(tr("Frequency"), frequency_box_); freq_layout->addLayout(freqf_layout); freq_group->setLayout(freq_layout); layout->addRow(freq_group); QGroupBox *samples_group = new QGroupBox(tr("Samples")); QHBoxLayout *samples_layout = new QHBoxLayout; QFormLayout *samplesi_layout = new QFormLayout; QFormLayout *samplesc_layout = new QFormLayout; interval_box_ = new QDoubleSpinBox(); interval_box_->setMinimum(0); interval_box_->setMaximum(100000); interval_box_->setSingleStep(0.1); interval_box_->setDecimals(3); interval_box_->setSuffix(" s"); connect(interval_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_interval_changed); samplesi_layout->addRow(tr("Time between samples"), interval_box_); samples_layout->addLayout(samplesi_layout); sample_count_box_ = new QSpinBox(); sample_count_box_->setMinimum(0); sample_count_box_->setMaximum(1000000); connect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); samplesc_layout->addRow(tr("Number of samples"), sample_count_box_); samples_layout->addLayout(samplesc_layout); samples_group->setLayout(samples_layout); layout->addRow(samples_group); QGroupBox *phi_group = new QGroupBox(tr("Phase offset")); QHBoxLayout *phi_layout = new QHBoxLayout; QFormLayout *phid_layout = new QFormLayout; QFormLayout *phir_layout = new QFormLayout; phi_deg_box_ = new QDoubleSpinBox(); phi_deg_box_->setMinimum(-360); phi_deg_box_->setMaximum(360); phi_deg_box_->setSingleStep(0.1); phi_deg_box_->setDecimals(1); phi_deg_box_->setSuffix(QString(" %1").arg(QChar(0x00B0))); connect(phi_deg_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_deg_changed); phid_layout->addRow(tr("%1 (deg)").arg(QChar(0x03C6)), phi_deg_box_); phi_layout->addLayout(phid_layout); phi_rad_box_ = new QDoubleSpinBox(); phi_rad_box_->setMinimum(-2 * pi); phi_rad_box_->setMaximum(2 * pi); phi_rad_box_->setSingleStep(0.01); phi_rad_box_->setDecimals(3); connect(phi_rad_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_rad_changed); phir_layout->addRow(tr("%1 (rad)").arg(QChar(0x03C6)), phi_rad_box_); phi_layout->addLayout(phir_layout); phi_group->setLayout(phi_layout); layout->addRow(phi_group); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); layout->addRow(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &GenerateWaveformDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &GenerateWaveformDialog::reject); // Set values min_value_box_->setValue(min_value_); max_value_box_->setValue(max_value_); periode_box_->setValue(60); interval_box_->setValue(0.1); phi_deg_box_->setValue(270); this->setLayout(layout); } vector GenerateWaveformDialog::sequence_values() const { return sequence_values_; } vector GenerateWaveformDialog::sequence_delays() const { return sequence_delays_; } void GenerateWaveformDialog::accept() { double amplitude = amplitude_box_->value(); double offset = offset_box_->value(); double interval = interval_box_->value(); double periode; double frequency; // Get the most precise values for periode and frequency, because // the values from the spin boxes are truncated! if (frequency_box_->value() > 1) { frequency = frequency_box_->value(); periode = 1 / frequency; } else { periode = periode_box_->value(); frequency = 1 / periode; } double phi = phi_rad_box_->value(); double omega = 2 * pi * frequency; WaveformType w_type = waveform_box_->currentData().value(); // NOTE: We must not rely on floating point types for loop termination. // Resolves clang-analyzer-security.FloatLoopCounter warnings. double time = .0; size_t const count = std::floor(periode / interval); for (size_t i = 0; i < count; ++i) { double x = omega * time + phi; // NOLINT(readability-identifier-length) time += interval; double value; if (w_type == WaveformType::Sine) value = std::sin(x); else if (w_type == WaveformType::Square) value = std::sin(x) < 0 ? -1 : 1; else if (w_type == WaveformType::Triangle) value = (std::asin(std::sin(x))) / (pi/2); else if (w_type == WaveformType::Sawtooth) // y = −arctan(cotan(x)) value = -1 * std::atan(1 / std::tan(x)) / (pi/2); else if (w_type == WaveformType::SawtoothInv) value = std::atan(1 / std::tan(x)) / (pi/2); else value = 0; value = (amplitude * value) + offset; sequence_values_.push_back(value); sequence_delays_.push_back(interval); } QDialog::accept(); } void GenerateWaveformDialog::on_waveform_changed() { WaveformType w_type = waveform_box_->currentData().value(); if (w_type == WaveformType::Sine || w_type == WaveformType::Triangle) phi_deg_box_->setValue(270); else phi_deg_box_->setValue(0); } void GenerateWaveformDialog::on_min_max_changed() { disconnect(amplitude_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); disconnect(offset_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); amplitude_box_->setValue((max_value_box_->value() - min_value_box_->value()) / 2); offset_box_->setValue(amplitude_box_->value() + min_value_box_->value()); connect(amplitude_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); connect(offset_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_amp_offs_changed); } void GenerateWaveformDialog::on_amp_offs_changed() { disconnect(min_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); disconnect(max_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); min_value_box_->setValue(offset_box_->value() - amplitude_box_->value()); max_value_box_->setValue(offset_box_->value() + amplitude_box_->value()); connect(min_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); connect(max_value_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_min_max_changed); } void GenerateWaveformDialog::on_periode_changed() { disconnect(frequency_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_frequency_changed); disconnect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); frequency_box_->setValue(1 / periode_box_->value()); sample_count_box_->setValue( std::floor(periode_box_->value() / interval_box_->value())); connect(frequency_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_frequency_changed); connect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); } void GenerateWaveformDialog::on_frequency_changed() { disconnect(periode_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_periode_changed); disconnect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); periode_box_->setValue(1 / frequency_box_->value()); sample_count_box_->setValue( std::floor(periode_box_->value() / interval_box_->value())); connect(periode_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_periode_changed); connect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); } void GenerateWaveformDialog::on_interval_changed() { disconnect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); sample_count_box_->setValue( std::floor(periode_box_->value() / interval_box_->value())); connect(sample_count_box_, QOverload::of(&QSpinBox::valueChanged), this, &GenerateWaveformDialog::on_sample_cnt_changed); } void GenerateWaveformDialog::on_sample_cnt_changed() { disconnect(interval_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_interval_changed); interval_box_->setValue(periode_box_->value() / sample_count_box_->value()); connect(interval_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_interval_changed); } void GenerateWaveformDialog::on_phi_deg_changed() { disconnect(phi_rad_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_rad_changed); phi_rad_box_->setValue(phi_deg_box_->value() * (pi/180)); connect(phi_rad_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_rad_changed); } void GenerateWaveformDialog::on_phi_rad_changed() { disconnect(phi_deg_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_deg_changed); phi_deg_box_->setValue(phi_rad_box_->value() * (180/pi)); connect(phi_deg_box_, QOverload::of(&QDoubleSpinBox::valueChanged), this, &GenerateWaveformDialog::on_phi_deg_changed); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/generatewaveformdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_GENERATEWAVEFORMDIALOG_HPP #define UI_DIALOGS_GENERATEWAVEFORMDIALOG_HPP #include #include #include #include #include #include #include #include using std::shared_ptr; using std::vector; namespace sv { namespace data { namespace properties { class DoubleProperty; } } namespace ui { namespace dialogs { enum class WaveformType { Sine, Square, Triangle, Sawtooth, SawtoothInv, }; class GenerateWaveformDialog : public QDialog { Q_OBJECT public: GenerateWaveformDialog(double min_value, double max_value, double step, int decimals = 3, const QString &unit = "", QWidget *parent = nullptr); explicit GenerateWaveformDialog( shared_ptr property, QWidget *parent = nullptr); vector sequence_values() const; vector sequence_delays() const; private: void setup_ui(); const double pi = std::acos(-1); double min_value_; double max_value_; double step_; int decimals_; QString unit_; vector sequence_values_; vector sequence_delays_; QComboBox *waveform_box_; QDoubleSpinBox *min_value_box_; QDoubleSpinBox *max_value_box_; QDoubleSpinBox *amplitude_box_; QDoubleSpinBox *offset_box_; QDoubleSpinBox *periode_box_; QDoubleSpinBox *frequency_box_; QDoubleSpinBox *interval_box_; QSpinBox *sample_count_box_; QDoubleSpinBox *phi_deg_box_; QDoubleSpinBox *phi_rad_box_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; private Q_SLOTS: void on_waveform_changed(); void on_min_max_changed(); void on_amp_offs_changed(); void on_periode_changed(); void on_frequency_changed(); void on_interval_changed(); void on_sample_cnt_changed(); void on_phi_deg_changed(); void on_phi_rad_changed(); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_GENERATEWAVEFORMDIALOG_HPP ================================================ FILE: src/ui/dialogs/plotconfigdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plotconfigdialog.hpp" #include "src/data/datautil.hpp" #include "src/ui/views/baseplotview.hpp" #include "src/ui/widgets/plot/curve.hpp" #include "src/ui/widgets/plot/plot.hpp" Q_DECLARE_METATYPE(sv::ui::widgets::plot::PlotUpdateMode) using std::set; namespace sv { namespace ui { namespace dialogs { ColorItemDelegate::ColorItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget *ColorItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { (void)option; (void)index; QColorDialog *dialog = new QColorDialog(parent); dialog->setModal(true); return dialog; } void ColorItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { // Set background color QColor color = index.model()->data(index, Qt::EditRole).value(); painter->fillRect(option.rect, color); // Set text and text color depending on background color QStyleOptionViewItem item_option(option); initStyleOption(&item_option, index); item_option.text = color.name(); double brightness = std::sqrt( color.red() * color.red() * 0.241 + color.green() * color.green() * 0.691 + color.blue() * color.blue() * 0.068); if (brightness >= 130) item_option.palette.setColor(QPalette::Text, Qt::black); else item_option.palette.setColor(QPalette::Text, Qt::white); QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &item_option, painter); } void ColorItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QColor color = index.model()->data(index, Qt::EditRole).value(); QColorDialog *dialog = qobject_cast(editor); dialog->setCurrentColor(color); } void ColorItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QColorDialog *dialog = qobject_cast(editor); model->setData(index, QVariant(dialog->currentColor()), Qt::EditRole); } void ColorItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { (void)index; editor->setGeometry(option.rect); } PlotConfigDialog::PlotConfigDialog(widgets::plot::Plot *plot, views::PlotType plot_type, QWidget *parent) : QDialog(parent), plot_(plot), plot_type_(plot_type) { setup_ui(); } void PlotConfigDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Plot Config")); this->setMinimumWidth(500); QVBoxLayout *main_layout = new QVBoxLayout(); // Tabs tab_widget_ = new QTabWidget(); if (plot_type_ == views::PlotType::TimePlot) { this->setup_ui_plot_mode_tab(); } this->setup_ui_markers_tab(); //this->setup_ui_style_tab(); this->setup_ui_curve_colors_tab(); tab_widget_->setCurrentIndex(0); main_layout->addWidget(tab_widget_); // Buttons button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &PlotConfigDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &PlotConfigDialog::reject); this->setLayout(main_layout); } void PlotConfigDialog::setup_ui_plot_mode_tab() { QString title(tr("Plot mode")); QWidget *widget = new QWidget(); QFormLayout *layout = new QFormLayout(); plot_update_mode_combobox_ = new QComboBox(); int cb_index = 0; for (const auto &update_mode_pair : widgets::plot::plot_update_mode_name_map) { plot_update_mode_combobox_->addItem( update_mode_pair.second, QVariant::fromValue(update_mode_pair.first)); if (plot_->update_mode() == update_mode_pair.first) plot_update_mode_combobox_->setCurrentIndex(cb_index); ++cb_index; } connect( plot_update_mode_combobox_, QOverload::of(&QComboBox::currentIndexChanged), this, &PlotConfigDialog::on_update_mode_changed); layout->addRow(tr("Plot mode"), plot_update_mode_combobox_); time_span_edit_ = new QLineEdit(); time_span_edit_->setValidator(new QDoubleValidator); time_span_edit_->setText(QString("%1").arg(plot_->time_span(), 0, 'f')); layout->addRow(tr("Time span"), time_span_edit_); add_time_edit_ = new QLineEdit(); add_time_edit_->setValidator(new QDoubleValidator); add_time_edit_->setText(QString("%1").arg(plot_->add_time(), 0, 'f')); layout->addRow(tr("Add time"), add_time_edit_); switch (plot_->update_mode()) { case widgets::plot::PlotUpdateMode::Additive: setup_ui_additive(); break; case widgets::plot::PlotUpdateMode::Rolling: setup_ui_rolling(); break; case widgets::plot::PlotUpdateMode::Oscilloscope: setup_ui_oscilloscope(); break; } widget->setLayout(layout); tab_widget_->addTab(widget, title); } void PlotConfigDialog::setup_ui_markers_tab() { QString title(tr("Markers")); QWidget *widget = new QWidget(); QFormLayout *layout = new QFormLayout(); markers_box_pos_combobox_ = new QComboBox(); markers_box_pos_combobox_->addItem(tr("Top left"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignTop | Qt::AlignmentFlag::AlignLeft))); markers_box_pos_combobox_->addItem(tr("Top center"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignTop | Qt::AlignmentFlag::AlignHCenter))); markers_box_pos_combobox_->addItem(tr("Top right"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignTop | Qt::AlignmentFlag::AlignRight))); markers_box_pos_combobox_->addItem(tr("Center left"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft))); markers_box_pos_combobox_->addItem(tr("Center"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignHCenter))); markers_box_pos_combobox_->addItem(tr("Center right"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight))); markers_box_pos_combobox_->addItem(tr("Bottom left"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignBottom | Qt::AlignmentFlag::AlignLeft))); markers_box_pos_combobox_->addItem(tr("Bottom center"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignBottom | Qt::AlignmentFlag::AlignHCenter))); markers_box_pos_combobox_->addItem(tr("Bottom right"), QVariant::fromValue( (int)(Qt::AlignmentFlag::AlignBottom | Qt::AlignmentFlag::AlignRight))); for (int i = 0; i < markers_box_pos_combobox_->count(); ++i) { QVariant data_var = markers_box_pos_combobox_->itemData(i); if (data_var.toInt() == plot_->markers_label_alignment()) { markers_box_pos_combobox_->setCurrentIndex(i); break; } } layout->addRow(tr("Info box position"), markers_box_pos_combobox_); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void PlotConfigDialog::setup_ui_style_tab() { QString title(tr("Style")); QWidget *widget = new QWidget(); QFormLayout *layout = new QFormLayout(); // TODO: Plot background color? Axis position? What else? widget->setLayout(layout); tab_widget_->addTab(widget, title); } void PlotConfigDialog::setup_ui_curve_colors_tab() { QString title(tr("Default Curve Colors")); QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(); color_table_ = new QTableWidget(); color_table_->setColumnCount(2); QTableWidgetItem *quantity_header_item = new QTableWidgetItem(tr("Quantity")); quantity_header_item->setTextAlignment(Qt::AlignVCenter); color_table_->setHorizontalHeaderItem(0, quantity_header_item); color_table_->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); QTableWidgetItem *color_header_item = new QTableWidgetItem(tr("Color")); color_header_item->setTextAlignment(Qt::AlignVCenter); color_table_->setHorizontalHeaderItem(1, color_header_item); color_table_->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); color_table_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); color_table_->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); color_table_->setSelectionMode(QTableWidget::NoSelection); color_table_->setItemDelegateForColumn(1, new ColorItemDelegate()); for (const auto &q_n_pair : data::datautil::get_quantity_name_map()) { int last_row = color_table_->rowCount(); color_table_->insertRow(last_row); QTableWidgetItem *quantity_item = new QTableWidgetItem(q_n_pair.second); quantity_item->setData(Qt::UserRole, QString("%1_0").arg( data::datautil::get_sr_quantity_id(q_n_pair.first))); quantity_item->setFlags(quantity_item->flags() ^ Qt::ItemIsEditable); color_table_->setItem(last_row, 0, quantity_item); QTableWidgetItem *color_item = new QTableWidgetItem(q_n_pair.second); color_item->setData(Qt::EditRole, widgets::plot::Curve::default_color( q_n_pair.first, set())); color_table_->setItem(last_row, 1, color_item); // For Voltage and Current, we want to make AC/DC also configurable. if (q_n_pair.first == data::Quantity::Voltage || q_n_pair.first == data::Quantity::Current) { for (auto const &qf : {data::QuantityFlag::AC, data::QuantityFlag::DC}) { QString name = QString("%1 %2").arg( q_n_pair.second, data::datautil::format_quantity_flag(qf)); auto qfs = set{qf}; last_row = color_table_->rowCount(); color_table_->insertRow(last_row); QTableWidgetItem *q_item = new QTableWidgetItem(name); q_item->setData(Qt::UserRole, QString("%1_%2"). arg(data::datautil::get_sr_quantity_id(q_n_pair.first)). arg(data::datautil::get_sr_quantity_flags_id(qfs))); q_item->setFlags(q_item->flags() ^ Qt::ItemIsEditable); color_table_->setItem(last_row, 0, q_item); QTableWidgetItem *c_item = new QTableWidgetItem(name); c_item->setData(Qt::EditRole, widgets::plot::Curve::default_color(q_n_pair.first, qfs)); color_table_->setItem(last_row, 1, c_item); } } } layout->addWidget(color_table_); widget->setLayout(layout); tab_widget_->addTab(widget, title); } void PlotConfigDialog::setup_ui_additive() { time_span_edit_->setDisabled(true); add_time_edit_->setDisabled(false); } void PlotConfigDialog::setup_ui_rolling() { time_span_edit_->setDisabled(false); add_time_edit_->setDisabled(false); } void PlotConfigDialog::setup_ui_oscilloscope() { time_span_edit_->setDisabled(false); add_time_edit_->setDisabled(true); } void PlotConfigDialog::on_update_mode_changed() { QVariant update_mode_var = plot_update_mode_combobox_->currentData(); switch (update_mode_var.value()) { case widgets::plot::PlotUpdateMode::Additive: setup_ui_additive(); break; case widgets::plot::PlotUpdateMode::Rolling: setup_ui_rolling(); break; case widgets::plot::PlotUpdateMode::Oscilloscope: setup_ui_oscilloscope(); break; } } void PlotConfigDialog::accept() { if (plot_type_ == views::PlotType::TimePlot) { QVariant update_mode_var = plot_update_mode_combobox_->currentData(); sv::ui::widgets::plot::PlotUpdateMode update_mode = update_mode_var.value(); plot_->set_update_mode(update_mode); if (update_mode == widgets::plot::PlotUpdateMode::Rolling || update_mode == widgets::plot::PlotUpdateMode::Oscilloscope) plot_->set_time_span(time_span_edit_->text().toDouble()); if (update_mode == widgets::plot::PlotUpdateMode::Additive || update_mode == widgets::plot::PlotUpdateMode::Rolling) plot_->set_add_time(add_time_edit_->text().toDouble()); } plot_->set_markers_label_alignment( markers_box_pos_combobox_->currentData().toInt()); QSettings settings; settings.beginGroup("DefaultCurveColors"); for (int i=0; irowCount(); i++) { settings.setValue( color_table_->item(i, 0)->data(Qt::UserRole).toString(), color_table_->item(i, 1)->data(Qt::EditRole)); } settings.endGroup(); QDialog::accept(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/plotconfigdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_PLOTCONFIGDIALOG_HPP #define UI_DIALOGS_PLOTCONFIGDIALOG_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/ui/widgets/plot/plot.hpp" #include "src/ui/views/baseplotview.hpp" namespace sv { namespace ui { namespace dialogs { class ColorItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ColorItemDelegate(QObject *parent = nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; class PlotConfigDialog : public QDialog { Q_OBJECT public: PlotConfigDialog(widgets::plot::Plot *plot, views::PlotType plot_type, QWidget *parent = nullptr); private: void setup_ui(); void setup_ui_plot_mode_tab(); void setup_ui_markers_tab(); void setup_ui_style_tab(); void setup_ui_curve_colors_tab(); void setup_ui_additive(); void setup_ui_rolling(); void setup_ui_oscilloscope(); widgets::plot::Plot *plot_; views::PlotType plot_type_; QTabWidget *tab_widget_; QComboBox *plot_update_mode_combobox_; QLineEdit *time_span_edit_; QLineEdit *add_time_edit_; QComboBox *markers_box_pos_combobox_; QTableWidget *color_table_; QDialogButtonBox *button_box_; public Q_SLOTS: void on_update_mode_changed(); void accept() override; }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_PLOTCONFIGDIALOG_HPP ================================================ FILE: src/ui/dialogs/plotcurveconfigdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plotcurveconfigdialog.hpp" #include "src/ui/widgets/plot/curve.hpp" #include "src/ui/widgets/plot/plot.hpp" #include "src/ui/widgets/colorbutton.hpp" Q_DECLARE_METATYPE(Qt::PenStyle) Q_DECLARE_METATYPE(QwtSymbol::Style) namespace sv { namespace ui { namespace dialogs { PlotCurveConfigDialog::PlotCurveConfigDialog(widgets::plot::Curve *curve, widgets::plot::Plot *plot, QWidget *parent) : QDialog(parent), curve_(curve), plot_(plot) { assert(curve_); assert(plot_); setup_ui(); } void PlotCurveConfigDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Curve Config")); this->setMinimumWidth(500); QFormLayout *main_layout = new QFormLayout; name_edit_ = new QLineEdit(); name_edit_->setText(curve_->name()); main_layout->addRow(tr("Name"), name_edit_); visible_checkbox_ = new QCheckBox(); visible_checkbox_->setChecked(curve_->plot_curve()->isVisible()); main_layout->addRow(tr("Visible"), visible_checkbox_); color_button_ = new widgets::ColorButton(); color_button_->set_color(curve_->color()); main_layout->addRow(tr("Color"), color_button_); line_type_box_ = new QComboBox(); line_type_box_->addItem(tr("None"), QVariant::fromValue(Qt::NoPen)); line_type_box_->addItem(tr("Solid"), QVariant::fromValue(Qt::SolidLine)); line_type_box_->addItem(tr("Dots"), QVariant::fromValue(Qt::DotLine)); line_type_box_->addItem(tr("Dashes"), QVariant::fromValue(Qt::DashLine)); for (int i=0; icount(); ++i) { if (line_type_box_->itemData(i).value() == curve_->style()) { line_type_box_->setCurrentIndex(i); break; } } main_layout->addRow(tr("Line type"), line_type_box_); symbol_type_box_ = new QComboBox(); symbol_type_box_->addItem(tr("None"), QwtSymbol::NoSymbol); symbol_type_box_->addItem(tr("Dot"), QwtSymbol::Ellipse); symbol_type_box_->addItem(tr("Cross"), QwtSymbol::XCross); for (int i=0; icount(); ++i) { if (symbol_type_box_->itemData(i).value() == curve_->symbol()) { symbol_type_box_->setCurrentIndex(i); break; } } main_layout->addRow(tr("Symbol type"), symbol_type_box_); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); QPushButton *remove_button = new QPushButton( QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.png")), tr("Remove Curve")); button_box_->addButton(remove_button, QDialogButtonBox::DestructiveRole); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &PlotCurveConfigDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &PlotCurveConfigDialog::reject); connect(remove_button, &QPushButton::clicked, this, &PlotCurveConfigDialog::remove_curve); this->setLayout(main_layout); } void PlotCurveConfigDialog::accept() { curve_->set_name(name_edit_->text()); curve_->plot_curve()->setVisible(visible_checkbox_->isChecked()); curve_->set_color(color_button_->color()); curve_->set_style(line_type_box_->currentData().value()); curve_->set_symbol(symbol_type_box_->currentData().value()); QDialog::accept(); } void PlotCurveConfigDialog::remove_curve() { plot_->remove_curve(curve_); QDialog::close(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/plotcurveconfigdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_PLOTCURVECONFIGDIALOG_HPP #define UI_DIALOGS_PLOTCURVECONFIGDIALOG_HPP #include #include #include #include #include #include namespace sv { namespace ui { namespace widgets { class ColorButton; namespace plot { class Curve; class Plot; } } namespace dialogs { class PlotCurveConfigDialog : public QDialog { Q_OBJECT public: PlotCurveConfigDialog(widgets::plot::Curve *curve, widgets::plot::Plot *plot, QWidget *parent = nullptr); private: void setup_ui(); widgets::plot::Curve *curve_; widgets::plot::Plot *plot_; QLineEdit *name_edit_; QCheckBox *visible_checkbox_; widgets::ColorButton *color_button_; QComboBox *line_type_box_; QComboBox *symbol_type_box_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; void remove_curve(); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_PLOTCURVECONFIGDIALOG_HPP ================================================ FILE: src/ui/dialogs/plotdiffmarkerdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "plotdiffmarkerdialog.hpp" #include "src/ui/widgets/plot/plot.hpp" Q_DECLARE_METATYPE(QwtPlotMarker *) namespace sv { namespace ui { namespace dialogs { PlotDiffMarkerDialog::PlotDiffMarkerDialog( widgets::plot::Plot *plot, QWidget *parent) : QDialog(parent), plot_(plot) { setup_ui(); } void PlotDiffMarkerDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Plot Diff Merker")); this->setMinimumWidth(250); QVBoxLayout *main_layout = new QVBoxLayout(); QFormLayout *form_layout = new QFormLayout(); marker_1_combobox_ = new QComboBox(); for (const auto &mc_pair : plot_->marker_curve_map()) { marker_1_combobox_->addItem( mc_pair.first->title().text(), QVariant::fromValue(mc_pair.first)); } if (!plot_->marker_curve_map().empty()) marker_1_combobox_->setCurrentIndex(0); form_layout->addRow(tr("Marker 1"), marker_1_combobox_); marker_2_combobox_ = new QComboBox(); for (const auto &mc_pair : plot_->marker_curve_map()) { marker_2_combobox_->addItem( mc_pair.first->title().text(), QVariant::fromValue(mc_pair.first)); } if (plot_->marker_curve_map().size() >= 2) marker_2_combobox_->setCurrentIndex(1); else if (!plot_->marker_curve_map().empty()) marker_2_combobox_->setCurrentIndex(0); form_layout->addRow(tr("Marker 2"), marker_2_combobox_); main_layout->addLayout(form_layout); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &PlotDiffMarkerDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &PlotDiffMarkerDialog::reject); this->setLayout(main_layout); } void PlotDiffMarkerDialog::accept() { QVariant marker_1_var = marker_1_combobox_->currentData(); QVariant marker_2_var = marker_2_combobox_->currentData(); plot_->add_diff_marker( marker_1_var.value(), marker_2_var.value()); QDialog::accept(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/plotdiffmarkerdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_PLOTDIFFMARKERDIALOG_HPP #define UI_DIALOGS_PLOTDIFFMARKERDIALOG_HPP #include #include #include #include #include #include #include #include "src/ui/widgets/plot/plot.hpp" namespace sv { namespace ui { namespace dialogs { class PlotDiffMarkerDialog : public QDialog { Q_OBJECT public: explicit PlotDiffMarkerDialog(widgets::plot::Plot *plot, QWidget *parent = nullptr); private: void setup_ui(); widgets::plot::Plot *plot_; QComboBox *marker_1_combobox_; QComboBox *marker_2_combobox_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_PLOTDIFFMARKERDIALOG_HPP ================================================ FILE: src/ui/dialogs/selectsignaldialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "selectsignaldialog.hpp" #include "src/session.hpp" #include "src/data/basesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/devices/devicetree/devicetreeview.hpp" using std::shared_ptr; using std::vector; namespace sv { namespace ui { namespace dialogs { SelectSignalDialog::SelectSignalDialog(const Session &session, const shared_ptr expanded_device, QWidget *parent) : QDialog(parent), session_(session), expanded_device_(expanded_device) { setup_ui(); } void SelectSignalDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Select Signal")); this->setMinimumWidth(500); QVBoxLayout *main_layout = new QVBoxLayout; device_tree_ = new devices::devicetree::DeviceTreeView(session_, false, false, false, true, false, false, false, false); device_tree_->expand_device(expanded_device_); main_layout->addWidget(device_tree_); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &SelectSignalDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &SelectSignalDialog::reject); this->setLayout(main_layout); } vector> SelectSignalDialog::signals() { return signals_; } void SelectSignalDialog::accept() { for (const auto &signal : device_tree_->checked_signals()) { signals_.push_back(signal); } QDialog::accept(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/selectsignaldialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_SELECTSIGNALDIALOG_HPP #define UI_DIALOGS_SELECTSIGNALDIALOG_HPP #include #include #include #include #include "src/session.hpp" using std::shared_ptr; using std::vector; namespace sv { namespace data { class BaseSignal; } namespace devices { class BaseDevice; } namespace ui { namespace devices { namespace devicetree { class DeviceTreeView; } } namespace dialogs { class SelectSignalDialog : public QDialog { Q_OBJECT public: SelectSignalDialog(const Session &session, const shared_ptr expanded_device, QWidget *parent = nullptr); vector> signals(); private: void setup_ui(); const Session &session_; const shared_ptr expanded_device_; vector> signals_; ui::devices::devicetree::DeviceTreeView *device_tree_; QDialogButtonBox *button_box_; public Q_SLOTS: void accept() override; }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_SELECTSIGNALDIALOG_HPP ================================================ FILE: src/ui/dialogs/selectxysignalsdialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "selectxysignalsdialog.hpp" #include "src/session.hpp" #include "src/data/basesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/devices/selectsignalwidget.hpp" using std::shared_ptr; namespace sv { namespace ui { namespace dialogs { SelectXYSignalsDialog::SelectXYSignalsDialog(const Session &session, const shared_ptr selected_device, QWidget *parent) : QDialog(parent), session_(session), selected_device_(selected_device) { setup_ui(); } void SelectXYSignalsDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Select X/Y Signals")); this->setMinimumWidth(500); QVBoxLayout *main_layout = new QVBoxLayout; QHBoxLayout *signals_layout = new QHBoxLayout(); QGroupBox *x_signal_group = new QGroupBox(tr("X Signal")); QVBoxLayout *x_layout = new QVBoxLayout(); x_signal_widget_ = new ui::devices::SelectSignalWidget(session_); x_signal_widget_->select_device(selected_device_); x_layout->addWidget(x_signal_widget_); x_signal_group->setLayout(x_layout); signals_layout->addWidget(x_signal_group); QGroupBox *y_signal_group = new QGroupBox(tr("Y Signal")); QVBoxLayout *y_layout = new QVBoxLayout(); y_signal_widget_ = new ui::devices::SelectSignalWidget(session_); y_signal_widget_->select_device(selected_device_); y_layout->addWidget(y_signal_widget_); y_signal_group->setLayout(y_layout); signals_layout->addWidget(y_signal_group); main_layout->addLayout(signals_layout); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &SelectXYSignalsDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &SelectXYSignalsDialog::reject); this->setLayout(main_layout); } shared_ptr SelectXYSignalsDialog::x_signal() { return x_signal_widget_->selected_signal(); } shared_ptr SelectXYSignalsDialog::y_signal() { return y_signal_widget_->selected_signal(); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/selectxysignalsdialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_SELECTXYSIGNALSDIALOG_HPP #define UI_DIALOGS_SELECTXYSIGNALSDIALOG_HPP #include #include #include #include "src/session.hpp" using std::shared_ptr; namespace sv { namespace data { class BaseSignal; } namespace devices { class BaseDevice; } namespace ui { namespace devices { class SelectSignalWidget; } namespace dialogs { class SelectXYSignalsDialog : public QDialog { Q_OBJECT public: SelectXYSignalsDialog(const Session &session, const shared_ptr selected_device, QWidget *parent = nullptr); shared_ptr x_signal(); shared_ptr y_signal(); private: void setup_ui(); const Session &session_; const shared_ptr selected_device_; ui::devices::SelectSignalWidget *x_signal_widget_; ui::devices::SelectSignalWidget *y_signal_widget_; QDialogButtonBox *button_box_; }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_SELECTXYSIGNALSDIALOG_HPP ================================================ FILE: src/ui/dialogs/signalsavedialog.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "signalsavedialog.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/basesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/hardwaredevice.hpp" #include "src/ui/devices/devicetree/devicetreeview.hpp" using std::dynamic_pointer_cast; using std::ofstream; using std::string; Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) namespace sv { namespace ui { namespace dialogs { SignalSaveDialog::SignalSaveDialog(const Session &session, const shared_ptr selected_device, QWidget *parent) : QDialog(parent), session_(session), selected_device_(selected_device) { setup_ui(); QSettings settings; if (SettingsManager::restore_settings() && settings.childGroups().contains("SignalSaveDialog")) { restore_settings(settings); } else { file_dialog_path_ = QDir::homePath(); } } void SignalSaveDialog::setup_ui() { QIcon main_icon; main_icon.addFile(QStringLiteral(":/icons/smuview.ico"), QSize(), QIcon::Normal, QIcon::Off); this->setWindowIcon(main_icon); this->setWindowTitle(tr("Save Signals")); this->setMinimumWidth(450); this->setMinimumHeight(400); QVBoxLayout *main_layout = new QVBoxLayout; device_tree_ = new ui::devices::devicetree::DeviceTreeView( session_, false, false, false, true, false, false, false, false); device_tree_->expand_device(selected_device_); device_tree_->check_signals(selected_device_->signals()); main_layout->addWidget(device_tree_); QFormLayout *form_layout = new QFormLayout(); timestamps_combined_ = new QCheckBox(tr("Combine all timestamps")); form_layout->addRow("", timestamps_combined_); timestamps_combined_timeframe_ = new QSpinBox(); timestamps_combined_timeframe_->setValue(0); timestamps_combined_timeframe_->setRange( 0, std::numeric_limits::max()); timestamps_combined_timeframe_->setSuffix(" ms"); timestamps_combined_timeframe_->setDisabled( !timestamps_combined_->isChecked()); form_layout->addRow(tr("Combination time frame"), timestamps_combined_timeframe_); time_absolut_ = new QCheckBox(tr("Absolut time")); form_layout->addRow("", time_absolut_); separator_edit_ = new QLineEdit(); separator_edit_->setText(","); form_layout->addRow(tr("CSV separator"), separator_edit_); main_layout->addLayout(form_layout); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); main_layout->addWidget(button_box_); connect(timestamps_combined_, &QCheckBox::stateChanged, this, &SignalSaveDialog::toggle_combined); connect(button_box_, &QDialogButtonBox::accepted, this, &SignalSaveDialog::accept); connect(button_box_, &QDialogButtonBox::rejected, this, &SignalSaveDialog::reject); this->setLayout(main_layout); } void SignalSaveDialog::save(const QString &file_name) { ofstream output_file; string str_file_name = file_name.toStdString(); vector sample_counts; output_file.open(str_file_name); auto signals = device_tree_->checked_signals(); bool relative_time = !time_absolut_->isChecked(); string sep = separator_edit_->text().toStdString(); size_t max_sample_count = 0; // Header string start_sep; string device_header_line; string chg_name_header_line; string ch_name_header_line; string signal_name_header_line; for (const auto &signal : signals) { // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; size_t sample_count = analog_signal->sample_count(); if (sample_count > max_sample_count) max_sample_count = sample_count; sample_counts.push_back(sample_count); string name = analog_signal->name(); shared_ptr parent_channel = analog_signal->parent_channel(); qWarning() << "SaveDialog::save(): signal.name() = " << QString::fromStdString(name); qWarning() << "SaveDialog::save(): signal.parent_channel().name() = " << QString::fromStdString(parent_channel->name()); qWarning() << "SaveDialog::save(): signal.parent_channel().parent_device().name() = " << QString::fromStdString(parent_channel->parent_device()->name()); string chg_names; string chg_sep; for (const auto &chg_name : parent_channel->channel_group_names()) { chg_names += chg_sep; if (chg_name.empty()) chg_names += "\"\""; else chg_names += chg_name; // TODO: Ugly workaround. Implement escaping or quotation characters? chg_sep = sep == "," ? "; " : ", "; } device_header_line += start_sep; device_header_line += parent_channel->parent_device()->name(); // Time device_header_line += sep; device_header_line += parent_channel->parent_device()->name(); // Value chg_name_header_line += start_sep; chg_name_header_line += chg_names; // Time chg_name_header_line += sep; chg_name_header_line += chg_names; // Value ch_name_header_line += start_sep; ch_name_header_line += parent_channel->name(); // Time ch_name_header_line += sep; ch_name_header_line += parent_channel->name(); // Value signal_name_header_line += start_sep; signal_name_header_line += "Time "; signal_name_header_line += name; // Time signal_name_header_line += sep; signal_name_header_line += name; // Value start_sep = sep; } output_file << device_header_line << std::endl; output_file << chg_name_header_line << std::endl; output_file << ch_name_header_line << std::endl; output_file << signal_name_header_line << std::endl; // Data // TODO: we asume here, that the vector size is the same for all vectors.... for (size_t index = 0; index < max_sample_count; index++) { start_sep = ""; QString line(""); int sample_count_index = 0; for (const auto &signal : signals) { // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; QString time(""); QString value(""); size_t sample_count = sample_counts[sample_count_index]; if (index < sample_count-1) { // More samples for this signal auto sample = analog_signal->get_sample(index, relative_time); value = QString("%1").arg(sample.second); if (relative_time) time = QString("%1").arg(sample.first, 0, 'f', 4); else time = util::format_time_date(sample.first); } line.append(QString("%1%2%3%4").arg( QString::fromStdString(start_sep), time, QString::fromStdString(sep), value)); start_sep = sep; ++sample_count_index; } output_file << line.toStdString() << std::endl; } output_file.close(); } void SignalSaveDialog::save_combined(const QString &file_name) { ofstream output_file; string str_file_name = file_name.toStdString(); vector sample_counts; vector sample_pos; output_file.open(str_file_name); double combined_timeframe = .0; int combined_timeframe_ms = timestamps_combined_timeframe_->value(); if (combined_timeframe_ms != 0) combined_timeframe = ((double)combined_timeframe_ms) / 1000; auto signals = device_tree_->checked_signals(); bool relative_time = !time_absolut_->isChecked(); string sep = separator_edit_->text().toStdString(); // Header string device_header_line("Time"); // Time string chg_name_header_line("Time"); // Time string ch_name_header_line("Time"); // Time string signal_name_header_line("Time"); // Time for (const auto &signal : signals) { // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; shared_ptr parent_channel = analog_signal->parent_channel(); sample_counts.push_back(analog_signal->sample_count()); sample_pos.push_back(0); string chg_names; string chg_sep; for (const auto &chg_name : parent_channel->channel_group_names()) { chg_names += chg_sep; if (chg_name.empty()) chg_names += "\"\""; else chg_names += chg_name; // TODO: Ugly workaround. Implement escaping or quotation characters? chg_sep = sep == "," ? "; " : ", "; } device_header_line += sep; device_header_line += parent_channel->parent_device()->name(); // Value chg_name_header_line += sep; chg_name_header_line += chg_names; // Value ch_name_header_line += sep; ch_name_header_line += parent_channel->name(); // Value signal_name_header_line += sep; signal_name_header_line += analog_signal->name(); // Value } output_file << device_header_line << std::endl; output_file << chg_name_header_line << std::endl; output_file << ch_name_header_line << std::endl; output_file << signal_name_header_line << std::endl; // Data while (true) { double next_timestamp = -1; int index = 0; for (const auto &signal : signals) { // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; if (sample_pos[index] >= sample_counts[index]-1) continue; double timestamp = analog_signal->get_sample(sample_pos[index], relative_time).first; if (next_timestamp < 0 || timestamp < next_timestamp) next_timestamp = timestamp; ++index; } if (next_timestamp < 0) break; // Timestamp QString line; if (relative_time) line = QString("%1").arg(next_timestamp, 0, 'f', 4); else line = util::format_time_date(next_timestamp); // Values index = 0; for (const auto &signal : signals) { // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; line.append(QString::fromStdString(sep)); auto sample = analog_signal->get_sample(sample_pos[index], relative_time); double timestamp = sample.first; if (timestamp + combined_timeframe >= next_timestamp) { line.append(QString("%1").arg(sample.second, 0, 'g', -1)); ++sample_pos[index]; } ++index; } output_file << line.toStdString() << std::endl; } output_file.close(); } bool SignalSaveDialog::validate_combined_timeframe() { int combined_timeframe_ms = timestamps_combined_timeframe_->value(); if (combined_timeframe_ms == 0) return true; const double combined_timeframe = ((double)combined_timeframe_ms) / 1000; int num_signals = static_cast(device_tree_->checked_signals().size()); int act_signal = 0; QProgressDialog progress(tr("Validating combined timeframe ..."), tr("Abort validation"), 0, num_signals, this); progress.setMinimumDuration(500); progress.setWindowModality(Qt::WindowModal); double min_delta = combined_timeframe; for (const auto &signal : device_tree_->checked_signals()) { progress.setValue(act_signal++); size_t count = signal->sample_count(); if (count < 2) continue; // Only handle AnalogSignals auto analog_signal = dynamic_pointer_cast(signal); if (!analog_signal) continue; double ts1 = analog_signal->get_sample(0, false).first; for (size_t i = 1; iget_sample(i, false).first; const double delta = ts2 - ts1; if (delta < min_delta) min_delta = delta; ts1 = ts2; if (progress.wasCanceled()) return false; } } progress.setValue(num_signals); if (min_delta < combined_timeframe) { int min_delta_ms = (int)std::floor(min_delta * 1000); QMessageBox::critical(this, tr("Combination time frame too large"), tr("The combination time frame is too large. Time span must be " "smaller than %1 ms.").arg(min_delta_ms), QMessageBox::Ok); timestamps_combined_timeframe_->setValue(min_delta_ms - 1); return false; } return true; } void SignalSaveDialog::save_settings(QSettings &settings) const { settings.beginGroup("SignalSaveDialog"); settings.remove(""); // Remove all keys in this group settings.setValue("timestamps_combined", timestamps_combined_->isChecked()); settings.setValue("timestamps_combined_timeframe", timestamps_combined_timeframe_->value()); settings.setValue("time_absolut", time_absolut_->isChecked()); settings.setValue("csv_separator", separator_edit_->text()); settings.setValue("file_dialog_path", file_dialog_path_); settings.endGroup(); } void SignalSaveDialog::restore_settings(QSettings &settings) { settings.beginGroup("SignalSaveDialog"); if (settings.contains("timestamps_combined")) { timestamps_combined_->setChecked( settings.value("timestamps_combined").toBool()); } if (settings.contains("timestamps_combined_timeframe")) { timestamps_combined_timeframe_->setValue( settings.value("timestamps_combined_timeframe").toInt()); } if (settings.contains("time_absolut")) { time_absolut_->setChecked(settings.value("time_absolut").toBool()); } if (settings.contains("csv_separator")) { separator_edit_->setText(settings.value("csv_separator").toString()); } if (settings.contains("file_dialog_path")) { file_dialog_path_ = settings.value("file_dialog_path", QDir::homePath()).toString(); } settings.endGroup(); } void SignalSaveDialog::accept() { // Get file name QString file_name = QFileDialog::getSaveFileName(this, tr("Save CSV-File"), file_dialog_path_, tr("CSV Files (*.csv)")); if (file_name.isEmpty()) return; file_dialog_path_ = QDir().absoluteFilePath(file_name); if (timestamps_combined_->isChecked()) { if (!validate_combined_timeframe()) return; save_combined(file_name); } else { save(file_name); } QDialog::accept(); } void SignalSaveDialog::done(int result) { QSettings settings; save_settings(settings); QDialog::done(result); } void SignalSaveDialog::toggle_combined() { timestamps_combined_timeframe_->setDisabled( !timestamps_combined_->isChecked()); } } // namespace dialogs } // namespace ui } // namespace sv ================================================ FILE: src/ui/dialogs/signalsavedialog.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_DIALOGS_SIGNALSAVEDIALOG_HPP #define UI_DIALOGS_SIGNALSAVEDIALOG_HPP #include #include #include #include #include #include #include #include #include #include #include "src/session.hpp" using std::shared_ptr; using std::vector; namespace sv { namespace devices { class BaseDevice; } namespace ui { namespace devices { namespace devicetree { class DeviceTreeView; } } namespace dialogs { class SignalSaveDialog : public QDialog { Q_OBJECT public: SignalSaveDialog(const Session &session, const shared_ptr selected_device, QWidget *parent = nullptr); private: void setup_ui(); void save(const QString &file_name); void save_combined(const QString &file_name); bool validate_combined_timeframe(); void save_settings(QSettings &settings) const; void restore_settings(QSettings &settings); const Session &session_; const shared_ptr selected_device_; ui::devices::devicetree::DeviceTreeView *device_tree_; QCheckBox *timestamps_combined_; QSpinBox *timestamps_combined_timeframe_; QCheckBox *time_absolut_; QLineEdit *separator_edit_; QDialogButtonBox *button_box_; QString file_dialog_path_; public Q_SLOTS: void accept() override; /** The done() slot is handling the saving of the settings */ void done(int result) override; private Q_SLOTS: void toggle_combined(); }; } // namespace dialogs } // namespace ui } // namespace sv #endif // UI_DIALOGS_SIGNALSAVEDIALOG_HPP ================================================ FILE: src/ui/tabs/basetab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * Copyright (C) 2016 Soeren Apel * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "basetab.hpp" #include "src/session.hpp" #include "src/ui/tabs/tabdockwidget.hpp" namespace sv { namespace ui { namespace tabs { BaseTab::BaseTab(Session &session, QWidget *parent) : QMainWindow(parent), session_(session) { // Remove Qt::Window flag this->setWindowFlags(Qt::Widget); this->setDockNestingEnabled(true); this->setCentralWidget(new QWidget()); // Hide the central widget of the tab, so the views (dock widgets) can use // all of the available space. this->centralWidget()->hide(); } Session& BaseTab::session() { return session_; } const Session& BaseTab::session() const { return session_; } string BaseTab::id() const { return id_; } views::BaseView *BaseTab::get_view_from_view_id(const string &id) { return view_id_map_[id]; } TabDockWidget *BaseTab::create_dock_widget(views::BaseView *view, QDockWidget::DockWidgetFeatures features) { // The dock widget must be created here, because the layout must be set to // the central widget of the view main window before dock->setWidget() is // called. // Otherwise the application will flicker at startup.... TabDockWidget *dock = new TabDockWidget(view->title(), view); // An objectName is needed for QSettings dock->setObjectName(settings_id_ + ":" + QString::fromStdString(view->id())); dock->setAttribute(Qt::WA_DeleteOnClose); dock->setAllowedAreas(Qt::AllDockWidgetAreas); dock->setContextMenuPolicy(Qt::PreventContextMenu); dock->setFeatures(features); view_docks_map_[view] = dock; view_id_map_[view->id()] = view; connect(dock, &TabDockWidget::closed, this, &BaseTab::remove_view); return dock; } void BaseTab::closeEvent(QCloseEvent *event) { save_settings(); event->accept(); } void BaseTab::add_view(views::BaseView *view, Qt::DockWidgetArea area, int features) { if (!view) return; QDockWidget *dock = create_dock_widget( view, (QDockWidget::DockWidgetFeatures)features); this->addDockWidget(area, dock); // This fixes a qt bug. See: https://bugreports.qt.io/browse/QTBUG-65592 this->resizeDocks({dock}, {40}, Qt::Horizontal); } void BaseTab::add_view_ontop(views::BaseView *view, views::BaseView *existing_view, int features) { if (!view) return; QDockWidget *dock = create_dock_widget( view, (QDockWidget::DockWidgetFeatures)features); this->tabifyDockWidget(view_docks_map_[existing_view], dock); // This fixes a qt bug. See: https://bugreports.qt.io/browse/QTBUG-65592 this->resizeDocks({dock}, {40}, Qt::Horizontal); } void BaseTab::remove_view(const std::string &view_id) { auto *view = view_id_map_[view_id]; view_docks_map_.erase(view); view_id_map_.erase(view_id); } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/basetab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * Copyright (C) 2016 Soeren Apel * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_BASETAB_HPP #define UI_TABS_BASETAB_HPP #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::map; using std::string; namespace sv { class Session; namespace ui { namespace tabs { class TabDockWidget; enum class TabType { MeasurementTab, SourceSinkTab, UserTab, WelcomeTab }; /** * Use a QMainWindow (as tab widget) to allow for a tool bar. */ class BaseTab : public QMainWindow { Q_OBJECT public: explicit BaseTab(Session &session, QWidget *parent = nullptr); Session &session(); const Session &session() const; string id() const; virtual QString title() = 0; views::BaseView *get_view_from_view_id(const string &id); virtual bool request_close() = 0; private: TabDockWidget *create_dock_widget(views::BaseView *view, QDockWidget::DockWidgetFeatures features); /** This event is handling the saving of the settings. */ void closeEvent(QCloseEvent *event) override; protected: Session &session_; /** * id_ is used for accessing the tab (e.g. for the python bindings UiProxy). */ string id_; /** * settings_id_ is used for the objectNames of the tab and its views for * identification in the settings (e.g. geometry and status). */ QString settings_id_; map view_docks_map_; map view_id_map_; virtual void save_settings() const = 0; virtual void restore_settings() = 0; public Q_SLOTS: /* * When using QDockWidget::DockWidgetFeatures instead of int for features, * the given flags are or'ed to the default flags instead of replacing * the default falgs. */ void add_view(views::BaseView *view, Qt::DockWidgetArea area, int features = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); /* * When using QDockWidget::DockWidgetFeatures instead of int for features, * the given flags are or'ed to the default flags instead of replacing * the default falgs. */ void add_view_ontop(views::BaseView *view, views::BaseView *existing_view, int features = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); private Q_SLOTS: void remove_view(const std::string &view_id); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_BASETAB_HPP ================================================ FILE: src/ui/tabs/devicetab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "devicetab.hpp" #include "src/session.hpp" #include "src/channels/userchannel.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/dialogs/aboutdialog.hpp" #include "src/ui/dialogs/addmathchanneldialog.hpp" #include "src/ui/dialogs/addviewdialog.hpp" #include "src/ui/dialogs/signalsavedialog.hpp" #include "src/ui/tabs/basetab.hpp" #include "src/ui/tabs/tabdockwidget.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; using std::string; namespace sv { namespace ui { namespace tabs { const std::string DeviceTab::TAB_ID_PREFIX = "devicetab:"; DeviceTab::DeviceTab(Session &session, shared_ptr device, QWidget *parent) : BaseTab(session, parent), device_(device), action_aquire_(new QAction(this)), action_save_as_(new QAction(this)), action_add_control_view_(new QAction(this)), action_add_panel_view_(new QAction(this)), action_add_plot_view_(new QAction(this)), action_add_table_view_(new QAction(this)), action_add_math_channel_(new QAction(this)), action_about_(new QAction(this)) { id_ = TAB_ID_PREFIX + device_->id(); settings_id_ = QString::fromStdString(TAB_ID_PREFIX) + device_->settings_id(); setup_toolbar(); } QString DeviceTab::title() { return device_->short_name(); } bool DeviceTab::request_close() { QMessageBox::StandardButton reply = QMessageBox::information(this, tr("Close device tab"), tr("Closing the device tab will leave the device connected!"), QMessageBox::Ok | QMessageBox::Cancel); return reply == QMessageBox::Ok; } void DeviceTab::clear_signals() { } void DeviceTab::setup_toolbar() { action_aquire_->setText(tr("Stop")); action_aquire_->setIconText(tr("Stop")); action_aquire_->setIcon(QIcon(":/icons/status-green.svg")); action_aquire_->setCheckable(true); action_aquire_->setChecked(true); connect(action_aquire_, &QAction::triggered, this, &DeviceTab::on_action_aquire_triggered); QToolButton *aquire_button_ = new QToolButton(); aquire_button_->setDefaultAction(action_aquire_); aquire_button_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); action_save_as_->setText(tr("&Save As...")); action_save_as_->setIconText(""); action_save_as_->setIcon( QIcon::fromTheme("document-save", QIcon(":/icons/document-save.png"))); action_save_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); connect(action_save_as_, &QAction::triggered, this, &DeviceTab::on_action_save_as_triggered); action_add_control_view_->setText(tr("Add Control")); action_add_control_view_->setIcon( QIcon::fromTheme("mixer-front", QIcon(":/icons/mixer-front.png"))); connect(action_add_control_view_, &QAction::triggered, this, &DeviceTab::on_action_add_control_view_triggered); action_add_panel_view_->setText(tr("Add Panel")); action_add_panel_view_->setIcon( QIcon::fromTheme("chronometer", QIcon(":/icons/chronometer.png"))); connect(action_add_panel_view_, &QAction::triggered, this, &DeviceTab::on_action_add_panel_view_triggered); action_add_plot_view_->setText(tr("Add Plot")); action_add_plot_view_->setIcon( QIcon::fromTheme("office-chart-line", QIcon(":/icons/office-chart-line.png"))); connect(action_add_plot_view_, &QAction::triggered, this, &DeviceTab::on_action_add_plot_view_triggered); action_add_table_view_->setText(tr("Add data table")); action_add_table_view_->setIcon( QIcon::fromTheme("view-form-table", QIcon(":/icons/view-form-table.png"))); connect(action_add_table_view_, &QAction::triggered, this, &DeviceTab::on_action_add_table_view_triggered); action_add_math_channel_->setText(tr("Add Math Channel")); action_add_math_channel_->setIcon( QIcon::fromTheme("office-chart-line-percentage", QIcon(":/icons/office-chart-line-percentage.png"))); connect(action_add_math_channel_, &QAction::triggered, this, &DeviceTab::on_action_add_math_channel_triggered); action_about_->setText(tr("About")); action_about_->setIcon( QIcon::fromTheme("help-about", QIcon(":/icons/help-about.png"))); connect(action_about_, &QAction::triggered, this, &DeviceTab::on_action_about_triggered); toolbar_ = new QToolBar("Device Toolbar"); // objectName is needed for QSettings toolbar_->setObjectName("toolbar:" + settings_id_); toolbar_->addWidget(aquire_button_); toolbar_->addSeparator(); toolbar_->addAction(action_save_as_); toolbar_->addSeparator(); toolbar_->addAction(action_add_control_view_); toolbar_->addAction(action_add_panel_view_); toolbar_->addAction(action_add_plot_view_); toolbar_->addAction(action_add_table_view_); toolbar_->addSeparator(); toolbar_->addAction(action_add_math_channel_); toolbar_->addSeparator(); toolbar_->addAction(action_about_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void DeviceTab::restore_settings() { QSettings settings; // Restore device views settings.beginGroup(device_->settings_id()); const QStringList view_keys = settings.childGroups(); for (const auto &view_key : view_keys) { settings.beginGroup(view_key); auto *view = views::viewhelper::get_view_from_settings( session_, settings, device_); if (view) add_view(view, Qt::DockWidgetArea::TopDockWidgetArea); settings.endGroup(); } // Restore state and geometry for all view widgets. // NOTE: restoreGeometry() must be called _and_ the sizeHint() of the widget // (view) must be set to the last size, in order to restore the // correct size of the dock widget. Calling/Setting only one of them // is not working! if (settings.contains("geometry")) restoreGeometry(settings.value("geometry").toByteArray()); if (settings.contains("state")) restoreState(settings.value("state").toByteArray()); settings.endGroup(); } void DeviceTab::save_settings() const { QSettings settings; settings.beginGroup(device_->settings_id()); settings.remove(""); // Remove all keys in this group size_t index = 0; for (const auto &view_dock_pair : view_docks_map_) { settings.beginGroup(QString("view%1").arg(index)); view_dock_pair.first->save_settings(settings, device_); settings.endGroup(); ++index; } // Save state and geometry for all view widgets. // NOTE: geometry must be saved. See restore_settings(). settings.setValue("geometry", saveGeometry()); settings.setValue("state", saveState()); settings.endGroup(); } void DeviceTab::on_action_aquire_triggered() { if (action_aquire_->isChecked()) { action_aquire_->setText(tr("Stop")); action_aquire_->setIconText(tr("Stop")); action_aquire_->setIcon(QIcon(":/icons/status-green.svg")); device_->start_aquisition(); } else { action_aquire_->setText(tr("Start")); action_aquire_->setIconText(tr("Start")); action_aquire_->setIcon(QIcon(":/icons/status-red.svg")); device_->pause_aquisition(); } } void DeviceTab::on_action_save_as_triggered() { ui::dialogs::SignalSaveDialog dlg(session(), device_); dlg.exec(); } void DeviceTab::on_action_add_control_view_triggered() { shared_ptr device = nullptr; if (device_->type() != sv::devices::DeviceType::UserDevice) device = device_; ui::dialogs::AddViewDialog dlg(session(), device, 0); if (!dlg.exec()) return; for (const auto &view : dlg.views()) add_view(view, Qt::TopDockWidgetArea); } void DeviceTab::on_action_add_panel_view_triggered() { ui::dialogs::AddViewDialog dlg(session(), device_, 2); if (!dlg.exec()) return; for (const auto &view : dlg.views()) add_view(view, Qt::TopDockWidgetArea); } void DeviceTab::on_action_add_plot_view_triggered() { ui::dialogs::AddViewDialog dlg(session(), device_, 3); if (!dlg.exec()) return; for (const auto &view : dlg.views()) add_view(view, Qt::BottomDockWidgetArea); } void DeviceTab::on_action_add_table_view_triggered() { ui::dialogs::AddViewDialog dlg(session(), device_, 5); if (!dlg.exec()) return; for (const auto &view : dlg.views()) add_view(view, Qt::TopDockWidgetArea); } void DeviceTab::on_action_add_math_channel_triggered() { ui::dialogs::AddMathChannelDialog dlg(session(), device_); if (!dlg.exec()) return; auto channel = dlg.channel(); if (channel != nullptr) { device_->add_math_channel( channel, dlg.channel_group_name().toStdString()); } } void DeviceTab::on_action_about_triggered() { ui::dialogs::AboutDialog dlg(this->session().device_manager(), device_); dlg.exec(); } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/devicetab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_DEVICETAB_HPP #define UI_TABS_DEVICETAB_HPP #include #include #include #include #include #include #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/tabs/basetab.hpp" using std::shared_ptr; using std::string; namespace sv { class Session; namespace ui { namespace tabs { class DeviceTab : public BaseTab { Q_OBJECT private: public: DeviceTab(Session &session, shared_ptr device, QWidget *parent = nullptr); static const string TAB_ID_PREFIX; QString title() override; bool request_close() override; virtual void clear_signals(); protected: void save_settings() const override; void restore_settings() override; shared_ptr device_; util::TimeUnit time_unit_; private: void setup_toolbar(); QAction *const action_aquire_; QAction *const action_save_as_; QAction *const action_add_control_view_; QAction *const action_add_panel_view_; QAction *const action_add_plot_view_; QAction *const action_add_table_view_; QAction *const action_add_math_channel_; QAction *const action_about_; QToolBar *toolbar_; public Q_SLOTS: private Q_SLOTS: void on_action_aquire_triggered(); void on_action_save_as_triggered(); void on_action_add_control_view_triggered(); void on_action_add_panel_view_triggered(); void on_action_add_plot_view_triggered(); void on_action_add_table_view_triggered(); void on_action_add_math_channel_triggered(); void on_action_about_triggered(); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_DEVICETAB_HPP ================================================ FILE: src/ui/tabs/measurementtab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "measurementtab.hpp" #include "src/util.hpp" #include "src/settingsmanager.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/hardwaredevice.hpp" #include "src/devices/measurementdevice.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/timeplotview.hpp" #include "src/ui/views/valuepanelview.hpp" #include "src/ui/views/viewhelper.hpp" namespace sv { namespace ui { namespace tabs { MeasurementTab::MeasurementTab(Session &session, shared_ptr device, QWidget *parent) : DeviceTab(session, device, parent), measurement_device_(device) { if (SettingsManager::restore_settings() && SettingsManager::has_device_settings(device)) { restore_settings(); } else setup_ui(); } void MeasurementTab::setup_ui() { auto hw_device = static_pointer_cast(device_); // Device controls views::BaseView *first_conf_view = nullptr; size_t conf_view_count = 0; for (const auto &c_pair : hw_device->configurable_map()) { // Ignore logic controls from the demo devive. if (c_pair.first == "Logic") continue; auto configurable = c_pair.second; if (!configurable->is_controllable()) continue; auto configurable_views = views::viewhelper::get_views_for_configurable( session_, configurable); for (const auto &configurable_view : configurable_views) { conf_view_count++; if (!first_conf_view) { first_conf_view = configurable_view; add_view(configurable_view, Qt::TopDockWidgetArea); } else add_view_ontop(configurable_view, first_conf_view); } } if (first_conf_view != nullptr && conf_view_count > 1) { first_conf_view->show(); first_conf_view->raise(); } views::BaseView *first_panel_view = nullptr; for (const auto &ch_pair : measurement_device_->channel_map()) { // Ignore digital channels (starting with "D") from the demo devive. if (util::starts_with(ch_pair.first, "D")) continue; auto channel = ch_pair.second; // Value panel(s) auto *value_panel_view = new ui::views::ValuePanelView(session_); value_panel_view->set_channel(channel); if (!first_panel_view) { first_panel_view = value_panel_view; add_view(value_panel_view, Qt::TopDockWidgetArea); } else add_view_ontop(value_panel_view, first_panel_view); // Value plot(s) auto *value_plot_view = new ui::views::TimePlotView(session_); value_plot_view->set_channel(channel); add_view(value_plot_view, Qt::BottomDockWidgetArea); } if (first_panel_view != nullptr && measurement_device_->channel_map().size() > 1) { first_panel_view->show(); first_panel_view->raise(); } } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/measurementtab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_MEASUREMENTTAB_HPP #define UI_TABS_MEASUREMENTTAB_HPP #include #include #include "src/ui/tabs/devicetab.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class MeasurementDevice; } namespace ui { namespace tabs { class MeasurementTab : public DeviceTab { Q_OBJECT public: MeasurementTab(Session &session, shared_ptr device, QWidget *parent = nullptr); private: void setup_ui(); // TODO: remove, generic solution in hw_device shared_ptr measurement_device_; }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_MEASUREMENTTAB_HPP ================================================ FILE: src/ui/tabs/oscilloscopetab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "oscilloscopetab.hpp" #include "src/settingsmanager.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/basesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/hardwaredevice.hpp" #include "src/ui/tabs/devicetab.hpp" #include "src/ui/views/scopetriggercontrolview.hpp" #include "src/ui/views/viewhelper.hpp" namespace sv { namespace ui { namespace tabs { OscilloscopeTab::OscilloscopeTab(Session &session, shared_ptr device, QWidget *parent) : DeviceTab(session, device, parent) { if (SettingsManager::restore_settings() && SettingsManager::has_device_settings(device)) { restore_settings(); } else setup_ui(); } void OscilloscopeTab::setup_ui() { auto hw_device = static_pointer_cast(device_); // Device control(s) for (const auto &c_pair : hw_device->configurable_map()) { auto configurable = c_pair.second; if (!configurable->is_controllable()) continue; auto configurable_views = views::viewhelper::get_views_for_configurable( session_, configurable); for (const auto &configurable_view : configurable_views) { add_view(configurable_view, Qt::BottomDockWidgetArea); } } size_t added_channels = 0; //ui::views::TimePlotView *plot_view = NULL; for (const auto &chg_pair : device_->channel_group_map()) { /* TODO: for now, only the first two channles are displayed. */ if (added_channels >= 2) break; /* TODO: We assume, that every channel group has just one channel. */ // Only get the first channel, and ignore the others auto channel = chg_pair.second.at(0); if (!channel) continue; //shared_ptr signal; // TODO auto signal = static_pointer_cast( channel->actual_signal()); ++added_channels; // TODO: Voltage plot /* if (!plot_view) { plot_view = new ui::views::PlotView(session_, signal); add_view(plot_view, Qt::TopDockWidgetArea); } else plot_view->add_time_curve(signal); */ } } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/oscilloscopetab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_OSCILLOSCOPETAB_HPP #define UI_TABS_OSCILLOSCOPETAB_HPP #include #include #include "src/ui/tabs/devicetab.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class HardwareDevice; } namespace ui { namespace tabs { class OscilloscopeTab : public DeviceTab { Q_OBJECT public: OscilloscopeTab(Session &session, shared_ptr device, QWidget *parent = nullptr); private: void setup_ui(); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_OSCILLOSCOPETAB_HPP ================================================ FILE: src/ui/tabs/smuscripttab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "smuscripttab.hpp" #include "src/mainwindow.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/python/smuscriptrunner.hpp" #include "src/ui/tabs/basetab.hpp" #include "src/ui/tabs/tabdockwidget.hpp" #include "src/ui/views/smuscriptoutputview.hpp" #include "src/ui/views/smuscriptview.hpp" #include "src/ui/views/viewhelper.hpp" using std::string; namespace sv { namespace ui { namespace tabs { SmuScriptTab::SmuScriptTab(Session &session, const string &script_file_name, QWidget *parent) : BaseTab(session, parent), script_file_name_(script_file_name) { id_ = "smuscripttab:" + SettingsManager::format_key(script_file_name); // The settings_id_ is the same for all SmuScriptTabs, so they all look the // same when restored from the settings, independed of the filename. settings_id_ = "smuscripttab:"; setup_ui(); connect_signals(); QSettings settings; if (SettingsManager::restore_settings() && settings.childGroups().contains("SmuScriptTab")) { SmuScriptTab::restore_settings(); } } QString SmuScriptTab::title() { return smu_script_view_->title(); } bool SmuScriptTab::request_close() { return smu_script_view_->ask_to_save(tr("Close SmuScript tab")); } void SmuScriptTab::setup_ui() { smu_script_view_ = new views::SmuScriptView(session_); smu_script_view_->load_file(script_file_name_); add_view(smu_script_view_, Qt::TopDockWidgetArea, QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); smu_script_output_view_ = new views::SmuScriptOutputView(session_); add_view(smu_script_output_view_, Qt::BottomDockWidgetArea, QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); } void SmuScriptTab::connect_signals() { connect(smu_script_view_, &views::SmuScriptView::file_name_changed, this, &SmuScriptTab::on_file_name_changed); connect(smu_script_view_, &views::SmuScriptView::file_save_state_changed, this, &SmuScriptTab::on_file_save_state_changed); // This is for redirecting the python output connect(smu_script_view_, &views::SmuScriptView::script_started, this, &SmuScriptTab::on_script_started); connect(smu_script_view_, &views::SmuScriptView::script_finished, this, &SmuScriptTab::on_script_finished); } void SmuScriptTab::run_script() { smu_script_view_->run_script(); } void SmuScriptTab::stop_script() { smu_script_view_->stop_script(); } void SmuScriptTab::restore_settings() { QSettings settings; settings.beginGroup("SmuScriptTab"); // Both views are fixed and will not be restored by their uuid, to give all // SmuScriptTabs the same setings. settings.beginGroup("view0"); smu_script_view_->restore_settings(settings); settings.endGroup(); settings.beginGroup("view1"); smu_script_output_view_->restore_settings(settings); settings.endGroup(); // NOTE: restoreGeometry() must be called _and_ the sizeHint() of the widget // (view) must be set to the last size, in order to restore the // correct size of the dock widget. Calling/Setting only one of them // is not working! if (settings.contains("geometry")) restoreGeometry(settings.value("geometry").toByteArray()); if (settings.contains("state")) restoreState(settings.value("state").toByteArray()); settings.endGroup(); } void SmuScriptTab::save_settings() const { QSettings settings; settings.beginGroup("SmuScriptTab"); settings.remove(""); // Both views are fixed and will not be restored by their uuid, to give all // SmuScriptTabs the same setings. settings.beginGroup("view0"); smu_script_view_->save_settings(settings); settings.endGroup(); settings.beginGroup("view1"); smu_script_output_view_->save_settings(settings); settings.endGroup(); // Save state and geometry for all view widgets. // NOTE: geometry must be saved. See restore_settings(). settings.setValue("geometry", saveGeometry()); settings.setValue("state", saveState()); settings.endGroup(); } void SmuScriptTab::on_file_name_changed(const QString &file_name) { (void)file_name; view_docks_map_[smu_script_view_]->setWindowTitle(smu_script_view_->title()); session_.main_window()->change_tab_title(id_, smu_script_view_->title()); } void SmuScriptTab::on_file_save_state_changed(bool is_unsaved) { if (is_unsaved) session_.main_window()->change_tab_icon(id_, QIcon::fromTheme("document-save", QIcon(":/icons/document-save.png"))); else session_.main_window()->change_tab_icon(id_, QIcon()); } void SmuScriptTab::on_script_started() { // Redirect python output to SmuScriptOutputView connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::send_py_stdout, smu_script_output_view_, &views::SmuScriptOutputView::append_out_text); connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::send_py_stderr, smu_script_output_view_, &views::SmuScriptOutputView::append_err_text); } void SmuScriptTab::on_script_finished() { disconnect(session_.smu_script_runner().get(), &python::SmuScriptRunner::send_py_stdout, smu_script_output_view_, &views::SmuScriptOutputView::append_out_text); disconnect(session_.smu_script_runner().get(), &python::SmuScriptRunner::send_py_stderr, smu_script_output_view_, &views::SmuScriptOutputView::append_err_text); } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/smuscripttab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_SMUSCRIPTTAB_HPP #define UI_TABS_SMUSCRIPTTAB_HPP # include #include #include #include "src/ui/tabs/basetab.hpp" using std::string; namespace sv { class Session; namespace ui { namespace views { class SmuScriptOutputView; class SmuScriptView; } namespace tabs { class SmuScriptTab : public BaseTab { Q_OBJECT private: public: SmuScriptTab(Session &session, const string &script_file_name, QWidget *parent = nullptr); QString title() override; bool request_close() override; protected: void save_settings() const override; void restore_settings() override; private: void setup_ui(); void connect_signals(); string script_file_name_; views::SmuScriptView *smu_script_view_; views::SmuScriptOutputView *smu_script_output_view_; public Q_SLOTS: void run_script(); void stop_script(); private Q_SLOTS: void on_file_name_changed(const QString &file_name); void on_file_save_state_changed(bool is_unsaved); void on_script_started(); void on_script_finished(); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_SMUSCRIPTTAB_HPP ================================================ FILE: src/ui/tabs/sourcesinktab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "sourcesinktab.hpp" #include "src/settingsmanager.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/basesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/hardwaredevice.hpp" #include "src/ui/tabs/devicetab.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/powerpanelview.hpp" #include "src/ui/views/timeplotview.hpp" #include "src/ui/views/viewhelper.hpp" namespace sv { namespace ui { namespace tabs { SourceSinkTab::SourceSinkTab(Session &session, shared_ptr device, QWidget *parent) : DeviceTab(session, device, parent) { if (SettingsManager::restore_settings() && SettingsManager::has_device_settings(device)) { restore_settings(); } else setup_ui(); } void SourceSinkTab::setup_ui() { auto hw_device = static_pointer_cast(device_); // Device control(s) views::BaseView *first_conf_view = nullptr; size_t conf_view_count = 0; for (const auto &c_pair : hw_device->configurable_map()) { auto configurable = c_pair.second; if (!configurable->is_controllable()) continue; auto configurable_views = views::viewhelper::get_views_for_configurable( session_, configurable); for (const auto &configurable_view : configurable_views) { conf_view_count++; if (!first_conf_view) { first_conf_view = configurable_view; add_view(configurable_view, Qt::TopDockWidgetArea); } else add_view_ontop(configurable_view, first_conf_view); } } if (first_conf_view != nullptr && conf_view_count > 1) { first_conf_view->show(); first_conf_view->raise(); } // Get signals by their channel group. The signals in a channel are "fixed" // for power supplys and loads. views::BaseView *first_pp_view = nullptr; for (const auto &chg_pair : device_->channel_group_map()) { ui::views::TimePlotView *plot_view = nullptr; shared_ptr voltage_signal; shared_ptr current_signal; for (const auto &channel : chg_pair.second) { if (channel->fixed_signal()) { auto signal = static_pointer_cast( channel->actual_signal()); // Only plot voltage and current if (signal->quantity() == data::Quantity::Voltage) { voltage_signal = signal; // Voltage plot(s) if (!plot_view) { plot_view = new ui::views::TimePlotView(session_); add_view(plot_view, Qt::BottomDockWidgetArea); } plot_view->add_signal(voltage_signal); } if (signal->quantity() == data::Quantity::Current) { current_signal = signal; // Current plot(s) if (!plot_view) { plot_view = new ui::views::TimePlotView(session_); add_view(plot_view, Qt::BottomDockWidgetArea); } plot_view->add_signal(current_signal); } } } if (voltage_signal && current_signal) { // PowerPanel(s) auto *power_panel_view = new ui::views::PowerPanelView(session_); power_panel_view->set_signals(voltage_signal, current_signal); if (!first_pp_view) { first_pp_view = power_panel_view; add_view(power_panel_view, Qt::TopDockWidgetArea); } else { // NOLINTNEXTLINE(readability-suspicious-call-argument) add_view_ontop(power_panel_view, first_pp_view); } } } if (first_pp_view != nullptr && device_->channel_group_map().size() > 1) { first_pp_view->show(); first_pp_view->raise(); } } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/sourcesinktab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_SOURCESINKTAB_HPP #define UI_TABS_SOURCESINKTAB_HPP #include #include #include "src/ui/tabs/devicetab.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class HardwareDevice; } namespace ui { namespace tabs { class SourceSinkTab : public DeviceTab { Q_OBJECT public: SourceSinkTab(Session &session, shared_ptr device, QWidget *parent = nullptr); private: void setup_ui(); public Q_SLOTS: }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_SOURCESINKTAB_HPP ================================================ FILE: src/ui/tabs/tabdockwidget.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "tabdockwidget.hpp" #include "src/ui/views/baseview.hpp" namespace sv { namespace ui { namespace tabs { TabDockWidget::TabDockWidget(const QString &title, views::BaseView *view, QWidget *parent) : QDockWidget(title, parent) { setWidget(view); connect(view, &views::BaseView::title_changed, this, &TabDockWidget::on_view_title_changed); } void TabDockWidget::closeEvent(QCloseEvent *event) { string view_id = qobject_cast(widget())->id(); Q_EMIT closed(view_id); event->accept(); } void TabDockWidget::on_view_title_changed() { QString title = qobject_cast(widget())->title(); setWindowTitle(title); } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/tabdockwidget.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_TABDOCKWIDGET_HPP #define UI_TABS_TABDOCKWIDGET_HPP #include #include #include using std::string; namespace sv { namespace ui { namespace views { class BaseView; } namespace tabs { class TabDockWidget : public QDockWidget { Q_OBJECT public: TabDockWidget(const QString &title, views::BaseView *view, QWidget *parent = nullptr); private: void closeEvent(QCloseEvent *event) override; private Q_SLOTS: void on_view_title_changed(); Q_SIGNALS: void closed(const std::string &view_id); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_TABDOCKWIDGET_HPP ================================================ FILE: src/ui/tabs/tabhelper.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "tabhelper.hpp" #include "src/session.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/deviceutil.hpp" #include "src/devices/measurementdevice.hpp" #include "src/devices/oscilloscopedevice.hpp" #include "src/devices/sourcesinkdevice.hpp" #include "src/devices/userdevice.hpp" #include "src/ui/tabs/devicetab.hpp" #include "src/ui/tabs/measurementtab.hpp" #include "src/ui/tabs/oscilloscopetab.hpp" #include "src/ui/tabs/sourcesinktab.hpp" #include "src/ui/tabs/usertab.hpp" using std::shared_ptr; using std::static_pointer_cast; using sv::devices::DeviceType; namespace sv { namespace ui { namespace tabs { namespace tabhelper { DeviceTab *get_tab_for_device(Session &session, shared_ptr device, QWidget *parent) { if (!device) return nullptr; // Power supplies or electronic loads if (device->type() == DeviceType::PowerSupply || device->type() == DeviceType::ElectronicLoad) { return new SourceSinkTab(session, static_pointer_cast(device), parent); } // Oscilloscopes if (device->type() == DeviceType::Oscilloscope) { return new OscilloscopeTab(session, static_pointer_cast(device), parent); } // Measurement devices like DMMs, scales, LCR meters, etc., but also // the demo device(s) if (device->type() == DeviceType::Multimeter || device->type() == DeviceType::SoundLevelMeter || device->type() == DeviceType::Thermometer || device->type() == DeviceType::Hygrometer || device->type() == DeviceType::Energymeter || device->type() == DeviceType::LcrMeter || device->type() == DeviceType::Scale || device->type() == DeviceType::SignalGenerator || device->type() == DeviceType::Powermeter || device->type() == DeviceType::Multiplexer || device->type() == DeviceType::DemoDev) { return new MeasurementTab(session, static_pointer_cast(device), parent); } // User device tab if (device->type() == DeviceType::UserDevice) { return new UserTab(session, static_pointer_cast(device), parent); } return nullptr; } } // namespace tabhelper } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/tabhelper.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_TABHELPER_HPP #define UI_TABS_TABHELPER_HPP #include #include using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace tabs { class DeviceTab; namespace tabhelper { /** * Returns the fitting tab for the given device, by checking the device type. * * @param[in] session The reference to the actual SmuView session * @param[in] device The base device * @param[in] parent The parent of the tab (normaly nullptr) * * @return The tab for the device */ DeviceTab *get_tab_for_device(Session &session, shared_ptr device, QWidget *parent = nullptr); } // namespace tabhelper } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_TABHELPER_HPP ================================================ FILE: src/ui/tabs/usertab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "usertab.hpp" #include "src/session.hpp" #include "src/devices/userdevice.hpp" #include "src/ui/tabs/devicetab.hpp" namespace sv { namespace ui { namespace tabs { UserTab::UserTab(Session &session, shared_ptr device, QWidget *parent) : DeviceTab(session, device, parent) { } void UserTab::restore_settings() { } void UserTab::save_settings() const { } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/usertab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_USERTAB_HPP #define UI_TABS_USERTAB_HPP #include #include #include "src/ui/tabs/devicetab.hpp" namespace sv { class Session; namespace devices { class UserDevice; } namespace ui { namespace tabs { class UserTab : public DeviceTab { Q_OBJECT public: UserTab(Session &session, shared_ptr device, QWidget *parent = nullptr); protected: void save_settings() const override; void restore_settings() override; }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_USERTAB_HPP ================================================ FILE: src/ui/tabs/welcometab.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "welcometab.hpp" #include "src/session.hpp" #include "src/ui/tabs/basetab.hpp" #include namespace sv { namespace ui { namespace tabs { WelcomeTab::WelcomeTab(Session &session, QWidget *parent) : BaseTab(session, parent) { id_ = "welcometab:0"; settings_id_ = "welcometab:0"; setup_ui(); } QString WelcomeTab::title() { return tr("Welcome"); } bool WelcomeTab::request_close() { return true; } void WelcomeTab::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); QString welcome(""); welcome. append("
"). append("Welcome to SmuView
"). append("Multimeters, Power Supplies and Loads

"). append("Version ").append(SV_VERSION_STRING).append("

"). append("Copyright 2017-2022, Frank Stettner
"). append("Lizenz: GNU General Public License Version 3

"). append("github.com/knarfS/smuview
"). append("
"); QLabel *welcome_label = new QLabel(); welcome_label->setTextFormat(Qt::RichText); welcome_label->setTextInteractionFlags(Qt::TextBrowserInteraction); welcome_label->setOpenExternalLinks(true); welcome_label->setText(welcome); layout->addWidget(welcome_label); // Show the central widget of the tab (hidden by BaseTab) this->centralWidget()->show(); this->centralWidget()->setLayout(layout); } void WelcomeTab::restore_settings() { } void WelcomeTab::save_settings() const { } } // namespace tabs } // namespace ui } // namespace sv ================================================ FILE: src/ui/tabs/welcometab.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_TABS_WELCOMETAB_HPP #define UI_TABS_WELCOMETAB_HPP #include #include #include "src/ui/tabs/basetab.hpp" namespace sv { class Session; namespace ui { namespace tabs { class WelcomeTab : public BaseTab { Q_OBJECT private: public: explicit WelcomeTab(Session &session, QWidget *parent = nullptr); QString title() override; /** The WelcomeTab can always be closed */ bool request_close() override; protected: void save_settings() const override; void restore_settings() override; private: void setup_ui(); }; } // namespace tabs } // namespace ui } // namespace sv #endif // UI_TABS_WELCOMETAB_HPP ================================================ FILE: src/ui/views/baseplotview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "baseplotview.hpp" #include "src/session.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/dialogs/plotconfigdialog.hpp" #include "src/ui/dialogs/plotdiffmarkerdialog.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/widgets/plot/curve.hpp" #include "src/ui/widgets/plot/plot.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" using std::shared_ptr; Q_DECLARE_METATYPE(sv::ui::widgets::plot::Curve *) namespace sv { namespace ui { namespace views { BasePlotView::BasePlotView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), action_add_marker_(new QAction(this)), action_add_diff_marker_(new QAction(this)), action_zoom_best_fit_(new QAction(this)), action_add_curve_(new QAction(this)), action_save_(new QAction(this)), action_config_plot_(new QAction(this)) { setup_ui(); setup_toolbar(); connect_signals(); plot_->start(); } void BasePlotView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); plot_ = new widgets::plot::Plot(session_); plot_->set_update_mode(widgets::plot::PlotUpdateMode::Additive); plot_->set_plot_interval(200); // 200ms layout->addWidget(plot_); this->central_widget_->setLayout(layout); } void BasePlotView::setup_toolbar() { add_marker_menu_ = new QMenu(); update_add_marker_menu(); add_marker_button_ = new QToolButton(); add_marker_button_->setText(tr("Add marker")); add_marker_button_->setIcon( QIcon::fromTheme("snap-orthogonal", QIcon(":/icons/snap-orthogonal.png"))); add_marker_button_->setMenu(add_marker_menu_); add_marker_button_->setPopupMode(QToolButton::MenuButtonPopup); action_add_diff_marker_->setText(tr("Add diff-marker")); action_add_diff_marker_->setIcon( QIcon::fromTheme("snap-guideline", QIcon(":/icons/snap-guideline.png"))); action_add_diff_marker_->setDisabled(true); connect(action_add_diff_marker_, &QAction::triggered, this, &BasePlotView::on_action_add_diff_marker_triggered); action_zoom_best_fit_->setText(tr("Best fit")); action_zoom_best_fit_->setIcon( QIcon::fromTheme("zoom-fit-best", QIcon(":/icons/zoom-fit-best.png"))); connect(action_zoom_best_fit_, &QAction::triggered, this, &BasePlotView::on_action_zoom_best_fit_triggered); action_add_curve_->setText(tr("Add Curve")); action_add_curve_->setIcon( QIcon::fromTheme("office-chart-line", QIcon(":/icons/office-chart-line.png"))); connect(action_add_curve_, &QAction::triggered, this, &BasePlotView::on_action_add_curve_triggered); action_save_->setText(tr("Save")); action_save_->setIcon( QIcon::fromTheme("document-save", QIcon(":/icons/document-save.png"))); connect(action_save_, &QAction::triggered, this, &BasePlotView::on_action_save_triggered); action_config_plot_->setText(tr("Configure Plot")); action_config_plot_->setIcon( QIcon::fromTheme("configure", QIcon(":/icons/configure.png"))); connect(action_config_plot_, &QAction::triggered, this, &BasePlotView::on_action_config_plot_triggered); toolbar_ = new QToolBar("Plot Toolbar"); toolbar_->addWidget(add_marker_button_); toolbar_->addAction(action_add_diff_marker_); toolbar_->addSeparator(); toolbar_->addAction(action_zoom_best_fit_); toolbar_->addSeparator(); toolbar_->addAction(action_add_curve_); toolbar_->addSeparator(); toolbar_->addAction(action_save_); toolbar_->addSeparator(); toolbar_->addAction(action_config_plot_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void BasePlotView::update_add_marker_menu() { // First remove all existing actions const auto actions = add_marker_menu_->actions(); for (QAction *action : actions) { disconnect(action, &QAction::triggered, this, &BasePlotView::on_action_add_marker_triggered); add_marker_menu_->removeAction(action); delete action; } // One "add marker" action for each curve for (const auto &curve : plot_->curve_map()) { QAction *action = new QAction(this); action->setText(curve.second->name()); action->setData(QVariant::fromValue(curve.second)); connect(action, &QAction::triggered, this, &BasePlotView::on_action_add_marker_triggered); add_marker_menu_->addAction(action); } } void BasePlotView::connect_signals() { connect(plot_, &ui::widgets::plot::Plot::curve_added, this, &BasePlotView::update_add_marker_menu); connect(plot_, &ui::widgets::plot::Plot::curve_removed, this, &BasePlotView::update_add_marker_menu); } bool BasePlotView::set_curve_name(const string &curve_id, const QString &name) { if (plot_->curve_map().count(curve_id) == 0) return false; plot_->curve_map()[curve_id]->set_name(name); return true; } bool BasePlotView::set_curve_color(const string &curve_id, const QColor &color) { if (plot_->curve_map().count(curve_id) == 0) return false; plot_->curve_map()[curve_id]->set_color(color); return true; } void BasePlotView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); settings.setValue("markers_label_alignment", plot_->markers_label_alignment()); } void BasePlotView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); if (settings.contains("markers_label_alignment")) { plot_->set_markers_label_alignment( settings.value("markers_label_alignment").toInt()); } } void BasePlotView::on_action_add_marker_triggered() { QAction *action = qobject_cast(sender()); if (action) plot_->add_marker(action->data().value()); if (plot_->marker_curve_map().size() >= 2) action_add_diff_marker_->setDisabled(false); else action_add_diff_marker_->setDisabled(true); } void BasePlotView::on_action_add_diff_marker_triggered() { ui::dialogs::PlotDiffMarkerDialog dlg(plot_); dlg.exec(); } void BasePlotView::on_action_zoom_best_fit_triggered() { plot_->set_all_axis_locked(false); } void BasePlotView::on_action_save_triggered() { QString filter("SVG Image (*.svg);;PDF File (*.pdf)"); const auto supported_formats = QImageWriter::supportedImageFormats(); for (const auto &supported : supported_formats) { filter += ";;" + supported.toUpper() + " Image (*." + supported + ")"; } QString *selected_filter = new QString("SVG Image (*.svg)"); QString file_name = QFileDialog::getSaveFileName(this, tr("Save Plot"), QDir::homePath(), filter, selected_filter); delete selected_filter; if (file_name.length() <= 0) return; // TODO QSizeF size(300, 300); int resolution = 90; QwtPlotRenderer renderer; renderer.renderDocument(plot_, file_name, size, resolution); } void BasePlotView::on_action_config_plot_triggered() { ui::dialogs::PlotConfigDialog dlg(plot_, plot_type_); dlg.exec(); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/baseplotview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_BASEPLOTVIEW_HPP #define UI_VIEWS_BASEPLOTVIEW_HPP #include #include #include #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; using std::string; using std::vector; namespace sv { class Session; namespace channels { class BaseChannel; } namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { class BaseCurveData; class Plot; } } namespace views { enum class PlotType { TimePlot, XYPlot, }; class BasePlotView : public BaseView { Q_OBJECT public: explicit BasePlotView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); /** Helper function to change a curve name. */ bool set_curve_name(const string &curve_id, const QString &name); /** Helper function to change a curve color. */ bool set_curve_color(const string &curve_id, const QColor &color); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; protected: PlotType plot_type_; widgets::plot::Plot *plot_; private: void setup_ui(); void setup_toolbar(); void connect_signals(); QMenu *add_marker_menu_; QToolButton *add_marker_button_; QAction *const action_add_marker_; QAction *const action_add_diff_marker_; QAction *const action_zoom_best_fit_; QAction *const action_add_curve_; QAction *const action_save_; QAction *const action_config_plot_; QToolBar *toolbar_; protected Q_SLOTS: void update_add_marker_menu(); virtual void on_action_add_curve_triggered() = 0; private Q_SLOTS: void on_action_add_marker_triggered(); void on_action_add_diff_marker_triggered(); void on_action_zoom_best_fit_triggered(); void on_action_save_triggered(); void on_action_config_plot_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_BASEPLOTVIEW_HPP ================================================ FILE: src/ui/views/baseview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "baseview.hpp" #include "src/session.hpp" #include "src/devices/basedevice.hpp" using std::shared_ptr; using std::string; namespace sv { namespace ui { namespace views { BaseView::BaseView(Session &session, QUuid uuid, QWidget *parent) : QMainWindow(parent), session_(session), size_(QSize(-1, -1)) { // Every view gets its own unique id uuid_ = uuid.isNull() ? QUuid::createUuid() : uuid; // Remove Qt::Window flag this->setWindowFlags(Qt::Widget); // Use a QMainWindow (in the dock widget) to allow for a tool bar central_widget_ = new QWidget(); this->setCentralWidget(central_widget_); } Session &BaseView::session() { return session_; } const Session &BaseView::session() const { return session_; } QUuid BaseView::uuid() const { return uuid_; } string BaseView::id() const { return id_; } void BaseView::save_settings(QSettings &settings, shared_ptr origin_device) const { (void)origin_device; settings.setValue("uuid", QVariant(uuid())); settings.setValue("id", QVariant(QString::fromStdString(id()))); // NOTE: The size must be saved together with the geometry (saveGeometry()) // of all dock widgets, see DeviceTab::save_settings(). settings.setValue("size", size()); } void BaseView::restore_settings(QSettings &settings, shared_ptr origin_device) { (void)origin_device; // NOTE: The size must be restored together with the geometry // (restoreGeometry()) of all dock widgets, see // DeviceTab::restore_settings(). size_ = settings.value("size").toSize(); } QSize BaseView::sizeHint() const { if (size_.width() >= 0 && size_.height() >= 0) return size_; return QMainWindow::sizeHint(); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/baseview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_BASEVIEW_HPP #define UI_VIEWS_BASEVIEW_HPP #include #include #include #include #include #include #include #include using std::shared_ptr; using std::string; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace views { enum class ViewType { DataView, DemoControlView, DeviceTreeView, MeasurementControlView, PlotView, PowerPanelView, SourceSinkControlView, ValuePanelView }; class BaseView : public QMainWindow { Q_OBJECT public: explicit BaseView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); Session &session(); const Session &session() const; QUuid uuid() const; string id() const; virtual QString title() const = 0; virtual void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const; virtual void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr); /** Return a size hint for restoring the correct view size from QSettings. */ QSize sizeHint() const override; protected: Session &session_; QWidget *central_widget_; QUuid uuid_; string id_; /** The size for sizeHint(). */ QSize size_; Q_SIGNALS: void title_changed(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_BASEVIEW_HPP ================================================ FILE: src/ui/views/dataview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dataview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogbasesignal.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/dialogs/selectsignaldialog.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; using std::dynamic_pointer_cast; namespace sv { namespace ui { namespace views { DataView::DataView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), auto_scroll_(true), action_auto_scroll_(new QAction(this)), action_add_signal_(new QAction(this)) { id_ = "data:" + util::format_uuid(uuid_); setup_ui(); setup_toolbar(); } QString DataView::title() const { QString title = tr("Data"); if (!signals_.empty()) title = title.append(" ").append(signals_.at(0)->display_name()); return title; } void DataView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); data_table_ = new QTableWidget(); data_table_->setColumnCount(1); QTableWidgetItem *time_header_item = new QTableWidgetItem(tr("Time [s]")); time_header_item->setTextAlignment(Qt::AlignVCenter); data_table_->setHorizontalHeaderItem(0, time_header_item); data_table_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); layout->addWidget(data_table_); this->central_widget_->setLayout(layout); } void DataView::setup_toolbar() { action_auto_scroll_->setText(tr("Auto scroll")); action_auto_scroll_->setIcon( QIcon::fromTheme("go-bottom", QIcon(":/icons/go-bottom.png"))); action_auto_scroll_->setCheckable(true); action_auto_scroll_->setChecked(auto_scroll_); connect(action_auto_scroll_, &QAction::triggered, this, &DataView::on_action_auto_scroll_triggered); action_add_signal_->setText(tr("Add signal")); action_add_signal_->setIcon( QIcon::fromTheme("office-chart-line", QIcon(":/icons/office-chart-line.png"))); connect(action_add_signal_, &QAction::triggered, this, &DataView::on_action_add_signal_triggered); toolbar_ = new QToolBar("Data View Toolbar"); toolbar_->addAction(action_auto_scroll_); toolbar_->addSeparator(); toolbar_->addAction(action_add_signal_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void DataView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); size_t index = 0; for (const auto &signal : signals_) { settings.beginGroup(QString("signal%1").arg(index++)); SettingsManager::save_signal(signal, settings, origin_device); settings.endGroup(); } } void DataView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); const auto groups = settings.childGroups(); for (const auto &group : groups) { if (group.startsWith("signal")) { settings.beginGroup(group); auto signal = SettingsManager::restore_signal( session_, settings, origin_device); if (signal) { add_signal( dynamic_pointer_cast(signal)); } settings.endGroup(); } } } void DataView::add_signal(shared_ptr signal) { signals_.push_back(signal); next_signal_pos_.push_back(0); last_timestamp_.push_back(nullptr); int pos = static_cast(signals_.size()); QTableWidgetItem *value_header_item = new QTableWidgetItem( signal->display_name()); value_header_item->setTextAlignment(Qt::AlignVCenter); data_table_->insertColumn(pos); data_table_->setHorizontalHeaderItem(pos, value_header_item); this->populate_table(); connect(signal.get(), &data::AnalogBaseSignal::sample_appended, this, &DataView::populate_table); Q_EMIT title_changed(); } void DataView::populate_table() { std::unique_lock lock(populate_mutex_, std::try_to_lock); if (!lock.owns_lock()) return; for (size_t i=0; isample_count(); while (next_signal_pos_[i] < signal_size) { auto sample = signals_[i]->get_sample(next_signal_pos_[i], true); int row_count = data_table_->rowCount(); int last_row = -1; if (last_timestamp_[i]) last_row = data_table_->row(last_timestamp_[i]); bool new_row = false; if (row_count <= last_row+1) { data_table_->insertRow(last_row+1); new_row = true; } else { // Find position of new sample while (last_row+1 < row_count) { auto *item = data_table_->item(last_row+1, 0); double timestamp = item->data(0).toDouble(); if (timestamp > sample.first) { data_table_->insertRow(last_row+1); new_row = true; break; } if (timestamp == sample.first) { last_timestamp_[i] = item; new_row = false; break; } if (row_count == last_row+2) { ++last_row; data_table_->insertRow(last_row+1); new_row = true; break; } last_row++; } } if (new_row) { QTableWidgetItem *time_item = new QTableWidgetItem( QString::number(sample.first, 'f', 3)); time_item->setData(0, QVariant(sample.first)); data_table_->setItem(last_row+1, 0, time_item); last_timestamp_[i] = time_item; } int prefix = util::prefix_from_value( sample.second, signals_[i]->sr_digits()); int decimal_places = util::decimal_places_from_prefix( prefix, signals_[i]->sr_digits()); QTableWidgetItem *value_item = new QTableWidgetItem( QString::number(sample.second, 'f', decimal_places)); value_item->setData(0, QVariant(sample.second)); data_table_->setItem(last_row+1, (int)i+1, value_item); ++next_signal_pos_[i]; } } if (auto_scroll_) data_table_->scrollToBottom(); } void DataView::on_action_auto_scroll_triggered() { auto_scroll_ = !auto_scroll_; action_auto_scroll_->setChecked(auto_scroll_); } void DataView::on_action_add_signal_triggered() { shared_ptr selected_device; if (signals_[0]) selected_device = signals_[0]->parent_channel()->parent_device(); ui::dialogs::SelectSignalDialog dlg(session(), selected_device); if (!dlg.exec()) return; for (const auto &signal : dlg.signals()) { add_signal(dynamic_pointer_cast(signal)); } } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/dataview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_DATAVIEW_HPP #define UI_VIEWS_DATAVIEW_HPP #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; using std::vector; namespace sv { class Session; namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace views { class DataView : public BaseView { Q_OBJECT public: explicit DataView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void add_signal(shared_ptr signal); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: vector> signals_; vector next_signal_pos_; vector last_timestamp_; bool auto_scroll_; std::mutex populate_mutex_; QAction *const action_auto_scroll_; QAction *const action_add_signal_; QToolBar *toolbar_; QTableWidget *data_table_; void setup_ui(); void setup_toolbar(); private Q_SLOTS: void populate_table(); void on_action_auto_scroll_triggered(); void on_action_add_signal_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_DATAVIEW_HPP ================================================ FILE: src/ui/views/democontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "democontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/data/datautil.hpp" #include "src/data/properties/baseproperty.hpp" #include "src/data/properties/measuredquantityproperty.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/data/quantitycombobox.hpp" #include "src/ui/data/quantityflagslist.hpp" #include "src/ui/datatypes/doublecontrol.hpp" #include "src/ui/datatypes/stringcombobox.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; using std::static_pointer_cast; using sv::devices::ConfigKey; Q_DECLARE_METATYPE(sv::data::measured_quantity_t) namespace sv { namespace ui { namespace views { DemoControlView::DemoControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "democontrol:" + util::format_uuid(uuid_); setup_ui(); connect_signals(); } QString DemoControlView::title() const { return tr("Control") + " " + configurable_->display_name(); } void DemoControlView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); if (configurable_->has_get_config(ConfigKey::MeasuredQuantity) || configurable_->has_set_config(ConfigKey::MeasuredQuantity)) { // The demo dmm device has no listable measurement quantities / // quantity flags, so we use all... auto mq_prop = static_pointer_cast( configurable_->get_property(ConfigKey::MeasuredQuantity)); quantity_box_ = new ui::data::QuantityComboBox(); quantity_box_->select_quantity( mq_prop->measured_quantity_value().first); layout->addWidget(quantity_box_); quantity_flags_list_ = new ui::data::QuantityFlagsList(); quantity_flags_list_->select_quantity_flags( mq_prop->measured_quantity_value().second); layout->addWidget(quantity_flags_list_); set_button_ = new QPushButton(); set_button_->setText(tr("Set")); layout->addWidget(set_button_, 0); } if (configurable_->has_get_config(ConfigKey::PatternMode) || configurable_->has_set_config(ConfigKey::PatternMode)) { pattern_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::PatternMode), true, true); layout->addWidget(pattern_box_); } QHBoxLayout *controls_layout = new QHBoxLayout(); amplitude_control_ = new ui::datatypes::DoubleControl( configurable_->get_property(ConfigKey::Amplitude), true, true, tr("Amplitude")); controls_layout->addWidget(amplitude_control_); offset_control_ = new ui::datatypes::DoubleControl( configurable_->get_property(ConfigKey::Offset), true, true, tr("Offset")); controls_layout->addWidget(offset_control_); layout->addLayout(controls_layout); this->central_widget_->setLayout(layout); } void DemoControlView::connect_signals() { // Control elements -> Device if (configurable_->has_get_config(ConfigKey::MeasuredQuantity) || configurable_->has_set_config(ConfigKey::MeasuredQuantity)) { connect(set_button_, &QPushButton::clicked, this, &DemoControlView::on_quantity_set); } // Device -> control elements } void DemoControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void DemoControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } DemoControlView *DemoControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new DemoControlView(session, configurable, uuid); return nullptr; } void DemoControlView::on_quantity_set() { sv::data::Quantity quantity = quantity_box_->selected_quantity(); set quantity_flags = quantity_flags_list_->selected_quantity_flags(); auto mq = make_pair(quantity, quantity_flags); auto prop = configurable_->get_property(ConfigKey::MeasuredQuantity); prop->change_value(QVariant::fromValue(mq)); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/democontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_DEMOCONTROLVIEW_HPP #define UI_VIEWS_DEMOCONTROLVIEW_HPP #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace data { class QuantityComboBox; class QuantityFlagsList; } namespace datatypes { class DoubleControl; class StringComboBox; } namespace views { class DemoControlView : public BaseView { Q_OBJECT public: DemoControlView(Session &session, shared_ptr configurable, QUuid uuid = QUuid(), QWidget *parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static DemoControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::data::QuantityComboBox *quantity_box_; ui::data::QuantityFlagsList *quantity_flags_list_; QPushButton *set_button_; ui::datatypes::StringComboBox *pattern_box_; ui::datatypes::DoubleControl *amplitude_control_; ui::datatypes::DoubleControl *offset_control_; void setup_ui(); void connect_signals(); private Q_SLOTS: void on_quantity_set(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_DEMOCONTROLVIEW_HPP ================================================ FILE: src/ui/views/devicesview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "devicesview.hpp" #include "src/devicemanager.hpp" #include "src/mainwindow.hpp" #include "src/session.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/basesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/hardwaredevice.hpp" #include "src/devices/userdevice.hpp" #include "src/ui/devices/devicetree/devicetreemodel.hpp" #include "src/ui/devices/devicetree/devicetreeview.hpp" #include "src/ui/devices/devicetree/treeitem.hpp" #include "src/ui/dialogs/connectdialog.hpp" #include "src/ui/tabs/devicetab.hpp" #include "src/ui/views/baseview.hpp" using std::shared_ptr; using sv::ui::devices::devicetree::DeviceTreeModel; using sv::ui::devices::devicetree::TreeItem; using sv::ui::devices::devicetree::TreeItemType; Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr) namespace sv { namespace ui { namespace views { DevicesView::DevicesView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), action_add_device_(new QAction(this)), action_add_userdevice_(new QAction(this)), action_disconnect_device_(new QAction(this)) { id_ = "devices:" + util::format_uuid(uuid_); setup_ui(); setup_toolbar(); connect_signals(); } QString DevicesView::title() const { return tr("Device Tree"); } void DevicesView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); device_tree_ = new devices::devicetree::DeviceTreeView(session(), false, false, false, false, false, false, true, true); layout->addWidget(device_tree_); layout->setContentsMargins(2, 2, 2, 2); this->central_widget_->setLayout(layout); } void DevicesView::setup_toolbar() { action_add_device_->setText(tr("Add device")); action_add_device_->setIcon( QIcon::fromTheme("document-new", QIcon(":/icons/document-new.png"))); connect(action_add_device_, &QAction::triggered, this, &DevicesView::on_action_add_device_triggered); action_add_userdevice_->setText(tr("Add virtual user device")); action_add_userdevice_->setIcon( QIcon::fromTheme("tab-new-background", QIcon(":/icons/tab-new-background.png"))); connect(action_add_userdevice_, &QAction::triggered, this, &DevicesView::on_action_add_userdevice_triggered); action_disconnect_device_->setText(tr("Disconnect device")); action_disconnect_device_->setIcon( QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.png"))); connect(action_disconnect_device_, &QAction::triggered, this, &DevicesView::on_action_disconnect_device_triggered); toolbar_ = new QToolBar("Device Tree Toolbar"); toolbar_->addAction(action_add_device_); toolbar_->addAction(action_add_userdevice_); toolbar_->addSeparator(); toolbar_->addAction(action_disconnect_device_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void DevicesView::connect_signals() { } void DevicesView::save_settings(QSettings &settings, shared_ptr origin_device) const { (void)settings; (void)origin_device; } void DevicesView::restore_settings(QSettings &settings, shared_ptr origin_device) { (void)settings; (void)origin_device; } void DevicesView::on_action_add_device_triggered() { ui::dialogs::ConnectDialog dlg(session().device_manager()); if (dlg.exec()) { auto device = dlg.get_selected_device(); // NOTE: add_device() must be called, before the device tab // tries to access the device (device is not opend yet). session().add_device(device); session().main_window()->add_device_tab(device); } } void DevicesView::on_action_add_userdevice_triggered() { // NOTE: add_user_device() must be called, before the device tab // tries to access the device (device is not opend yet). auto device = session().add_user_device(); session().main_window()->add_device_tab(device); } void DevicesView::on_action_disconnect_device_triggered() { TreeItem *item = device_tree_->selected_item(); if (!item) return; if (item->type() == (int)TreeItemType::DeviceItem) { auto device = item->data(DeviceTreeModel::DataRole). value>(); QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Close device"), tr("Closing the device \"%1\" will also delete all aquired data!"). arg(device->short_name()), QMessageBox::Yes | QMessageBox::Cancel); if (reply == QMessageBox::Yes) { session().main_window()->remove_tab( ui::tabs::DeviceTab::TAB_ID_PREFIX + device->id()); session().remove_device(device); } } else if (item->type() == (int)TreeItemType::ChannelItem) { /* TODO auto channel = item->data(DeviceTreeModel::DataRole). value>(); QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Delete signals from channel"), tr("Deleting all signals from channel \"%1\" will also delete all aquired data!"). arg(QString::fromStdString(channel->name())), QMessageBox::Yes | QMessageBox::Cancel); if (reply == QMessageBox::Yes) { channel->clear_signals(); } */ } else if (item->type() == (int)TreeItemType::SignalItem) { auto signal = item->data(DeviceTreeModel::DataRole). value>(); QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Delete signal"), tr("Deleting the signal \"%1\" will also delete all aquired data!"). arg(signal->display_name()), QMessageBox::Yes | QMessageBox::Cancel); if (reply == QMessageBox::Yes) { signal->clear(); } } } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/devicesview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_DEVICESVIEW_HPP #define UI_VIEWS_DEVICESVIEW_HPP #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace devices { namespace devicetree { class DeviceTreeView; } } namespace views { class DevicesView : public BaseView { Q_OBJECT public: explicit DevicesView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: QAction *const action_add_device_; QAction *const action_add_userdevice_; QAction *const action_disconnect_device_; QToolBar *toolbar_; devices::devicetree::DeviceTreeView *device_tree_; void setup_ui(); void setup_toolbar(); void connect_signals(); private Q_SLOTS: void on_action_add_device_triggered(); void on_action_add_userdevice_triggered(); void on_action_disconnect_device_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_DEVICESVIEW_HPP ================================================ FILE: src/ui/views/genericcontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "genericcontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/data/properties/baseproperty.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/datatypes/datatypehelper.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; namespace sv { namespace ui { namespace views { GenericControlView::GenericControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "genericcontrol:" + util::format_uuid(uuid_); setup_ui(); connect_signals(); } QString GenericControlView::title() const { return tr("Control") + " " + configurable_->display_name(); } void GenericControlView::setup_ui() { QFormLayout *layout = new QFormLayout(); for (const auto &prop : configurable_->property_map()) { QString text = devices::deviceutil::format_config_key(prop.first); QWidget *dt_widget = datatypes::datatypehelper::get_widget_for_property( prop.second, true, true); layout->addRow(text, dt_widget); } this->central_widget_->setLayout(layout); } void GenericControlView::connect_signals() { // Control elements -> Device // Device -> control elements } void GenericControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void GenericControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } GenericControlView *GenericControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new GenericControlView(session, configurable, uuid); return nullptr; } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/genericcontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_GENERICCONTROLVIEW_HPP #define UI_VIEWS_GENERICCONTROLVIEW_HPP #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace views { class GenericControlView : public BaseView { Q_OBJECT public: GenericControlView(Session& session, shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static GenericControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; void setup_ui(); void connect_signals(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_GENERICCONTROLVIEW_HPP ================================================ FILE: src/ui/views/measurementcontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "measurementcontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/datatypes/measuredquantitycombobox.hpp" #include "src/ui/datatypes/stringcombobox.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; using sv::devices::ConfigKey; namespace sv { namespace ui { namespace views { MeasurementControlView::MeasurementControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "measurementcontrol:" + util::format_uuid(uuid_); setup_ui(); } QString MeasurementControlView::title() const { return tr("Control") + " " + configurable_->display_name(); } void MeasurementControlView::setup_ui() { QFormLayout *layout = new QFormLayout(); measured_quantity_box_ = new ui::datatypes::MeasuredQuantityComboBox( configurable_->get_property(ConfigKey::MeasuredQuantity), true, true); layout->addRow(tr("Quantity"), measured_quantity_box_); range_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::Range), true, true); layout->addRow(tr("Range"), range_box_); digits_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::Digits), true, true); layout->addRow(tr("Digits"), digits_box_); this->central_widget_->setLayout(layout); } void MeasurementControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void MeasurementControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } MeasurementControlView *MeasurementControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new MeasurementControlView(session, configurable, uuid); return nullptr; } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/measurementcontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_MEASUREMENTCONTROLVIEW_HPP #define UI_VIEWS_MEASUREMENTCONTROLVIEW_HPP #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace datatypes { class MeasuredQuantityComboBox; class StringComboBox; } namespace views { class MeasurementControlView : public BaseView { Q_OBJECT public: MeasurementControlView(Session& session, shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static MeasurementControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::datatypes::MeasuredQuantityComboBox *measured_quantity_box_; ui::datatypes::StringComboBox *range_box_; ui::datatypes::StringComboBox *digits_box_; void setup_ui(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_MEASUREMENTCONTROLVIEW_HPP ================================================ FILE: src/ui/views/powerpanelview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "powerpanelview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogbasesignal.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" #include "src/ui/widgets/monofontdisplay.hpp" using std::dynamic_pointer_cast; using std::set; using std::shared_ptr; using sv::data::QuantityFlag; namespace sv { namespace ui { namespace views { PowerPanelView::PowerPanelView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), voltage_signal_(nullptr), current_signal_(nullptr), voltage_min_(std::numeric_limits::max()), voltage_max_(std::numeric_limits::lowest()), current_min_(std::numeric_limits::max()), current_max_(std::numeric_limits::lowest()), resistance_min_(std::numeric_limits::max()), resistance_max_(std::numeric_limits::lowest()), power_min_(std::numeric_limits::max()), power_max_(std::numeric_limits::lowest()), actual_amp_hours_(0), actual_watt_hours_(0), action_reset_displays_(new QAction(this)) { id_ = "powerpanel:" + util::format_uuid(uuid_); setup_ui(); setup_toolbar(); connect_signals(); reset_displays(); timer_ = new QTimer(this); init_timer(); } PowerPanelView::~PowerPanelView() { stop_timer(); } QString PowerPanelView::title() const { QString title = tr("Power Panel"); if (voltage_signal_ && current_signal_) title = title.append(" ").append(voltage_signal_->display_name()). append(" / ").append(current_signal_->display_name()); return title; } void PowerPanelView::set_signals( shared_ptr voltage_signal, shared_ptr current_signal) { assert(voltage_signal); assert(current_signal); disconnect_signals(); stop_timer(); voltage_signal_ = voltage_signal; current_signal_ = current_signal; init_timer(); init_displays(); connect_signals(); Q_EMIT title_changed(); } void PowerPanelView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); QGridLayout *panel_layout = new QGridLayout(); voltage_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRangeWithSRDigits, "", "", "", false); voltage_min_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Min), true); voltage_max_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Max), true); current_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRangeWithSRDigits, "", "", "", false); current_min_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Min), true); current_max_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Max), true); resistance_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Ohm), "", "", false); resistance_min_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Ohm), "", data::datautil::format_quantity_flag(QuantityFlag::Min), true); resistance_max_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Ohm), "", data::datautil::format_quantity_flag(QuantityFlag::Max), true); power_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Watt), "", "", false); power_min_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Watt), "", data::datautil::format_quantity_flag(QuantityFlag::Min), true); power_max_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::Watt), "", data::datautil::format_quantity_flag(QuantityFlag::Max), true); amp_hour_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::AmpereHour), "", "", false); watt_hour_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, data::datautil::format_unit(data::Unit::WattHour), "", "", false); panel_layout->addWidget(voltage_display_, 0, 0, 1, 2, Qt::AlignHCenter); panel_layout->addWidget(voltage_min_display_, 1, 0, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(voltage_max_display_, 1, 1, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(current_display_, 2, 0, 1, 2, Qt::AlignHCenter); panel_layout->addWidget(current_min_display_, 3, 0, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(current_max_display_, 3, 1, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(resistance_display_, 0, 2, 1, 2, Qt::AlignHCenter); panel_layout->addWidget(resistance_min_display_, 1, 2, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(resistance_max_display_, 1, 3, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(power_display_, 2, 2, 1, 2, Qt::AlignHCenter); panel_layout->addWidget(power_min_display_, 3, 2, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(power_max_display_, 3, 3, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(amp_hour_display_, 0, 4, 2, 1, Qt::AlignCenter); panel_layout->addWidget(watt_hour_display_, 2, 4, 2, 1, Qt::AlignCenter); layout->addLayout(panel_layout); layout->addStretch(1); this->central_widget_->setLayout(layout); } void PowerPanelView::setup_toolbar() { action_reset_displays_->setText(tr("Reset displays")); action_reset_displays_->setIcon( QIcon::fromTheme("view-refresh", QIcon(":/icons/view-refresh.png"))); connect(action_reset_displays_, &QAction::triggered, this, &PowerPanelView::on_action_reset_displays_triggered); toolbar_ = new QToolBar("Power Panel Toolbar"); toolbar_->addAction(action_reset_displays_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void PowerPanelView::init_displays() { QString voltage_unit_suffix(""); set voltage_qfs = voltage_signal_->quantity_flags(); if (voltage_qfs.count(QuantityFlag::AC) > 0) { //voltage_unit_suffix = QString::fromUtf8("\u23E6"); voltage_unit_suffix = sv::data::datautil::format_quantity_flag( QuantityFlag::AC); voltage_qfs.erase(QuantityFlag::AC); } else if (voltage_qfs.count(QuantityFlag::DC) > 0) { //voltage_unit_suffix = QString::fromUtf8("\u2393"); voltage_unit_suffix = sv::data::datautil::format_quantity_flag( QuantityFlag::DC); voltage_qfs.erase(QuantityFlag::DC); } set voltage_qfs_min = voltage_qfs; voltage_qfs_min.insert(QuantityFlag::Min); set voltage_qfs_max = voltage_qfs; voltage_qfs_max.insert(QuantityFlag::Max); voltage_display_->set_unit(voltage_signal_->unit_name()); voltage_display_->set_unit_suffix(voltage_unit_suffix); voltage_display_->set_extra_text( sv::data::datautil::format_quantity_flags(voltage_qfs, "\n")); voltage_display_->set_sr_digits( voltage_signal_->total_digits(), voltage_signal_->sr_digits()); voltage_min_display_->set_unit(voltage_signal_->unit_name()); voltage_min_display_->set_unit_suffix(voltage_unit_suffix); voltage_min_display_->set_extra_text( sv::data::datautil::format_quantity_flags(voltage_qfs_min, "\n")); voltage_min_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); voltage_max_display_->set_unit(voltage_signal_->unit_name()); voltage_max_display_->set_unit_suffix(voltage_unit_suffix); voltage_max_display_->set_extra_text( sv::data::datautil::format_quantity_flags(voltage_qfs_max, "\n")); voltage_max_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); QString current_unit_suffix(""); set current_qfs = current_signal_->quantity_flags(); if (current_qfs.count(QuantityFlag::AC) > 0) { current_unit_suffix = sv::data::datautil::format_quantity_flag( QuantityFlag::AC); current_qfs.erase(QuantityFlag::AC); } else if (current_qfs.count(QuantityFlag::DC) > 0) { current_unit_suffix = sv::data::datautil::format_quantity_flag( QuantityFlag::DC); current_qfs.erase(QuantityFlag::DC); } set current_qfs_min = current_qfs; current_qfs_min.insert(QuantityFlag::Min); set current_qfs_max = current_qfs; current_qfs_max.insert(QuantityFlag::Max); current_display_->set_unit(current_signal_->unit_name()); current_display_->set_unit_suffix(current_unit_suffix); current_display_->set_extra_text( sv::data::datautil::format_quantity_flags(current_qfs, "\n")); current_display_->set_sr_digits( current_signal_->total_digits(), current_signal_->sr_digits()); current_min_display_->set_unit(current_signal_->unit_name()); current_min_display_->set_unit_suffix(current_unit_suffix); current_min_display_->set_extra_text( sv::data::datautil::format_quantity_flags(current_qfs_min, "\n")); current_min_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); current_max_display_->set_unit(current_signal_->unit_name()); current_max_display_->set_unit_suffix(current_unit_suffix); current_max_display_->set_extra_text( sv::data::datautil::format_quantity_flags(current_qfs_max, "\n")); current_max_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); resistance_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); resistance_min_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); resistance_max_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); power_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); power_min_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); power_max_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); amp_hour_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); watt_hour_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); } void PowerPanelView::connect_signals() { if (!voltage_signal_ || !current_signal_) return; connect(voltage_signal_.get(), &data::AnalogBaseSignal::digits_changed, this, &PowerPanelView::on_digits_changed); connect(current_signal_.get(), &data::AnalogBaseSignal::digits_changed, this, &PowerPanelView::on_digits_changed); } void PowerPanelView::disconnect_signals() { if (voltage_signal_) { disconnect( voltage_signal_.get(), &data::AnalogBaseSignal::digits_changed, this, &PowerPanelView::on_digits_changed); } if (current_signal_) { disconnect( current_signal_.get(), &data::AnalogBaseSignal::digits_changed, this, &PowerPanelView::on_digits_changed); } } void PowerPanelView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); if (voltage_signal_) SettingsManager::save_signal(voltage_signal_, settings, origin_device, "v_"); if (current_signal_) SettingsManager::save_signal(current_signal_, settings, origin_device, "i_"); } void PowerPanelView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); auto v_signal = SettingsManager::restore_signal( session_, settings, origin_device, "v_"); auto i_signal = SettingsManager::restore_signal( session_, settings, origin_device, "i_"); if (v_signal && i_signal) { set_signals( dynamic_pointer_cast(v_signal), dynamic_pointer_cast(i_signal)); } } void PowerPanelView::reset_displays() { voltage_display_->reset_value(); voltage_min_display_->reset_value(); voltage_max_display_->reset_value(); current_display_->reset_value(); current_min_display_->reset_value(); current_max_display_->reset_value(); resistance_display_->reset_value(); resistance_min_display_->reset_value(); resistance_max_display_->reset_value(); power_display_->reset_value(); power_min_display_->reset_value(); power_max_display_->reset_value(); amp_hour_display_->reset_value(); watt_hour_display_->reset_value(); } void PowerPanelView::init_timer() { start_time_ = QDateTime::currentMSecsSinceEpoch(); last_time_ = start_time_; voltage_min_ = std::numeric_limits::max(); voltage_max_ = std::numeric_limits::lowest(); current_min_ = std::numeric_limits::max(); current_max_ = std::numeric_limits::lowest(); resistance_min_ = std::numeric_limits::max(); resistance_max_ = std::numeric_limits::lowest(); power_min_ = std::numeric_limits::max(); power_max_ = std::numeric_limits::lowest(); actual_amp_hours_ = 0; actual_watt_hours_ = 0; connect(timer_, &QTimer::timeout, this, &PowerPanelView::on_update); timer_->start(250); } void PowerPanelView::stop_timer() { if (!timer_->isActive()) return; timer_->stop(); disconnect(timer_, &QTimer::timeout, this, &PowerPanelView::on_update); reset_displays(); } void PowerPanelView::on_update() { if (!voltage_signal_ || voltage_signal_->sample_count() == 0 || !current_signal_ || current_signal_->sample_count() == 0) return; qint64 now = QDateTime::currentMSecsSinceEpoch(); double elapsed_time = (double)(now - last_time_) / (double)3600000; // / 1h last_time_ = now; double voltage = voltage_signal_->last_value(); if (voltage_min_ > voltage) voltage_min_ = voltage; if (voltage_max_ < voltage) voltage_max_ = voltage; double current = current_signal_->last_value(); if (current_min_ > current) current_min_ = current; if (current_max_ < current) current_max_ = current; double resistance = current == 0. ? std::numeric_limits::max() : voltage / current; if (resistance_min_ > resistance) resistance_min_ = resistance; if (resistance_max_ < resistance) resistance_max_ = resistance; double power = voltage * current; if (power_min_ > power) power_min_ = power; if (power_max_ < power) power_max_ = power; actual_amp_hours_ = actual_amp_hours_ + (current * elapsed_time); actual_watt_hours_ = actual_watt_hours_ + (power * elapsed_time); voltage_display_->set_value(voltage); voltage_min_display_->set_value(voltage_min_); voltage_max_display_->set_value(voltage_max_); current_display_->set_value(current); current_min_display_->set_value(current_min_); current_max_display_->set_value(current_max_); resistance_display_->set_value(resistance); resistance_min_display_->set_value(resistance_min_); resistance_max_display_->set_value(resistance_max_); power_display_->set_value(power); power_min_display_->set_value(power_min_); power_max_display_->set_value(power_max_); amp_hour_display_->set_value(actual_amp_hours_); watt_hour_display_->set_value(actual_watt_hours_); } void PowerPanelView::on_action_reset_displays_triggered() { stop_timer(); init_timer(); } void PowerPanelView::on_digits_changed() { voltage_display_->set_sr_digits( voltage_signal_->total_digits(), voltage_signal_->sr_digits()); current_display_->set_sr_digits( current_signal_->total_digits(), current_signal_->sr_digits()); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/powerpanelview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_POWERPANELVIEW_HPP #define UI_VIEWS_POWERPANELVIEW_HPP #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace widgets { class MonoFontDisplay; } namespace views { class PowerPanelView : public BaseView { Q_OBJECT public: explicit PowerPanelView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); ~PowerPanelView(); QString title() const override; void set_signals(shared_ptr voltage_signal, shared_ptr current_signal); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: shared_ptr voltage_signal_; shared_ptr current_signal_; QTimer *timer_; qint64 start_time_; qint64 last_time_; // Min/max/actual values are stored here, so they can be reseted double voltage_min_; double voltage_max_; double current_min_; double current_max_; double resistance_min_; double resistance_max_; double power_min_; double power_max_; double actual_amp_hours_; double actual_watt_hours_; QAction *const action_reset_displays_; QToolBar *toolbar_; widgets::MonoFontDisplay *voltage_display_; widgets::MonoFontDisplay *voltage_min_display_; widgets::MonoFontDisplay *voltage_max_display_; widgets::MonoFontDisplay *current_display_; widgets::MonoFontDisplay *current_min_display_; widgets::MonoFontDisplay *current_max_display_; widgets::MonoFontDisplay *resistance_display_; widgets::MonoFontDisplay *resistance_min_display_; widgets::MonoFontDisplay *resistance_max_display_; widgets::MonoFontDisplay *power_display_; widgets::MonoFontDisplay *power_min_display_; widgets::MonoFontDisplay *power_max_display_; widgets::MonoFontDisplay *amp_hour_display_; widgets::MonoFontDisplay *watt_hour_display_; void setup_ui(); void setup_toolbar(); void init_displays(); void connect_signals(); void disconnect_signals(); void reset_displays(); void init_timer(); void stop_timer(); private Q_SLOTS: void on_update(); void on_action_reset_displays_triggered(); void on_digits_changed(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_POWERPANELVIEW_HPP ================================================ FILE: src/ui/views/scopehorizontalcontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "scopehorizontalcontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/data/properties/baseproperty.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/ui/datatypes/boolcheckbox.hpp" #include "src/ui/datatypes/rationalcombobox.hpp" #include "src/ui/datatypes/uint64combobox.hpp" #include "src/ui/datatypes/uint64label.hpp" #include "src/ui/datatypes/uint64spinbox.hpp" using sv::devices::ConfigKey; namespace sv { namespace ui { namespace views { ScopeHorizontalControlView::ScopeHorizontalControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "scopehorizontalcontrol:" + util::format_uuid(uuid_); setup_ui(); } QString ScopeHorizontalControlView::title() const { return tr("Horizontal Control") + " " + configurable_->display_name(); } void ScopeHorizontalControlView::setup_ui() { QFormLayout *layout = new QFormLayout(); // Samplerate samplerate_label_ = new ui::datatypes::UInt64Label( configurable_->get_property(ConfigKey::Samplerate), true); layout->addRow(tr("Samplerate"), samplerate_label_); // Timebase timebase_box_ = new ui::datatypes::RationalComboBox( configurable_->get_property(ConfigKey::TimeBase), true, true); layout->addRow(tr("Timebase"), timebase_box_); // Buffer buffer_size_box_ = new ui::datatypes::UInt64ComboBox( configurable_->get_property(ConfigKey::BufferSize), true, true); layout->addRow(tr("Buffer size"), buffer_size_box_); // Average mode average_check_ = new ui::datatypes::BoolCheckBox( configurable_->get_property(ConfigKey::Averaging), true, true); layout->addRow(tr("Averaging"), average_check_); // Average count auto avg_samples_prop = configurable_->get_property(ConfigKey::AvgSamples); if (avg_samples_prop->is_listable()) { average_count_box_ = new ui::datatypes::UInt64ComboBox( avg_samples_prop, true, true); layout->addRow(tr("Average count"), average_count_box_); } else { average_count_spin_ = new ui::datatypes::UInt64SpinBox( avg_samples_prop, true, true); layout->addRow(tr("Average count"), average_count_spin_); } this->central_widget_->setLayout(layout); } void ScopeHorizontalControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void ScopeHorizontalControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } ScopeHorizontalControlView *ScopeHorizontalControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new ScopeHorizontalControlView(session, configurable, uuid); return nullptr; } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/scopehorizontalcontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SCOPEHORIZONTALCONTROLVIEW_HPP #define UI_VIEWS_SCOPEHORIZONTALCONTROLVIEW_HPP #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace datatypes { class BoolCheckBox; class RationalComboBox; class UInt64ComboBox; class UInt64Label; class UInt64SpinBox; } namespace views { class ScopeHorizontalControlView : public BaseView { Q_OBJECT public: ScopeHorizontalControlView(Session& session, std::shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static ScopeHorizontalControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::datatypes::UInt64Label *samplerate_label_; ui::datatypes::RationalComboBox *timebase_box_; ui::datatypes::UInt64ComboBox *buffer_size_box_; ui::datatypes::BoolCheckBox *average_check_; ui::datatypes::UInt64ComboBox *average_count_box_; ui::datatypes::UInt64SpinBox *average_count_spin_; void setup_ui(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SCOPEHORIZONTALCONTROLVIEW_HPP ================================================ FILE: src/ui/views/scopetriggercontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "scopetriggercontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/ui/datatypes/doublespinbox.hpp" #include "src/ui/datatypes/stringcombobox.hpp" using sv::devices::ConfigKey; namespace sv { namespace ui { namespace views { ScopeTriggerControlView::ScopeTriggerControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "scopetriggercontrol:" + util::format_uuid(uuid_); setup_ui(); } QString ScopeTriggerControlView::title() const { return tr("Trigger Control") + " " + configurable_->display_name(); } void ScopeTriggerControlView::setup_ui() { QFormLayout *layout = new QFormLayout(); // Source source_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::TriggerSource), true, true); layout->addRow(tr("Source"), source_box_); // Slope slope_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::TriggerSlope), true, true); layout->addRow(tr("Slope"), slope_box_); // Level level_spin_ = new ui::datatypes::DoubleSpinBox( configurable_->get_property(ConfigKey::TriggerLevel), true, true); layout->addRow(tr("Level"), level_spin_); // HPos hpos_spin_ = new ui::datatypes::DoubleSpinBox( configurable_->get_property(ConfigKey::HorizTriggerPos), true, true); layout->addRow(tr("Horiz. Pos"), hpos_spin_); this->central_widget_->setLayout(layout); } void ScopeTriggerControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void ScopeTriggerControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } ScopeTriggerControlView *ScopeTriggerControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new ScopeTriggerControlView(session, configurable, uuid); return nullptr; } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/scopetriggercontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SCOPEHTRIGGERCONTROLVIEW_HPP #define UI_VIEWS_SCOPEHTRIGGERCONTROLVIEW_HPP #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace datatypes { class DoubleSpinBox; class StringComboBox; } namespace views { class ScopeTriggerControlView : public BaseView { Q_OBJECT public: ScopeTriggerControlView(Session& session, std::shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static ScopeTriggerControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::datatypes::StringComboBox *source_box_; ui::datatypes::StringComboBox *slope_box_; ui::datatypes::DoubleSpinBox *level_spin_; ui::datatypes::DoubleSpinBox *hpos_spin_; void setup_ui(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SCOPEHTRIGGERCONTROLVIEW_HPP ================================================ FILE: src/ui/views/scopeverticalcontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "scopeverticalcontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/ui/datatypes/boolcheckbox.hpp" #include "src/ui/datatypes/rationalcombobox.hpp" #include "src/ui/datatypes/stringcombobox.hpp" #include "src/ui/datatypes/uint64combobox.hpp" using sv::devices::ConfigKey; namespace sv { namespace ui { namespace views { ScopeVerticalControlView::ScopeVerticalControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "scopeverticalcontrol:" + util::format_uuid(uuid_); setup_ui(); } QString ScopeVerticalControlView::title() const { return tr("Vertical Control") + " " + configurable_->display_name(); } void ScopeVerticalControlView::setup_ui() { QFormLayout *layout = new QFormLayout(); // Enable channel enable_check_ = new ui::datatypes::BoolCheckBox( configurable_->get_property(ConfigKey::Enabled), true, true); layout->addRow(tr("Enable"), enable_check_); // VDiv vdiv_box_ = new ui::datatypes::RationalComboBox( configurable_->get_property(ConfigKey::VDiv), true, true); layout->addRow(tr("VDiv"), vdiv_box_); // Coupling coupling_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::Coupling), true, true); layout->addRow(tr("Coupling"), coupling_box_); // Filter filter_check_ = new ui::datatypes::BoolCheckBox( configurable_->get_property(ConfigKey::Filter), true, true); layout->addRow(tr("Filter"), filter_check_); // Probe factor probe_factor_box_ = new ui::datatypes::UInt64ComboBox( configurable_->get_property(ConfigKey::ProbeFactor), true, true); layout->addRow(tr("Probe"), probe_factor_box_); this->central_widget_->setLayout(layout); } void ScopeVerticalControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void ScopeVerticalControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } ScopeVerticalControlView *ScopeVerticalControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (configurable) return new ScopeVerticalControlView(session, configurable, uuid); return nullptr; } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/scopeverticalcontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SCOPEVERTICALCONTROLVIEW_HPP #define UI_VIEWS_SCOPEVERTICALCONTROLVIEW_HPP #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace datatypes { class BoolCheckBox; class RationalComboBox; class StringComboBox; class UInt64ComboBox; } namespace views { class ScopeVerticalControlView : public BaseView { Q_OBJECT public: ScopeVerticalControlView(Session& session, std::shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static ScopeVerticalControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::datatypes::BoolCheckBox *enable_check_; ui::datatypes::RationalComboBox *vdiv_box_; ui::datatypes::StringComboBox *coupling_box_; ui::datatypes::BoolCheckBox *filter_check_; ui::datatypes::UInt64ComboBox *probe_factor_box_; void setup_ui(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SCOPEVERTICALCONTROLVIEW_HPP ================================================ FILE: src/ui/views/sequenceoutputview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sequenceoutputview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/data/properties/doubleproperty.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/datatypes/doublespinbox.hpp" #include "src/ui/dialogs/generatewaveformdialog.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::dynamic_pointer_cast; using std::shared_ptr; using std::string; using std::vector; namespace sv { namespace ui { namespace views { DoubleSpinBoxDelegate::DoubleSpinBoxDelegate( double min, double max, double step, int decimals, QObject *parent) : QStyledItemDelegate(parent), min_(min), max_(max), step_(step), decimals_(decimals) { } QWidget *DoubleSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { (void)option; (void)index; QDoubleSpinBox *editor = new QDoubleSpinBox(parent); editor->setFrame(false); editor->setMinimum(min_); editor->setMaximum(max_); editor->setSingleStep(step_); editor->setDecimals(decimals_); return editor; } QString DoubleSpinBoxDelegate::displayText( const QVariant &value, const QLocale &locale) const { (void)locale; return QString("%L1").arg(value.toDouble(), 0, 'f', decimals_); } void DoubleSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { double value = index.model()->data(index, Qt::EditRole).toDouble(); QDoubleSpinBox *spin_box = qobject_cast(editor); spin_box->setValue(value); } void DoubleSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QDoubleSpinBox *spin_box = qobject_cast(editor); spin_box->interpretText(); model->setData(index, QVariant(spin_box->value()), Qt::EditRole); } void DoubleSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { (void)index; editor->setGeometry(option.rect); } SequenceOutputView::SequenceOutputView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), property_(nullptr), action_run_(new QAction(this)), action_add_row_(new QAction(this)), action_delete_row_(new QAction(this)), action_delete_all_(new QAction(this)), action_load_from_file_(new QAction(this)), action_generate_waveform_(new QAction(this)), sequence_pos_(0) { id_ = "sequenceoutput:" + util::format_uuid(uuid_); setup_ui(); setup_toolbar(); timer_ = new QTimer(this); } SequenceOutputView::~SequenceOutputView() { stop_timer(); } QString SequenceOutputView::title() const { QString title = tr("Sequence Output"); if (property_) title = title.append(" ").append(property_->display_name()); return title; } void SequenceOutputView::set_property( shared_ptr property) { assert(property); stop_timer(); property_ = property; sequence_table_->setItemDelegateForColumn(0, new DoubleSpinBoxDelegate(property_->min(), property_->max(), property_->step(), property_->decimal_places())); Q_EMIT title_changed(); } void SequenceOutputView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); QHBoxLayout *repeat_layout = new QHBoxLayout(); repeat_layout->addWidget(new QLabel(tr("Repeat"))); repeat_layout->addSpacing(8); repeat_infinite_box_ = new QCheckBox(tr("infinite")); repeat_infinite_box_->setChecked(true); connect(repeat_infinite_box_, &QCheckBox::stateChanged, this, &SequenceOutputView::on_repeat_infinite_changed); repeat_layout->addWidget(repeat_infinite_box_); repeat_layout->addSpacing(8); repeat_count_box_ = new QSpinBox(); repeat_count_box_->setValue(1); repeat_count_box_->setMinimum(1); repeat_count_box_->setMaximum(1000000); repeat_count_box_->setSuffix(tr(" cycle(s)")); repeat_count_box_->setDisabled(true); repeat_layout->addWidget(repeat_count_box_); repeat_layout->addStretch(1); layout->addItem(repeat_layout); sequence_table_ = new QTableWidget(); sequence_table_->setColumnCount(2); QTableWidgetItem *value_header_item = new QTableWidgetItem(tr("Value")); sequence_table_->setHorizontalHeaderItem(0, value_header_item); sequence_table_->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::Stretch); QTableWidgetItem *delay_header_item = new QTableWidgetItem(tr("Delay [s]")); sequence_table_->setHorizontalHeaderItem(1, delay_header_item); sequence_table_->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch); sequence_table_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); sequence_table_->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); sequence_table_->setItemDelegateForColumn(1, new DoubleSpinBoxDelegate(0, 100000, 0.1, 3)); //sequence_table_->setRowCount(1); layout->addWidget(sequence_table_); this->central_widget_->setLayout(layout); } void SequenceOutputView::setup_toolbar() { action_run_->setText(tr("Run generator")); action_run_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_->setCheckable(true); action_run_->setChecked(false); connect(action_run_, &QAction::triggered, this, &SequenceOutputView::on_action_run_triggered); action_add_row_->setText(tr("Insert row")); action_add_row_->setIcon( QIcon::fromTheme("edit-table-insert-row-under", QIcon(":/icons/edit-table-insert-row-under.png"))); connect(action_add_row_, &QAction::triggered, this, &SequenceOutputView::on_action_add_row); action_delete_row_->setText(tr("Delete row")); action_delete_row_->setIcon( QIcon::fromTheme("edit-table-delete-row", QIcon(":/icons/edit-table-delete-row.png"))); connect(action_delete_row_, &QAction::triggered, this, &SequenceOutputView::on_action_delete_row); action_delete_all_->setText(tr("Delete all")); action_delete_all_->setIcon( QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.png"))); connect(action_delete_all_, &QAction::triggered, this, &SequenceOutputView::on_action_delete_all); action_load_from_file_->setText(tr("Load from file")); action_load_from_file_->setIcon( QIcon::fromTheme("document-open", QIcon(":/icons/document-open.png"))); connect(action_load_from_file_, &QAction::triggered, this, &SequenceOutputView::on_action_load_from_file_triggered); action_generate_waveform_->setText(tr("Run generator")); action_generate_waveform_->setIcon( QIcon::fromTheme("office-chart-line", // TODO: better icon QIcon(":/icons/office-chart-line.png"))); connect(action_generate_waveform_, &QAction::triggered, this, &SequenceOutputView::on_action_generate_waveform_triggered); toolbar_ = new QToolBar("Generator Toolbar"); toolbar_->addAction(action_run_); toolbar_->addSeparator(); toolbar_->addAction(action_add_row_); toolbar_->addAction(action_delete_row_); toolbar_->addAction(action_delete_all_); toolbar_->addSeparator(); toolbar_->addAction(action_load_from_file_); toolbar_->addAction(action_generate_waveform_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void SequenceOutputView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); if (!property_) return; SettingsManager::save_property(property_, settings, origin_device); settings.setValue("repeat_infinite", QVariant(repeat_infinite_box_->checkState())); settings.setValue("repeat_count", QVariant(repeat_count_box_->value())); // Save sequence int row_count = sequence_table_->rowCount(); settings.setValue("sequence_row_count", QVariant(row_count)); for (int pos=0; positem(pos, 0); QTableWidgetItem *delay_item = sequence_table_->item(pos, 1); if (!value_item || !delay_item) continue; settings.beginGroup(QString("sequence_").append(QString::number(pos))); settings.setValue("value", value_item->data(0)); settings.setValue("delay", delay_item->data(0)); settings.endGroup(); } } void SequenceOutputView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); auto property = SettingsManager::restore_property( session_, settings, origin_device); if (!property) return; set_property( dynamic_pointer_cast(property)); if (settings.contains("repeat_infinite")) repeat_infinite_box_->setCheckState( settings.value("repeat_infinite").value()); if (settings.contains("repeat_count")) repeat_count_box_->setValue(settings.value("repeat_count").toInt()); // Restore sequence int row_count = settings.value("sequence_row_count").toInt(); for (int pos=0; posinsertRow(pos); QTableWidgetItem *value_item = new QTableWidgetItem(QString::number( value.toDouble(), 'f', 3)); value_item->setData(0, value); sequence_table_->setItem(pos, 0, value_item); QTableWidgetItem *delay_item = new QTableWidgetItem(QString::number( delay.toDouble(), 'f', property_->decimal_places())); delay_item->setData(0, delay); sequence_table_->setItem(pos, 1, delay_item); settings.endGroup(); } } void SequenceOutputView::start_timer() { if (timer_->isActive()) { timer_->stop(); disconnect(timer_, &QTimer::timeout, this, &SequenceOutputView::on_timer_update); } sequence_pos_ = 0; sequence_reperat_count_ = 0; if (sequence_table_->rowCount() == 0) return; connect(timer_, &QTimer::timeout, this, &SequenceOutputView::on_timer_update); timer_->start(); action_run_->setText(tr("Stop")); action_run_->setIcon( QIcon::fromTheme("media-playback-stop", QIcon(":/icons/media-playback-stop.png"))); action_run_->setChecked(true); } void SequenceOutputView::stop_timer() { action_run_->setText(tr("Run")); action_run_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_->setChecked(false); if (!timer_->isActive()) return; timer_->stop(); disconnect(timer_, &QTimer::timeout, this, &SequenceOutputView::on_timer_update); sequence_pos_ = 0; sequence_reperat_count_ = 0; } void SequenceOutputView::insert_row(int row, double value, double delay) { if (!property_) { QMessageBox::warning(this, tr("No property assigned."), tr("Please assign a property to this sequence output view first."), QMessageBox::Ok); } sequence_table_->insertRow(row); QTableWidgetItem *value_item = new QTableWidgetItem( QString::number(value, 'f', 3)); value_item->setData(0, QVariant(value)); sequence_table_->setItem(row, 0, value_item); QTableWidgetItem *delay_item = new QTableWidgetItem( QString::number(delay, 'f', property_->decimal_places())); delay_item->setData(0, QVariant(delay)); sequence_table_->setItem(row, 1, delay_item); } void SequenceOutputView::on_timer_update() { if (!property_) { stop_timer(); return; } bool found_value = sequence_pos_ > 0; double value = .0; int delay_ms = 0; // Cycle through the row until a duration is found. do { if (sequence_table_->rowCount() == 0) { stop_timer(); return; } if (sequence_pos_ >= sequence_table_->rowCount()) { // Jump up to the first row sequence_pos_ = 0; if (!found_value) { stop_timer(); return; } if (!repeat_infinite_box_->isChecked()) { sequence_reperat_count_++; if (sequence_reperat_count_ >= repeat_count_box_->value()) { stop_timer(); return; } } } QTableWidgetItem *value_item = sequence_table_->item(sequence_pos_, 0); if (value_item) value = value_item->data(0).toDouble(); QTableWidgetItem *delay_item = sequence_table_->item(sequence_pos_, 1); if (delay_item) delay_ms = (int)std::round(delay_item->data(0).toDouble() * 1000); sequence_table_->selectRow(sequence_pos_); sequence_pos_++; } while (delay_ms <= 0); property_->change_value(value); timer_->setInterval(delay_ms); } void SequenceOutputView::on_repeat_infinite_changed() { if (repeat_infinite_box_->isChecked()) repeat_count_box_->setDisabled(true); else repeat_count_box_->setDisabled(false); } void SequenceOutputView::on_action_run_triggered() { if (action_run_->isChecked()) start_timer(); else stop_timer(); } void SequenceOutputView::on_action_add_row() { int row = sequence_table_->currentRow() + 1; insert_row(row, .0, .0); } void SequenceOutputView::on_action_delete_row() { // NOTE: If the cells are empty, there is no item to be returned by // selectedItems() and selectedIndexes() is protected... const auto items = sequence_table_->selectedItems(); for (const auto &item : items) { sequence_table_->removeRow(item->row()); } } void SequenceOutputView::on_action_delete_all() { sequence_table_->setRowCount(0); } void SequenceOutputView::on_action_load_from_file_triggered() { QString file_name = QFileDialog::getOpenFileName(this, tr("Open Sequence-File"), QDir::homePath(), tr("CSV Files (*.csv)")); if (file_name.length() <= 0) return; std::ifstream file(file_name.toStdString()); if (file.is_open()) { string line; int row = 0; while (std::getline(file, line)) { auto fields = sv::util::parse_csv_line(line); // TODO: Define CSV file format somehow/somewhere... // TODO: Parse header if (fields.size() < 2) continue; bool ok; // NOTE: QString::toDouble() to avoid locales and use the C locale double value = QString::fromStdString(fields[0]).toDouble(&ok); if (!ok) continue; // NOTE: QString::toDouble() to avoid locales and use the C locale double delay = QString::fromStdString(fields[1]).toDouble(&ok); if (!ok) continue; insert_row(row, value, delay); row++; } } file.close(); } void SequenceOutputView::on_action_generate_waveform_triggered() { if (!property_) { QMessageBox::warning(this, tr("No property assigned."), tr("Please assign a property to this sequence output view first."), QMessageBox::Ok); } ui::dialogs::GenerateWaveformDialog dlg(property_); if (!dlg.exec()) return; vector sequence_values = dlg.sequence_values(); vector sequence_delays = dlg.sequence_delays(); for (size_t i=0; i(i), sequence_values[i], sequence_delays[i]); } } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/sequenceoutputview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SEQUENCEOUTPUTVIEW_HPP #define UI_VIEWS_SEQUENCEOUTPUTVIEW_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace data { namespace properties { class DoubleProperty; } } namespace devices { class BaseDevice; } namespace ui { namespace views { class DoubleSpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: DoubleSpinBoxDelegate(double min, double max, double step, int decimals, QObject *parent = nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QString displayText(const QVariant &value, const QLocale &locale) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: double min_; double max_; double step_; int decimals_; }; class SequenceOutputView : public BaseView { Q_OBJECT public: explicit SequenceOutputView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); ~SequenceOutputView(); QString title() const override; void set_property(shared_ptr property); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: shared_ptr property_; QAction *const action_run_; QAction *const action_add_row_; QAction *const action_delete_row_; QAction *const action_delete_all_; QAction *const action_load_from_file_; QAction *const action_generate_waveform_; QToolBar *toolbar_; QTimer *timer_; QCheckBox *repeat_infinite_box_; QSpinBox *repeat_count_box_; QTableWidget *sequence_table_; int sequence_pos_; int sequence_reperat_count_; void setup_ui(); void setup_toolbar(); void start_timer(); void stop_timer(); void insert_row(int row, double value, double delay); QStringList parse_csv_line(QString line); private Q_SLOTS: void on_timer_update(); void on_repeat_infinite_changed(); void on_action_run_triggered(); void on_action_add_row(); void on_action_delete_row(); void on_action_delete_all(); void on_action_load_from_file_triggered(); void on_action_generate_waveform_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SEQUENCEOUTPUTVIEW_HPP ================================================ FILE: src/ui/views/smuscriptoutputview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smuscriptoutputview.hpp" #include "src/session.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { namespace ui { namespace views { SmuScriptOutputView::SmuScriptOutputView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), auto_scroll_(true), action_auto_scroll_(new QAction(this)), action_clear_output_(new QAction(this)) { // The uuid is ignored here to give all SmuScriptOutputViews the same look, // when restored from the settings. id_ = "smuscriptoutput:"; setup_ui(); setup_toolbar(); } QString SmuScriptOutputView::title() const { return tr("SmuScript Output"); } void SmuScriptOutputView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); output_edit_ = new QPlainTextEdit(); output_edit_->setReadOnly(true); // Same as QCodeEditor::initFont() auto font = QFontDatabase::systemFont(QFontDatabase::FixedFont); font.setFixedPitch(true); font.setPointSize(10); output_edit_->setFont(font); layout->addWidget(output_edit_); this->central_widget_->setLayout(layout); } void SmuScriptOutputView::setup_toolbar() { action_auto_scroll_->setText(tr("Auto scroll")); action_auto_scroll_->setIcon( QIcon::fromTheme("go-bottom", QIcon(":/icons/go-bottom.png"))); action_auto_scroll_->setCheckable(true); action_auto_scroll_->setChecked(auto_scroll_); connect(action_auto_scroll_, &QAction::triggered, this, &SmuScriptOutputView::on_action_auto_scroll_triggered); action_clear_output_->setText(tr("Clear output")); action_clear_output_->setIcon( QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.png"))); connect(action_clear_output_, &QAction::triggered, this, &SmuScriptOutputView::on_action_clear_output_triggered); toolbar_ = new QToolBar("SmuScript Output Toolbar"); toolbar_->addAction(action_auto_scroll_); toolbar_->addSeparator(); toolbar_->addAction(action_clear_output_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void SmuScriptOutputView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); } void SmuScriptOutputView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } void SmuScriptOutputView::scroll_to_bottom() { output_edit_->verticalScrollBar()->setValue( output_edit_->verticalScrollBar()->maximum()); } void SmuScriptOutputView::append_out_text(const std::string &text) { output_edit_->appendPlainText(QString::fromStdString(text)); if (auto_scroll_) scroll_to_bottom(); } void SmuScriptOutputView::append_err_text(const std::string &text) { QTextCharFormat tcf = output_edit_->currentCharFormat(); QBrush old_brush = tcf.foreground(); tcf.setForeground(QBrush(Qt::red)); output_edit_->setCurrentCharFormat(tcf); output_edit_->appendPlainText(QString::fromStdString(text)); tcf.setForeground(old_brush); output_edit_->setCurrentCharFormat(tcf); if (auto_scroll_) scroll_to_bottom(); } void SmuScriptOutputView::on_action_auto_scroll_triggered() { auto_scroll_ = !auto_scroll_; action_auto_scroll_->setChecked(auto_scroll_); } void SmuScriptOutputView::on_action_clear_output_triggered() { output_edit_->clear(); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/smuscriptoutputview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SMUSCRIPTOUTPUTVIEW_HPP #define UI_VIEWS_SMUSCRIPTOUTPUTVIEW_HPP #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace views { class SmuScriptOutputView : public BaseView { Q_OBJECT public: explicit SmuScriptOutputView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: bool auto_scroll_; QAction *const action_auto_scroll_; QAction *const action_clear_output_; QToolBar *toolbar_; QPlainTextEdit *output_edit_; void setup_ui(); void setup_toolbar(); void scroll_to_bottom(); public Q_SLOTS: void append_out_text(const std::string &text); void append_err_text(const std::string &text); private Q_SLOTS: void on_action_auto_scroll_triggered(); void on_action_clear_output_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SMUSCRIPTOUTPUTVIEW_HPP ================================================ FILE: src/ui/views/smuscripttreeview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smuscripttreeview.hpp" #include "src/mainwindow.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/python/smuscriptrunner.hpp" #include "src/ui/views/baseview.hpp" using std::shared_ptr; using std::string; namespace sv { namespace ui { namespace views { SmuScriptTreeView::SmuScriptTreeView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), action_new_script_(new QAction(this)), action_open_script_(new QAction(this)), action_run_script_(new QAction(this)) { id_ = "smuscripttree:" + util::format_uuid(uuid_); QSettings settings; if (SettingsManager::restore_settings() && settings.childGroups().contains("SmuScriptTree")) { SmuScriptTreeView::restore_settings(settings); } else script_dir_ = QDir::homePath(); setup_ui(); setup_toolbar(); connect_signals(); } QString SmuScriptTreeView::title() const { return tr("SmuScript"); } void SmuScriptTreeView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); file_system_model_ = new QFileSystemModel(); file_system_model_->setRootPath(""); file_system_tree_ = new QTreeView(); file_system_tree_->setModel(file_system_model_); file_system_tree_->setAnimated(false); file_system_tree_->setIndentation(20); file_system_tree_->setSortingEnabled(true); file_system_tree_->sortByColumn(0, Qt::SortOrder::AscendingOrder); layout->addWidget(file_system_tree_); layout->setContentsMargins(2, 2, 2, 2); this->central_widget_->setLayout(layout); file_system_tree_->setColumnWidth(0, file_system_tree_->width()); // NOTE: QFileSystemModel::index() doesn't return the correct row the first // time or when call a second time directly after the first. // Therefore it is called via a timer the second time. QModelIndex script_path_index = file_system_model_->index(script_dir_); file_system_tree_->expand(script_path_index); file_system_tree_->setCurrentIndex(script_path_index); QTimer::singleShot(250, this, &SmuScriptTreeView::scroll_to_script_dir); } void SmuScriptTreeView::setup_toolbar() { action_new_script_->setText(tr("New script")); action_new_script_->setIconText(tr("New script")); action_new_script_->setIcon( QIcon::fromTheme("document-new", QIcon(":/icons/document-new.png"))); connect(action_new_script_, &QAction::triggered, this, &SmuScriptTreeView::on_action_new_script_triggered); action_open_script_->setText(tr("Open script")); action_open_script_->setIconText(tr("Open script")); action_open_script_->setIcon( QIcon::fromTheme("document-open", QIcon(":/icons/document-open.png"))); connect(action_open_script_, &QAction::triggered, this, &SmuScriptTreeView::on_action_open_script_triggered); action_run_script_->setText(tr("Run script")); action_run_script_->setIconText(tr("Run script")); action_run_script_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_script_->setCheckable(true); connect(action_run_script_, &QAction::triggered, this, &SmuScriptTreeView::on_action_run_script_triggered); if (session_.smu_script_runner()->is_running()) action_run_script_->setChecked(true); else action_run_script_->setChecked(false); toolbar_ = new QToolBar("SmuScript Toolbar"); toolbar_->addAction(action_new_script_); toolbar_->addAction(action_open_script_); toolbar_->addSeparator(); toolbar_->addAction(action_run_script_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void SmuScriptTreeView::connect_signals() { connect(file_system_tree_, &QTreeView::doubleClicked, this, &SmuScriptTreeView::on_tree_double_clicked); connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::script_started, this, &SmuScriptTreeView::on_script_started); connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::script_finished, this, &SmuScriptTreeView::on_script_finished); } void SmuScriptTreeView::save_settings(QSettings &settings, shared_ptr origin_device) const { settings.beginGroup("SmuScriptTree"); settings.remove(""); // Remove all keys in this group BaseView::save_settings(settings, origin_device); QModelIndex index = file_system_tree_->selectionModel()->currentIndex(); if (index.isValid()) { QFileInfo file_info = file_system_model_->fileInfo(index); if (file_info.isDir()) settings.setValue("directory", file_info.canonicalFilePath()); else settings.setValue("directory", file_info.canonicalPath()); } else settings.setValue("directory", script_dir_); settings.endGroup(); } void SmuScriptTreeView::restore_settings(QSettings &settings, shared_ptr origin_device) { settings.beginGroup("SmuScriptTree"); BaseView::restore_settings(settings, origin_device); if (settings.contains("directory")) { script_dir_ = settings.value("directory").toString(); if (!QFileInfo::exists(script_dir_)) script_dir_ = QDir::homePath(); } else script_dir_ = QDir::homePath(); settings.endGroup(); } void SmuScriptTreeView::scroll_to_script_dir() { QModelIndex script_path_index = file_system_model_->index(script_dir_); file_system_tree_->scrollTo(script_path_index, QAbstractItemView::PositionAtTop); } void SmuScriptTreeView::open_script_file(const QModelIndex &index) { if (!index.isValid()) return; QFileInfo file_info = file_system_model_->fileInfo(index); if (!file_info.isFile()) return; if (!file_info.fileName().endsWith(".py")) return; session().main_window()->add_smuscript_tab( file_info.filePath().toStdString()); } void SmuScriptTreeView::on_action_new_script_triggered() { session().main_window()->add_smuscript_tab(""); } void SmuScriptTreeView::on_action_open_script_triggered() { QModelIndex index = file_system_tree_->selectionModel()->currentIndex(); open_script_file(index); } void SmuScriptTreeView::on_action_run_script_triggered() { if (action_run_script_->isChecked()) { QModelIndex index = file_system_tree_->selectionModel()->currentIndex(); if (index.isValid()) session_.smu_script_runner()->run( file_system_model_->filePath(index).toStdString()); } else session_.smu_script_runner()->stop(); } void SmuScriptTreeView::on_tree_double_clicked(const QModelIndex& index) { open_script_file(index); } void SmuScriptTreeView::on_script_started() { action_run_script_->setText(tr("Stop")); action_run_script_->setIconText(tr("Stop")); action_run_script_->setIcon( QIcon::fromTheme("media-playback-stop", QIcon(":/icons/media-playback-stop.png"))); action_run_script_->setChecked(true); } void SmuScriptTreeView::on_script_finished() { action_run_script_->setText(tr("Run")); action_run_script_->setIconText(tr("Run")); action_run_script_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_script_->setChecked(false); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/smuscripttreeview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SMUSCRIPTTREEVIEW_HPP #define UI_VIEWS_SMUSCRIPTTREEVIEW_HPP #include #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace views { class SmuScriptTreeView : public BaseView { Q_OBJECT public: explicit SmuScriptTreeView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: QAction *const action_new_script_; QAction *const action_open_script_; QAction *const action_run_script_; QString script_dir_; QToolBar *toolbar_; QFileSystemModel *file_system_model_; QTreeView *file_system_tree_; void setup_ui(); void setup_toolbar(); void connect_signals(); void scroll_to_script_dir(); void open_script_file(const QModelIndex& index); private Q_SLOTS: void on_action_new_script_triggered(); void on_action_open_script_triggered(); void on_action_run_script_triggered(); void on_tree_double_clicked(const QModelIndex& index); void on_script_started(); void on_script_finished(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SMUSCRIPTTREEVIEW_HPP ================================================ FILE: src/ui/views/smuscriptview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smuscriptview.hpp" #include "src/session.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/python/smuscriptrunner.hpp" #include "src/ui/views/baseview.hpp" using std::shared_ptr; using std::string; namespace sv { namespace ui { namespace views { SmuScriptView::SmuScriptView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), action_open_(new QAction(this)), action_save_(new QAction(this)), action_save_as_(new QAction(this)), action_run_(new QAction(this)), action_find_(new QAction(this)), text_changed_(false), started_from_here_(false) { // The uuid is ignored here to give all SmuScriptViews the same look, when // restored from the settings. id_ = "smuscript:"; setup_ui(); setup_toolbar(); connect_signals(); } QString SmuScriptView::title() const { if (script_file_name_.empty()) return tr("Untitled"); std::size_t found = script_file_name_.find_last_of("/\\"); return QString::fromStdString(script_file_name_.substr(found+1)); } void SmuScriptView::load_file(const string &file_name) { if (file_name.empty()) return; QFile file(QString::fromStdString(file_name)); if (file.open(QFile::ReadOnly | QFile::Text)) { editor_->setPlainText(file.readAll()); file.close(); text_changed_ = false; Q_EMIT file_save_state_changed(false); } // Check if filename has changed if (script_file_name_ != file_name) { script_file_name_ = file_name; Q_EMIT file_name_changed(QString::fromStdString(file_name)); } } bool SmuScriptView::ask_to_save(const QString &title) { if (!text_changed_) return true; QMessageBox::StandardButton reply = QMessageBox::warning(this, title, tr("The file \"%1\" has unsaved changes. Would you like to save them?"). arg(QString::fromStdString(script_file_name_)), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (reply == QMessageBox::Yes) { if (!this->save(QString::fromStdString(script_file_name_))) return false; } else if (reply == QMessageBox::Cancel) return false; return true; } void SmuScriptView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); editor_ = new QCodeEditor(); //editor_->setSyntaxStyle(); editor_->setCompleter(new QPythonCompleter); editor_->setHighlighter(new QPythonHighlighter); editor_->setAutoIndentation(true); editor_->setWordWrapMode(QTextOption::WordWrap); // NOTE: The extra bottom margin will mess up the textChanged() signal! editor_->setExtraBottomMargin(false); layout->addWidget(editor_); this->central_widget_->setLayout(layout); find_dialog_ = new FindReplaceDialog(this); find_dialog_->setModal(false); find_dialog_->setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); find_dialog_->setTextEdit(editor_); } void SmuScriptView::setup_toolbar() { action_open_->setText(tr("&Open")); action_open_->setIconText(tr("Open")); action_open_->setIcon( QIcon::fromTheme("document-open", QIcon(":/icons/document-open.png"))); action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O)); connect(action_open_, &QAction::triggered, this, &SmuScriptView::on_action_open_triggered); action_save_->setText(tr("&Save")); action_save_->setIconText(tr("Save")); action_save_->setIcon( QIcon::fromTheme("document-save", QIcon(":/icons/document-save.png"))); action_save_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); connect(action_save_, &QAction::triggered, this, &SmuScriptView::on_action_save_triggered); action_save_as_->setText(tr("Save &As")); action_save_as_->setIconText(tr("Save As")); action_save_as_->setIcon( QIcon::fromTheme("document-save-as", QIcon(":/icons/document-save-as.png"))); connect(action_save_as_, &QAction::triggered, this, &SmuScriptView::on_action_save_as_triggered); action_run_->setText(tr("Run")); action_run_->setIconText(tr("Run")); action_run_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_->setCheckable(true); action_run_->setChecked(false); connect(action_run_, &QAction::triggered, this, &SmuScriptView::on_action_run_triggered); if (session_.smu_script_runner()->is_running()) action_run_->setDisabled(true); action_find_->setText(tr("&Find and Replace")); action_find_->setIconText(tr("Find and Replace")); action_find_->setIcon( QIcon::fromTheme("edit-find", QIcon(":/icons/edit-find.png"))); action_find_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F)); connect(action_find_, &QAction::triggered, this, &SmuScriptView::on_action_find_triggered); toolbar_ = new QToolBar("SmuScript Toolbar"); toolbar_->addAction(action_open_); toolbar_->addAction(action_save_); toolbar_->addAction(action_save_as_); toolbar_->addSeparator(); toolbar_->addAction(action_run_); toolbar_->addSeparator(); toolbar_->addAction(action_find_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void SmuScriptView::connect_signals() { connect(editor_, &QCodeEditor::textChanged, this, &SmuScriptView::on_text_changed); connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::script_started, this, &SmuScriptView::on_script_started); connect(session_.smu_script_runner().get(), &python::SmuScriptRunner::script_finished, this, &SmuScriptView::on_script_finished); } void SmuScriptView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); find_dialog_->writeSettings(settings); } void SmuScriptView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); find_dialog_->readSettings(settings); } bool SmuScriptView::save(QString file_name) { if (file_name.length() <= 0) { file_name = QFileDialog::getSaveFileName(this, tr("Save SmuScript-File"), QDir::homePath(), tr("Python Files (*.py)")); if (file_name.length() <= 0) return false; } QFile file(file_name); if (file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) { QTextStream stream(&file); stream << editor_->toPlainText(); stream.flush(); file.close(); text_changed_ = false; Q_EMIT file_save_state_changed(false); } else { QMessageBox::critical(this, tr("File error"), tr("Could not save to file \"%1\".").arg(file_name), QMessageBox::Ok); return false; } // Check if filename has changed if (script_file_name_ != file_name.toStdString()) { script_file_name_ = file_name.toStdString(); Q_EMIT file_name_changed(file_name); } return true; } void SmuScriptView::run_script() { if (action_run_->isChecked()) { // Already running! return; } action_run_->activate(QAction::Trigger); } void SmuScriptView::stop_script() { if (!action_run_->isChecked()) { // Not running! return; } action_run_->activate(QAction::Trigger); } void SmuScriptView::on_action_open_triggered() { if (!ask_to_save(tr("Open new script file"))) return; QString file_name = QFileDialog::getOpenFileName(this, tr("Open SmuScript-File"), QDir::homePath(), tr("Python Files (*.py)")); load_file(file_name.toStdString()); } void SmuScriptView::on_action_save_triggered() { this->save(QString::fromStdString(script_file_name_)); } void SmuScriptView::on_action_save_as_triggered() { this->save(""); } void SmuScriptView::on_action_find_triggered() { find_dialog_->showDialog(editor_->textCursor().selectedText()); } void SmuScriptView::on_text_changed() { text_changed_ = true; Q_EMIT file_save_state_changed(true); } void SmuScriptView::on_action_run_triggered() { if (action_run_->isChecked()) { if (!ask_to_save(tr("File changed"))) return; action_run_->setText(tr("Stop")); action_run_->setIconText(tr("Stop")); action_run_->setIcon( QIcon::fromTheme("media-playback-stop", QIcon(":/icons/media-playback-stop.png"))); started_from_here_ = true; session_.smu_script_runner()->run(script_file_name_); } else { action_run_->setText(tr("Run")); action_run_->setIconText(tr("Run")); action_run_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); started_from_here_ = false; session_.smu_script_runner()->stop(); } } void SmuScriptView::on_script_started() { if (started_from_here_) Q_EMIT script_started(); else action_run_->setDisabled(true); } void SmuScriptView::on_script_finished() { if (started_from_here_) { action_run_->setText(tr("Run")); action_run_->setIconText(tr("Run")); action_run_->setIcon( QIcon::fromTheme("media-playback-start", QIcon(":/icons/media-playback-start.png"))); action_run_->setChecked(false); started_from_here_ = false; Q_EMIT script_finished(); } else action_run_->setDisabled(false); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/smuscriptview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SMUSCRIPTVIEW_HPP #define UI_VIEWS_SMUSCRIPTVIEW_HPP #include #include #include #include #include #include #include #include #include "src/ui/views/baseview.hpp" using std::shared_ptr; using std::string; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace views { class SmuScriptView : public BaseView { Q_OBJECT public: explicit SmuScriptView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void load_file(const string &file_name); bool ask_to_save(const QString &title); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: string script_file_name_; QAction *const action_open_; QAction *const action_save_; QAction *const action_save_as_; QAction *const action_run_; QAction *const action_find_; QToolBar *toolbar_; QCodeEditor *editor_; FindReplaceDialog *find_dialog_; bool text_changed_; bool started_from_here_; void setup_ui(); void setup_toolbar(); void connect_signals(); bool save(QString file_name); public Q_SLOTS: void run_script(); void stop_script(); private Q_SLOTS: void on_action_open_triggered(); void on_action_save_triggered(); void on_action_save_as_triggered(); void on_action_run_triggered(); void on_action_find_triggered(); void on_text_changed(); void on_script_started(); void on_script_finished(); Q_SIGNALS: void file_name_changed(const QString &file_name); void file_save_state_changed(bool is_unsaved); void script_started(); void script_finished(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SMUSCRIPTVIEW_HPP ================================================ FILE: src/ui/views/sourcesinkcontrolview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "sourcesinkcontrolview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/datatypes/boolbutton.hpp" #include "src/ui/datatypes/boolled.hpp" #include "src/ui/datatypes/doublecontrol.hpp" #include "src/ui/datatypes/stringcombobox.hpp" #include "src/ui/datatypes/stringled.hpp" #include "src/ui/datatypes/thresholdcontrol.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" using std::shared_ptr; using sv::devices::ConfigKey; namespace sv { namespace ui { namespace views { SourceSinkControlView::SourceSinkControlView(Session &session, shared_ptr configurable, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), configurable_(configurable) { id_ = "sourcesinkcontrol:" + util::format_uuid(uuid_); setup_ui(); } QString SourceSinkControlView::title() const { return tr("Control") + " " + configurable_->display_name(); } void SourceSinkControlView::setup_ui() { QIcon red_icon(":/icons/status-red.svg"); QIcon green_icon(":/icons/status-green.svg"); QIcon grey_icon(":/icons/status-grey.svg"); QVBoxLayout *layout = new QVBoxLayout(); QGridLayout *info_layout = new QGridLayout(); // Enable button enable_button_ = new ui::datatypes::BoolButton( configurable_->get_property(ConfigKey::Enabled), true, true); //info_layout->addWidget(enable_button_, 0, 0, 2, 1, Qt::AlignLeft); info_layout->addWidget(enable_button_, 0, 0, Qt::AlignLeft); // Regulation selector (sinks) / range selector (power supplies) if (configurable_->device_type() == devices::DeviceType::ElectronicLoad) { regulation_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::Regulation), true, true); info_layout->addWidget(regulation_box_, 1, 0, Qt::AlignLeft); } else if (configurable_->device_type() == devices::DeviceType::PowerSupply && (configurable_->has_get_config(ConfigKey::Range) || configurable_->has_set_config(ConfigKey::Range) || configurable_->has_list_config(ConfigKey::Range))) { range_box_ = new ui::datatypes::StringComboBox( configurable_->get_property(ConfigKey::Range), true, true); info_layout->addWidget(range_box_, 1, 0, Qt::AlignLeft); } // Regulation indicators for power supplies if (configurable_->device_type() == devices::DeviceType::PowerSupply) { cv_led_ = new ui::datatypes::StringLed( configurable_->get_property(ConfigKey::Regulation), true, green_icon, grey_icon, grey_icon, "CV", "", tr("CV")); info_layout->addWidget(cv_led_, 0, 1, Qt::AlignLeft); cc_led_ = new ui::datatypes::StringLed( configurable_->get_property(ConfigKey::Regulation), true, red_icon, grey_icon, grey_icon, "CC", "", tr("CC")); info_layout->addWidget(cc_led_, 1, 1, Qt::AlignLeft); } // Protection indicators ovp_led_ = new ui::datatypes::BoolLed( configurable_->get_property(ConfigKey::OverVoltageProtectionActive), true, red_icon, grey_icon, grey_icon, tr("OVP")); info_layout->addWidget(ovp_led_, 0, 2, Qt::AlignLeft); ocp_led_ = new ui::datatypes::BoolLed( configurable_->get_property(ConfigKey::OverCurrentProtectionActive), true, red_icon, grey_icon, grey_icon, tr("OCP")); info_layout->addWidget(ocp_led_, 1, 2, Qt::AlignLeft); otp_led_ = new ui::datatypes::BoolLed( configurable_->get_property(ConfigKey::OverTemperatureProtectionActive), true, red_icon, grey_icon, grey_icon, tr("OTP")); info_layout->addWidget(otp_led_, 0, 3, Qt::AlignLeft); uvc_led_ = new ui::datatypes::BoolLed( configurable_->get_property(ConfigKey::UnderVoltageConditionActive), true, red_icon, grey_icon, grey_icon, tr("UVC")); info_layout->addWidget(uvc_led_, 1, 3, Qt::AlignLeft); info_layout->setColumnStretch(4, 1); layout->addLayout(info_layout, 0); QHBoxLayout *ctrl_layout = new QHBoxLayout(); // TODO: Change control when switching regulation mode for sinks if (configurable_->has_get_config(ConfigKey::VoltageTarget) || configurable_->has_set_config(ConfigKey::VoltageTarget)) { voltage_control_ = new ui::datatypes::DoubleControl( configurable_->get_property(ConfigKey::VoltageTarget), true, true, tr("Voltage")); voltage_control_->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); ctrl_layout->addWidget(voltage_control_); } // TODO: Change control when switching regulation mode for sinks if (configurable_->has_get_config(ConfigKey::CurrentLimit) || configurable_->has_set_config(ConfigKey::CurrentLimit)) { current_control_ = new ui::datatypes::DoubleControl( configurable_->get_property(ConfigKey::CurrentLimit), true, true, tr("Current")); current_control_->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); ctrl_layout->addWidget(current_control_, 1, Qt::AlignLeft); } layout->addLayout(ctrl_layout, 0); QHBoxLayout *opt_ctrl_layout = new QHBoxLayout(); ovp_control_ = new ui::datatypes::ThresholdControl( configurable_->get_property(ConfigKey::OverVoltageProtectionThreshold), configurable_->get_property(ConfigKey::OverVoltageProtectionEnabled), true, true, tr("OVP")); opt_ctrl_layout->addWidget(ovp_control_); ocp_control_ = new ui::datatypes::ThresholdControl( configurable_->get_property(ConfigKey::OverCurrentProtectionThreshold), configurable_->get_property(ConfigKey::OverCurrentProtectionEnabled), true, true, tr("OCP")); opt_ctrl_layout->addWidget(ocp_control_); uvc_control_ = new ui::datatypes::ThresholdControl( configurable_->get_property(ConfigKey::UnderVoltageConditionThreshold), configurable_->get_property(ConfigKey::UnderVoltageConditionEnabled), true, true, tr("UVC")); opt_ctrl_layout->addWidget(uvc_control_, 1, Qt::AlignLeft); layout->addLayout(opt_ctrl_layout, 0); layout->addStretch(1); this->central_widget_->setLayout(layout); } void SourceSinkControlView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); SettingsManager::save_configurable(configurable_, settings, origin_device); } void SourceSinkControlView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); } SourceSinkControlView *SourceSinkControlView::init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device) { auto configurable = SettingsManager::restore_configurable( session, settings, origin_device); if (!configurable) return nullptr; return new SourceSinkControlView(session, configurable, uuid); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/sourcesinkcontrolview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_SOURCESINKCONTROLVIEW_HPP #define UI_VIEWS_SOURCESINKCONTROLVIEW_HPP #include #include #include #include "src/devices/deviceutil.hpp" #include "src/ui/views/baseview.hpp" using std::shared_ptr; namespace sv { class Session; namespace devices { class BaseDevice; class Configurable; } namespace ui { namespace datatypes { class BoolButton; class BoolLed; class DoubleControl; class StringComboBox; class StringLed; class ThresholdControl; } namespace views { class SourceSinkControlView : public BaseView { Q_OBJECT public: SourceSinkControlView(Session& session, std::shared_ptr configurable, QUuid uuid = QUuid(), QWidget* parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; static SourceSinkControlView *init_from_settings( Session &session, QSettings &settings, QUuid uuid, shared_ptr origin_device); private: shared_ptr configurable_; ui::datatypes::StringLed *cc_led_; ui::datatypes::StringLed *cv_led_; ui::datatypes::BoolLed *ovp_led_; ui::datatypes::BoolLed *ocp_led_; ui::datatypes::BoolLed *otp_led_; ui::datatypes::BoolLed *uvc_led_; ui::datatypes::BoolButton *enable_button_; ui::datatypes::StringComboBox *regulation_box_; ui::datatypes::StringComboBox *range_box_; ui::datatypes::DoubleControl *voltage_control_; ui::datatypes::DoubleControl *current_control_; ui::datatypes::ThresholdControl *ovp_control_; ui::datatypes::ThresholdControl *ocp_control_; ui::datatypes::ThresholdControl *uvc_control_; void setup_ui(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_SOURCESINKCONTROLVIEW_HPP ================================================ FILE: src/ui/views/timeplotview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "timeplotview.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/dialogs/selectsignaldialog.hpp" #include "src/ui/views/baseplotview.hpp" #include "src/ui/views/viewhelper.hpp" #include #include "src/ui/widgets/plot/plot.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" #include "src/ui/widgets/plot/timecurvedata.hpp" using std::dynamic_pointer_cast; using std::shared_ptr; using std::static_pointer_cast; using std::string; Q_DECLARE_METATYPE(sv::ui::widgets::plot::BaseCurveData *) namespace sv { namespace ui { namespace views { TimePlotView::TimePlotView(Session &session, QUuid uuid, QWidget *parent) : BasePlotView(session, uuid, parent), channel_(nullptr) { id_ = "timeplot:" + util::format_uuid(uuid_); plot_type_ = PlotType::TimePlot; } QString TimePlotView::title() const { QString title; if (channel_) title = tr("Channel ").append(channel_->display_name()); else { title = tr("Signal"); QString sep(" "); for (const auto &curve : plot_->curve_map()) { title = title.append(sep).append(curve.second->name()); sep = ", "; } } return title; } void TimePlotView::set_channel(shared_ptr channel) { assert(channel); if (channel_) { disconnect(channel_.get(), &channels::BaseChannel::signal_added, this, &TimePlotView::on_signal_changed); disconnect(channel_.get(), &channels::BaseChannel::signal_changed, this, &TimePlotView::on_signal_changed); } plot_->remove_all_curves(); channel_ = channel; shared_ptr signal; if (channel_->actual_signal()) signal = static_pointer_cast( channel_->actual_signal()); if (signal) add_signal(signal); connect(channel_.get(), &channels::BaseChannel::signal_added, this, &TimePlotView::on_signal_changed); connect(channel_.get(), &channels::BaseChannel::signal_changed, this, &TimePlotView::on_signal_changed); Q_EMIT title_changed(); } string TimePlotView::add_signal(shared_ptr signal) { assert(signal); string id; // Check if new actual_signal is already added to this plot for (const auto &curve : plot_->curve_map()) { auto *curve_data = qobject_cast( curve.second->curve_data()); if (!curve_data) continue; if (curve_data->signal() == signal) return id; } auto *curve = new widgets::plot::TimeCurveData(signal); id = plot_->add_curve(curve); if (!id.empty()) { Q_EMIT title_changed(); } else { QMessageBox::warning(this, tr("Cannot add signal"), tr("Cannot add time signal to plot!"), QMessageBox::Ok); } return id; } void TimePlotView::save_settings(QSettings &settings, shared_ptr origin_device) const { BasePlotView::save_settings(settings, origin_device); // TODO: Can the channel be saved inside the plot widget? bool save_curves = true; if (channel_) { SettingsManager::save_channel(channel_, settings, origin_device); save_curves = false; } plot_->save_settings(settings, save_curves, origin_device); } void TimePlotView::restore_settings(QSettings &settings, shared_ptr origin_device) { BasePlotView::restore_settings(settings, origin_device); // TODO: Can the channel be restored inside the plot widget? bool restore_curves = true; auto channel = SettingsManager::restore_channel( session_, settings, origin_device); if (channel) { set_channel(channel); restore_curves = false; } plot_->restore_settings(settings, restore_curves, origin_device); } void TimePlotView::on_action_add_curve_triggered() { shared_ptr selected_device; if (channel_) selected_device = channel_->parent_device(); ui::dialogs::SelectSignalDialog dlg(session(), selected_device); if (!dlg.exec()) return; for (const auto &signal : dlg.signals()) { add_signal(dynamic_pointer_cast(signal)); } } void TimePlotView::on_signal_changed() { if (!channel_) return; shared_ptr signal; if (channel_->actual_signal()) signal = dynamic_pointer_cast( channel_->actual_signal()); if (signal) add_signal(signal); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/timeplotview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_TIMEPLOTVIEW_HPP #define UI_VIEWS_TIMEPLOTVIEW_HPP #include #include #include #include #include "src/ui/views/baseplotview.hpp" using std::shared_ptr; using std::string; namespace sv { class Session; namespace channels { class BaseChannel; } namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace views { class TimePlotView : public BasePlotView { Q_OBJECT public: explicit TimePlotView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; /** * Add a new channel to the time plot. */ void set_channel(shared_ptr channel); /** * Add a new signal to the time plot and return the curve id. */ string add_signal(shared_ptr signal); private: shared_ptr channel_; protected Q_SLOTS: void on_action_add_curve_triggered() override; private Q_SLOTS: void on_signal_changed(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_TIMEPLOTVIEW_HPP ================================================ FILE: src/ui/views/valuepanelview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "valuepanelview.hpp" #include "src/data/datautil.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/channels/basechannel.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/basesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/viewhelper.hpp" #include "src/ui/widgets/monofontdisplay.hpp" using std::dynamic_pointer_cast; using std::shared_ptr; namespace sv { namespace ui { namespace views { ValuePanelView::ValuePanelView(Session &session, QUuid uuid, QWidget *parent) : BaseView(session, uuid, parent), channel_(nullptr), signal_(nullptr), value_min_(std::numeric_limits::max()), value_max_(std::numeric_limits::lowest()), action_reset_display_(new QAction(this)) { id_ = "valuepanel:" + util::format_uuid(uuid_); setup_ui(); setup_toolbar(); reset_display(); timer_ = new QTimer(this); init_timer(); } ValuePanelView::~ValuePanelView() { stop_timer(); } QString ValuePanelView::title() const { QString title; if (channel_) title = tr("Channel"); else title = tr("Signal"); if (channel_) title = title.append(" ").append(channel_->display_name()); else if (signal_) title = title.append(" ").append(signal_->display_name()); return title; } void ValuePanelView::set_channel(shared_ptr channel) { assert(channel); disconnect_signals_channel(); disconnect_signals_signal(); channel_ = channel; signal_ = dynamic_pointer_cast( channel_->actual_signal()); if (signal_) { init_displays(); connect_signals_signal(); } connect_signals_channel(); Q_EMIT title_changed(); } void ValuePanelView::set_signal(shared_ptr signal) { assert(signal); disconnect_signals_channel(); disconnect_signals_signal(); channel_ = nullptr; signal_ = signal; init_displays(); connect_signals_signal(); Q_EMIT title_changed(); } void ValuePanelView::setup_ui() { QVBoxLayout *layout = new QVBoxLayout(); QGridLayout *panel_layout = new QGridLayout(); value_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRangeWithSRDigits, "", "", "", false); value_min_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Min), true); value_max_display_ = new widgets::MonoFontDisplay( widgets::MonoFontDisplayType::AutoRange, "", "", data::datautil::format_quantity_flag(data::QuantityFlag::Max), true); panel_layout->addWidget(value_display_, 0, 0, 1, 2, Qt::AlignHCenter); panel_layout->addWidget(value_min_display_, 1, 0, 1, 1, Qt::AlignHCenter); panel_layout->addWidget(value_max_display_, 1, 1, 1, 1, Qt::AlignHCenter); layout->addLayout(panel_layout); layout->addStretch(1); this->central_widget_->setLayout(layout); } void ValuePanelView::setup_toolbar() { action_reset_display_->setText(tr("Reset display")); action_reset_display_->setIcon( QIcon::fromTheme("view-refresh", QIcon(":/icons/view-refresh.png"))); connect(action_reset_display_, &QAction::triggered, this, &ValuePanelView::on_action_reset_display_triggered); toolbar_ = new QToolBar("Panel Toolbar"); toolbar_->addAction(action_reset_display_); this->addToolBar(Qt::TopToolBarArea, toolbar_); } void ValuePanelView::init_displays() { QString unit = signal_->unit_name(); QString unit_suffix; set quantity_flags = signal_->quantity_flags(); int total_digits = signal_->total_digits(); int sr_digits = signal_->sr_digits(); if (quantity_flags.count(sv::data::QuantityFlag::AC) > 0) { //unit_suffix = QString::fromUtf8("\u23E6"); unit_suffix = sv::data::datautil::format_quantity_flag( sv::data::QuantityFlag::AC); quantity_flags.erase(sv::data::QuantityFlag::AC); } else if (quantity_flags.count(sv::data::QuantityFlag::DC) > 0) { //unit_suffix = QString::fromUtf8("\u2393"); unit_suffix = sv::data::datautil::format_quantity_flag( sv::data::QuantityFlag::DC); quantity_flags.erase(sv::data::QuantityFlag::DC); } set quantity_flags_min = quantity_flags; quantity_flags_min.insert(sv::data::QuantityFlag::Min); set quantity_flags_max = quantity_flags; quantity_flags_max.insert(sv::data::QuantityFlag::Max); value_display_->set_unit(unit); value_display_->set_unit_suffix(unit_suffix); value_display_->set_extra_text( sv::data::datautil::format_quantity_flags(quantity_flags, "\n")); value_display_->set_sr_digits(total_digits, sr_digits); value_min_display_->set_unit(unit); value_min_display_->set_unit_suffix(unit_suffix); value_min_display_->set_extra_text( sv::data::datautil::format_quantity_flags(quantity_flags_min, "\n")); value_min_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); value_max_display_->set_unit(unit); value_max_display_->set_unit_suffix(unit_suffix); value_max_display_->set_extra_text( sv::data::datautil::format_quantity_flags(quantity_flags_max, "\n")); value_max_display_->set_decimal_places( sv::data::DefaultTotalDigits, sv::data::DefaultDecimalPlaces); } void ValuePanelView::connect_signals_channel() { if (!channel_) return; connect(channel_.get(), &channels::BaseChannel::signal_added, this, &ValuePanelView::on_signal_changed); connect(channel_.get(), &channels::BaseChannel::signal_changed, this, &ValuePanelView::on_signal_changed); } void ValuePanelView::disconnect_signals_channel() { if (!channel_) return; disconnect(channel_.get(), &channels::BaseChannel::signal_added, this, &ValuePanelView::on_signal_changed); disconnect(channel_.get(), &channels::BaseChannel::signal_changed, this, &ValuePanelView::on_signal_changed); } void ValuePanelView::connect_signals_signal() { if (!signal_) return; //connect(signal_.get(), SIGNAL(unit_changed(QString)), // value_display_, SLOT(set_unit(const String))); connect(signal_.get(), &data::AnalogTimeSignal::digits_changed, value_display_, &widgets::MonoFontDisplay::set_sr_digits); } void ValuePanelView::disconnect_signals_signal() { if (!signal_) return; //disconnect(signal_.get(), SIGNAL(unit_changed(QString)), // value_display_, SLOT(set_unit(QString))); disconnect(signal_.get(), &data::AnalogTimeSignal::digits_changed, value_display_, &widgets::MonoFontDisplay::set_sr_digits); } void ValuePanelView::save_settings(QSettings &settings, shared_ptr origin_device) const { BaseView::save_settings(settings, origin_device); if (signal_) SettingsManager::save_signal(signal_, settings, origin_device); else SettingsManager::save_channel(channel_, settings, origin_device); } void ValuePanelView::restore_settings(QSettings &settings, shared_ptr origin_device) { BaseView::restore_settings(settings, origin_device); auto signal = SettingsManager::restore_signal( session_, settings, origin_device); if (signal) { set_signal( dynamic_pointer_cast(signal)); return; } auto channel = SettingsManager::restore_channel( session_, settings, origin_device); if (channel) set_channel(channel); } void ValuePanelView::reset_display() { value_display_->reset_value(); value_min_display_->reset_value(); value_max_display_->reset_value(); } void ValuePanelView::init_timer() { value_min_ = std::numeric_limits::max(); value_max_ = std::numeric_limits::lowest(); connect(timer_, &QTimer::timeout, this, &ValuePanelView::on_update); timer_->start(250); } void ValuePanelView::stop_timer() { if (!timer_->isActive()) return; timer_->stop(); disconnect(timer_, &QTimer::timeout, this, &ValuePanelView::on_update); reset_display(); } void ValuePanelView::on_update() { if (!signal_ || signal_->sample_count() == 0) return; double value = signal_->last_value(); if (value_min_ > value) value_min_ = value; if (value_max_ < value) value_max_ = value; value_display_->set_value(value); value_min_display_->set_value(value_min_); value_max_display_->set_value(value_max_); } void ValuePanelView::on_signal_changed() { // When channel_ is not set, we have a fixed signal_ and nothing will change if (!channel_) return; disconnect_signals_signal(); signal_ = dynamic_pointer_cast( channel_->actual_signal()); if (!signal_) return; init_displays(); connect_signals_signal(); Q_EMIT title_changed(); } void ValuePanelView::on_action_reset_display_triggered() { stop_timer(); init_timer(); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/valuepanelview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_VALUEPANELVIEW_HPP #define UI_VIEWS_VALUEPANELVIEW_HPP #include #include #include #include #include #include #include #include #include "src/data/datautil.hpp" #include "src/ui/views/baseview.hpp" using std::set; using std::shared_ptr; namespace sv { class Session; namespace channels { class BaseChannel; } namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace widgets { class MonoFontDisplay; } namespace views { class ValuePanelView : public BaseView { Q_OBJECT public: explicit ValuePanelView(Session& session, QUuid uuid = QUuid(), QWidget* parent = nullptr); ~ValuePanelView(); QString title() const override; void set_channel(shared_ptr channel); void set_signal(shared_ptr signal); void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; private: shared_ptr channel_; shared_ptr signal_; QTimer *timer_; // Min/max/actual values are stored here, so they can be reseted double value_min_; double value_max_; QAction *const action_reset_display_; QToolBar *toolbar_; widgets::MonoFontDisplay *value_display_; widgets::MonoFontDisplay *value_min_display_; widgets::MonoFontDisplay *value_max_display_; void setup_ui(); void setup_toolbar(); void init_displays(); void connect_signals_channel(); void disconnect_signals_channel(); void connect_signals_signal(); void disconnect_signals_signal(); void reset_display(); void init_timer(); void stop_timer(); private Q_SLOTS: void on_update(); void on_signal_changed(); void on_action_reset_display_triggered(); }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_VALUEPANELVIEW_HPP ================================================ FILE: src/ui/views/viewhelper.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "viewhelper.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/devices/basedevice.hpp" #include "src/devices/configurable.hpp" #include "src/devices/deviceutil.hpp" #include "src/ui/views/dataview.hpp" #include "src/ui/views/baseview.hpp" #include "src/ui/views/democontrolview.hpp" #include "src/ui/views/genericcontrolview.hpp" #include "src/ui/views/measurementcontrolview.hpp" #include "src/ui/views/powerpanelview.hpp" #include "src/ui/views/scopehorizontalcontrolview.hpp" #include "src/ui/views/scopetriggercontrolview.hpp" #include "src/ui/views/scopeverticalcontrolview.hpp" #include "src/ui/views/sequenceoutputview.hpp" #include "src/ui/views/smuscriptoutputview.hpp" #include "src/ui/views/smuscriptview.hpp" #include "src/ui/views/sourcesinkcontrolview.hpp" #include "src/ui/views/timeplotview.hpp" #include "src/ui/views/valuepanelview.hpp" #include "src/ui/views/xyplotview.hpp" using std::shared_ptr; using std::vector; using sv::devices::ConfigKey; using sv::devices::DeviceType; namespace sv { namespace ui { namespace views { namespace viewhelper { vector get_views_for_configurable(Session &session, shared_ptr configurable) { vector views; if (!configurable) return views; // Power supplies or electronic loads control view if ((configurable->device_type() == DeviceType::PowerSupply || configurable->device_type() == DeviceType::ElectronicLoad) && (configurable->has_get_config(ConfigKey::Enabled) || configurable->has_set_config(ConfigKey::Enabled) || configurable->has_get_config(ConfigKey::Regulation) || configurable->has_set_config(ConfigKey::Regulation) || configurable->has_get_config(ConfigKey::VoltageTarget) || configurable->has_set_config(ConfigKey::VoltageTarget) || configurable->has_get_config(ConfigKey::CurrentLimit) || configurable->has_set_config(ConfigKey::CurrentLimit) || configurable->has_get_config(ConfigKey::OverVoltageProtectionEnabled) || configurable->has_set_config(ConfigKey::OverVoltageProtectionEnabled) || configurable->has_get_config(ConfigKey::OverVoltageProtectionThreshold) || configurable->has_set_config(ConfigKey::OverVoltageProtectionThreshold) || configurable->has_get_config(ConfigKey::OverCurrentProtectionEnabled) || configurable->has_set_config(ConfigKey::OverCurrentProtectionEnabled) || configurable->has_get_config(ConfigKey::OverCurrentProtectionThreshold) || configurable->has_set_config(ConfigKey::OverCurrentProtectionThreshold) || configurable->has_get_config(ConfigKey::UnderVoltageConditionEnabled) || configurable->has_set_config(ConfigKey::UnderVoltageConditionEnabled) || configurable->has_get_config(ConfigKey::UnderVoltageConditionThreshold) || configurable->has_set_config(ConfigKey::UnderVoltageConditionThreshold))) { views.push_back(new SourceSinkControlView(session, configurable)); } // Vertical control for scopes if (configurable->device_type() == DeviceType::Oscilloscope && (configurable->has_get_config(ConfigKey::Enabled) || configurable->has_set_config(ConfigKey::Enabled) || configurable->has_get_config(ConfigKey::VDiv) || configurable->has_set_config(ConfigKey::VDiv) || configurable->has_get_config(ConfigKey::Coupling) || configurable->has_set_config(ConfigKey::Coupling) || configurable->has_get_config(ConfigKey::Filter) || configurable->has_set_config(ConfigKey::Filter))) { views.push_back(new ScopeVerticalControlView(session, configurable)); } // Trigger control for scopes if (configurable->device_type() == DeviceType::Oscilloscope && (configurable->has_get_config(ConfigKey::TriggerSource) || configurable->has_set_config(ConfigKey::TriggerSource) || configurable->has_get_config(ConfigKey::TriggerSlope) || configurable->has_set_config(ConfigKey::TriggerSlope) || configurable->has_get_config(ConfigKey::TriggerLevel) || configurable->has_set_config(ConfigKey::TriggerLevel))) { views.push_back(new ScopeTriggerControlView(session, configurable)); } // Horizontal control for scopes if (configurable->device_type() == DeviceType::Oscilloscope && (configurable->has_get_config(ConfigKey::TimeBase) || configurable->has_set_config(ConfigKey::TimeBase))) { views.push_back(new ScopeHorizontalControlView(session, configurable)); } // View for Demo Device if (configurable->device_type() == DeviceType::DemoDev && (configurable->has_get_config(ConfigKey::MeasuredQuantity) || configurable->has_set_config(ConfigKey::MeasuredQuantity) || configurable->has_get_config(ConfigKey::Amplitude) || configurable->has_set_config(ConfigKey::Amplitude) || configurable->has_get_config(ConfigKey::Offset) || configurable->has_set_config(ConfigKey::Offset))) { views.push_back(new DemoControlView(session, configurable)); } // Measurement devices like DMMs, scales, LCR meters, etc. if ((configurable->device_type() == DeviceType::Multimeter || configurable->device_type() == DeviceType::SoundLevelMeter || configurable->device_type() == DeviceType::Thermometer || configurable->device_type() == DeviceType::Hygrometer || configurable->device_type() == DeviceType::Energymeter || configurable->device_type() == DeviceType::LcrMeter || configurable->device_type() == DeviceType::Scale || configurable->device_type() == DeviceType::Powermeter || // TODO: Multiplexers doesn't really fit here configurable->device_type() == DeviceType::Multiplexer) && (configurable->has_get_config(ConfigKey::MeasuredQuantity) || configurable->has_set_config(ConfigKey::MeasuredQuantity) || configurable->has_get_config(ConfigKey::Range) || configurable->has_set_config(ConfigKey::Range))) { views.push_back(new MeasurementControlView(session, configurable)); } // TODO: SignalGenerators need their own view (waveforms, etc.) // Fallback: Generic control view if nothing else fits (e.g. signal // generators for now). if (views.empty() && (!configurable->getable_configs().empty() || !configurable->setable_configs().empty())) { views.push_back(new GenericControlView(session, configurable)); } return views; } BaseView *get_view_from_settings(Session &session, QSettings &settings, shared_ptr origin_device) { QString id = settings.value("id").toString(); QUuid uuid = settings.value("uuid").toUuid(); QStringList id_list = id.split(':'); QString type = id_list[0]; BaseView *view = nullptr; if (type == "data") { view = new DataView(session, uuid); } else if (type == "timeplot") { view = new TimePlotView(session, uuid); } else if (type == "xyplot") { view = new XYPlotView(session, uuid); } else if (type == "powerpanel") { view = new PowerPanelView(session, uuid); } else if (type == "valuepanel") { view = new ValuePanelView(session, uuid); } else if (type == "sequenceoutput") { view = new SequenceOutputView(session, uuid); } else if (type == "smuscriptoutput") { view = new SmuScriptOutputView(session, uuid); } else if (type == "smuscript") { view = new SmuScriptView(session, uuid); } else if (type == "democontrol") { view = DemoControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "genericcontrol") { view = GenericControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "measurementcontrol") { view = MeasurementControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "scopehorizontalcontrol") { view = ScopeHorizontalControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "scopetriggercontrol") { view = ScopeTriggerControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "scopeverticalcontrol") { view = ScopeVerticalControlView::init_from_settings( session, settings, uuid, origin_device); } else if (type == "sourcesinkcontrol") { view = SourceSinkControlView::init_from_settings( session, settings, uuid, origin_device); } if (view) view->restore_settings(settings, origin_device); return view; } } // namespace viewhelper } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/viewhelper.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_VIEWHELPER_HPP #define UI_VIEWS_VIEWHELPER_HPP #include #include #include using std::shared_ptr; using std::vector; namespace sv { class Session; namespace devices { class Configurable; class BaseDevice; } namespace ui { namespace views { class BaseView; namespace viewhelper { /** * Returns the fitting control views for the given configurable, by checking for * get-/setable config keys and the device type. * * @param[in] session The reference to the actual SmuView session. * @param[in] configurable The Configurable. * * @return The control views for the configurable. */ vector get_views_for_configurable(Session &session, shared_ptr configurable); /** * Return the view defined in the actual settings group. * * @param[in] session The reference to the actual SmuView session. * @param[in] settings The settings. * @param[in] origin_device The origin device for this settings. * * @return The view defined by settings. */ BaseView *get_view_from_settings(Session &session, QSettings &settings, shared_ptr origin_device); } // namespace viewhelper } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_VIEWHELPER_HPP ================================================ FILE: src/ui/views/xyplotview.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "xyplotview.hpp" #include "src/session.hpp" #include "src/util.hpp" #include "src/data/analogtimesignal.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/dialogs/selectxysignalsdialog.hpp" #include "src/ui/views/baseplotview.hpp" #include "src/ui/views/viewhelper.hpp" #include #include "src/ui/widgets/plot/plot.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" #include "src/ui/widgets/plot/xycurvedata.hpp" using std::dynamic_pointer_cast; using std::shared_ptr; using std::string; Q_DECLARE_METATYPE(sv::ui::widgets::plot::BaseCurveData *) namespace sv { namespace ui { namespace views { XYPlotView::XYPlotView(Session &session, QUuid uuid, QWidget *parent) : BasePlotView(session, uuid, parent) { id_ = "xyplot:" + util::format_uuid(uuid_); plot_type_ = PlotType::XYPlot; } QString XYPlotView::title() const { QString title = tr("Signal"); QString sep(" "); for (const auto &curve : plot_->curve_map()) { title = title.append(sep).append(curve.second->name()); sep = ", "; } return title; } string XYPlotView::add_signals(shared_ptr x_signal, shared_ptr y_signal) { auto *curve = new widgets::plot::XYCurveData(x_signal, y_signal); string id = plot_->add_curve(curve); if (id.empty()) { QMessageBox::warning(this, tr("Cannot add signal"), tr("Cannot add xy signal to plot!"), QMessageBox::Ok); } return id; } void XYPlotView::save_settings(QSettings &settings, shared_ptr origin_device) const { BasePlotView::save_settings(settings, origin_device); plot_->save_settings(settings, true, origin_device); } void XYPlotView::restore_settings(QSettings &settings, shared_ptr origin_device) { BasePlotView::restore_settings(settings, origin_device); plot_->restore_settings(settings, true, origin_device); } void XYPlotView::on_action_add_curve_triggered() { ui::dialogs::SelectXYSignalsDialog dlg(session(), nullptr); if (!dlg.exec()) return; add_signals( dynamic_pointer_cast(dlg.x_signal()), dynamic_pointer_cast(dlg.y_signal())); } } // namespace views } // namespace ui } // namespace sv ================================================ FILE: src/ui/views/xyplotview.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_VIEWS_XYPLOTVIEW_HPP #define UI_VIEWS_XYPLOTVIEW_HPP #include #include #include #include "src/ui/views/baseplotview.hpp" using std::shared_ptr; namespace sv { class Session; namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace views { class XYPlotView : public BasePlotView { Q_OBJECT public: explicit XYPlotView(Session &session, QUuid uuid = QUuid(), QWidget *parent = nullptr); QString title() const override; void save_settings(QSettings &settings, shared_ptr origin_device = nullptr) const override; void restore_settings(QSettings &settings, shared_ptr origin_device = nullptr) override; /** * Add a new x/y curve to the xy plot. Return the curve id. */ string add_signals(shared_ptr x_signal, shared_ptr y_signal); protected Q_SLOTS: void on_action_add_curve_triggered() override; }; } // namespace views } // namespace ui } // namespace sv #endif // UI_VIEWS_XYPLOTVIEW_HPP ================================================ FILE: src/ui/widgets/clickablelabel.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "clickablelabel.hpp" namespace sv { namespace ui { namespace widgets { ClickableLabel::ClickableLabel(const QString &text, QWidget *parent) : QLabel(text, parent) { } ClickableLabel::~ClickableLabel() { } void ClickableLabel::mousePressEvent(QMouseEvent* event) { (void)event; Q_EMIT clicked(); } } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/clickablelabel.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_CLICKABLELABEL_HPP #define UI_WIDGETS_CLICKABLELABEL_HPP #include #include #include #include namespace sv { namespace ui { namespace widgets { class ClickableLabel : public QLabel { Q_OBJECT public: explicit ClickableLabel(const QString &text = "", QWidget *parent = nullptr); ~ClickableLabel(); protected: void mousePressEvent(QMouseEvent* event) override; Q_SIGNALS: void clicked(); }; } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_CLICKABLELABEL_HPP ================================================ FILE: src/ui/widgets/colorbutton.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "colorbutton.hpp" namespace sv { namespace ui { namespace widgets { const int ColorButton::SwatchMargin = 7; ColorButton::ColorButton(QWidget *parent) : QPushButton(parent) { connect(this, &ColorButton::clicked, this, &ColorButton::change_color); } void ColorButton::set_color(const QColor &color) { color_ = color; } const QColor &ColorButton::color() { return color_; } void ColorButton::change_color() { QColor new_color = QColorDialog::getColor(color_, parentWidget()); if (new_color != color_) set_color(new_color); } void ColorButton::paintEvent(QPaintEvent *event) { QPushButton::paintEvent(event); const QRect rect_adjusted = rect().adjusted( SwatchMargin, SwatchMargin, -SwatchMargin, -SwatchMargin); QPainter painter(this); painter.setPen(QApplication::palette().color(QPalette::Dark)); painter.setBrush(QBrush(color_)); painter.drawRect(rect_adjusted); } } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/colorbutton.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_COLORBUTTON_HPP #define UI_WIDGETS_COLORBUTTON_HPP #include #include #include #include namespace sv { namespace ui { namespace widgets { class ColorButton : public QPushButton { Q_OBJECT public: explicit ColorButton(QWidget *parent = nullptr); void set_color(const QColor &color); const QColor &color(); private: static const int SwatchMargin; QColor color_; private: void paintEvent(QPaintEvent *event) override; public Q_SLOTS: void change_color(); }; } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_COLORBUTTON_HPP ================================================ FILE: src/ui/widgets/monofontdisplay.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "monofontdisplay.hpp" #include "src/util.hpp" namespace sv { namespace ui { namespace widgets { MonoFontDisplay::MonoFontDisplay(const MonoFontDisplayType display_type, const QString &unit, const QString &unit_suffix, const QString &extra_text, const bool small, QWidget *parent) : QFrame(parent), display_type_(display_type), total_digits_(7), total_digits_changed_(true), sr_digits_(2), decimal_places_(2), extra_text_(extra_text), extra_text_changed_(true), unit_(unit), unit_si_prefix_(""), unit_suffix_(unit_suffix), unit_changed_(true), small_(small), value_(.0) { MonoFontDisplay::setup_ui(); reset_value(); } void MonoFontDisplay::setup_ui() { // Use embedded mono space font QFont monospace_font = QFont("DejaVu Sans Mono"); // Get standard font sizes int monospace_font_size = monospace_font.pointSize(); int std_font_size = QFont().pointSize(); QFont value_font(monospace_font); QFont unit_font; int unit_spacer_size; if (!small_) { value_font.setPointSize(monospace_font_size + 12); // = 22 value_font.setBold(true); value_font.setWeight(QFont::Bold); unit_font.setPointSize(std_font_size + 8); // = 18 extra_font_.setPointSize(std_font_size); // = 10 unit_spacer_size = 5; } else { value_font.setPointSize(monospace_font_size + 4); // = 14 unit_font.setPointSize(std_font_size); // = 10 extra_font_.setPointSize(std_font_size - 3); // = 7 unit_spacer_size = 3; } // Qt::AlignBaseline is not working, so we have to calculate the // difference of the ascents for positioning the unit label. QFontMetrics value_font_metrics(value_font); QFontMetrics unit_font_metrics(unit_font); ascent_diff_ = value_font_metrics.ascent() - unit_font_metrics.ascent(); //this->setFrameShape(QFrame::Box); QSizePolicy layout_size_policy(QSizePolicy::Fixed, QSizePolicy::Fixed); layout_size_policy.setHorizontalStretch(0); layout_size_policy.setVerticalStretch(0); this->setSizePolicy(layout_size_policy); layout_ = new QGridLayout(); // Set the margin and spacing to 0, so we can position the value and // the unit by their baselines exactly. layout_->setMargin(0); layout_->setSpacing(0); // Value value_label_ = new QLabel(); value_label_->setFont(value_font); value_label_->setAlignment(Qt::AlignRight); value_label_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //value_label_->setFrameShape(QFrame::Box); layout_->addWidget(value_label_, 0, 0, 2, 1, Qt::AlignRight | Qt::AlignVCenter); // Spacer between the value and the unit labels. QSpacerItem *unit_spacer = new QSpacerItem(unit_spacer_size, 1, QSizePolicy::Fixed, QSizePolicy::Fixed); layout_->addItem(unit_spacer, 0, 1, 2, 1, Qt::AlignCenter); // Extra spacer (used when extra text is empty to "fake" Qt::AlignBaseline) extra_label_ = nullptr; extra_spacer_ = new QSpacerItem(1, ascent_diff_, QSizePolicy::Fixed, QSizePolicy::Fixed); layout_->addItem(extra_spacer_, 0, 2, 1, 1, Qt::AlignCenter); // Unit unit_label_ = new QLabel(); unit_label_->setFont(unit_font); // Qt::AlignTop is not working! unit_label_->setAlignment(Qt::AlignRight | Qt::AlignVCenter); unit_label_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //unit_label_->setFrameShape(QFrame::Box); layout_->addWidget(unit_label_, 1, 2, 1, 1, Qt::AlignHCenter | Qt::AlignTop); layout_->setRowStretch(1, 1); this->setLayout(layout_); } void MonoFontDisplay::update_value_widget_dimensions() { // Set the widget to a fixed width, so it doesn't jump around when the // length of the string is changing (e.g. minus sign). // TODO: Displays are too small (not wide enough) for neg. values, esp. for // the power panel for W/Ah/Wh for the 6632B. B/c of the decimal // point? QString str(""); size_t str_len = total_digits_ + 2; // digits + decimal point + sign for (size_t i=0; ifontMetrics(); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) value_label_->setFixedWidth(metrics.horizontalAdvance(str)); #else value_label_->setFixedWidth(metrics.width(str)); #endif } void MonoFontDisplay::update_extra_widget_dimensions() { // Nothing to do here } void MonoFontDisplay::update_unit_widget_dimensions() { // Set the widget to a fixed width, so it doesn't jump around when the // SI prefix is changing. QString str; if (display_type_ == MonoFontDisplayType::AutoRangeWithSRDigits || display_type_ == MonoFontDisplayType::AutoRange) { // 'm' is the widest character for non monospace fonts str.append("m"); } str.append(unit_); if (!unit_suffix_.isEmpty()) { str.append(" ").append(unit_suffix_); } QFontMetrics metrics = unit_label_->fontMetrics(); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) unit_label_->setFixedWidth(metrics.horizontalAdvance(str)); #else unit_label_->setFixedWidth(metrics.width(str)); #endif } void MonoFontDisplay::show_value(const QString &value) { value_label_->setText(value); } void MonoFontDisplay::show_extra_text(const QString &extra_text) { if (extra_text_.isEmpty() && !extra_spacer_) { // Remove label layout_->removeWidget(extra_label_); delete extra_label_; extra_label_ = nullptr; // Insert spacer extra_spacer_ = new QSpacerItem(1, ascent_diff_, QSizePolicy::Fixed, QSizePolicy::Fixed); layout_->addItem(extra_spacer_, 0, 2, 1, 1, Qt::AlignCenter); } else if (!extra_text_.isEmpty()) { if (!extra_label_) { // Remove spacer layout_->removeItem(extra_spacer_); delete extra_spacer_; extra_spacer_ = nullptr; // Insert label extra_label_ = new QLabel(); extra_label_->setFont(extra_font_); extra_label_->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); extra_label_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); //extra_label_->setFrameShape(QFrame::Box); layout_->addWidget(extra_label_, 0, 2, 1, 1, Qt::AlignCenter); } extra_label_->setText(extra_text); } } void MonoFontDisplay::show_unit(const QString &unit) { unit_label_->setText(unit); } double MonoFontDisplay::value() const { return value_; } void MonoFontDisplay::set_value(const double value) { value_ = value; update_display(); } void MonoFontDisplay::set_extra_text(const QString &extra_text) { extra_text_ = extra_text; extra_text_changed_ = true; update_display(); } void MonoFontDisplay::set_unit(const QString &unit) { unit_ = unit; unit_changed_ = true; update_display(); } void MonoFontDisplay::set_unit_suffix(const QString &unit_suffix) { unit_suffix_ = unit_suffix; unit_changed_ = true; update_display(); } void MonoFontDisplay::set_sr_digits(const int total_digits, const int sr_digits) { if (total_digits != total_digits_) { total_digits_ = total_digits; total_digits_changed_ = true; } sr_digits_ = sr_digits; update_display(); } void MonoFontDisplay::set_decimal_places(const int total_digits, const int decimal_places) { if (total_digits != total_digits_) { total_digits_ = total_digits; total_digits_changed_ = true; } decimal_places_ = decimal_places; update_display(); } void MonoFontDisplay::reset_value() { QString init_value(""); for (int i=0; i= std::numeric_limits::max() || value_ == std::numeric_limits::infinity()) { // TODO: Replace with "ol" or "overl", depending on the avail. digits. value_str = QString("OL"); } else if (value_ <= std::numeric_limits::lowest()) { // TODO: Replace with "ul" or "underf", depending on the avail. digits. value_str = QString("UL"); } else if (display_type_ == MonoFontDisplayType::FixedRange) { // Use actual locale (%L) for formating value_str = QString("%L1"). arg(value_, total_digits_, 'f', decimal_places_, QChar(' ')); } else if (display_type_ == MonoFontDisplayType::AutoRangeWithSRDigits) { util::format_value_si(value_, total_digits_, sr_digits_, value_str, si_prefix); } else if (display_type_ == MonoFontDisplayType::AutoRange) { util::format_value_si_autoscale(value_, total_digits_, decimal_places_, value_str, si_prefix); } show_value(value_str); if (total_digits_changed_) { total_digits_changed_ = false; this->update_value_widget_dimensions(); } if (extra_text_changed_) { extra_text_changed_ = false; show_extra_text(extra_text_); this->update_extra_widget_dimensions(); } if (si_prefix != unit_si_prefix_ || unit_changed_) { unit_si_prefix_ = si_prefix; QString unit_str = QString("%1%2").arg(unit_si_prefix_, unit_); if (!unit_suffix_.isEmpty()) { unit_str.append(" ").append(unit_suffix_); } show_unit(unit_str); } if (unit_changed_) { unit_changed_ = false; this->update_unit_widget_dimensions(); } } } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/monofontdisplay.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_MONOFONTDISPLAY_HPP #define UI_WIDGETS_MONOFONTDISPLAY_HPP #include #include #include #include #include #include namespace sv { namespace ui { namespace widgets { enum class MonoFontDisplayType { AutoRange, AutoRangeWithSRDigits, FixedRange }; class MonoFontDisplay : public QFrame { Q_OBJECT public: MonoFontDisplay(const MonoFontDisplayType display_type, const QString &unit, const QString &unit_suffix, const QString &extra_text, const bool small, QWidget *parent = nullptr); double value() const; private: const MonoFontDisplayType display_type_; int total_digits_; bool total_digits_changed_; int sr_digits_; int decimal_places_; QString extra_text_; bool extra_text_changed_; QString unit_; QString unit_si_prefix_; QString unit_suffix_; bool unit_changed_; const bool small_; double value_; int ascent_diff_; QGridLayout *layout_; QLabel *value_label_; QFont extra_font_; QLabel *extra_label_; QSpacerItem *extra_spacer_; QLabel *unit_label_; virtual void setup_ui(); void update_value_widget_dimensions(); void update_extra_widget_dimensions(); void update_unit_widget_dimensions(); void show_value(const QString &value); void show_extra_text(const QString &extra_text); void show_unit(const QString &unit); public Q_SLOTS: void set_value(const double value); void set_extra_text(const QString &extra_text); void set_unit(const QString &unit); void set_unit_suffix(const QString &unit_suffix); void set_sr_digits(const int total_digits, const int sr_digits); void set_decimal_places(const int total_digits, const int decimal_places); void reset_value(); void update_display(); }; } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_MONOFONTDISPLAY_HPP ================================================ FILE: src/ui/widgets/plot/axislocklabel.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "axislocklabel.hpp" #include "src/ui/widgets/clickablelabel.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { AxisLockLabel::AxisLockLabel( const int axis_id, const AxisBoundary axis_boundary, const QString &text, QWidget *parent) : ClickableLabel(text, parent), axis_id_(axis_id), axis_boundary_(axis_boundary), locked_(false) { QIcon unlocked_icon = QIcon::fromTheme("object-unlocked", QIcon(":/icons/object-unlocked.png")); unlocked_pixmap_ = unlocked_icon.pixmap(QSize(16, 16)); QIcon locked_icon = QIcon::fromTheme("object-locked", QIcon(":/icons/object-locked.png")); locked_pixmap_ = locked_icon.pixmap(QSize(16, 16)); setup_ui(); } AxisLockLabel::~AxisLockLabel() { } void AxisLockLabel::setup_ui() { this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); set_locked(locked_); } int AxisLockLabel::get_axis_id() const { return axis_id_; } AxisBoundary AxisLockLabel::get_axis_boundary() const { return axis_boundary_; } bool AxisLockLabel::is_locked() const { return locked_; } void AxisLockLabel::set_locked(bool locked) { locked_ = locked; if (locked_) this->setPixmap(locked_pixmap_); else this->setPixmap(unlocked_pixmap_); } void AxisLockLabel::on_axis_lock_changed(const int axis_id, const sv::ui::widgets::plot::AxisBoundary axis_boundary, bool locked) { if (axis_id != axis_id_ || axis_boundary != axis_boundary_) return; set_locked(locked); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/axislocklabel.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_AXISLOCKLABEL_HPP #define UI_WIDGETS_PLOT_AXISLOCKLABEL_HPP #include #include #include #include "src/ui/widgets/clickablelabel.hpp" #include "src/ui/widgets/plot/plot.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { class AxisLockLabel : public ClickableLabel { Q_OBJECT public: explicit AxisLockLabel(const int axis_id, const AxisBoundary axis_boundary, const QString &text = "", QWidget *parent = nullptr); ~AxisLockLabel(); int get_axis_id() const; AxisBoundary get_axis_boundary() const; bool is_locked() const; void set_locked(bool locked); public Q_SLOTS: void on_axis_lock_changed(const int axis_id, const sv::ui::widgets::plot::AxisBoundary axis_boundary, bool locked); private: void setup_ui(); const int axis_id_; const AxisBoundary axis_boundary_; QPixmap unlocked_pixmap_; QPixmap locked_pixmap_; bool locked_; }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_AXISLOCKLABEL_HPP ================================================ FILE: src/ui/widgets/plot/axispopup.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "axispopup.hpp" #include "src/ui/widgets/popup.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { AxisPopup::AxisPopup(Plot *plot, int axis_id, QWidget *parent) : Popup(parent), plot_(plot), axis_id_(axis_id) { setup_ui(); } void AxisPopup::setup_ui() { QFormLayout *form_layout = new QFormLayout; // Lower boundary double lower_value = plot_->axisScaleDiv(axis_id_).lowerBound(); axis_lower_edit_ = new QLineEdit(); axis_lower_edit_->setValidator(new QDoubleValidator()); axis_lower_edit_->setText(QString("%1").arg(lower_value, 0, 'f')); QString lower_label; if (axis_id_ == QwtPlot::xTop || axis_id_ == QwtPlot::xBottom) lower_label = tr("Left boundary"); else lower_label = tr("Bottom boundary"); connect(axis_lower_edit_, &QLineEdit::returnPressed, this, &AxisPopup::on_accept); axis_lower_locked_check_ = new QCheckBox(tr("Locked")); axis_lower_locked_check_->setChecked(plot_->is_axis_locked( axis_id_, AxisBoundary::LowerBoundary)); QHBoxLayout *lower_layout = new QHBoxLayout; lower_layout->addWidget(axis_lower_edit_); lower_layout->addSpacing(15); lower_layout->addWidget(axis_lower_locked_check_); QWidget *lower_widget = new QWidget(); lower_widget->setLayout(lower_layout); // Upper boundary double upper_value = plot_->axisScaleDiv(axis_id_).upperBound(); axis_upper_edit_ = new QLineEdit(); axis_upper_edit_->setValidator(new QDoubleValidator()); axis_upper_edit_->setText(QString("%1").arg(upper_value, 0, 'f')); QString upper_label; if (axis_id_ == QwtPlot::xTop || axis_id_ == QwtPlot::xBottom) upper_label = tr("Right boundary"); else upper_label = tr("Top boundary"); connect(axis_upper_edit_, &QLineEdit::returnPressed, this, &AxisPopup::on_accept); axis_upper_locked_check_ = new QCheckBox(tr("Locked")); axis_upper_locked_check_->setChecked(plot_->is_axis_locked( axis_id_, AxisBoundary::UpperBoundary)); QHBoxLayout *upper_layout = new QHBoxLayout; upper_layout->addWidget(axis_upper_edit_); upper_layout->addSpacing(15); upper_layout->addWidget(axis_upper_locked_check_); QWidget *upper_widget = new QWidget(); upper_widget->setLayout(upper_layout); if (axis_id_ == QwtPlot::xTop || axis_id_ == QwtPlot::xBottom) { form_layout->addRow(lower_label, lower_widget); form_layout->addRow(upper_label, upper_widget); } else { // Reverse the display order for y axes form_layout->addRow(upper_label, upper_widget); form_layout->addRow(lower_label, lower_widget); } bool is_log_scale = false; if (dynamic_cast(plot_->axisScaleEngine(axis_id_))) is_log_scale = true; axis_log_check_ = new QCheckBox(); axis_log_check_->setChecked(is_log_scale); form_layout->addRow(tr("Logarithmic scale"), axis_log_check_); button_box_ = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); form_layout->addWidget(button_box_); connect(button_box_, &QDialogButtonBox::accepted, this, &AxisPopup::on_accept); connect(button_box_, &QDialogButtonBox::rejected, this, &AxisPopup::close); this->setLayout(form_layout); } void AxisPopup::showEvent(QShowEvent *event) { widgets::Popup::showEvent(event); } void AxisPopup::on_accept() { plot_->setAxisScale(axis_id_, axis_lower_edit_->text().toDouble(), axis_upper_edit_->text().toDouble()); plot_->set_axis_locked(axis_id_, AxisBoundary::LowerBoundary, axis_lower_locked_check_->isChecked()); plot_->set_axis_locked(axis_id_, AxisBoundary::UpperBoundary, axis_upper_locked_check_->isChecked()); if (axis_log_check_->isChecked()) plot_->setAxisScaleEngine(axis_id_, new QwtLogScaleEngine); else plot_->setAxisScaleEngine(axis_id_, new QwtLinearScaleEngine); plot_->replot(); this->close(); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/axispopup.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_AXISPOPUP_HPP #define UI_WIDGETS_PLOT_AXISPOPUP_HPP #include #include #include #include #include #include "src/ui/widgets/popup.hpp" #include "src/ui/widgets/plot/plot.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { class AxisPopup : public widgets::Popup { Q_OBJECT public: AxisPopup(Plot *plot, int axis_id, QWidget *parent); private: Plot *plot_; int axis_id_; QLineEdit *axis_lower_edit_; QCheckBox *axis_lower_locked_check_; QLineEdit *axis_upper_edit_; QCheckBox *axis_upper_locked_check_; QCheckBox *axis_log_check_; QDialogButtonBox *button_box_; void setup_ui(); void showEvent(QShowEvent *event) override; private Q_SLOTS: void on_accept(); }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_AXISPOPUP_HPP ================================================ FILE: src/ui/widgets/plot/basecurvedata.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "basecurvedata.hpp" #include "src/data/datautil.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { BaseCurveData::BaseCurveData(CurveType curve_type) : QwtSeriesData(), type_(curve_type), relative_time_(true) { } CurveType BaseCurveData::type() const { return type_; } void BaseCurveData::set_relative_time(bool is_relative_time) { relative_time_ = is_relative_time; } bool BaseCurveData::is_relative_time() const { return relative_time_; } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/basecurvedata.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_BASECURVEDATA_HPP #define UI_WIDGETS_PLOT_BASECURVEDATA_HPP #include #include #include #include #include #include #include #include #include #include #include "src/data/datautil.hpp" using std::set; using std::shared_ptr; using std::string; namespace sv { namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { enum class CurveType { TimeCurve, XYCurve }; class BaseCurveData : public QObject, public QwtSeriesData { Q_OBJECT public: explicit BaseCurveData(CurveType curve_type); virtual ~BaseCurveData() = default; CurveType type() const; virtual QString name() const = 0; virtual string id_prefix() const = 0; void set_relative_time(bool is_relative_time); bool is_relative_time() const; virtual bool is_equal(const BaseCurveData *other) const = 0; virtual QPointF sample(size_t i) const = 0; virtual size_t size() const = 0; virtual QRectF boundingRect() const = 0; virtual QPointF closest_point(const QPointF &pos, double *dist) const = 0; virtual sv::data::Quantity x_quantity() const = 0; virtual set x_quantity_flags() const = 0; virtual sv::data::Unit x_unit() const = 0; virtual QString x_unit_str() const = 0; virtual QString x_title() const = 0; virtual sv::data::Quantity y_quantity() const = 0; virtual set y_quantity_flags() const = 0; virtual sv::data::Unit y_unit() const = 0; virtual QString y_unit_str() const = 0; virtual QString y_title() const = 0; virtual void save_settings(QSettings &settings, shared_ptr origin_device) const = 0; protected: const CurveType type_; bool relative_time_; }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_BASECURVEDATA_HPP ================================================ FILE: src/ui/widgets/plot/curve.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include #endif #include #include #include #include #include #include #include #include #include #include "curve.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/util.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" #include "src/ui/widgets/plot/timecurvedata.hpp" #include "src/ui/widgets/plot/xycurvedata.hpp" using std::shared_ptr; Q_DECLARE_METATYPE(QwtSymbol::Style) namespace sv { namespace ui { namespace widgets { namespace plot { Curve::Curve(BaseCurveData *curve_data, int x_axis_id, int y_axis_id, const QString &custom_name, const QColor &custom_color) : curve_data_(curve_data), plot_direct_painter_(new QwtPlotDirectPainter()), painted_points_(0) { id_ = curve_data->id_prefix() + ":" + util::format_uuid(QUuid::createUuid()); QPen pen; //pen.setColor(color_); pen.setWidthF(2.0); pen.setStyle(Qt::SolidLine); pen.setCosmetic(false); plot_curve_ = new QwtPlotCurve(); plot_curve_->setYAxis(y_axis_id); plot_curve_->setXAxis(x_axis_id); plot_curve_->setStyle(QwtPlotCurve::Lines); plot_curve_->setPen(pen); // Set empty symbol, used in the PlotCurveConfigDialog. plot_curve_->setSymbol(new QwtSymbol(QwtSymbol::NoSymbol)); plot_curve_->setRenderHint(QwtPlotItem::RenderAntialiased, true); plot_curve_->setPaintAttribute(QwtPlotCurve::ClipPolygons, false); plot_curve_->setData(curve_data_); //plot_curve_->setRawSamples(); // TODO: is this an option? // Curves have the lowest z order, everything else will be painted ontop. plot_curve_->setZ(1); set_name(custom_name); set_color(custom_color); } Curve::~Curve() { delete plot_curve_; delete plot_direct_painter_; } BaseCurveData *Curve::curve_data() const { return curve_data_; } QwtPlotCurve *Curve::plot_curve() const { return plot_curve_; } QwtPlotDirectPainter *Curve::plot_direct_painter() const { return plot_direct_painter_; } void Curve::set_name(const QString &custom_name) { if (custom_name.size() > 0) { has_custom_name_ = true; name_ = custom_name; } else { has_custom_name_ = false; name_ = curve_data_->name(); } plot_curve_->setTitle(name_); } QString Curve::name() const { return name_; } string Curve::id() const { return id_; } int Curve::x_axis_id() const { return plot_curve_->xAxis(); } int Curve::y_axis_id() const { return plot_curve_->yAxis(); } void Curve::set_painted_points(size_t painted_points) { painted_points_ = painted_points; } size_t Curve::painted_points() const { return painted_points_; } void Curve::set_color(const QColor &custom_color) { if (custom_color.isValid()) { has_custom_color_ = true; color_ = custom_color; } else { has_custom_color_ = false; color_ = Curve::default_color( curve_data_->y_quantity(), curve_data_->y_quantity_flags()); } QPen pen = plot_curve_->pen(); pen.setColor(color_); plot_curve_->setPen(pen); } QColor Curve::color() const { return color_; } void Curve::set_style(const Qt::PenStyle style) { QPen pen = plot_curve_->pen(); pen.setStyle(style); plot_curve_->setPen(pen); } Qt::PenStyle Curve::style() const { return plot_curve_->pen().style(); } void Curve::set_symbol(const QwtSymbol::Style style) { QwtSymbol *curve_symbol = new QwtSymbol(style); curve_symbol->setBrush(QBrush(color_)); curve_symbol->setPen(color_, 2); if (style == QwtSymbol::XCross) curve_symbol->setSize(QSize(8, 8)); else curve_symbol->setSize(QSize(4, 4)); plot_curve_->setSymbol(curve_symbol); } QwtSymbol::Style Curve::symbol() const { return plot_curve_->symbol()->style(); } QwtPlotMarker *Curve::add_marker(const QString &name_postfix) { QwtSymbol *marker_symbol = new QwtSymbol( QwtSymbol::Diamond, QBrush(color_), QPen(color_), QSize(9, 9)); QString marker_name = QString("M%1").arg(name_postfix); QwtPlotMarker *marker = new QwtPlotMarker(marker_name); marker->setSymbol(marker_symbol); marker->setLineStyle(QwtPlotMarker::Cross); marker->setLinePen(Qt::white, 1.0, Qt::DashLine); marker->setXAxis(x_axis_id()); marker->setYAxis(y_axis_id()); // Markers will be painted ontop of curves but below the markers label box. marker->setZ(2); // Label QwtText marker_label = QwtText(marker_name); marker_label.setColor(Qt::black); marker_label.setPaintAttribute(QwtText::PaintBackground, true); QColor background(Qt::gray); background.setAlpha(200); marker_label.setBackgroundBrush(background); QPen pen(Qt::black, 1.0, Qt::SolidLine); marker_label.setBorderPen(pen); marker_label.setBorderRadius(3); marker->setLabel(marker_label); marker->setLabelAlignment(Qt::AlignTop | Qt::AlignRight); return marker; } void Curve::save_settings(QSettings &settings, shared_ptr origin_device) const { settings.beginGroup(QString::fromStdString(id_)); curve_data_->save_settings(settings, origin_device); settings.setValue("x_axis_id", x_axis_id()); settings.setValue("y_axis_id", y_axis_id()); if (has_custom_name_) settings.setValue("custom_name", name_); if (has_custom_color_) settings.setValue("custom_color", color_); // Qt::PenSytle cannot be saved directly settings.setValue("style", QVariant(QPen(style()))); settings.setValue("symbol", symbol()); settings.endGroup(); } Curve *Curve::init_from_settings( Session &session, QSettings &settings, const QString &group, shared_ptr origin_device) { if (!group.startsWith("timecurve:") && !group.startsWith("xycurve:")) return nullptr; settings.beginGroup(group); BaseCurveData *curve_data = nullptr; if (group.startsWith("timecurve:")) { curve_data = TimeCurveData::init_from_settings( session, settings, origin_device); } else if (group.startsWith("xycurve:")) { curve_data = XYCurveData::init_from_settings( session, settings, origin_device); } if (!curve_data) { settings.endGroup(); return nullptr; } int x_axis_id = settings.value("x_axis_id").toInt(); int y_axis_id = settings.value("y_axis_id").toInt(); QString custom_name = ""; if (settings.contains("custom_name")) custom_name = settings.value("custom_name").toString(); QColor custom_color = QColor(); if (settings.contains("custom_color")) custom_color = settings.value("custom_color").value(); Curve *curve = new Curve(curve_data, x_axis_id, y_axis_id, custom_name, custom_color); if (settings.contains("style")) curve->set_style(settings.value("style").value().style()); if (settings.contains("symbol")) curve->set_symbol(settings.value("symbol").value()); settings.endGroup(); return curve; } QColor Curve::default_color(sv::data::Quantity quantity, const set &quantity_flags) { // First, try to get color from QSettings QColor curve_color = QColor(); QSettings settings; if (SettingsManager::restore_settings() && settings.childGroups().contains("DefaultCurveColors")) { settings.beginGroup("DefaultCurveColors"); QString key = QString("%1_%2"). arg(data::datautil::get_sr_quantity_id(quantity)). arg(data::datautil::get_sr_quantity_flags_id(quantity_flags)); if (settings.childKeys().contains(key)) curve_color = settings.value(key).value(); settings.endGroup(); } if (curve_color.isValid()) return curve_color; // Predefined colors if (quantity == sv::data::Quantity::Voltage && quantity_flags.count(sv::data::QuantityFlag::DC) > 0) return Qt::red; if (quantity == sv::data::Quantity::Voltage && quantity_flags.count(sv::data::QuantityFlag::AC) > 0) return Qt::darkRed; if (quantity == sv::data::Quantity::Voltage) // Fallback for Voltage without quantity flag return Qt::red; if (quantity == sv::data::Quantity::Current && quantity_flags.count(sv::data::QuantityFlag::DC) > 0) return Qt::green; if (quantity == sv::data::Quantity::Current && quantity_flags.count(sv::data::QuantityFlag::AC) > 0) return Qt::darkGreen; if (quantity == sv::data::Quantity::Current) // Fallback for current without quantity flag return Qt::green; if (quantity == sv::data::Quantity::Resistance) return Qt::cyan; if (quantity == sv::data::Quantity::Power) return Qt::yellow; if (quantity == sv::data::Quantity::Energy) return Qt::darkYellow; if (quantity == sv::data::Quantity::Temperature) return Qt::darkCyan; if (quantity == sv::data::Quantity::Capacitance) return Qt::gray; if (quantity == sv::data::Quantity::Frequency) return Qt::magenta; if (quantity == sv::data::Quantity::Time) return Qt::darkMagenta; if (quantity == sv::data::Quantity::PowerFactor) return Qt::lightGray; if (quantity == sv::data::Quantity::ElectricCharge) return Qt::darkGray; // Random color for the rest #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) return QColor::fromRgb(QRandomGenerator::global()->generate()); #else return QColor::fromRgb(qrand()); #endif } void Curve::save_settings_default_color(sv::data::Quantity quantity, const set &quantity_flags, const QColor &color) { QSettings settings; settings.beginGroup("DefaultCurveColors"); QString key = QString("%1_%2"). arg(data::datautil::get_sr_quantity_id(quantity)). arg(data::datautil::get_sr_quantity_flags_id(quantity_flags)); settings.setValue(key, QVariant::fromValue(color)); settings.endGroup(); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/curve.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2020-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_CURVE_HPP #define UI_WIDGETS_PLOT_CURVE_HPP #include #include #include #include #include #include #include #include #include #include #include #include "src/data/datautil.hpp" using std::shared_ptr; using std::string; using std::vector; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { class BaseCurveData; class Curve : public QObject { Q_OBJECT public: Curve(BaseCurveData *curve_data, int x_axis_id, int y_axis_id, const QString &custom_name = "", const QColor &custom_color = QColor()); ~Curve(); static QColor default_color(sv::data::Quantity quantity, const set &quantity_flags); static void save_settings_default_color(sv::data::Quantity quantity, const set &quantity_flags, const QColor &color); void save_settings(QSettings &settings, shared_ptr origin_device) const; static Curve *init_from_settings( Session &session, QSettings &settings, const QString &group, shared_ptr origin_device); BaseCurveData *curve_data() const; QwtPlotCurve *plot_curve() const; QwtPlotDirectPainter *plot_direct_painter() const; void set_name(const QString &custom_name); QString name() const; string id() const; int x_axis_id() const; int y_axis_id() const; void set_painted_points(size_t painted_points); size_t painted_points() const; void set_color(const QColor &custom_color); QColor color() const; void set_style(const Qt::PenStyle style); Qt::PenStyle style() const; void set_symbol(const QwtSymbol::Style style); QwtSymbol::Style symbol() const; QwtPlotMarker *add_marker(const QString &name_postfix); private: BaseCurveData *curve_data_; QwtPlotCurve *plot_curve_; QwtPlotDirectPainter *plot_direct_painter_; bool has_custom_name_; QString name_; string id_; size_t painted_points_; bool has_custom_color_; QColor color_; }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_CURVE_HPP ================================================ FILE: src/ui/widgets/plot/plot.cpp ================================================ /* * This file is part of the SmuView project. * This file is based on the QWT Oscilloscope Example. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" // This header uses deprecated declarations, disable checks. #include #pragma GCC diagnostic pop #include #include #include #include #include #include #include #include "plot.hpp" #include "src/session.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/dialogs/plotcurveconfigdialog.hpp" #include "src/ui/widgets/plot/axislocklabel.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" #include "src/ui/widgets/plot/curve.hpp" #include "src/ui/widgets/plot/plotmagnifier.hpp" #include "src/ui/widgets/plot/plotscalepicker.hpp" #include "src/ui/widgets/plot/timecurvedata.hpp" #include "src/ui/widgets/plot/xycurvedata.hpp" using std::make_pair; using std::shared_ptr; using std::string; namespace sv { namespace ui { namespace widgets { namespace plot { class Canvas : public QwtPlotCanvas { public: explicit Canvas(QwtPlot *plot = nullptr) : QwtPlotCanvas(plot) { /* * NOTE: * The backing store is important, when working with widget overlays * (f.e rubberbands for zooming). Here we don't have them and the * internal backing store of QWidget is good enough. */ setPaintAttribute(QwtPlotCanvas::BackingStore, false); /* * NOTE: * ImmediatePaint is necessary so "old" curves will be deleted. * QwtPlot::repaint() in replot() will also work */ setPaintAttribute(QwtPlotCanvas::ImmediatePaint, true); setBorderRadius(10); if (QwtPainter::isX11GraphicsSystem()) { /* * NOTE: * Disabling the backing store of Qt improves the performance for * the direct painter even more, but the canvas becomes a native * window of the window system, receiving paint events for resize * and expose operations. Those might be expensive when there are * many points and the backing store of the canvas is disabled. So * in this application we better don't disable both backing stores. */ if (testPaintAttribute(QwtPlotCanvas::BackingStore)) { setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_NoSystemBackground, true); } } setupPalette(); } private: void setupPalette() { QPalette pal = palette(); QLinearGradient gradient; gradient.setCoordinateMode(QGradient::StretchToDeviceMode); gradient.setColorAt(0.0, QColor(0, 49, 110)); gradient.setColorAt(1.0, QColor(0, 87, 174)); pal.setBrush(QPalette::Window, QBrush(gradient)); // QPalette::WindowText is used for the curve color pal.setColor(QPalette::WindowText, Qt::green); setPalette(pal); } }; Plot::Plot(Session &session, QWidget *parent) : QwtPlot(parent), session_(session), plot_interval_(200), timer_id_(-1), time_span_(120.), add_time_(30.), active_marker_(nullptr), markers_label_(nullptr), markers_label_alignment_(Qt::AlignBottom | Qt::AlignHCenter), marker_select_picker_(nullptr), marker_move_picker_(nullptr) { this->setAutoReplot(false); this->setCanvas(new Canvas()); // This must be done, because when the QwtPlot widget is directly or // indirectly in a (Main)Window, therefor the minimum size is way to big. this->setMinimumSize(250, 250); //qWarning() << "Plot::Plot(): sizeHint() = " << sizeHint() << // ", minimumSizeHint() = " << minimumSizeHint(); //qWarning() << "Plot::Plot(): size() = " << size() << // ", minimumSize() = " << minimumSize(); this->plotLayout()->setAlignCanvasToScales(true); QwtLegend *legend = new QwtLegend; legend->setDefaultItemMode(QwtLegendData::Clickable); this->insertLegend(legend, QwtPlot::BottomLegend); connect(legend, &QwtLegend::clicked, this, &Plot::on_legend_clicked); QwtPlotGrid *grid = new QwtPlotGrid(); grid->setPen(Qt::gray, 0.0, Qt::DotLine); grid->enableX(true); grid->enableXMin(true); grid->enableY(true); grid->enableYMin(false); grid->attach(this); // Disable all x axis to have a known state for init_x_axis() this->enableAxis(QwtPlot::xBottom, false); this->enableAxis(QwtPlot::xTop, false); // Disable all y axis to have a known state for init_y_axis() this->enableAxis(QwtPlot::yLeft, false); this->enableAxis(QwtPlot::yRight, false); // Zooming and panning via the axes (void)new PlotScalePicker(this); // Panning via the canvas plot_panner_ = new QwtPlotPanner(this->canvas()); connect(plot_panner_, &QwtPlotPanner::panned, this, &Plot::lock_all_axis); // Zooming via the canvas plot_magnifier_ = new PlotMagnifier(this->canvas()); connect(plot_magnifier_, &PlotMagnifier::magnified, this, &Plot::lock_all_axis); } Plot::~Plot() { this->stop(); for (const auto &marker_pair : marker_curve_map_) delete marker_pair.first; for (const auto &curve_pair : curve_map_) delete curve_pair.second; } void Plot::start() { timer_id_ = startTimer(plot_interval_); } void Plot::stop() { //qWarning() << "Plot::stop() for " << curve_data_->name(); killTimer(timer_id_); } void Plot::replot() { //qWarning() << "Plot::replot()"; for (const auto &curve : curve_map_) curve.second->set_painted_points(0); QwtPlot::replot(); } string Plot::add_curve(BaseCurveData *curve_data) { assert(curve_data); // Check y axis int y_axis_id = this->init_y_axis(curve_data); if (y_axis_id < 0) return ""; // Check x axis int x_axis_id = this->init_x_axis(curve_data); if (x_axis_id < 0) return ""; Curve *curve = new Curve(curve_data, x_axis_id, y_axis_id); curve->plot_curve()->attach(this); curve_map_.insert(make_pair(curve->id(), curve)); QwtPlot::replot(); Q_EMIT curve_added(); return curve->id(); } bool Plot::add_curve(Curve *curve) { if (curve_map_.count(curve->id()) > 0) return false; // Check y axis int y_axis_id = this->init_y_axis(curve->curve_data(), curve->y_axis_id()); if (y_axis_id < 0) return false; // Check x axis int x_axis_id = this->init_x_axis(curve->curve_data(), curve->x_axis_id()); if (x_axis_id < 0) return false; curve->plot_curve()->attach(this); curve_map_.insert(make_pair(curve->id(), curve)); QwtPlot::replot(); Q_EMIT curve_added(); return true; } void Plot::remove_curve(Curve *curve) { // Delete markers vector delete_markers; for (const auto &mc_pair : marker_curve_map_) { if (mc_pair.second == curve) delete_markers.push_back(mc_pair.first); } for (const auto &marker : delete_markers) { remove_marker(marker); } // Delete curve curve_map_.erase(curve->id()); curve->plot_curve()->detach(); delete curve; Q_EMIT curve_removed(); } void Plot::remove_all_curves() { for (const auto &curve_pair : curve_map_) { remove_curve(curve_pair.second); } } int Plot::init_x_axis(BaseCurveData *curve_data, int x_axis_id) { assert(curve_data); if (x_axis_id < 0) { // Check if there already is an axis with the same unit. This is done // via the strings to get potential AC/DC flags. for (const auto &curve : curve_map_) { if (curve_data->x_unit_str() == curve.second->curve_data()->x_unit_str()) return curve.second->x_axis_id(); } // No existing axis was found, try to use the bottom or top axis. if (!this->axisEnabled(QwtPlot::xBottom)) x_axis_id = QwtPlot::xBottom; else if (!this->axisEnabled(QwtPlot::xTop)) x_axis_id = QwtPlot::xTop; else return -1; } else { // Check if the given axis id is already initialised with the proper // unit. for (const auto &curve : curve_map_) { if (curve.second->x_axis_id() == x_axis_id && curve.second->curve_data()->x_unit_str() == curve_data->x_unit_str()) return x_axis_id; else if (curve.second->x_axis_id() == x_axis_id) // NOLINT return -1; } } double min; double max; if (curve_data->type() == CurveType::TimeCurve && curve_data->is_relative_time()) { min = 0.; max = add_time_; } else if (curve_data->type() == CurveType::TimeCurve && !curve_data->is_relative_time()) { min = Session::session_start_timestamp; max = min + add_time_; } else if (curve_data->type() == CurveType::XYCurve) { // Values +/- 10% min = curve_data->boundingRect().left() - (std::fabs(curve_data->boundingRect().left()) * 0.1); max = curve_data->boundingRect().right() + (std::fabs(curve_data->boundingRect().right()) * 0.1); } else { throw std::runtime_error( "Plot::init_x_axis(): Curve type not implemented!"); } this->init_axis(x_axis_id, min, max, curve_data->x_title(), true); if (curve_data->type() == CurveType::TimeCurve && !curve_data->is_relative_time()) this->setAxisScaleEngine(x_axis_id, new QwtDateScaleEngine()); return x_axis_id; } int Plot::init_y_axis(BaseCurveData *curve_data, int y_axis_id) { assert(curve_data); if (y_axis_id < 0) { // Check if there already is an axis with the same unit. This is done // via the strings to get potential AC/DC flags. for (const auto &curve : curve_map_) { if (curve_data->y_unit_str() == curve.second->curve_data()->y_unit_str()) return curve.second->y_axis_id(); } // No existing axis was found, try to use the left or right axis. if (!this->axisEnabled(QwtPlot::yLeft)) y_axis_id = QwtPlot::yLeft; else if (!this->axisEnabled(QwtPlot::yRight)) y_axis_id = QwtPlot::yRight; else return -1; } else { // Check if the given axis id is already initialised with the proper // unit. for (const auto &curve : curve_map_) { if (curve.second->y_axis_id() == y_axis_id && curve.second->curve_data()->y_unit_str() == curve_data->y_unit_str()) return y_axis_id; else if (curve.second->y_axis_id() == y_axis_id) // NOLINT return -1; } } // Values +/- 10% double min = curve_data->boundingRect().bottom() - (std::fabs(curve_data->boundingRect().bottom()) * 0.1); double max = curve_data->boundingRect().top() + (std::fabs(curve_data->boundingRect().top()) * 0.1); this->init_axis(y_axis_id, min, max, curve_data->y_title(), false); return y_axis_id; } void Plot::init_axis(int axis_id, double min, double max, const QString &title, bool auto_scale) { map locks; locks.insert(make_pair( AxisBoundary::LowerBoundary, false)); locks.insert(make_pair( AxisBoundary::UpperBoundary, false)); axis_lock_map_.insert(make_pair(axis_id, locks)); this->setAxisTitle(axis_id, title); this->setAxisScale(axis_id, min, max); this->setAxisAutoScale(axis_id, auto_scale); // TODO: Not working!? this->enableAxis(axis_id); this->add_axis_icons(axis_id); } void Plot::add_axis_icons(const int axis_id) { AxisLockLabel *upper_lock_label = new AxisLockLabel( axis_id, AxisBoundary::UpperBoundary, ""); connect(upper_lock_label, &AxisLockLabel::clicked, this, &Plot::on_axis_lock_clicked); connect(this, &Plot::axis_lock_changed, upper_lock_label, &AxisLockLabel::on_axis_lock_changed); AxisLockLabel *lower_lock_label = new AxisLockLabel( axis_id, AxisBoundary::LowerBoundary, ""); connect(lower_lock_label, &AxisLockLabel::clicked, this, &Plot::on_axis_lock_clicked); connect(this, &Plot::axis_lock_changed, lower_lock_label, &AxisLockLabel::on_axis_lock_changed); QBoxLayout *scale_layout; if (axis_id == QwtPlot::xTop || axis_id == QwtPlot::xBottom) { scale_layout = new QHBoxLayout(); if (axis_id == QwtPlot::xTop) scale_layout->setAlignment(Qt::AlignTop); else scale_layout->setAlignment(Qt::AlignBottom); scale_layout->addWidget(lower_lock_label); scale_layout->addStretch(1); scale_layout->addWidget(upper_lock_label); // NOTE: setContentsMargins() places the locks more nicely in Windows. scale_layout->setContentsMargins(0, 2, 0, 2); } else { scale_layout = new QVBoxLayout(); if (axis_id == QwtPlot::yLeft) scale_layout->setAlignment(Qt::AlignLeft); else scale_layout->setAlignment(Qt::AlignRight); scale_layout->addWidget(upper_lock_label); scale_layout->addStretch(1); scale_layout->addWidget(lower_lock_label); // NOTE: setContentsMargins() places the locks more nicely in Windows. scale_layout->setContentsMargins(2, 0, 2, 0); } QwtScaleWidget *scale_widget = this->axisWidget(axis_id); scale_widget->setLayout(scale_layout); } void Plot::set_axis_locked(int axis_id, AxisBoundary axis_boundary, bool locked) { axis_lock_map_[axis_id][axis_boundary] = locked; Q_EMIT axis_lock_changed(axis_id, axis_boundary, locked); } void Plot::set_all_axis_locked(bool locked) { for (const auto &axis_lock : axis_lock_map_) { set_axis_locked(axis_lock.first, AxisBoundary::LowerBoundary, locked); set_axis_locked(axis_lock.first, AxisBoundary::UpperBoundary, locked); } } void Plot::lock_all_axis() { this->set_all_axis_locked(true); } void Plot::on_axis_lock_clicked() { AxisLockLabel *lock_label = qobject_cast(sender()); if (lock_label) { bool locked = is_axis_locked( lock_label->get_axis_id(), lock_label->get_axis_boundary()); lock_label->set_locked(!locked); set_axis_locked(lock_label->get_axis_id(), lock_label->get_axis_boundary(), !locked); } } void Plot::set_time_span(double time_span) { time_span_ = time_span; // time_span_ is used in rolling mode and oscilloscope mode. Find the // last/highest x value/timestamp and use it to calculate the new // x axis interval. double last_timestamp = .0; for (const auto &curve : curve_map_) { if (curve.second->curve_data()->boundingRect().right() > last_timestamp) last_timestamp = curve.second->curve_data()->boundingRect().right(); } // max must be set to the last timestamp to keep the manual calculation // of the scale divs for oscilloscope mode working. double max = last_timestamp; double min = max - time_span_; this->setAxisScale(QwtPlot::xBottom, min, max); this->replot(); } void Plot::add_marker(sv::ui::widgets::plot::Curve *curve) { auto *marker = curve->add_marker( QString::number(marker_curve_map().size()+1)); // Initial marker position is in the middle of the plot screen or // at the end of the curve. QwtInterval x_interval = this->axisInterval(curve->x_axis_id()); double x_mid = (x_interval.minValue() + x_interval.maxValue()) / 2; QwtInterval y_interval = this->axisInterval(curve->y_axis_id()); double y_mid = (y_interval.minValue() + y_interval.maxValue()) / 2; marker->setValue( curve->curve_data()->closest_point(QPointF(x_mid, y_mid), nullptr)); // Attach to plot marker->attach(this); marker_curve_map_.insert(make_pair(marker, curve)); active_marker_ = marker; // Add pickers for _all_ markers, no matter of the axis if (!marker_select_picker_) { // Use QwtPlot::xBottom and QwtPlot::yLeft as axis. We calculate the // canvas positions for the markers in on_marker_selected() marker_select_picker_ = new QwtPlotPicker( QwtPlot::xBottom, QwtPlot::yLeft, QwtPlotPicker::NoRubberBand, QwtPicker::AlwaysOff, this->canvas()); marker_select_picker_->setStateMachine(new QwtPickerClickPointMachine()); connect( marker_select_picker_, QOverload::of(&QwtPlotPicker::selected), this, &Plot::on_marker_selected); } if (!marker_move_picker_) { // Use QwtPlot::xBottom and QwtPlot::yLeft as axis. We calculate the // canvas positions for the markers in on_marker_moved() marker_move_picker_ = new QwtPlotPicker( QwtPlot::xBottom, QwtPlot::yLeft, QwtPlotPicker::NoRubberBand, QwtPicker::AlwaysOff, this->canvas()); marker_move_picker_->setStateMachine(new QwtPickerDragPointMachine()); connect(marker_move_picker_, &QwtPlotPicker::moved, this, &Plot::on_marker_moved); } /* * TODO: Maybe we could use a QwtPickerTrackerMachine for mouse movement. * This way we can avoid the mouse click event (problems with QwtPlotPanner) * and also highlight the marker that in the "selectable range" of the * mouse pointer. Maybe this would be a performance issue? */ update_markers_label(); replot(); Q_EMIT marker_added(); } void Plot::add_diff_marker(QwtPlotMarker *marker1, QwtPlotMarker *marker2) { if (!marker1 || !marker2) return; diff_markers_.push_back(make_pair(marker1, marker2)); update_markers_label(); replot(); Q_EMIT marker_added(); } // TODO: Implement GUI remove marker function void Plot::remove_marker(QwtPlotMarker *marker) { // Delete diff markers. for (auto it = diff_markers_.begin(); it != diff_markers_.end(); ) { if (it->first == marker || it->second == marker) it = diff_markers_.erase(it); else ++it; } // Delete marker. marker_curve_map_.erase(marker); if (active_marker_ == marker) active_marker_ = nullptr; marker->detach(); delete marker; if (marker_curve_map_.empty()) { // No markers left. disconnect( marker_select_picker_, QOverload::of(&QwtPlotPicker::selected), this, &Plot::on_marker_selected); delete marker_select_picker_; marker_select_picker_ = nullptr; disconnect(marker_move_picker_, &QwtPlotPicker::moved, this, &Plot::on_marker_moved); delete marker_move_picker_; marker_move_picker_ = nullptr; } update_markers_label(); replot(); Q_EMIT marker_removed(); } void Plot::on_marker_selected(const QPointF mouse_pos) { if (marker_curve_map_.empty()) return; // Mouse canvas coordinates. Use QwtPlot::xBottom and QwtPlot::yLeft // as axis for marker_select_picker_. const double mouse_canvas_x = transform(QwtPlot::xBottom, mouse_pos.x()); const double mouse_canvas_y = transform(QwtPlot::yLeft, mouse_pos.y()); // Check if mouse pointer is near a marker double d_min = 15.; // Minimum distance to marker for selecting double d_lowest = 1.0e10; QwtPlotMarker *selected_marker = nullptr; for (const auto &mc_pair : marker_curve_map_) { // Marker canvas coordinates. Use axis ids form plot. const double marker_canvas_x = transform(mc_pair.second->x_axis_id(), mc_pair.first->xValue()); const double marker_canvas_y = transform(mc_pair.second->y_axis_id(), mc_pair.first->yValue()); const double d_x = marker_canvas_x - mouse_canvas_x; const double d_y = marker_canvas_y - mouse_canvas_y; const double d_actual = qSqrt(qwtSqr(d_x) + qwtSqr(d_y)); if (d_actual <= d_min && d_actual < d_lowest) { d_lowest = d_actual; selected_marker = mc_pair.first; } } if (selected_marker) { plot_panner_->setEnabled(false); active_marker_ = selected_marker; } else { /* * TODO: Maybe activate the plot panner via a timer after 1s of no * marker move event. This would avoid the "double" click to deselect * the marker/enable the panner (1) and register a new panning event (2) */ plot_panner_->setEnabled(true); active_marker_ = nullptr; } // TODO: connect/disconnect marker_move_picker_ } void Plot::on_marker_moved(const QPointF mouse_pos) { if (!active_marker_) return; BaseCurveData *curve_data = marker_curve_map_[active_marker_]->curve_data(); QPointF marker_pos = curve_data->closest_point(mouse_pos, nullptr); active_marker_->setValue(marker_pos); update_markers_label(); replot(); } void Plot::on_legend_clicked(const QVariant &item_info, int index) { (void)index; QwtPlotItem *plot_item = infoToItem(item_info); if (!plot_item) return; Curve *curve = get_curve_from_plot_curve( static_cast(plot_item)); if (!curve) return; ui::dialogs::PlotCurveConfigDialog dlg(curve, this); dlg.exec(); } void Plot::update_curves() { for (const auto &curve : curve_map_) { const size_t painted_points = curve.second->painted_points(); const size_t num_points = curve.second->curve_data()->size(); if (num_points > painted_points) { //qWarning() << QString("Plot::updateCurve(): num_points = %1, painted_points = %2"). // arg(num_points).arg(painted_points); const bool clip = !canvas()->testAttribute(Qt::WA_PaintOnScreen); if (clip) { /* * NOTE: * Depending on the platform setting a clip might be an * important performance issue. F.e. for Qt Embedded this * reduces the part of the backing store that has to be copied * out - maybe to an unaccelerated frame buffer device. */ const QwtScaleMap x_map = canvasMap(curve.second->x_axis_id()); const QwtScaleMap y_map = canvasMap(curve.second->y_axis_id()); QRectF br = qwtBoundingRect(*curve.second->plot_curve()->data(), (int)painted_points - 1, (int)num_points - 1); curve.second->plot_direct_painter()->setClipRegion( QwtScaleMap::transform(x_map, y_map, br).toRect()); } curve.second->plot_direct_painter()->drawSeries( curve.second->plot_curve(), (int)painted_points - 1, (int)num_points - 1); curve.second->set_painted_points(num_points); } //replot(); } } void Plot::update_intervals() { bool intervals_changed = false; for (const auto &curve : curve_map_) { if (update_x_interval(curve.second)) intervals_changed = true; if (update_y_interval(curve.second)) intervals_changed = true; } if (intervals_changed) replot(); } bool Plot::update_x_interval(Curve *curve) { if (axis_lock_map_[QwtPlot::xBottom][AxisBoundary::LowerBoundary] && axis_lock_map_[QwtPlot::xBottom][AxisBoundary::UpperBoundary]) return false; bool interval_changed = false; QRectF boundaries = curve->curve_data()->boundingRect(); QwtInterval x_interval = this->axisInterval(QwtPlot::xBottom); double min = x_interval.minValue(); double max = x_interval.maxValue(); // There are no plot modes when showing xy curves, just extend the intervals if (curve->curve_data()->type() == CurveType::XYCurve) { if (!axis_lock_map_[QwtPlot::xBottom][AxisBoundary::LowerBoundary] && boundaries.left() < min) { // New value - 10% min = boundaries.left() - (std::fabs(boundaries.left()) * 0.1); interval_changed = true; } if (!axis_lock_map_[QwtPlot::xBottom][AxisBoundary::UpperBoundary] && boundaries.right() > max) { // New value + 10% max = boundaries.right() + (std::fabs(boundaries.right()) * 0.1); interval_changed = true; } if (interval_changed) setAxisScale(QwtPlot::xBottom, min, max); } // Handle the Additive plot mode else if (update_mode_ == PlotUpdateMode::Additive) { if (!axis_lock_map_[QwtPlot::xBottom][AxisBoundary::LowerBoundary] && boundaries.left() < min) { min = 0; interval_changed = true; } if (!axis_lock_map_[QwtPlot::xBottom][AxisBoundary::UpperBoundary] && boundaries.right() > max) { if (boundaries.right()+add_time_ > max) max = boundaries.right() + add_time_; else max = x_interval.maxValue() + add_time_; interval_changed = true; } if (interval_changed) setAxisScale(QwtPlot::xBottom, min, max); } // Handle the Rolling plot mode else if (update_mode_ == PlotUpdateMode::Rolling) { // TODO: axis locking. Lock/Unlock both upper and lower together! if (boundaries.right() <= max) return false; if (boundaries.right() > max+time_span_) min = boundaries.right(); else min += add_time_; max = min + time_span_; interval_changed = true; setAxisScale(QwtPlot::xBottom, min, max); } // Handle the Oscilloscope plot mode else if (update_mode_ == PlotUpdateMode::Oscilloscope) { // TODO: axis locking. Lock/Unlock both upper and lower together? if (boundaries.right() <= max) return false; if (boundaries.right() > max+time_span_) min = boundaries.right(); else min += time_span_; max = min + time_span_; /* * NOTE: * To avoid, that the grid is jumping, we disable the autocalculation * of the ticks and shift them manually instead. */ QwtScaleDiv scaleDiv = axisScaleDiv(QwtPlot::xBottom); scaleDiv.setInterval(min, max); for (int i = 0; i < QwtScaleDiv::NTickTypes; i++) { QList ticks = scaleDiv.ticks(i); for (int j = 0; j < ticks.size(); j++) { ticks[j] += x_interval.width(); } scaleDiv.setTicks(i, ticks); } interval_changed = true; setAxisScaleDiv(QwtPlot::xBottom, scaleDiv); curve->set_painted_points(0); } return interval_changed; } bool Plot::update_y_interval(const Curve *curve) { int y_axis_id = curve->y_axis_id(); if (axis_lock_map_[y_axis_id][AxisBoundary::LowerBoundary] && axis_lock_map_[y_axis_id][AxisBoundary::UpperBoundary]) return false; QRectF boundaries = curve->curve_data()->boundingRect(); QwtInterval y_interval = this->axisInterval(y_axis_id); double min = y_interval.minValue(); double max = y_interval.maxValue(); bool interval_changed = false; if (!axis_lock_map_[y_axis_id][AxisBoundary::LowerBoundary] && boundaries.bottom() < min) { // New value - 10% min = boundaries.bottom() - (std::fabs(boundaries.bottom()) * 0.1); interval_changed = true; } if (!axis_lock_map_[y_axis_id][AxisBoundary::UpperBoundary] && boundaries.top() > max) { // New value + 10% max = boundaries.top() + (std::fabs(boundaries.top()) * 0.1); interval_changed = true; } if (interval_changed ) setAxisScale(y_axis_id, min, max); return interval_changed; } void Plot::set_markers_label_alignment(int alignment) { markers_label_alignment_ = alignment; if (markers_label_) { // TODO: Maybe there is a better way to replot the label? markers_label_->detach(); delete markers_label_; markers_label_ = nullptr; update_markers_label(); } } void Plot::update_markers_label() { if (!markers_label_) { markers_label_ = new QwtPlotTextLabel(); markers_label_->setMargin(5); // The markers label will be painted ontop of curves. markers_label_->setZ(3); markers_label_->attach(this); } QString table(""); for (const auto &mc_pair : marker_curve_map_) { table.append(""); table.append(QString(""). arg(mc_pair.first->title().text())); table.append(QString(""). arg(mc_pair.first->yValue()). arg(mc_pair.second->curve_data()->y_unit_str())); table.append(QString(""). arg(mc_pair.first->xValue()). arg(mc_pair.second->curve_data()->x_unit_str())); table.append(""); } for (const auto &marker_pair : diff_markers_) { double d_x = marker_pair.first->xValue() - marker_pair.second->xValue(); double d_y = marker_pair.first->yValue() - marker_pair.second->yValue(); QString x_unit(""); QString m1_x_unit = marker_curve_map_[marker_pair.first]->curve_data()->x_unit_str(); QString m2_x_unit = marker_curve_map_[marker_pair.second]->curve_data()->x_unit_str(); if (m1_x_unit == m2_x_unit) x_unit = m1_x_unit; QString y_unit(""); QString m1_y_unit = marker_curve_map_[marker_pair.first]->curve_data()->y_unit_str(); QString m2_y_unit = marker_curve_map_[marker_pair.second]->curve_data()->y_unit_str(); if (m1_y_unit == m2_y_unit) y_unit = m1_y_unit; table.append(""); table.append(QString("").arg( marker_pair.first->title().text(), marker_pair.second->title().text())); table.append(QString(""). arg(d_y).arg(y_unit)); table.append(QString(""). arg(d_x).arg(x_unit)); table.append(""); } table.append("
%1:%2 %3%4 %5
%1 - %2:%1 %2%1 %2
"); QwtText text = QwtText(table); text.setPaintAttribute(QwtText::PaintBackground, true); QColor background(Qt::gray); background.setAlpha(200); text.setBackgroundBrush(background); text.setBorderRadius(3); QPen pen; pen.setColor(Qt::black); pen.setWidthF(1.0); pen.setStyle(Qt::SolidLine); text.setBorderPen(pen); text.setRenderFlags(markers_label_alignment_); markers_label_->setText(text); } void Plot::timerEvent(QTimerEvent *event) { if (event->timerId() == timer_id_) { update_intervals(); update_curves(); return; } QwtPlot::timerEvent(event); } void Plot::resizeEvent(QResizeEvent *event) { for (const auto &curve : curve_map_) { curve.second->plot_direct_painter()->reset(); } QwtPlot::resizeEvent(event); } void Plot::showEvent(QShowEvent *event) { (void)event; replot(); } bool Plot::eventFilter(QObject *object, QEvent *event) { return QwtPlot::eventFilter(object, event); } void Plot::save_settings(QSettings &settings, bool save_curves, shared_ptr origin_device) const { // TODO: Use Q_ENUM_NS for PlotUpdateMode to add meta-object support (Qt >= 5.8) settings.setValue("update_mode", (int)update_mode()); settings.setValue("time_span", time_span_); settings.setValue("add_time", add_time_); if (!save_curves) return; for (const auto &curve : curve_map_) { curve.second->save_settings(settings, origin_device); } } void Plot::restore_settings(QSettings &settings, bool restore_curves, shared_ptr origin_device) { if (settings.contains("update_mode")) update_mode_ = static_cast( settings.value("update_mode").toInt()); if (settings.contains("time_span")) time_span_ = settings.value("time_span").toDouble(); if (settings.contains("add_time")) add_time_ = settings.value("add_time").toDouble(); if (!restore_curves) return; const auto groups = settings.childGroups(); for (const auto &group : groups) { if (group.startsWith("timecurve:") || group.startsWith("xycurve:")) { Curve *curve = Curve::init_from_settings( session_, settings, group, origin_device); if (curve) add_curve(curve); } } } Curve *Plot::get_curve_from_plot_curve(const QwtPlotCurve *plot_curve) const { for (const auto &curve : curve_map_) { if (curve.second->plot_curve() == plot_curve) return curve.second; } return nullptr; } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/plot.hpp ================================================ /* * This file is part of the SmuView project. * This file is based on the QWT Oscilloscope Example. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_PLOT_HPP #define UI_WIDGETS_PLOT_PLOT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::map; using std::pair; using std::shared_ptr; using std::string; using std::vector; namespace sv { class Session; namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { class BaseCurveData; class Curve; class PlotMagnifier; enum class AxisBoundary { LowerBoundary, UpperBoundary }; enum class PlotUpdateMode { Additive = 0, Rolling, Oscilloscope }; // TODO: Use tr(), QCoreApplication::translate(), QT_TR_NOOP() or // QT_TRANSLATE_NOOP() for translation. // See: http://doc.qt.io/qt-5/i18n-source-translation.html typedef map plot_update_mode_name_map_t; static plot_update_mode_name_map_t plot_update_mode_name_map = { { sv::ui::widgets::plot::PlotUpdateMode::Additive, QString("Additive") }, { sv::ui::widgets::plot::PlotUpdateMode::Rolling, QString("Rolling") }, { sv::ui::widgets::plot::PlotUpdateMode::Oscilloscope, QString("Oscilloscope") }, }; class Plot : public QwtPlot { Q_OBJECT public: explicit Plot(Session &session, QWidget *parent = nullptr); virtual ~Plot(); virtual void replot() override; virtual bool eventFilter(QObject * object, QEvent *event) override; /** * Return the id of the new curve. Empty string when curve couldn't be * added. */ string add_curve(BaseCurveData *curve_data); bool add_curve(Curve *curve); void remove_curve(Curve *curve); void remove_all_curves(); /** Return a map of all curves. */ map curve_map() const { return curve_map_; } bool is_axis_locked(int axis_id, AxisBoundary axis_boundary) { return axis_lock_map_[axis_id][axis_boundary]; } void set_axis_locked(int axis_id, AxisBoundary axis_boundary, bool locked); void set_all_axis_locked(bool locked); void set_plot_interval(int plot_interval) { plot_interval_ = plot_interval; } void set_update_mode(PlotUpdateMode update_mode) { update_mode_ = update_mode; } PlotUpdateMode update_mode() const { return update_mode_; }; void set_time_span(double time_span); double time_span() const { return time_span_; } void set_add_time(double add_time) { add_time_ = add_time; } double add_time() const { return add_time_; } map marker_curve_map() const { return marker_curve_map_; } void set_markers_label_alignment(int alignment); int markers_label_alignment() const { return markers_label_alignment_; } void save_settings(QSettings &settings, bool save_curves, shared_ptr origin_device) const; void restore_settings(QSettings &settings, bool restore_curves, shared_ptr origin_device); public Q_SLOTS: void start(); void stop(); void add_axis_icons(const int axis_id); void lock_all_axis(); void on_axis_lock_clicked(); void add_marker(sv::ui::widgets::plot::Curve *curve); void add_diff_marker(QwtPlotMarker *marker1, QwtPlotMarker *marker2); void remove_marker(QwtPlotMarker *marker); void on_marker_selected(const QPointF mouse_pos); void on_marker_moved(const QPointF mouse_pos); void on_legend_clicked(const QVariant &item_info, int index); protected: virtual void showEvent(QShowEvent *event) override; virtual void resizeEvent(QResizeEvent *event) override; virtual void timerEvent(QTimerEvent *event) override; private: int init_x_axis(BaseCurveData *curve_data, int x_axis_id = -1); int init_y_axis(BaseCurveData *curve_data, int y_axis_id = -1); void init_axis(int axis_id, double min, double max, const QString &title, bool auto_scale); void update_curves(); void update_intervals(); bool update_x_interval(Curve *curve); bool update_y_interval(const Curve *curve); void update_markers_label(); Curve *get_curve_from_plot_curve(const QwtPlotCurve *plot_curve) const; Session &session_; map curve_map_; map> axis_lock_map_; // map> int plot_interval_; int timer_id_; PlotUpdateMode update_mode_; double time_span_; double add_time_; QwtPlotPanner *plot_panner_; PlotMagnifier *plot_magnifier_; map marker_curve_map_; vector> diff_markers_; QwtPlotMarker *active_marker_; QwtPlotTextLabel *markers_label_; int markers_label_alignment_; QwtPlotPicker *marker_select_picker_; QwtPlotPicker *marker_move_picker_; Q_SIGNALS: void axis_lock_changed(int axis_id, sv::ui::widgets::plot::AxisBoundary axis_boundary, bool locked); void curve_added(); void curve_removed(); void marker_added(); void marker_removed(); }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_PLOT_HPP ================================================ FILE: src/ui/widgets/plot/plotmagnifier.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "plotmagnifier.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { PlotMagnifier::PlotMagnifier(QWidget *canvas) : QwtPlotMagnifier(canvas) { } void PlotMagnifier::rescale(double factor) { QwtPlotMagnifier::rescale(factor); Q_EMIT magnified(factor); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/plotmagnifier.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2019-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_PLOTMAGNIFIER_HPP #define UI_WIDGETS_PLOT_PLOTMAGNIFIER_HPP #include #include namespace sv { namespace ui { namespace widgets { namespace plot { /** * QwtPlotMagnifier doesn't have signals, so we have to create them. * * TODO: Reimplement to zomm in/out at the mouse pointer position, not only * at the center of the canvas. */ class PlotMagnifier : public QwtPlotMagnifier { Q_OBJECT public: explicit PlotMagnifier(QWidget *canvas); protected: void rescale(double factor) override; Q_SIGNALS: void magnified(double factor); }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_PLOTMAGNIFIER_HPP ================================================ FILE: src/ui/widgets/plot/plotscalepicker.cpp ================================================ /* * This file is part of the SmuView project. * This file is based on the QWT EventFilter Example. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "plotscalepicker.hpp" #include "src/ui/widgets/plot/axispopup.hpp" #include "src/ui/widgets/plot/plot.hpp" namespace sv { namespace ui { namespace widgets { namespace plot { PlotScalePicker::PlotScalePicker(Plot *plot) : QObject(plot), plot_(plot), is_double_clicked(false), wheel_factor_(0.9) { for (int i = 0; i < QwtPlot::axisCnt; i++) { QwtScaleWidget *scale_widget = plot->axisWidget(i); if (scale_widget) scale_widget->installEventFilter(this); } } bool PlotScalePicker::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QwtScaleWidget *scale_widget = qobject_cast(object); if (scale_widget) { QMouseEvent *mouse_event = static_cast(event); if ((mouse_event->buttons() & Qt::LeftButton) != 0) { QPoint pos = mouse_event->pos(); switch (scale_widget->alignment()) { case QwtScaleDraw::LeftScale: case QwtScaleDraw::RightScale: last_pan_p_value_ = pos.y(); break; case QwtScaleDraw::BottomScale: case QwtScaleDraw::TopScale: last_pan_p_value_ = pos.x(); break; } return true; } } } else if (event->type() == QEvent::MouseMove) { QwtScaleWidget *scale_widget = qobject_cast(object); if (scale_widget) { QMouseEvent *mouse_event = static_cast(event); if ((mouse_event->buttons() & Qt::LeftButton) != 0) { QPoint pos = mouse_event->pos(); int axis_id = -1; int p_value = 0; switch (scale_widget->alignment()) { case QwtScaleDraw::LeftScale: axis_id = QwtPlot::yLeft; p_value = pos.y(); break; case QwtScaleDraw::RightScale: axis_id = QwtPlot::yRight; p_value = pos.y(); break; case QwtScaleDraw::BottomScale: axis_id = QwtPlot::xBottom; p_value = pos.x(); break; case QwtScaleDraw::TopScale: axis_id = QwtPlot::xTop; p_value = pos.x(); break; } const bool auto_replot = plot_->autoReplot(); plot_->setAutoReplot(false); const QwtScaleMap scale_map = scale_widget->scaleDraw()->scaleMap(); const double p1 = scale_map.transform( plot_->axisScaleDiv(axis_id).lowerBound()); const double p2 = scale_map.transform( plot_->axisScaleDiv(axis_id).upperBound()); int p_diff = p_value - last_pan_p_value_; double s1 = scale_map.invTransform(p1 - p_diff); double s2 = scale_map.invTransform(p2 - p_diff); last_pan_p_value_ = p_value; plot_->set_axis_locked( axis_id, AxisBoundary::LowerBoundary, true); plot_->set_axis_locked( axis_id, AxisBoundary::UpperBoundary, true); plot_->setAxisScale(axis_id, s1, s2); plot_->setAutoReplot(auto_replot); plot_->replot(); return true; } } } else if (event->type() == QEvent::Wheel) { QwtScaleWidget *scale_widget = qobject_cast(object); if (scale_widget) { QWheelEvent *wheel_event = static_cast(event); if (wheel_event) { double factor = std::pow(wheel_factor_, std::fabs(wheel_event->angleDelta().y() / 120.0)); if (wheel_event->angleDelta().y() > 0) factor = 1 / factor; factor = std::fabs(factor); if (factor == 1.0 || factor == 0.0) return true; int axis_id = -1; double mouse_pos = -1; // Mouse position in the scale widget. switch (scale_widget->alignment()) { case QwtScaleDraw::LeftScale: axis_id = QwtPlot::yLeft; mouse_pos = get_wheel_pos(wheel_event).y(); break; case QwtScaleDraw::RightScale: axis_id = QwtPlot::yRight; mouse_pos = get_wheel_pos(wheel_event).y(); break; case QwtScaleDraw::BottomScale: axis_id = QwtPlot::xBottom; mouse_pos = get_wheel_pos(wheel_event).x(); break; case QwtScaleDraw::TopScale: axis_id = QwtPlot::xTop; mouse_pos = get_wheel_pos(wheel_event).x(); break; } const bool auto_replot = plot_->autoReplot(); plot_->setAutoReplot(false); const QwtScaleMap scale_map = plot_->canvasMap(axis_id); double v1 = scale_map.s1(); double v2 = scale_map.s2(); if (scale_map.transformation()) { // The coordinate system of the paint device is // always linear v1 = scale_map.transform(v1); v2 = scale_map.transform(v2); } const double center = scale_map.invTransform(mouse_pos); const double upper = (v2 - center) * factor; const double lower = (center - v1) * factor; v1 = center - lower; v2 = center + upper; if (scale_map.transformation()) { v1 = scale_map.invTransform(v1); v2 = scale_map.invTransform(v2); } plot_->set_axis_locked( axis_id, AxisBoundary::LowerBoundary, true); plot_->set_axis_locked( axis_id, AxisBoundary::UpperBoundary, true); plot_->setAxisScale(axis_id, v1, v2); plot_->setAutoReplot(auto_replot); plot_->replot(); return true; } } } else if (event->type() == QEvent::MouseButtonDblClick) { QwtScaleWidget *scale_widget = qobject_cast(object); if (scale_widget) { QMouseEvent *mouse_event = static_cast(event); if ((mouse_event->buttons() & Qt::LeftButton) != 0) { is_double_clicked = true; return true; } } is_double_clicked = false; } else if (event->type() == QEvent::MouseButtonRelease) { QwtScaleWidget *scale_widget = qobject_cast(object); if (scale_widget) { QMouseEvent *mouse_event = static_cast(event); if (mouse_event->button() == Qt::LeftButton && is_double_clicked) { is_double_clicked = false; int axis_id = -1; PopupPosition popup_pos = PopupPosition::Right; switch (scale_widget->alignment()) { case QwtScaleDraw::LeftScale: axis_id = QwtPlot::yLeft; popup_pos = PopupPosition::Right; break; case QwtScaleDraw::RightScale: axis_id = QwtPlot::yRight; popup_pos = PopupPosition::Left; break; case QwtScaleDraw::BottomScale: axis_id = QwtPlot::xBottom; popup_pos = PopupPosition::Top; break; case QwtScaleDraw::TopScale: axis_id = QwtPlot::xTop; popup_pos = PopupPosition::Bottom; break; } AxisPopup *const axis_popup = new AxisPopup(plot_, axis_id, scale_widget); axis_popup->set_position(scale_widget->mapToGlobal( mouse_event->pos()), popup_pos); axis_popup->show(); return true; } } } return QObject::eventFilter(object, event); } QPointF PlotScalePicker::get_wheel_pos(QWheelEvent *wheel_event) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) return wheel_event->position(); #else return wheel_event->posF(); #endif } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/plotscalepicker.hpp ================================================ /* * This file is part of the SmuView project. * This file is based on the QWT EventFilter Example. * * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_PLOTSCALEPICKER_HPP #define UI_WIDGETS_PLOT_PLOTSCALEPICKER_HPP #include #include #include #include namespace sv { namespace ui { namespace widgets { namespace plot { class Plot; class PlotScalePicker : public QObject { Q_OBJECT public: explicit PlotScalePicker(Plot *plot); virtual bool eventFilter(QObject *object, QEvent *event); private: Plot *plot_; bool is_double_clicked; int last_pan_p_value_; double wheel_factor_; QPointF get_wheel_pos(QWheelEvent *wheel_event); }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_PLOTSCALEPICKER_HPP ================================================ FILE: src/ui/widgets/plot/timecurvedata.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "timecurvedata.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" using std::dynamic_pointer_cast; using std::set; using std::shared_ptr; namespace sv { namespace ui { namespace widgets { namespace plot { TimeCurveData::TimeCurveData(shared_ptr signal) : BaseCurveData(CurveType::TimeCurve), signal_(signal) { } bool TimeCurveData::is_equal(const BaseCurveData *other) const { const TimeCurveData *tcd = dynamic_cast(other); if (tcd == nullptr) return false; return signal_ == tcd->signal(); } QPointF TimeCurveData::sample(size_t index) const { //signal_data_->lock(); auto sample = signal_->get_sample(index, relative_time_); QPointF sample_point(sample.first, sample.second); //signal_data_->.unlock(); return sample_point; } size_t TimeCurveData::size() const { // TODO: Synchronize x/y sample data return signal_->sample_count(); } QRectF TimeCurveData::boundingRect() const { /* qWarning() << "TimeCurveData::boundingRect(): min time = " << signal_->first_timestamp(relative_time_); qWarning() << "TimeCurveData::boundingRect(): max time = " << signal_->last_timestamp(relative_time_); qWarning() << "TimeCurveData::boundingRect(): min value = " << signal_->min_value(); qWarning() << "TimeCurveData::boundingRect(): max value = " << signal_->max_value(); */ // top left, bottom right return QRectF( QPointF(signal_->first_timestamp(relative_time_), signal_->max_value()), QPointF(signal_->last_timestamp(relative_time_), signal_->min_value())); } QPointF TimeCurveData::closest_point(const QPointF &pos, double *dist) const { (void)dist; const double x_value = pos.x(); const int index_max = (int)size() - 1; // Corner cases if (index_max < 0) return QPointF(0, 0); if (x_value <= sample(0).x()) return sample(0); if (x_value >= sample(index_max).x()) return sample(index_max); size_t index_min = 0; size_t index = index_max; while (index > 0) { const size_t half = index >> 1; const size_t index_mid = index_min + half; if (x_value < sample(index_mid).x()) { index = half; } else { index_min = index_mid + 1; index -= half + 1; } } return sample(index_min); } QString TimeCurveData::name() const { return signal_->display_name(); } string TimeCurveData::id_prefix() const { return "timecurve"; } sv::data::Quantity TimeCurveData::x_quantity() const { return sv::data::Quantity::Time; } set TimeCurveData::x_quantity_flags() const { return set(); } sv::data::Unit TimeCurveData::x_unit() const { return sv::data::Unit::Second; } QString TimeCurveData::x_unit_str() const { return data::datautil::format_unit(x_unit()); } QString TimeCurveData::x_title() const { return QString("%1 [%2]"). arg(data::datautil::format_quantity(x_quantity()), x_unit_str()); } sv::data::Quantity TimeCurveData::y_quantity() const { return signal_->quantity(); } set TimeCurveData::y_quantity_flags() const { return signal_->quantity_flags(); } sv::data::Unit TimeCurveData::y_unit() const { return signal_->unit(); } QString TimeCurveData::y_unit_str() const { return data::datautil::format_unit(y_unit(), y_quantity_flags()); } QString TimeCurveData::y_title() const { // Don't use only the unit, so we can add AC/DC to axis label. return QString("%1 [%2]"). arg(data::datautil::format_quantity(y_quantity()), y_unit_str()); } shared_ptr TimeCurveData::signal() const { return signal_; } void TimeCurveData::save_settings(QSettings &settings, shared_ptr origin_device) const { SettingsManager::save_signal(signal_, settings, origin_device); } TimeCurveData *TimeCurveData::init_from_settings( Session &session, QSettings &settings, shared_ptr origin_device) { auto data_signal = SettingsManager::restore_signal( session, settings, origin_device); if (!data_signal) return nullptr; return new TimeCurveData( dynamic_pointer_cast(data_signal)); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/timecurvedata.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_TIMECURVEDATA_HPP #define UI_WIDGETS_PLOT_TIMECURVEDATA_HPP #include #include #include #include #include #include #include #include "src/data/datautil.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" using std::set; using std::shared_ptr; using std::string; namespace sv { class Session; namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { class TimeCurveData : public BaseCurveData { Q_OBJECT public: explicit TimeCurveData(shared_ptr signal); bool is_equal(const BaseCurveData *other) const override; QPointF sample(size_t index) const override; size_t size() const override; QRectF boundingRect() const override; QPointF closest_point(const QPointF &pos, double *dist) const override; QString name() const override; string id_prefix() const override; sv::data::Quantity x_quantity() const override; set x_quantity_flags() const override; sv::data::Unit x_unit() const override; QString x_unit_str() const override; QString x_title() const override; sv::data::Quantity y_quantity() const override; set y_quantity_flags() const override; sv::data::Unit y_unit() const override; QString y_unit_str() const override; QString y_title() const override; shared_ptr signal() const; void save_settings(QSettings &settings, shared_ptr origin_device) const override; static TimeCurveData *init_from_settings( Session &session, QSettings &settings, shared_ptr origin_device); private: shared_ptr signal_; }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_TIMECURVEDATA_HPP ================================================ FILE: src/ui/widgets/plot/xycurvedata.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "xycurvedata.hpp" #include "src/session.hpp" #include "src/settingsmanager.hpp" #include "src/data/analogtimesignal.hpp" #include "src/data/datautil.hpp" #include "src/devices/basedevice.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" using std::dynamic_pointer_cast; using std::lock_guard; using std::make_shared; using std::mutex; using std::set; using std::shared_ptr; namespace sv { namespace ui { namespace widgets { namespace plot { XYCurveData::XYCurveData(shared_ptr x_t_signal, shared_ptr y_t_signal) : BaseCurveData(CurveType::XYCurve), x_t_signal_(x_t_signal), y_t_signal_(y_t_signal), x_t_signal_pos_(0), y_t_signal_pos_(0) { x_data_ = make_shared>(); y_data_ = make_shared>(); // Prefill data vectors this->on_sample_appended(); connect(x_t_signal_.get(), &sv::data::AnalogTimeSignal::sample_appended, this, &XYCurveData::on_sample_appended); connect(y_t_signal_.get(), &sv::data::AnalogTimeSignal::sample_appended, this, &XYCurveData::on_sample_appended); } bool XYCurveData::is_equal(const BaseCurveData *other) const { const XYCurveData *xycd = dynamic_cast(other); if (xycd == nullptr) return false; return (x_t_signal_ == xycd->x_t_signal()) && (y_t_signal_ == xycd->y_t_signal()); } QPointF XYCurveData::sample(size_t index) const { QPointF sample_point(x_data_->at(index), y_data_->at(index)); return sample_point; } size_t XYCurveData::size() const { return x_data_->size(); } QRectF XYCurveData::boundingRect() const { // top left, bottom right return QRectF( QPointF(x_t_signal_->min_value(), y_t_signal_->max_value()), QPointF(x_t_signal_->max_value(), y_t_signal_->min_value())); } QPointF XYCurveData::closest_point(const QPointF &pos, double *dist) const { const size_t num_samples = size(); if (num_samples == 0) return QPointF(0, 0); // TODO size_t index = -1; double d_min = 1.0e10; for (size_t i=0; i < num_samples; i++) { const QPointF sample_point = sample(i); const double d_x = sample_point.x() - pos.x(); const double d_y = sample_point.y() - pos.y(); const double d_actual = qwtSqr(d_x) + qwtSqr(d_y); if (d_actual < d_min) { index = i; d_min = d_actual; } } if (dist) *dist = qSqrt(d_min); return sample(index); } QString XYCurveData::name() const { return y_t_signal_->display_name().append(" -> "). append(x_t_signal_->display_name()); } string XYCurveData::id_prefix() const { return "xycurve"; } sv::data::Quantity XYCurveData::x_quantity() const { return x_t_signal_->quantity(); } set XYCurveData::x_quantity_flags() const { return x_t_signal_->quantity_flags(); } sv::data::Unit XYCurveData::x_unit() const { return x_t_signal_->unit(); } QString XYCurveData::x_unit_str() const { return data::datautil::format_unit(x_unit(), x_quantity_flags()); } QString XYCurveData::x_title() const { // Don't use only the unit, so we can add AC/DC to axis label. return QString("%1 [%2]"). arg(data::datautil::format_quantity(x_quantity()), x_unit_str()); } sv::data::Quantity XYCurveData::y_quantity() const { return y_t_signal_->quantity(); } set XYCurveData::y_quantity_flags() const { return y_t_signal_->quantity_flags(); } sv::data::Unit XYCurveData::y_unit() const { return y_t_signal_->unit(); } QString XYCurveData::y_unit_str() const { return data::datautil::format_unit(y_unit(), y_quantity_flags()); } QString XYCurveData::y_title() const { // Don't use only the unit, so we can add AC/DC to axis label. return QString("%1 [%2]"). arg(data::datautil::format_quantity(y_quantity()), y_unit_str()); } shared_ptr XYCurveData::x_t_signal() const { return x_t_signal_; } shared_ptr XYCurveData::y_t_signal() const { return y_t_signal_; } void XYCurveData::save_settings(QSettings &settings, shared_ptr origin_device) const { SettingsManager::save_signal(x_t_signal_, settings, origin_device, "x_"); SettingsManager::save_signal(y_t_signal_, settings, origin_device, "y_"); } XYCurveData *XYCurveData::init_from_settings( Session &session, QSettings &settings, shared_ptr origin_device) { auto x_t_data_signal = SettingsManager::restore_signal( session, settings, origin_device, "x_"); auto y_t_data_signal = SettingsManager::restore_signal( session, settings, origin_device, "y_"); if (!x_t_data_signal || !y_t_data_signal) return nullptr; return new XYCurveData( dynamic_pointer_cast(x_t_data_signal), dynamic_pointer_cast(y_t_data_signal)); } void XYCurveData::on_sample_appended() { lock_guard lock(sample_append_mutex_); shared_ptr> time = make_shared>(); sv::data::AnalogTimeSignal::combine_signals( x_t_signal_, x_t_signal_pos_, y_t_signal_, y_t_signal_pos_, time, x_data_, y_data_); } } // namespace plot } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/plot/xycurvedata.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2017-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_PLOT_XYCURVEDATA_HPP #define UI_WIDGETS_PLOT_XYCURVEDATA_HPP #include #include #include #include #include #include #include #include #include #include "src/data/datautil.hpp" #include "src/ui/widgets/plot/basecurvedata.hpp" using std::mutex; using std::set; using std::shared_ptr; using std::string; using std::vector; namespace sv { class Session; namespace data { class AnalogTimeSignal; } namespace devices { class BaseDevice; } namespace ui { namespace widgets { namespace plot { /* * NOTE: XYCurveData must also inherit QObject (Important: first QObject, * then BaseCurvedata), to get signals/slots working! */ class XYCurveData : public BaseCurveData { Q_OBJECT public: XYCurveData(shared_ptr x_t_signal, shared_ptr y_t_signal); bool is_equal(const BaseCurveData *other) const override; QPointF sample(size_t index) const override; size_t size() const override; QRectF boundingRect() const override; QPointF closest_point(const QPointF &pos, double *dist) const override; QString name() const override; string id_prefix() const override; sv::data::Quantity x_quantity() const override; set x_quantity_flags() const override; sv::data::Unit x_unit() const override; QString x_unit_str() const override; QString x_title() const override; sv::data::Quantity y_quantity() const override; set y_quantity_flags() const override; sv::data::Unit y_unit() const override; QString y_unit_str() const override; QString y_title() const override; shared_ptr x_t_signal() const; shared_ptr y_t_signal() const; void save_settings(QSettings &settings, shared_ptr origin_device) const override; static XYCurveData *init_from_settings( Session &session, QSettings &settings, shared_ptr origin_device); private: shared_ptr x_t_signal_; shared_ptr y_t_signal_; size_t x_t_signal_pos_; size_t y_t_signal_pos_; // TODO: use some sort of AnalogSignal instead of 2 vectors? shared_ptr> x_data_; shared_ptr> y_data_; mutex sample_append_mutex_; private Q_SLOTS: void on_sample_appended(); }; } // namespace plot } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_PLOT_XYCURVEDATA_HPP ================================================ FILE: src/ui/widgets/popup.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2013 Joel Holdsworth * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "popup.hpp" using std::max; using std::min; namespace sv { namespace ui { namespace widgets { const int Popup::ArrowLength = 10; const int Popup::ArrowOverlap = 3; const int Popup::MarginWidth = 6; Popup::Popup(QWidget *parent) : QWidget(parent, Qt::Popup | Qt::FramelessWindowHint), pos_(PopupPosition::Left), mouse_pressed_(false) { } const QPoint& Popup::point() const { return point_; } PopupPosition Popup::position() const { return pos_; } void Popup::set_position(const QPoint point, PopupPosition pos) { point_ = point; pos_ = pos; setContentsMargins( MarginWidth + ((pos == PopupPosition::Right) ? ArrowLength : 0), MarginWidth + ((pos == PopupPosition::Bottom) ? ArrowLength : 0), MarginWidth + ((pos == PopupPosition::Left) ? ArrowLength : 0), MarginWidth + ((pos == PopupPosition::Top) ? ArrowLength : 0)); } bool Popup::eventFilter(QObject *obj, QEvent *event) { (void)obj; (void)event; return false; /* * Deactivaded, because when catching key_Enter and _key_Return in here, * the retunPressed() signal in AxisPopup isn't working correctly. QKeyEvent *key_event; (void)obj; if (event->type() == QEvent::KeyPress) { key_event = static_cast(event); if (key_event->key() == Qt::Key_Enter || key_event->key() == Qt::Key_Return) { close(); return true; } } return false; */ } void Popup::show() { QWidget::show(); // We want to close the popup when the Enter key was // pressed and the first editable widget had focus. QLineEdit *le = this->findChild(); if (le) { // For combo boxes we need to hook into the parent of // the line edit (i.e. the QComboBox). For edit boxes // we hook into the widget directly. if (le->parent()->metaObject()->className() == this->metaObject()->className()) le->installEventFilter(this); else le->parent()->installEventFilter(this); le->selectAll(); le->setFocus(); } } bool Popup::space_for_arrow() const { // Check if there is room for the arrow switch (pos_) { case PopupPosition::Right: return point_.x() <= x(); case PopupPosition::Bottom: return point_.y() <= y(); case PopupPosition::Left: return point_.x() >= (x() + width()); case PopupPosition::Top: return point_.y() >= (y() + height()); } return true; } QPolygon Popup::arrow_polygon() const { QPolygon poly; const QPoint widget_point = mapFromGlobal(point_); const int total_len = ArrowLength + ArrowOverlap; switch (pos_) { case PopupPosition::Right: poly << QPoint(widget_point.x() + total_len, widget_point.y() - total_len); break; case PopupPosition::Bottom: poly << QPoint(widget_point.x() - total_len, widget_point.y() + total_len); break; case PopupPosition::Left: case PopupPosition::Top: poly << QPoint(widget_point.x() - total_len, widget_point.y() - total_len); break; } poly << widget_point; switch (pos_) { case PopupPosition::Right: case PopupPosition::Bottom: poly << QPoint(widget_point.x() + total_len, widget_point.y() + total_len); break; case PopupPosition::Left: poly << QPoint(widget_point.x() - total_len, widget_point.y() + total_len); break; case PopupPosition::Top: poly << QPoint(widget_point.x() + total_len, widget_point.y() - total_len); break; } return poly; } QRegion Popup::arrow_region() const { return QRegion(arrow_polygon()); } QRect Popup::bubble_rect() const { return QRect( QPoint((pos_ == PopupPosition::Right) ? ArrowLength : 0, (pos_ == PopupPosition::Bottom) ? ArrowLength : 0), QSize( width() - ((pos_ == PopupPosition::Left || pos_ == PopupPosition::Right) ? ArrowLength : 0), height() - ((pos_ == PopupPosition::Top || pos_ == PopupPosition::Bottom) ? ArrowLength : 0))); } QRegion Popup::bubble_region() const { const QRect rect(bubble_rect()); const int radius = MarginWidth; const int diameter = 2 * radius; return QRegion(rect.adjusted(radius, 0, -radius, 0)) .united(QRegion(rect.adjusted(0, radius, 0, -radius))) .united(QRegion(rect.left(), rect.top(), diameter, diameter, QRegion::Ellipse)) .united(QRegion(rect.right() - diameter, rect.top(), diameter, diameter, QRegion::Ellipse)) .united(QRegion(rect.left(), rect.bottom() - diameter, diameter, diameter, QRegion::Ellipse)) .united(QRegion(rect.right() - diameter, rect.bottom() - diameter, diameter, diameter, QRegion::Ellipse)); } QRegion Popup::popup_region() const { if (space_for_arrow()) return arrow_region().united(bubble_region()); else // NOLINT return bubble_region(); } void Popup::reposition_widget() { QPoint new_pos; #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) QScreen *screen = QGuiApplication::screenAt(point_); if(!screen) return; const QRect screen_rect = screen->availableGeometry(); #else const QRect screen_rect = QApplication::desktop()->availableGeometry( QApplication::desktop()->screenNumber(point_)); #endif if (pos_ == PopupPosition::Right || pos_ == PopupPosition::Left) new_pos.ry() = -height() / 2; else new_pos.rx() = -width() / 2; if (pos_ == PopupPosition::Left) new_pos.rx() = -width(); else if (pos_ == PopupPosition::Top) new_pos.ry() = -height(); new_pos += point_; move(max(min(new_pos.x(), screen_rect.right() - width()), screen_rect.left()), max(min(new_pos.y(), screen_rect.bottom() - height()), screen_rect.top())); } void Popup::closeEvent(QCloseEvent *event) { (void)event; Q_EMIT closed(); } void Popup::paintEvent(QPaintEvent *event) { (void)event; QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const QColor outline_color(QApplication::palette().color( QPalette::Dark)); // Draw the bubble const QRegion bubble = bubble_region(); const QRegion bubble_outline = QRegion(rect()).subtracted( bubble.translated(1, 0).intersected(bubble.translated(0, 1).intersected( bubble.translated(-1, 0).intersected(bubble.translated(0, -1))))); painter.setPen(Qt::NoPen); painter.setBrush(QApplication::palette().brush(QPalette::Window)); painter.drawRect(rect()); // Draw the arrow if (!space_for_arrow()) return; const QPoint ArrowOffsets[] = { QPoint(1, 0), QPoint(0, -1), QPoint(-1, 0), QPoint(0, 1)}; const QRegion arrow(arrow_region()); const QRegion arrow_outline = arrow.subtracted( arrow.translated(ArrowOffsets[static_cast(pos_)])); painter.setClipRegion(bubble_outline.subtracted(arrow).united(arrow_outline)); painter.setBrush(outline_color); painter.drawRect(rect()); } void Popup::resizeEvent(QResizeEvent *event) { (void)event; reposition_widget(); setMask(popup_region()); } void Popup::mousePressEvent(QMouseEvent *event) { (void)event; mouse_pressed_ = true; } void Popup::mouseReleaseEvent(QMouseEvent *event) { assert(event); if (!mouse_pressed_) return; mouse_pressed_ = false; // We need our own out-of-bounds click handler because QWidget counts // the drop-shadow region as inside the widget if (!bubble_rect().contains(event->pos())) close(); } void Popup::showEvent(QShowEvent *event) { (void)event; reposition_widget(); } } // namespace widgets } // namespace ui } // namespace sv ================================================ FILE: src/ui/widgets/popup.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2013 Joel Holdsworth * Copyright (C) 2018-2021 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UI_WIDGETS_POPUP_HPP #define UI_WIDGETS_POPUP_HPP #include #include #include #include #include #include #include #include #include #include #include #include namespace sv { namespace ui { namespace widgets { enum class PopupPosition : int { Right, Top, Left, Bottom }; class Popup : public QWidget { Q_OBJECT private: static const int ArrowLength; static const int ArrowOverlap; static const int MarginWidth; public: explicit Popup(QWidget *parent); const QPoint &point() const; PopupPosition position() const; void set_position(const QPoint point, PopupPosition pos); bool eventFilter(QObject *obj, QEvent *event) override; void show(); private: bool space_for_arrow() const; QPolygon arrow_polygon() const; QRegion arrow_region() const; QRect bubble_rect() const; QRegion bubble_region() const; QRegion popup_region() const; void reposition_widget(); void closeEvent(QCloseEvent *event) override; void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; protected: void showEvent(QShowEvent *event) override; Q_SIGNALS: void closed(); private: QPoint point_; PopupPosition pos_; bool mouse_pressed_; }; } // namespace widgets } // namespace ui } // namespace sv #endif // UI_WIDGETS_POPUP_HPP ================================================ FILE: src/util.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * Copyright (C) 2017-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "extdef.h" #include "util.hpp" using std::fixed; using std::max; using std::ostringstream; using std::setfill; using std::setprecision; using std::showpos; using std::string; using std::vector; namespace sv { namespace util { static QTextStream &operator<<(QTextStream &stream, SIPrefix prefix) { switch (prefix) { case SIPrefix::yocto: return stream << 'y'; case SIPrefix::zepto: return stream << 'z'; case SIPrefix::atto: return stream << 'a'; case SIPrefix::femto: return stream << 'f'; case SIPrefix::pico: return stream << 'p'; case SIPrefix::nano: return stream << 'n'; case SIPrefix::micro: return stream << QChar(0x03BC); case SIPrefix::milli: return stream << 'm'; case SIPrefix::kilo: return stream << 'k'; case SIPrefix::mega: return stream << 'M'; case SIPrefix::giga: return stream << 'G'; case SIPrefix::tera: return stream << 'T'; case SIPrefix::peta: return stream << 'P'; case SIPrefix::exa: return stream << 'E'; case SIPrefix::zetta: return stream << 'Z'; case SIPrefix::yotta: return stream << 'Y'; default: return stream; } } int exponent(SIPrefix prefix) { return 3 * (static_cast(prefix) - static_cast(SIPrefix::none)); } static SIPrefix successor(SIPrefix prefix) { assert(prefix != SIPrefix::yotta); return static_cast(static_cast(prefix) + 1); } static int prefix_from_si_prefix(SIPrefix prefix) { return static_cast(prefix) - static_cast(SIPrefix::none); } static SIPrefix si_prefix_from_prefix(int prefix) { return static_cast(static_cast(SIPrefix::none) + prefix); } // Insert the timestamp value into the stream in fixed-point notation // (and honor the precision) static QTextStream &operator<<(QTextStream &stream, const Timestamp ×tamp) { // The multiprecision types already have a function and a stream insertion // operator to convert them to a string, however these functions abuse a // precision value of zero to print all available decimal places instead of // none, and the boost authors refuse to fix this because they don't want // to break buggy code that relies on this bug. // (https://svn.boost.org/trac/boost/ticket/10103) // Therefore we have to work around the case where precision is zero. int precision = stream.realNumberPrecision(); ostringstream ss; ss << fixed; if ((stream.numberFlags() & QTextStream::ForceSign) != 0) ss << showpos; if (0 == precision) ss << setprecision(1) << round(timestamp); else ss << setprecision(precision) << timestamp; string str(ss.str()); if (0 == precision) { // remove the separator and the unwanted decimal place str.resize(str.size() - 2); } return stream << QString::fromStdString(str); } int prefix_from_value(const double value, const int sr_digits) { double logval = log10(fabs(value)); int prefix = static_cast((logval / 3) - static_cast(logval < 1)); if (value == 0 || value == NAN || value == std::numeric_limits::infinity() || value >= std::numeric_limits::max() || value <= std::numeric_limits::lowest()) { prefix = prefix_from_si_prefix(SIPrefix::none); } else if (prefix < prefix_from_si_prefix(SIPrefix::yocto)) prefix = prefix_from_si_prefix(SIPrefix::yocto); else if (prefix > prefix_from_si_prefix(SIPrefix::yotta)) prefix = prefix_from_si_prefix(SIPrefix::yotta); else if (3 * prefix < -sr_digits) prefix = (-sr_digits + 2 * static_cast(sr_digits < 0)) / 3; return prefix; } int decimal_places_from_prefix(const int prefix, const int sr_digits) { int decimal_places = sr_digits + (3 * prefix); decimal_places = decimal_places < 0 ? 0 : decimal_places; return decimal_places; } void format_value_si( const double value, const int total_digits, const int sr_digits, QString &value_str, QString &si_prefix_str, const bool use_locale) { int prefix = prefix_from_value(value, sr_digits); SIPrefix si_prefix = si_prefix_from_prefix(prefix); assert(si_prefix >= SIPrefix::yocto); assert(si_prefix <= SIPrefix::yotta); int decimal_places = decimal_places_from_prefix(prefix, sr_digits); double new_value = value * pow(10, -3 * prefix); // Check if, use current locale (%L) for formating. QString format_string = use_locale ? "%L1" : "%1"; value_str = QString(format_string) .arg(new_value, total_digits, 'f', decimal_places, QChar(' ')); QTextStream si_prefix_stream(&si_prefix_str); si_prefix_stream << si_prefix; } void format_value_si_autoscale( const double value, const int total_digits, const int decimal_places, QString &value_str, QString &si_prefix_str, const bool use_locale) { SIPrefix si_prefix; if (value == 0 || value == NAN || value == std::numeric_limits::infinity() || value >= std::numeric_limits::max() || value <= std::numeric_limits::lowest()) { si_prefix = SIPrefix::none; } else { int exp = exponent(SIPrefix::yotta); si_prefix = SIPrefix::yocto; while ((fabs(value) * pow(10, exp)) > 999 && si_prefix < SIPrefix::yotta) { si_prefix = successor(si_prefix); exp -= 3; } } assert(si_prefix >= SIPrefix::yocto); assert(si_prefix <= SIPrefix::yotta); const double multiplier = pow(10, -exponent(si_prefix)); // Check if, use current locale (%L) for formating. QString format_string = use_locale ? "%L1" : "%1"; value_str = QString(format_string). arg(value * multiplier, total_digits, 'f', decimal_places, QChar(' ')); QTextStream si_prefix_stream(&si_prefix_str); si_prefix_stream << si_prefix; } QString format_time_si(const Timestamp ×tamp, SIPrefix prefix, unsigned int precision, const QString &unit, bool sign) { if (prefix == SIPrefix::unspecified) { // No prefix given, calculate it if (timestamp.is_zero()) { prefix = SIPrefix::none; } else { int exp = exponent(SIPrefix::yotta); prefix = SIPrefix::yocto; while ((fabs(timestamp) * pow(Timestamp(10), exp)) > 999 && prefix < SIPrefix::yotta) { prefix = successor(prefix); exp -= 3; } } } assert(prefix >= SIPrefix::yocto); assert(prefix <= SIPrefix::yotta); const Timestamp multiplier = pow(Timestamp(10), -exponent(prefix)); QString str; QTextStream ts(&str); if (sign && !timestamp.is_zero()) ts.setNumberFlags(ts.numberFlags() | QTextStream::ForceSign); ts << qSetRealNumberPrecision((int)precision) << (timestamp * multiplier) << ' ' << prefix << unit; return str; } QString format_time_si_adjusted(const Timestamp ×tamp, SIPrefix prefix, unsigned precision, const QString &unit, bool sign) { // The precision is always given without taking the prefix into account // so we need to deduct the number of decimals the prefix might imply const int prefix_order = -exponent(prefix); const unsigned int relative_prec = (prefix >= SIPrefix::none) ? precision : max((int)(precision - prefix_order), 0); return format_time_si(timestamp, prefix, relative_prec, unit, sign); } // Helper for 'format_time_minutes()'. static QString pad_number(unsigned int number, int length) { return QString("%1").arg(number, length, 10, QChar('0')); } QString format_time_minutes(const Timestamp ×tamp, signed precision, bool sign) { const Timestamp whole_seconds = floor(abs(timestamp)); const Timestamp days = floor(whole_seconds / (60 * 60 * 24)); const unsigned int hours = fmod(whole_seconds / (60 * 60), 24).convert_to(); const unsigned int minutes = fmod(whole_seconds / 60, 60).convert_to(); const unsigned int seconds = fmod(whole_seconds, 60).convert_to(); QString str; QTextStream ts(&str); if (timestamp < 0) ts << "-"; else if (sign) ts << "+"; bool use_padding = false; // DD if (days) { ts << days.str().c_str() << ":"; use_padding = true; } // HH if (hours || days) { ts << pad_number(hours, use_padding ? 2 : 0) << ":"; use_padding = true; } // MM ts << pad_number(minutes, use_padding ? 2 : 0); ts << ":"; // SS ts << pad_number(seconds, 2); if (precision) { ts << "."; const Timestamp fraction = fabs(timestamp) - whole_seconds; ostringstream ss; ss << fixed << setprecision(precision) << setfill('0') << fraction; string fs = ss.str(); // Copy all digits, inserting spaces as unit separators for (int i = 1; i <= precision; i++) { // Start at index 2 to skip the "0." at the beginning ts << fs.at(1 + i); if ((i > 0) && (i % 3 == 0) && (i != precision)) ts << " "; } } return str; } QString format_time_date(double timestamp) { QDateTime date; date.setMSecsSinceEpoch(static_cast(timestamp * 1000)); return date.toString("yyyy.MM.dd hh:mm:ss.zzz"); } string format_uuid(QUuid uuid) { #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) return uuid.toString(QUuid::WithoutBraces).toStdString(); #else return uuid.toString().replace("{", "").replace("}", "").toStdString(); #endif } vector split_string(string text, const string &separator) { vector result; size_t pos; while ((pos = text.find(separator)) != std::string::npos) { result.push_back(text.substr(0, pos)); text = text.substr(pos + separator.length()); } result.push_back(text); return result; } bool starts_with(const string &str, const string &start_str) { return start_str.length() <= str.length() && std::equal(start_str.begin(), start_str.end(), str.begin()); } int count_int_digits(int number) { if (number == 0) return 0; int abs_number = abs(number); int digits = 1; while (abs_number >= 10) { abs_number /= 10; digits++; } return digits; } int count_double_digits(double max_value, double step) { int count_int = util::count_int_digits((int)floor(max_value)); int count_frac = util::count_decimal_places(fmod(max_value, 1.0)); int count_step = util::count_decimal_places(step); return count_int + (count_frac > count_step ? count_frac : count_step); } int count_decimal_places(double step) { double frac_part = fmod(step, 1.0); if (frac_part == 0) return 0; std::stringstream stream; stream << frac_part; std::string frac_str = stream.str(); // Check for exponential notation size_t e_pos = frac_str.find('e'); if (e_pos != std::string::npos) { std::string exponent_str = frac_str.substr(e_pos + 1); return -(std::stoi(exponent_str)); } // Check for the decimal point size_t point_pos = frac_str.find('.'); if (point_pos != std::string::npos) { return static_cast(frac_str.length() - point_pos - 1); } return 0; /* * Old implementation, didn't worked for e.g. `0.111` double frac_part = fmod(step, 1.0); if (frac_part == 0) return 0; int decimal = (int)floor(1/frac_part) - 1; return util::count_int_digits(decimal); */ } int get_sr_digits(double step) { if (step == 0) return 0; int count_frac = util::count_decimal_places(fmod(step, 1.0)); if (count_frac > 0) return count_frac; // Count the zeros at the end of the integer part std::stringstream stream; stream << static_cast(floor(step)); std::string int_str = stream.str(); int int_count = 0; size_t int_str_pos = int_str.length(); while (int_str_pos > 0) { int_str_pos--; if (int_str[int_str_pos] != '0') break; int_count++; } return -int_count; } /* * Based on https://stackoverflow.com/a/30338543 */ vector parse_csv_line(const string &line) { enum State { UnquotedField, QuotedField, QuotedQuote } state = UnquotedField; std::vector fields{""}; size_t index = 0; // index of the current field for (char chr : line) { switch (state) { case State::UnquotedField: switch (chr) { case ',': // end of field fields.push_back(""); index++; break; case '"': state = State::QuotedField; break; default: fields[index].push_back(chr); break; } break; case State::QuotedField: switch (chr) { case '"': state = State::QuotedQuote; break; default: fields[index].push_back(chr); break; } break; case State::QuotedQuote: switch (chr) { case ',': // , after closing quote fields.push_back(""); index++; state = State::UnquotedField; break; case '"': // "" -> " fields[index].push_back('"'); state = State::QuotedField; break; default: // end of quote state = State::UnquotedField; break; } break; } } return fields; } } // namespace util } // namespace sv ================================================ FILE: src/util.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * Copyright (C) 2017-2022 Frank Stettner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UTIL_HPP #define UTIL_HPP #include #include #include #include #include #ifndef Q_MOC_RUN #include #include #endif #include #include #include #include using std::map; using std::set; using std::string; using std::vector; namespace sv { namespace util { enum class TimeUnit { Time = 1, Samples = 2 }; enum class SIPrefix { unspecified = -1, yocto, zepto, atto, femto, pico, nano, micro, milli, none, kilo, mega, giga, tera, peta, exa, zetta, yotta }; /// Returns the exponent that corresponds to a given prefix. int exponent(SIPrefix prefix); /// Timestamp type providing yoctosecond resolution. typedef boost::multiprecision::number< boost::multiprecision::cpp_dec_float<24>, boost::multiprecision::et_off> Timestamp; /** * Returns the SI prefix as `int` based on the `value` and the sigrok digits. * * @param value The value. * @param sr_digits The digits from the sigrok analog payload. Something like * exponent with reversed polarity. * * TODO: move to data */ int prefix_from_value(const double value, const int sr_digits); /** * Returns the number of decimal places based on the `prefix` and the sigrok digits. * * @param prefix The SI prefix as an `int`. * @param sr_digits The digits from the sigrok analog payload. Something like * exponent with reversed polarity. * * TODO: move to data */ int decimal_places_from_prefix(const int prefix, const int sr_digits); /** * Formats and rescales a given double value and stores the results in * `value_str` and `si_prefix`. * * @param value The value to format. * @param total_digits The number of total digits (incl. the decimal places) * for `value_str`. This is directly passed to * `QString.arg()` as `fieldWidth`. * @param sr_digits The digits from the sigrok analog payload. Something like * exponent with reversed polarity. * @param value_str A reference to a `QString` to stre the digits to. * @param si_prefix A reference to a `QString` to store the SI prefix to. * @param use_locale Format `value_str` by using the locale settings, otherwise * the "C" locale will be used. * * TODO: move to data */ void format_value_si( const double value, const int total_digits, const int sr_digits, QString &value_str, QString &si_prefix_str, const bool use_locale = true); void format_value_si_autoscale( const double value, const int total_digits, const int decimal_places, QString &value_str, QString &si_prefix_str, const bool use_locale = true); /** * Formats a given timestamp with the specified SI prefix. * * If 'prefix' is left 'unspecified', the function chooses a prefix so that * the value in front of the decimal point is between 1 and 999. * * The default value "s" for the unit argument makes the most sense when * formatting time values, but a different value can be given if the function * is reused to format a value of another quantity. * * @param timestamp The value to format. * @param prefix The SI prefix to use. * @param precision The number of digits after the decimal separator. * @param unit The unit of quantity. * @param sign Whether or not to add a sign also for positive numbers. * * @return The formatted value. * * TODO: move to data */ QString format_time_si(const Timestamp& timestamp, SIPrefix prefix = SIPrefix::unspecified, unsigned precision = 0, const QString &unit = "s", bool sign = true); /** * Wrapper around 'format_time_si()' that interprets the given 'precision' * value as the number of decimal places if the timestamp would be formatted * without a SI prefix (using 'SIPrefix::none') and adjusts the precision to * match the given 'prefix' * * @param timestamp The value to format. * @param prefix The SI prefix to use. * @param precision The number of digits after the decimal separator if the * 'prefix' would be 'SIPrefix::none', see above for more information. * @param unit The unit of quantity. * @param sign Whether or not to add a sign also for positive numbers. * * @return The formatted value. * * TODO: move to data */ QString format_time_si_adjusted(const Timestamp& timestamp, SIPrefix prefix, unsigned precision = 0, const QString &unit = "s", bool sign = true); /** * Formats the given timestamp using "[+-]DD:HH:MM:SS.mmm uuu nnn ppp..." format. * * "DD" and "HH" are left out if they would be "00" (but if "DD" is generated, * "HH" is also always generated. The "MM:SS" part is always produced, the * number of subsecond digits can be influenced using the 'precision' parameter. * * @param timestamp The value to format. * @param precision The number of digits after the decimal separator. * @param sign Whether or not to add a sign also for positive numbers. * * @return The formatted value. * * TODO: move to data */ QString format_time_minutes(const Timestamp& timestamp, signed precision = 0, bool sign = true); /** * Formats the given timestamp as a date using * "yyyy.MM.dd hh:mm:ss.zzz" QDateTime.toString() format. * * The number of subsecond digits can be influenced using the * 'precision' parameter. * * @param timestamp The value to format. * @param precision The number of digits after the decimal separator. * * @return The formatted date. * * TODO: move to data */ QString format_time_date(double timestamp); /** * Format the given UUID as a string without braches. * * @param uuid The UUID to format. * * @return The formated UUID. */ string format_uuid(QUuid uuid); /** * Split a string into tokens at occurences of the separator. * * @param[in] text The input string to split. * @param[in] separator The delimiter between tokens. * * @return A vector of broken down tokens. */ vector split_string(string text, const string &separator); /** * Check if a string 'str' starts with the string 'start_str'. * * @param[in] str The string to check. * @param[in] start_str The start string. * * @return True if string str starts with string start_str. */ bool starts_with(const string &str, const string &start_str); /** * Counts the number of digits for the given integer. * * @param[in] int The integers digits to count. * * @return Number of total digits. */ int count_int_digits(int number); /** * Get the number of digits for the given double. * * @param[in] value The value of the double. * @param[in] step Step size of the double. * * @return Number of total digits */ int count_double_digits(double value, double step); /** * Count the number of decimal places (number of digits after the decimal point) * * @param[in] step The step size from which to calculate the decimal places * * @return Number of decimal places */ int count_decimal_places(double step); /** * Get the sr_digits as used in the analog payload from the step size. * * @param[in] step The step size from which to calculate the decimal places * * @return The sr_digits */ int get_sr_digits(double step); /** * Parse a single CSV line. * Based on https://stackoverflow.com/a/30338543 * * @param[in] line The CSV line to parse. * * @return A vector of the values. */ vector parse_csv_line(const string &line); } // namespace util } // namespace sv Q_DECLARE_METATYPE(sv::util::Timestamp) #endif // UTIL_HPP ================================================ FILE: stuff/gstreamer.txt ================================================ Docs ==== - https://code.launchpad.net/~richwareham/firtree/gstreamer-integration Pipeline editors ================ - https://github.com/virinext/pipeviz - https://github.com/emdash/gst-editor - https://github.com/metratec/gst-editor - https://code.google.com/archive/p/gst-editor/ - https://code.launchpad.net/firtree - https://code.launchpad.net/~richwareham/firtree/pipeline-editor - https://code.launchpad.net/~richwareham/firtree/gstreamer-integration Node editors ============ - https://github.com/paceholder/nodeeditor ================================================ FILE: stuff/linuxgpib_build.txt ================================================ https://xdevs.com/guide/agilent_gpib_rpi/ https://gist.github.com/turingbirds/6eb05c9267a6437183a9567700e8581a https://www.linuxquestions.org/questions/linux-software-2/how-to-enable-agilent-82357a-usb-gpib-dongle-and-remain-sane-4175498814/ https://www.linuxquestions.org/questions/linux-software-2/how-to-enable-hp-82357b-usb-gpib-dongle-and-remain-sane-4175485218/ My Way (Debian Testing, Clone Agilent 82357B): ============================================== After each Kernel update: - Install Kernel Headers (linux-headers-4.XX.X-X-all) - Install fxload (fxload) - Download last linux-gpib (https://sourceforge.net/projects/linux-gpib/files/) - Download last firmware (http://linux-gpib.sourceforge.net/firmware/) Since linux-gpib 4.2.0 there are two seperated builds (kernel modules + user space programms) # tar -xvzf linux-gpib-4.2.0.tar.gz # cd linux-gpib-4.2.0 # tar -xvzf linux-gpib-kernel-4.2.0.tar.gz # cd linux-gpib-kernel-4.2.0 # ./configure # No longer needed? # make # sudo make install # sudo ldconfig # cd .. # tar -xvzf linux-gpib-user-4.2.0.tar.gz # cd linux-gpib-user-4.2.0 # ./configure --sysconfdir=/etc # make # sudo make install - Copy firmware to /usr/local/share/usb (old: /usr/share/usb) (e.g. agilent_82357a -> /usr/local/share/usb) as root (see udev rules in /etc/udev/rules.d which scripts/directories are used!) - Edit file /etc/gpib.conf: /*********************************************************************** GPIB.CONF IEEE488 library config file ------------------- copyright : (C) 2002 by Frank Mori Hess (C) 1994 by C.Schroeter email : fmhess@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * Syntax: * * interface { ... } starts new interface board section * device {...} device configuration * ***************************************************************************/ /* This section configures the configurable driver characteristics * for an interface board, such as board address, and interrupt level. * minor = 0 configures /dev/gpib0, minor = 1 configures /dev/gpib1, etc. */ interface { minor = 0 /* board index, minor = 0 uses /dev/gpib0, minor = 1 uses /dev/gpib1, etc. */ board_type = "agilent_82357a" /* type of interface board being used */ name = "agi" /* optional name, allows you to get a board descriptor using ibfind() */ pad = 0 /* primary address of interface */ sad = 0 /* secondary address of interface */ timeout = T3s /* timeout for commands */ eos = 0x0a /* EOS Byte, 0xa is newline and 0xd is carriage return */ set-reos = yes /* Terminate read if EOS */ set-bin = no /* Compare EOS 8-bit */ set-xeos = no /* Assert EOI whenever EOS byte is sent */ set-eot = yes /* Assert EOI with last byte on writes */ /* settings for boards that lack plug-n-play capability */ base = 0 /* Base io ADDRESS */ irq = 0 /* Interrupt request level */ dma = 0 /* DMA channel (zero disables) */ /* pci_bus and pci_slot can be used to distinguish two pci boards supported by the same driver */ /* pci_bus = 0 */ /* pci_slot = 7 */ master = yes /* interface board is system controller */ } /* This is how you might set up a pcIIa board on /dev/gpib1, uncomment to use. */ /******************* interface { minor = 1 board_type = "pcIIa" pad = 0 sad = 0 timeout = T3s eos = 0x0a set-reos = yes set-bin = no base = 0x2e1 irq = 7 dma = 1 master = yes } *********************/ /* Now the device sections define the device characteristics for each device. * These are only used if you want to open the device using ibfind() (instead * of ibdev() ) */ device { minor = 0 /* minor number for interface board this device is connected to */ name = "hp3478a" /* device mnemonic */ pad = 24 /* The Primary Address */ sad = 0 /* Secondary Address */ eos = 0xa /* EOS Byte */ set-reos = no /* Terminate read if EOS */ set-bin = no /* Compare EOS 8-bit */ } device { minor = 0 /* minor number for interface board this device is connected to */ name = "voltmeter" /* device mnemonic */ pad = 7 /* The Primary Address */ sad = 0 /* Secondary Address */ eos = 0xa /* EOS Byte */ set-reos = no /* Terminate read if EOS */ set-bin = no /* Compare EOS 8-bit */ } device { minor = 0 name = "scope" pad = 8 sad = 0 } - add users to group "gpib" (/etc/group) - load config: # sudo gpib_config CHECK: ====== # lsmod agilent_82357a 24576 0 gpib_common 36864 1 agilent_82357a # dmesg Linux-GPIB 4.1.0 Driver agilent_82357a_gpib driver loading usbcore: registered new interface driver agilent_82357a_gpib gpib: registered agilent_82357a interface # ls -l /dev/gpib* crw-rw---- 1 root gpib 160, 1 Okt 31 17:25 gpib0 ... crw-rw---- 1 root gpib 160, 9 Okt 31 17:25 gpib9 # ibtest -d 24 ================================================ FILE: stuff/loads.txt ================================================ Re:load Pro =========== static const uint32_t drvopts[] = { SR_CONF_ELECTRONIC_LOAD, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, }; static const uint32_t devopts_cg[] = { SR_CONF_ENABLED | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET, SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; M97 === static const uint32_t drvopts[] = { SR_CONF_ELECTRONIC_LOAD, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, }; static const uint32_t devopts_cg[] = { SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET, SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET, }; General ======= SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, SR_CONF_ENABLED | SR_CONF_SET | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET | SR_CONF_SET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET, SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION_ACTIVE | SR_CONF_GET, SR_CONF_UNDER_VOLTAGE_CONDITION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, ================================================ FILE: stuff/mxe.txt ================================================ make MXE_TARGETS=i686-w64-mingw32.static.posix gcc glib libzip libusb1 libftdi1 glibmm qt5 boost check git clone https://github.com/mxe/mxe.git mxe-git cd mxe-git patch -p1 < mxe_fixes.patch ls -l patch -p1 < ~/Projekte/elektronik/sigrok/sigrok-util/cross-compile/mingw/mxe_fixes.patch make MXE_TARGETS=i686-w64-mingw32.static.posix gcc glib libzip libusb1 libftdi1 glibmm qt5 boost check make MXE_TARGETS=i686-w64-mingw32.static.posix \ MXE_PLUGIN_DIRS=plugins/examples/qt5-freeze \ gcc glib libzip libusb1 libftdi1 glibmm qtbase qtimageformats qtsvg \ boost check gendef qtbase_CONFIGURE_OPTS='-no-sql-mysql' git clone https://github.com/mxe/mxe.git mxe-git-qt5_10 cd mxe-git-qt5_10 patch -p1 < ~/Projekte/elektronik/sigrok/sigrok-util/cross-compile/mingw/mxe_fixes.patch make MXE_TARGETS=i686-w64-mingw32.static.posix gcc glib libzip libusb1 libftdi1 glibmm qt5 qwt boost check gendef make MXE_TARGETS=i686-w64-mingw32.static.posix gcc glib libzip libusb1 libftdi1 glibmm qt5 qwt boost check gendef MXE_CONFIGURE_OPTS='--with-default-msvcrt=msvcr100' mingw-w64_SOURCE_TREE=/home/frank/mingw-w64 git clone https://github.com/mxe/mxe.git mxe-git-std cd mxe-git-std patch -p1 < ~/Projekte/elektronik/sigrok/sigrok-util/cross-compile/mingw/mxe_fixes.patch make MXE_TARGETS=i686-w64-mingw32.static.posix MXE_PLUGIN_DIRS=plugins/examples/qt5-freeze gcc glib libzip libusb1 libftdi1 glibmm qtbase qtimageformats qtsvg qwt boost check gendef qtbase_CONFIGURE_OPTS='-no-sql-mysql' make MXE_TARGETS=i686-w64-mingw32.static.posix MXE_PLUGIN_DIRS=plugins/examples/qt5-freeze gcc glib libzip libusb1 libftdi1 glibmm qtbase qtimageformats qtsvg qwt boost check gendef qtbase_CONFIGURE_OPTS='-no-sql-mysql' mingw-w64_SOURCE_TREE=/home/frank/mingw-w64 mingw-w64_CONFIGURE_OPTS='--with-default-msvcrt=msvcr100' ================================================ FILE: stuff/parameter.txt ================================================ --driver peaktech-4390a:conn=/dev/ttyUSB2 -l 5 --driver hp-3478a:conn=libgpib/hp3478a -l 5 --driver eevblog-121gw:conn=bt/ble122/88-6B-0F-81-AA-C3 --driver arachnid-labs-re-load-pro:conn=/dev/ttyUSB0 -l 5 --driver conrad-digi-35-cpu:conn=/dev/ttyUSB1 -l 5 --driver scpi-pps:conn=libgpib/hp6632b -l 5 --driver hp-59306a:conn=libgpib/hp59306a -l 5 --driver icstation:conn=/dev/ttyUSB0 -l 5 --driver zketech-ebd-usb:conn=/dev/ttyUSB0 -l 5 --driver conrad-digi-35-cpu:conn=/dev/ttyUSB2 --driver arachnid-labs-re-load-pro:conn=/dev/ttyUSB0 -l 5 --driver conrad-digi-35-cpu:conn=/dev/ttyUSB2 --driver peaktech-4390a:conn=/dev/ttyUSB1 -l 5 --driver conrad-digi-35-cpu:conn=/dev/ttyUSB2 --driver hp-3478a:conn=libgpib/hp3478a -l 5 --driver arachnid-labs-re-load-pro:conn=/dev/ttyUSB0 --driver hp-3478a:conn=libgpib/hp3478a -l 5 ================================================ FILE: stuff/pps.txt ================================================ conrad-digi-35-cpu ================== static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const uint32_t devopts[] = { SR_CONF_VOLTAGE_TARGET | SR_CONF_SET, SR_CONF_CURRENT_LIMIT | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_SET, }; korad-kaxxxxp ============= static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, }; manson-hcs-3xxx =============== static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, }; motech-lps-30x ============== static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; /** Hardware capabilities channel 1, 2. */ static const uint32_t devopts_cg_ch12[] = { SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, }; /** Hardware capabilities channel 3 (LPS-304/305 only). */ static const uint32_t devopts_cg_ch3[] = { SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, }; atten-pps3xxx ============= static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const uint32_t devopts[] = { SR_CONF_CONTINUOUS, SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, }; static const uint32_t devopts_cg[] = { SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, }; scpi-pps ======== static const uint32_t drvopts[] = { SR_CONF_POWER_SUPPLY, }; static const struct pps_channel_instance pci[] = { { SR_MQ_VOLTAGE, SCPI_CMD_GET_MEAS_VOLTAGE, "V" }, { SR_MQ_CURRENT, SCPI_CMD_GET_MEAS_CURRENT, "I" }, { SR_MQ_POWER, SCPI_CMD_GET_MEAS_POWER, "P" }, { SR_MQ_FREQUENCY, SCPI_CMD_GET_MEAS_FREQUENCY, "F" }, }; SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET, SR_CONF_OUTPUT_FREQUENCY_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET TODO: ===== - atten-pps3xxx missing SR_CONF_LIMIT_SAMPLES and SR_CONF_LIMIT_MSEC General ======= SR_CONF_CONTINUOUS, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, SR_CONF_CHANNEL_CONFIG | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_VOLTAGE | SR_CONF_GET, SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CURRENT | SR_CONF_GET, SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET, SR_CONF_OUTPUT_FREQUENCY_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_REGULATION | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET, SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET | SR_CONF_SET, SCPI-PPS ======== Agilent N5763A -------------- Multi Commands: Agilent-N5700.pdf -> p.59 Chroma 61604 ------------ Multi Commands: Chroma-61500.pdf -> p.8-4 Chroma 62000 series ------------------- Multi Commands: Chroma-62000H.pdf -> chpt 5.3.5.8 HP 6633A -------- Multi Commands: NOT working! Returns only last requested value! HP 6632B -------- Multi Commands: Working! Return: ret1;ret2 Rigol DP800 series ------------------ (DP821A, DP831A, DP832, DP832A) Multi Commands: Rigol-DP800_Prog.pdf -> ??? nothing found Philips/Fluke PM2800 series --------------------------- Multi Commands: Philips-PM2800_Ref.pdf -> p.1-68, p.1-84 Rohde & Schwarz HMC8043 ----------------------- Multi Commands: ================================================ FILE: stuff/python.txt ================================================ C++, Python, boost::python, ... =============================== Introduction ------------ Passing C++ class instances to python ------------------------------------- https://stackoverflow.com/questions/5055443/boost-python-how-to-pass-a-c-class-instance-to-a-python-class https://stackoverflow.com/questions/8225934/exposing-a-c-class-instance-to-a-python-embedded-interpreter https://stackoverflow.com/questions/9062152/pass-c-object-to-python-function-by-boostpython Examples -------- https://github.com/TNG/boost-python-examples/tree/master/10-Embedding https://github.com/wynemo/boost-python-embed std::shard_ptr -------------- https://stackoverflow.com/questions/13986581/using-boost-python-stdshared-ptr https://wiki.python.org/moin/boost.python/PointersAndSmartPointers https://www.boost.org/doc/libs/1_45_0/libs/python/doc/v2/register_ptr_to_python.html http://yyc.solvcon.net/writing/2016/resource/ https://codereview.stackexchange.com/questions/130044/same-pyobject-for-a-shared-ptr-c-object ================================================ FILE: stuff/relays/relays.txt ================================================ Finder ====== 55.34.9.012.5040 (AgNi + Au plated) 55.34.9.006.0040 (AgNi) A 7A medium power relay with AgNi contacts has a minimum switching specification of 5 V / 5 mA / 300 mW. This relay is also available with gold plated contacts; when the revised values become 5 V / 2 mA / 50 mW. If a much lower voltage must be reliably switched, consider two contacts in parallel. This dramatically lowers the minimum switching load - two parallel gold contacts make it possible to handle loads down to 0.1 V / 1 mA / 1 mW. It may be useful to appreciate that statistically the unreliability of two contacts in parallel is equal to the unreliability of the single contact raised to the power of two. So, just to illustrate the maths, a 1% unreliable switching circuit becomes 0.01% unreliable - i.e. a 100x improvement in reliability. And for three contacts in parallel the unreliability would be raised to the power of three – a 10,000x improvement in reliability! (https://www.findernet.com/en/unitedkingdom/news/relay-contact-materials-does-it-matter) Minimum switching load: The minimum values of power, voltage and current that a contact can reliably switch. For example, if minimum values are 300 mW, 5 V / 5 mA: - with 5 V the current must be at least 60 mA; - with 24 V the current must be at least 12.5 mA; - with 5 mA the voltage must be at least 60 V; For gold contact variants, loads no less than 50 mW, 5 V / 2 mA are suggested. With 2 gold contacts in parallel, it is possible to switch 1 mW, 0.1 V / 1 mA. (https://www.finder-relais.net/en/Finder-general-technical-information-en.pdf) ================================================ FILE: stuff/release.txt ================================================ Prepare a virtual machine (Ubuntuy Xenial 16.04 LTS) for the AppImage build process =================================================================================== Install a Ubuntu 16.04.6 64bit or 32bit Server VM with hostname ubuntu[32|64]. Then update and install some base packets: $ sudo apt update && sudo apt -y upgrade $ sudo apt -y install gpm vim apt-file After the base installation take a snapshot of the VM. --------------- Install packages for libserialport: $ sudo apt -y install git wget unzip gcc make autoconf automake libtool Install packages for libsigrok: $ sudo apt -y install g++ autoconf-archive pkg-config libglib2.0-dev libglibmm-2.4-dev libzip-dev libusb-1.0-0-dev libftdi1-dev libhidapi-dev libbluetooth-dev libvisa-dev nettle-dev libavahi-client-dev libieee1284-3-dev check doxygen python3-numpy python3-dev python-gi-dev python3-setuptools swig Set python3 as the default python version $ sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 $ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2 $ sudo update-alternatives --set python /usr/bin/python3 Install packages for smuview: $ sudo apt -y install libboost-all-dev For QCodeEditor and (modified) pybind11 you need cmake >= 3.6: $ wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - $ sudo apt-add-repository 'deb https://apt.kitware.com/ubuntu/ xenial main' $ sudo apt update $ sudo apt -y install kitware-archive-keyring $ sudo apt-key --keyring /etc/apt/trusted.gpg del C1F34CDD40CD72DA $ sudo apt -y install cmake Install Qt 5.12.9 (LTS) (SmuView needs Qt >= 5.7 but Ubuntu 16.04 comes with 5.5) $ sudo add-apt-repository -y ppa:beineri/opt-qt-5.12.9-xenial $ sudo apt update $ sudo apt -y install qt512base qt512-meta-full $ source /opt/qt512/bin/qt512-env.sh Install newest Qwt 6.1.4: $ sudo apt -y install mesa-common-dev libgl1-mesa-dev $ wget https://sourceforge.net/projects/qwt/files/qwt/6.1.4/qwt-6.1.4.tar.bz2 $ tar xf qwt-6.1.4.tar.bz2 $ cd qwt-6.1.4 $ qmake qwt.pro $ make Now change the QWT_INSTALL_PREFIX in qwtconfig.pri to /usr and then install: $ sudo make install Install shellcheck for i686 (The x86_64 appimagecraft AppImage comes with an actual shellcheck, but i686 not). Only v0.5.0 builds with Ubuntu 16.04: $ sudo apt -y install cabal-install $ sudo cabal update $ wget https://github.com/koalaman/shellcheck/archive/v0.5.0.tar.gz $ tar xf v0.5.0.tar.gz $ cd shellcheck-0.5.0 $ sudo cabal install --prefix=/usr After the advanced installation take a snapshot of the VM. Build the SmuView release ========================= $ source /opt/qt512/bin/qt512-env.sh $ sudo ldconfig $ cd $ wget https://github.com/TheAssassin/appimagecraft/releases/download/continuous/appimagecraft-x86_64.AppImage $ chmod ug+x appimagecraft-x86_64.AppImage or $ wget https://github.com/TheAssassin/appimagecraft/releases/download/continuous/appimagecraft-i386.AppImage $ chmod ug+x appimagecraft-i386.AppImage $ git clone https://github.com/knarfS/smuview.git $ cd smuview $ ../appimagecraft-x86_64.AppImage genscripts -d build/ # TODO: Single command $ cd build # TODO $ ./build.sh # TODO --------------- The alternative to linuxdeployqt is the sigrok AppImageKit script (not tested in a long time!): $ cd $ sudo apt-get -y install desktop-file-utils libcairo2-dev libfuse-dev zsync $ git clone --single-branch --recursive https://github.com/AppImage/AppImageKit $ cd AppImageKit $ sudo apt -y install zsync git libarchive-dev autoconf libtool make gcc g++ libtool libfuse-dev liblzma-dev libglib2.0-dev libssl-dev libinotifytools0-dev liblz4-dev libcairo-dev desktop-file-utils $ patch -p1 < ../sigrok-util/cross-compile/appimage/chdir.patch $ ./build.sh $ mkdir -p build/lib/appimagekit $ cp build/out/mksquashfs build/lib/appimagekit Misc ==== To debug the SmuView AppImage: $ export QT_DEBUG_PLUGINS=1 $ LD_DEBUG=libs ./SmuView.AppImage ================================================ FILE: stuff/scpi-use.txt ================================================ static int parse_strict_bool(const char *str, gboolean *ret) static struct sr_dev_inst *sr_scpi_scan_resource(struct drv_context *drvc, const char *resource, const char *serialcomm, struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)) SR_PRIV GSList *sr_scpi_scan(struct drv_context *drvc, GSList *options, struct sr_dev_inst *(*probe_device)(struct sr_scpi_dev_inst *scpi)) hardware/rohde-schwarz-sme-0x/api.c hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/api.c SR_PRIV struct sr_scpi_dev_inst *scpi_dev_inst_new(struct drv_context *drvc, const char *resource, const char *serialcomm) SR_PRIV int sr_scpi_open(struct sr_scpi_dev_inst *scpi) hardware/rohde-schwarz-sme-0x/api.c hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/api.c SR_PRIV int sr_scpi_source_add(struct sr_session *session, struct sr_scpi_dev_inst *scpi, int events, int timeout, sr_receive_data_callback cb, void *cb_data) hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/api.c SR_PRIV int sr_scpi_source_remove(struct sr_session *session, struct sr_scpi_dev_inst *scpi) hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/yokogawa-dlm/api.c SR_PRIV int sr_scpi_send(struct sr_scpi_dev_inst *scpi, const char *format, ...) scpi/helpers.c hardware/rohde-schwarz-sme-0x/protocol.c hardware/rigol-ds/protocol.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/protocol.c hardware/lecroy-xstream/api.c hardware/gwinstek-gds-800/protocol.c hardware/hp-3457a/protocol.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/protocol_wrappers.c SR_PRIV int sr_scpi_send_variadic(struct sr_scpi_dev_inst *scpi, const char *format, va_list args) scpi/helpers.c hardware/rigol-ds/protocol.c SR_PRIV int sr_scpi_read_begin(struct sr_scpi_dev_inst *scpi) hardware/rigol-ds/protocol.c hardware/gwinstek-gds-800/protocol.c hardware/yokogawa-dlm/protocol.c SR_PRIV int sr_scpi_read_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) hardware/rigol-ds/protocol.c hardware/lecroy-xstream/protocol.c hardware/gwinstek-gds-800/protocol.c hardware/yokogawa-dlm/protocol.c SR_PRIV int sr_scpi_write_data(struct sr_scpi_dev_inst *scpi, char *buf, int maxlen) SR_PRIV int sr_scpi_read_complete(struct sr_scpi_dev_inst *scpi) hardware/rigol-ds/protocol.c hardware/gwinstek-gds-800/protocol.c hardware/yokogawa-dlm/protocol.c SR_PRIV int sr_scpi_close(struct sr_scpi_dev_inst *scpi) hardware/rohde-schwarz-sme-0x/api.c hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/api.c SR_PRIV void sr_scpi_free(struct sr_scpi_dev_inst *scpi) std.c SR_PRIV int sr_scpi_get_string(struct sr_scpi_dev_inst *scpi, const char *command, char **scpi_response) scpi/helpers.c hardware/rigol-ds/protocol.c hardware/hameg-hmo/protocol.c hardware/lecroy-xstream/protocol.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/protocol.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/protocol_wrappers.c SR_PRIV int sr_scpi_read_response(struct sr_scpi_dev_inst *scpi, GString *response, gint64 abs_timeout_us) SR_PRIV int sr_scpi_get_data(struct sr_scpi_dev_inst *scpi, const char *command, GString **scpi_response) SR_PRIV int sr_scpi_get_bool(struct sr_scpi_dev_inst *scpi, const char *command, gboolean *scpi_response) hardware/rigol-ds/protocol.c hardware/hameg-hmo/protocol.c hardware/lecroy-xstream/protocol.c hardware/yokogawa-dlm/protocol_wrappers.c SR_PRIV int sr_scpi_get_int(struct sr_scpi_dev_inst *scpi, const char *command, int *scpi_response) hardware/rigol-ds/protocol.c hardware/hameg-hmo/protocol.c hardware/yokogawa-dlm/protocol_wrappers.c SR_PRIV int sr_scpi_get_float(struct sr_scpi_dev_inst *scpi, const char *command, float *scpi_response) hardware/rigol-ds/protocol.c hardware/hameg-hmo/protocol.c hardware/lecroy-xstream/protocol.c hardware/hp-3457a/protocol.c hardware/hp-3457a/api.c hardware/yokogawa-dlm/protocol_wrappers.c SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi, const char *command, double *scpi_response) scpi/helpers.c hardware/rohde-schwarz-sme-0x/protocol.c hardware/lecroy-xstream/protocol.c hardware/scpi-pps/protocol.c hardware/scpi-pps/api.c hardware/hp-3457a/protocol.c SR_PRIV int sr_scpi_get_opc(struct sr_scpi_dev_inst *scpi) hardware/rigol-ds/protocol.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/protocol.c hardware/lecroy-xstream/api.c hardware/yokogawa-dlm/api.c SR_PRIV int sr_scpi_get_floatv(struct sr_scpi_dev_inst *scpi, const char *command, GArray **scpi_response) hardware/hp-3457a/api.c SR_PRIV int sr_scpi_get_uint8v(struct sr_scpi_dev_inst *scpi, const char *command, GArray **scpi_response) SR_PRIV int sr_scpi_get_block(struct sr_scpi_dev_inst *scpi, const char *command, GByteArray **scpi_response) hardware/hameg-hmo/protocol.c hardware/lecroy-xstream/protocol.c SR_PRIV int sr_scpi_get_hw_id(struct sr_scpi_dev_inst *scpi, struct sr_scpi_hw_info **scpi_response) hardware/rohde-schwarz-sme-0x/api.c hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/yokogawa-dlm/api.c SR_PRIV void sr_scpi_hw_info_free(struct sr_scpi_hw_info *hw_info) hardware/rohde-schwarz-sme-0x/api.c hardware/rigol-ds/api.c hardware/hameg-hmo/api.c hardware/lecroy-xstream/api.c hardware/scpi-pps/api.c hardware/gwinstek-gds-800/api.c hardware/yokogawa-dlm/api.c ================================================ FILE: stuff/snippets.txt ================================================ Thread ID ========= #define _GNU_SOURCE /* See feature_test_macros(7) */ #include #include #include /* For SYS_xxx definitions */ //pid_t tid = gettid(); pid_t tid = syscall(__NR_gettid); sr_spew("ThreadId = %i", tid); Call Stack ========== #include #include #include void some_function() { void* callstack[128]; int i, frames; char** strs; frames = backtrace(callstack, 128); strs = backtrace_symbols(callstack, frames); for (i = 0; i < frames; ++i) { printf("%s\n", strs[i]); } free(strs); } CMake ===== Output file in Docker: ---------------------- file(READ /home/runner/work/smuview/smuview/build/cmake-build/CMakeFiles/CMakeOutput.log OUT_LOG) message(WARNING "CMakeOutput.log: ${OUT_LOG}") See https://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script ---------------------------------------------------------------------------------------------------- Using the get_cmake_property function, the following loop will print out all CMake variables defined and their values: get_cmake_property(_variableNames VARIABLES) list (SORT _variableNames) foreach (_variableName ${_variableNames}) message(STATUS "${_variableName}=${${_variableName}}") endforeach() This can also be embedded in a convenience function which can optionally use a regular expression to print only a subset of variables with matching names function(dump_cmake_variables) get_cmake_property(_variableNames VARIABLES) list (SORT _variableNames) foreach (_variableName ${_variableNames}) if (ARGV0) unset(MATCHED) string(REGEX MATCH ${ARGV0} MATCHED ${_variableName}) if (NOT MATCHED) continue() endif() endif() message(STATUS "${_variableName}=${${_variableName}}") endforeach() endfunction() To print environment variables, use CMake's command mode: --------------------------------------------------------- execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment") To print all defined properties of a target: -------------------------------------------- # Get all propreties that cmake supports execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)` # Convert command output into a CMake list STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i list(FILTER CMAKE_PROPERTY_LIST EXCLUDE REGEX "^LOCATION$|^LOCATION_|_LOCATION$") # For some reason, "TYPE" shows up twice - others might too? list(REMOVE_DUPLICATES CMAKE_PROPERTY_LIST) # build whitelist by filtering down from CMAKE_PROPERTY_LIST in case cmake is # a different version, and one of our hardcoded whitelisted properties # doesn't exist! unset(CMAKE_WHITELISTED_PROPERTY_LIST) foreach(prop ${CMAKE_PROPERTY_LIST}) if(prop MATCHES "^(INTERFACE|[_a-z]|IMPORTED_LIBNAME_|MAP_IMPORTED_CONFIG_)|^(COMPATIBLE_INTERFACE_(BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|EXPORT_NAME|IMPORTED(_GLOBAL|_CONFIGURATIONS|_LIBNAME)?|NAME|TYPE|NO_SYSTEM_FROM_IMPORTED)$") list(APPEND CMAKE_WHITELISTED_PROPERTY_LIST ${prop}) endif() endforeach(prop) function(print_target_properties tgt) if(NOT TARGET ${tgt}) message("There is no target named '${tgt}'") return() endif() get_target_property(target_type ${tgt} TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") set(PROP_LIST ${CMAKE_WHITELISTED_PROPERTY_LIST}) else() set(PROP_LIST ${CMAKE_PROPERTY_LIST}) endif() foreach (prop ${PROP_LIST}) string(REPLACE "" "${CMAKE_BUILD_TYPE}" prop ${prop}) # message ("Checking ${prop}") get_property(propval TARGET ${tgt} PROPERTY ${prop} SET) if (propval) get_target_property(propval ${tgt} ${prop}) message ("${tgt} ${prop} = ${propval}") endif() endforeach(prop) endfunction(print_target_properties) 32bit build on 64bit: --------------------- export C="--host=i686-linux-gnu --build=i686-linux-gnu" export CC="gcc -m32" export CXX="g++ -m32" export CFLAGS="-O2 -march=i686" export CXXFLAGS="-O2 -march=i686" export LD="ld -melf_i386" export LDFLAGS="-m32" Qt for Ubuntu ============= https://launchpad.net/~beineri/ Instead for sourcing the env file: export QTDIR=/opt/qt512 export PATH=/opt/qt512/bin:$PATH export LD_LIBRARY_PATH=/opt/qt512/lib/i386-linux-gnu:/opt/qt512/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH=/opt/qt512/lib/pkgconfig:$PKG_CONFIG_PATH sudo ldconfig ================================================ FILE: test/CMakeLists.txt ================================================ ## ## This file is part of the SmuView project. ## ## Copyright (C) 2012 Joel Holdsworth ## Copyright (C) 2012 Alexandru Gagniuc ## Copyright (C) 2022 Frank Stettner ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## set(smuview_TEST_SOURCES ${PROJECT_SOURCE_DIR}/src/util.cpp test.cpp util.cpp ) # On MinGW we need to use static linking. if(NOT WIN32) add_definitions(-DBOOST_TEST_DYN_LINK) endif() add_executable(smuview-test ${smuview_TEST_SOURCES} ) target_link_libraries(smuview-test ${SMUVIEW_LINK_LIBS}) ================================================ FILE: test/test.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #define BOOST_TEST_MAIN #include "test/test.hpp" #include using std::ostream; ostream& operator<<(ostream& stream, const QString& str) { return stream << str.toUtf8().data(); } ================================================ FILE: test/test.hpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2015 Jens Steinhauser * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef SMUVIEW_TEST_TEST_HPP #define SMUVIEW_TEST_TEST_HPP #include using std::ostream; ostream& operator<<(ostream& stream, const QString& str); #endif // SMUVIEW_TEST_TEST_HPP ================================================ FILE: test/util.cpp ================================================ /* * This file is part of the SmuView project. * * Copyright (C) 2015 Jens Steinhauser * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include "src/util.hpp" #include "test/test.hpp" using namespace sv::util; using ts = sv::util::Timestamp; using std::bind; namespace { QChar mu = QChar(0x03BC); sv::util::SIPrefix unspecified = sv::util::SIPrefix::unspecified; sv::util::SIPrefix yocto = sv::util::SIPrefix::yocto; sv::util::SIPrefix nano = sv::util::SIPrefix::nano; /* sv::util::SIPrefix micro = sv::util::SIPrefix::micro; // Not currently used */ sv::util::SIPrefix milli = sv::util::SIPrefix::milli; sv::util::SIPrefix none = sv::util::SIPrefix::none; sv::util::SIPrefix kilo = sv::util::SIPrefix::kilo; sv::util::SIPrefix yotta = sv::util::SIPrefix::yotta; /* sv::util::TimeUnit Time = sv::util::TimeUnit::Time; // Not currently used */ } // namespace BOOST_AUTO_TEST_SUITE(UtilTest) BOOST_AUTO_TEST_CASE(exponent_test) { BOOST_CHECK_EQUAL(exponent(SIPrefix::yocto), -24); BOOST_CHECK_EQUAL(exponent(SIPrefix::zepto), -21); BOOST_CHECK_EQUAL(exponent(SIPrefix::atto), -18); BOOST_CHECK_EQUAL(exponent(SIPrefix::femto), -15); BOOST_CHECK_EQUAL(exponent(SIPrefix::pico), -12); BOOST_CHECK_EQUAL(exponent(SIPrefix::nano), -9); BOOST_CHECK_EQUAL(exponent(SIPrefix::micro), -6); BOOST_CHECK_EQUAL(exponent(SIPrefix::milli), -3); BOOST_CHECK_EQUAL(exponent(SIPrefix::none), 0); BOOST_CHECK_EQUAL(exponent(SIPrefix::kilo), 3); BOOST_CHECK_EQUAL(exponent(SIPrefix::mega), 6); BOOST_CHECK_EQUAL(exponent(SIPrefix::giga), 9); BOOST_CHECK_EQUAL(exponent(SIPrefix::tera), 12); BOOST_CHECK_EQUAL(exponent(SIPrefix::peta), 15); BOOST_CHECK_EQUAL(exponent(SIPrefix::exa), 18); BOOST_CHECK_EQUAL(exponent(SIPrefix::zetta), 21); BOOST_CHECK_EQUAL(exponent(SIPrefix::yotta), 24); } BOOST_AUTO_TEST_CASE(format_value_si_test) { QString value_str(""); QString si_prefix_str(""); /* Common values for DMMs */ format_value_si(4635000000. , -1, -6, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "G"); si_prefix_str = ""; format_value_si( 463500000. , -1, -5, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si( 46350000. , -1, -4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si( 4635000. , -1, -3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si( 463500. , -1, -2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 46350. , -1, -1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 4635. , -1, 0, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 463.5 , -1, 1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 46.35 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 4.635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( .4635 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( .04635 , -1, 5, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( .004635 , -1, 6, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( .0004635 , -1, 7, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si( .00004635 , -1, 8, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si( .000004635 , -1, 9, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si( .0000004635 , -1, 10, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.5"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); si_prefix_str = ""; format_value_si( .00000004635 , -1, 11, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.35"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); si_prefix_str = ""; format_value_si( .000000004635, -1, 12, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); /* Common values for power supplies */ si_prefix_str = ""; format_value_si( 123.456 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.456"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 123.4567 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4567"); BOOST_CHECK_EQUAL(si_prefix_str, ""); /* Edge cases */ si_prefix_str = ""; format_value_si(1234e25, -1, -25, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "12340"); BOOST_CHECK_EQUAL(si_prefix_str, "Y"); si_prefix_str = ""; format_value_si(1234e23, -1, -23, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4"); BOOST_CHECK_EQUAL(si_prefix_str, "Y"); si_prefix_str = ""; format_value_si(.4635e-23, -1, 27, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "y"); si_prefix_str = ""; format_value_si(.4635e-26, -1, 30, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.004635"); BOOST_CHECK_EQUAL(si_prefix_str, "y"); si_prefix_str = ""; format_value_si(0, -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.0000"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si(0, -1, 1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.0"); BOOST_CHECK_EQUAL(si_prefix_str, ""); // si_prefix_str = ""; // format_value_si(NAN, -1, 4, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "nan"); // BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si(std::numeric_limits::infinity(), -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "inf"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si(std::numeric_limits::max(), -1, 4, value_str, si_prefix_str, false); //BOOST_CHECK_EQUAL(value_str, "0.0000"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si(std::numeric_limits::lowest(), -1, 4, value_str, si_prefix_str, false); //BOOST_CHECK_EQUAL(value_str, "0.0000"); BOOST_CHECK_EQUAL(si_prefix_str, ""); /* Some more values from the libsigrok tests */ si_prefix_str = ""; format_value_si( 12.0 , -1, 1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "12.0"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 12.0 , -1, -1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.01"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 1024.0 , -1, 0, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1.024"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 1024.0 , -1, -1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1.02"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 1024.0 , -1, -3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si( 12.0e5 , -1, 0, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1.200000"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si( 0.123456 , -1, 0, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 0.123456 , -1, 1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.1"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 0.123456 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.12"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si( 0.123456 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.123456 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.123456 , -1, 5, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.45"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.123456 , -1, 6, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.456"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.123456 , -1, 7, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4560"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.0123 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "12.3"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.00123 , -1, 5, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1.23"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.000123 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.1"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.000123 , -1, 5, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.12"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si( 0.000123 , -1, 6, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si( 0.000123 , -1, 7, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.0"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si( 0.0001 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.1"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); } BOOST_AUTO_TEST_CASE(format_value_si_autoscale_test) { QString value_str(""); QString si_prefix_str(""); /* Common values for DMMs */ format_value_si_autoscale(4635000000. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "G"); si_prefix_str = ""; format_value_si_autoscale( 463500000. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si_autoscale( 46350000. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si_autoscale( 4635000. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si_autoscale( 463500. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si_autoscale( 46350. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si_autoscale( 4635. , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "k"); si_prefix_str = ""; format_value_si_autoscale( 463.5 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale( 46.35 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale( 4.635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale( .4635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( .04635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( .004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( .0004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si_autoscale( .00004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si_autoscale( .000004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si_autoscale( .0000004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "463.500"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); si_prefix_str = ""; format_value_si_autoscale( .00000004635 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "46.350"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); si_prefix_str = ""; format_value_si_autoscale( .000000004635, -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "n"); /* Common values for power supplies */ si_prefix_str = ""; format_value_si_autoscale( 123.456 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.456"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale( 123.4567 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.456"); BOOST_CHECK_EQUAL(si_prefix_str, ""); /* Edge cases */ si_prefix_str = ""; format_value_si_autoscale(1234e25, -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "12340.000"); BOOST_CHECK_EQUAL(si_prefix_str, "Y"); si_prefix_str = ""; format_value_si_autoscale(1234e23, -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.400"); BOOST_CHECK_EQUAL(si_prefix_str, "Y"); si_prefix_str = ""; format_value_si_autoscale(.4635e-23, -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "4.635"); BOOST_CHECK_EQUAL(si_prefix_str, "y"); si_prefix_str = ""; format_value_si_autoscale(.4635e-26, -1, 6, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.004635"); BOOST_CHECK_EQUAL(si_prefix_str, "y"); si_prefix_str = ""; format_value_si_autoscale(0, -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.00"); BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale(0, -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "0.000"); BOOST_CHECK_EQUAL(si_prefix_str, ""); // si_prefix_str = ""; // format_value_si(NAN, -1, 3, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "nan"); // BOOST_CHECK_EQUAL(si_prefix_str, ""); si_prefix_str = ""; format_value_si_autoscale(std::numeric_limits::infinity(), -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "inf"); BOOST_CHECK_EQUAL(si_prefix_str, ""); /* Some more values from the libsigrok tests */ // si_prefix_str = ""; // format_value_si( 12.0 , -1, 1, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "12.0"); // BOOST_CHECK_EQUAL(si_prefix_str, ""); // // si_prefix_str = ""; // format_value_si( 12.0 , -1, -1, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "0.01"); // BOOST_CHECK_EQUAL(si_prefix_str, "k"); // // si_prefix_str = ""; // format_value_si( 1024.0 , -1, 0, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "1.024"); // BOOST_CHECK_EQUAL(si_prefix_str, "k"); // // si_prefix_str = ""; // format_value_si( 1024.0 , -1, -1, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "1.02"); // BOOST_CHECK_EQUAL(si_prefix_str, "k"); // // si_prefix_str = ""; // format_value_si( 1024.0 , -1, -3, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "1"); // BOOST_CHECK_EQUAL(si_prefix_str, "k"); // // si_prefix_str = ""; // format_value_si( 12.0e5 , -1, 0, value_str, si_prefix_str, false); // BOOST_CHECK_EQUAL(value_str, "1.200000"); // BOOST_CHECK_EQUAL(si_prefix_str, "M"); si_prefix_str = ""; format_value_si_autoscale( 0.123456 , -1, 0, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.123456 , -1, 1, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.123456 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.45"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.123456 , -1, 3, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.456"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.123456 , -1, 4, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.4560"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.0123 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "12.30"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.00123 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "1.23"); BOOST_CHECK_EQUAL(si_prefix_str, "m"); si_prefix_str = ""; format_value_si_autoscale( 0.000123 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "123.00"); BOOST_CHECK_EQUAL(si_prefix_str, mu); si_prefix_str = ""; format_value_si_autoscale( 0.0001 , -1, 2, value_str, si_prefix_str, false); BOOST_CHECK_EQUAL(value_str, "100.00"); BOOST_CHECK_EQUAL(si_prefix_str, mu); } BOOST_AUTO_TEST_CASE(format_time_si_test) { // check prefix calculation BOOST_CHECK_EQUAL(format_time_si(ts("0")), "0 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-24")), "+1 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-23")), "+10 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-22")), "+100 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-21")), "+1 zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-20")), "+10 zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-19")), "+100 zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-18")), "+1 as"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-17")), "+10 as"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-16")), "+100 as"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-15")), "+1 fs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-14")), "+10 fs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-13")), "+100 fs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-12")), "+1 ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-11")), "+10 ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-10")), "+100 ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-9")), "+1 ns"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-8")), "+10 ns"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-7")), "+100 ns"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-6")), QString("+1 ") + mu + "s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-5")), QString("+10 ") + mu + "s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-4")), QString("+100 ") + mu + "s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-3")), "+1 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-2")), "+10 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-1")), "+100 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e0")), "+1 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e1")), "+10 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e2")), "+100 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e3")), "+1 ks"); BOOST_CHECK_EQUAL(format_time_si(ts("1e4")), "+10 ks"); BOOST_CHECK_EQUAL(format_time_si(ts("1e5")), "+100 ks"); BOOST_CHECK_EQUAL(format_time_si(ts("1e6")), "+1 Ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e7")), "+10 Ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e8")), "+100 Ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e9")), "+1 Gs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e10")), "+10 Gs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e11")), "+100 Gs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e12")), "+1 Ts"); BOOST_CHECK_EQUAL(format_time_si(ts("1e13")), "+10 Ts"); BOOST_CHECK_EQUAL(format_time_si(ts("1e14")), "+100 Ts"); BOOST_CHECK_EQUAL(format_time_si(ts("1e15")), "+1 Ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e16")), "+10 Ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e17")), "+100 Ps"); BOOST_CHECK_EQUAL(format_time_si(ts("1e18")), "+1 Es"); BOOST_CHECK_EQUAL(format_time_si(ts("1e19")), "+10 Es"); BOOST_CHECK_EQUAL(format_time_si(ts("1e20")), "+100 Es"); BOOST_CHECK_EQUAL(format_time_si(ts("1e21")), "+1 Zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e22")), "+10 Zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e23")), "+100 Zs"); BOOST_CHECK_EQUAL(format_time_si(ts("1e24")), "+1 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e25")), "+10 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e26")), "+100 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e27")), "+1000 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1234")), "+1 ks"); BOOST_CHECK_EQUAL(format_time_si(ts("1234"), kilo, 3), "+1.234 ks"); BOOST_CHECK_EQUAL(format_time_si(ts("1234.5678")), "+1 ks"); // check prefix BOOST_CHECK_EQUAL(format_time_si(ts("1e-24"), yocto), "+1 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-21"), yocto), "+1000 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("0"), yocto), "0 ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-4"), milli), "+0 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-4"), milli, 1), "+0.1 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1000"), milli), "+1000000 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("0"), milli), "0 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none), "+0 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none, 1), "+0.1 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none, 2), "+0.10 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1"), none), "+1 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e1"), none), "+10 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1e23"), yotta), "+0 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e23"), yotta, 1), "+0.1 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("1e27"), yotta), "+1000 Ys"); BOOST_CHECK_EQUAL(format_time_si(ts("0"), yotta), "0 Ys"); // check precision, rounding BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678")), "+1 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.4")), "+1 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.5")), "+2 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.9")), "+2 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), unspecified, 2), "+1.23 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), unspecified, 3), "+1.235 s"); BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), milli, 3), "+1234.568 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), milli, 0), "+1235 ms"); BOOST_CHECK_EQUAL(format_time_si(ts("1.2"), unspecified, 3), "+1.200 s"); // check unit and sign BOOST_CHECK_EQUAL(format_time_si(ts("-1"), none, 0, "V", true), "-1 V"); BOOST_CHECK_EQUAL(format_time_si(ts("-1"), none, 0, "V", false), "-1 V"); BOOST_CHECK_EQUAL(format_time_si(ts("1"), none, 0, "V", true), "+1 V"); BOOST_CHECK_EQUAL(format_time_si(ts("1"), none, 0, "V", false), "1 V"); } BOOST_AUTO_TEST_CASE(format_time_si_adjusted_test) { BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-1.5"), milli), "-1500 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-1.0"), milli), "-1000 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-0.2"), milli), "-200 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-0.1"), milli), "-100 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.0"), milli), "0 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.1"), milli), "+100 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.2"), milli), "+200 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.3"), milli), "+300 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.4"), milli), "+400 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.5"), milli), "+500 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.6"), milli), "+600 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.7"), milli), "+700 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.8"), milli), "+800 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.9"), milli), "+900 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.0"), milli), "+1000 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.1"), milli), "+1100 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.2"), milli), "+1200 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.3"), milli), "+1300 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.4"), milli), "+1400 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), milli), "+1500 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), milli, 6), "+1500.000 ms"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 6), "+1500000000 ns"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 8), "+1500000000 ns"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 9), "+1500000000 ns"); BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 10), "+1500000000.0 ns"); } BOOST_AUTO_TEST_CASE(format_time_minutes_test) { using namespace std::placeholders; auto fmt = bind(format_time_minutes, _1, _2, true); BOOST_CHECK_EQUAL(fmt(ts( 0), 0), "+0:00"); BOOST_CHECK_EQUAL(fmt(ts( 1), 0), "+0:01"); BOOST_CHECK_EQUAL(fmt(ts( 59), 0), "+0:59"); BOOST_CHECK_EQUAL(fmt(ts( 60), 0), "+1:00"); BOOST_CHECK_EQUAL(fmt(ts( -1), 0), "-0:01"); BOOST_CHECK_EQUAL(fmt(ts( -59), 0), "-0:59"); BOOST_CHECK_EQUAL(fmt(ts( -60), 0), "-1:00"); BOOST_CHECK_EQUAL(fmt(ts( 100), 0), "+1:40"); BOOST_CHECK_EQUAL(fmt(ts( -100), 0), "-1:40"); BOOST_CHECK_EQUAL(fmt(ts( 4000), 0), "+1:06:40"); BOOST_CHECK_EQUAL(fmt(ts(-4000), 0), "-1:06:40"); BOOST_CHECK_EQUAL(fmt(ts(12000), 0), "+3:20:00"); BOOST_CHECK_EQUAL(fmt(ts(15000), 0), "+4:10:00"); BOOST_CHECK_EQUAL(fmt(ts(20000), 0), "+5:33:20"); BOOST_CHECK_EQUAL(fmt(ts(25000), 0), "+6:56:40"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 0), "+123:04:05:06"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 1), "+123:04:05:06.0"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 2), "+123:04:05:06.01"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 3), "+123:04:05:06.007"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 4), "+123:04:05:06.007 0"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 5), "+123:04:05:06.007 01"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 6), "+123:04:05:06.007 008"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 7), "+123:04:05:06.007 008 0"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 8), "+123:04:05:06.007 008 01"); BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 9), "+123:04:05:06.007 008 009"); BOOST_CHECK_EQUAL(format_time_minutes(ts( 0), 0, false), "0:00"); BOOST_CHECK_EQUAL(format_time_minutes(ts( 100), 0, false), "1:40"); BOOST_CHECK_EQUAL(format_time_minutes(ts(-100), 0, false), "-1:40"); } BOOST_AUTO_TEST_CASE(count_int_digits_test) { BOOST_CHECK_EQUAL(count_int_digits( 101), 3); BOOST_CHECK_EQUAL(count_int_digits( 100), 3); BOOST_CHECK_EQUAL(count_int_digits( 99), 2); BOOST_CHECK_EQUAL(count_int_digits( 11), 2); BOOST_CHECK_EQUAL(count_int_digits( 10), 2); BOOST_CHECK_EQUAL(count_int_digits( 9), 1); BOOST_CHECK_EQUAL(count_int_digits( 1), 1); BOOST_CHECK_EQUAL(count_int_digits( 0), 0); BOOST_CHECK_EQUAL(count_int_digits( -1), 1); BOOST_CHECK_EQUAL(count_int_digits( -9), 1); BOOST_CHECK_EQUAL(count_int_digits( -10), 2); BOOST_CHECK_EQUAL(count_int_digits( -11), 2); BOOST_CHECK_EQUAL(count_int_digits( -99), 2); BOOST_CHECK_EQUAL(count_int_digits(-100), 3); BOOST_CHECK_EQUAL(count_int_digits(-101), 3); } BOOST_AUTO_TEST_CASE(count_double_digits_test) { BOOST_CHECK_EQUAL(count_double_digits( 100. , 1. ), 3); BOOST_CHECK_EQUAL(count_double_digits( 10. , 1. ), 2); BOOST_CHECK_EQUAL(count_double_digits( 1. , 1. ), 1); BOOST_CHECK_EQUAL(count_double_digits( 0. , 1. ), 0); BOOST_CHECK_EQUAL(count_double_digits( 100. , .1), 4); BOOST_CHECK_EQUAL(count_double_digits( 10. , .1), 3); BOOST_CHECK_EQUAL(count_double_digits( 1. , .1), 2); BOOST_CHECK_EQUAL(count_double_digits( 0. , .1 ), 1); BOOST_CHECK_EQUAL(count_double_digits( 100. , .01), 5); BOOST_CHECK_EQUAL(count_double_digits( 10. , .01), 4); BOOST_CHECK_EQUAL(count_double_digits( 1. , .01), 3); BOOST_CHECK_EQUAL(count_double_digits( 0. , .01), 2); BOOST_CHECK_EQUAL(count_double_digits( 1.1 , .01), 3); BOOST_CHECK_EQUAL(count_double_digits( 1.01 , .01), 3); BOOST_CHECK_EQUAL(count_double_digits( 1.001, .01), 4); } BOOST_AUTO_TEST_CASE(count_decimal_places_test) { BOOST_CHECK_EQUAL(count_decimal_places( .00001), 5); BOOST_CHECK_EQUAL(count_decimal_places( .0001), 4); BOOST_CHECK_EQUAL(count_decimal_places( .001), 3); BOOST_CHECK_EQUAL(count_decimal_places( .01), 2); BOOST_CHECK_EQUAL(count_decimal_places( .1), 1); BOOST_CHECK_EQUAL(count_decimal_places( 0.), 0); BOOST_CHECK_EQUAL(count_decimal_places( 1.), 0); BOOST_CHECK_EQUAL(count_decimal_places( 10.), 0); BOOST_CHECK_EQUAL(count_decimal_places(100.), 0); BOOST_CHECK_EQUAL(count_decimal_places( .00002), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00003), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00004), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00005), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00006), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00007), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00008), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00009), 5); BOOST_CHECK_EQUAL(count_decimal_places( .11 ), 2); BOOST_CHECK_EQUAL(count_decimal_places( .111 ), 3); BOOST_CHECK_EQUAL(count_decimal_places( .1111 ), 4); BOOST_CHECK_EQUAL(count_decimal_places( .9999 ), 4); BOOST_CHECK_EQUAL(count_decimal_places( 1.001 ), 3); BOOST_CHECK_EQUAL(count_decimal_places( 1.00001), 5); BOOST_CHECK_EQUAL(count_decimal_places( .00007), 5); } BOOST_AUTO_TEST_CASE(get_sr_digits_test) { BOOST_CHECK_EQUAL(get_sr_digits(1000000. ), -6); BOOST_CHECK_EQUAL(get_sr_digits( 100000. ), -5); BOOST_CHECK_EQUAL(get_sr_digits( 10000. ), -4); BOOST_CHECK_EQUAL(get_sr_digits( 1000. ), -3); BOOST_CHECK_EQUAL(get_sr_digits( 100. ), -2); BOOST_CHECK_EQUAL(get_sr_digits( 10. ), -1); BOOST_CHECK_EQUAL(get_sr_digits( 1. ), 0); BOOST_CHECK_EQUAL(get_sr_digits( 0. ), 0); BOOST_CHECK_EQUAL(get_sr_digits( .1 ), 1); BOOST_CHECK_EQUAL(get_sr_digits( .01 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .001 ), 3); BOOST_CHECK_EQUAL(get_sr_digits( .0001 ), 4); BOOST_CHECK_EQUAL(get_sr_digits( .00001 ), 5); BOOST_CHECK_EQUAL(get_sr_digits( .000001 ), 6); BOOST_CHECK_EQUAL(get_sr_digits( .0000001), 7); BOOST_CHECK_EQUAL(get_sr_digits(1100000. ), -5); BOOST_CHECK_EQUAL(get_sr_digits(1110000. ), -4); BOOST_CHECK_EQUAL(get_sr_digits(1111000. ), -3); BOOST_CHECK_EQUAL(get_sr_digits(1111100. ), -2); BOOST_CHECK_EQUAL(get_sr_digits(1111110. ), -1); BOOST_CHECK_EQUAL(get_sr_digits(1111111. ), 0); BOOST_CHECK_EQUAL(get_sr_digits(1111111.1 ), 1); BOOST_CHECK_EQUAL(get_sr_digits(1111111.11 ), 2); BOOST_CHECK_EQUAL(get_sr_digits(1111111.111 ), 3); BOOST_CHECK_EQUAL(get_sr_digits( .02 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .03 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .04 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .05 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .06 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .07 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .08 ), 2); BOOST_CHECK_EQUAL(get_sr_digits( .09 ), 2); } BOOST_AUTO_TEST_SUITE_END()