diff --git a/.gitignore b/.gitignore index d756dcf7e..b4fe2fab9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build lib *.sublime-workspace **/.idea/** +*.vscode diff --git a/rviz_common/include/rviz_common/visualization_frame.hpp b/rviz_common/include/rviz_common/visualization_frame.hpp index 81539066c..21d5be29e 100644 --- a/rviz_common/include/rviz_common/visualization_frame.hpp +++ b/rviz_common/include/rviz_common/visualization_frame.hpp @@ -84,6 +84,9 @@ class RVIZ_COMMON_PUBLIC VisualizationFrame : public QMainWindow, public WindowM { Q_OBJECT +protected: + explicit VisualizationFrame(QWidget * parent = nullptr); + public: explicit VisualizationFrame( ros_integration::RosNodeAbstractionIface::WeakPtr rviz_ros_node, QWidget * parent = nullptr); diff --git a/rviz_common/src/rviz_common/visualization_frame.cpp b/rviz_common/src/rviz_common/visualization_frame.cpp index d6587b2be..1f7b7c834 100644 --- a/rviz_common/src/rviz_common/visualization_frame.cpp +++ b/rviz_common/src/rviz_common/visualization_frame.cpp @@ -98,6 +98,30 @@ namespace rviz_common { +VisualizationFrame::VisualizationFrame(QWidget * parent) +: QMainWindow(parent), + app_(nullptr), + render_panel_(nullptr), + show_help_action_(nullptr), + file_menu_(nullptr), + recent_configs_menu_(nullptr), + toolbar_(nullptr), + manager_(nullptr), + splash_(nullptr), + toolbar_actions_(nullptr), + show_choose_new_master_option_(false), + panel_factory_(nullptr), + add_tool_action_(nullptr), + remove_tool_menu_(nullptr), + initialized_(false), + geom_change_detector_(new WidgetGeometryChangeDetector(this)), + loading_(false), + post_load_timer_(new QTimer(this)), + frame_count_(0), + toolbar_visible_(true) +{ +} + VisualizationFrame::VisualizationFrame( ros_integration::RosNodeAbstractionIface::WeakPtr rviz_ros_node, QWidget * parent) : QMainWindow(parent), diff --git a/rviz_python_bindings/CMakeLists.txt b/rviz_python_bindings/CMakeLists.txt new file mode 100644 index 000000000..66b02d4fc --- /dev/null +++ b/rviz_python_bindings/CMakeLists.txt @@ -0,0 +1,169 @@ +cmake_minimum_required(VERSION 3.17) +project(rviz_python_bindings) + +set(shiboken_qt_components + QtCore + QtGui + QtWidgets +) + +find_package(ament_cmake REQUIRED) +find_package(ament_cmake_python REQUIRED) +find_package(python_qt_binding REQUIRED) +include(${python_qt_binding_DIR}/shiboken_helper.cmake) #${CMAKE_CURRENT_SOURCE_DIR}/../../python_qt_binding/cmake/shiboken_helper.cmake)# +find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets) +find_package(rviz_common REQUIRED) +find_package(Python3 COMPONENTS Development) + +# name of the library to be generated by shiboken +set(shiboken_bindings_library "rviz_shiboken") +# path to the include wrapper used by shiboken for generation +set(shiboken_include_wrapper "${CMAKE_CURRENT_SOURCE_DIR}/conf/global.hpp") +# path to the typesystem file used by shiboken for generation +set(shiboken_typesystem "${CMAKE_CURRENT_SOURCE_DIR}/conf/typesystem.xml") +# sources generated by shiboken depends on typesystem +set(shiboken_generated_sources + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_shiboken_module_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_visualizerframepy_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_yamlconfigreader_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_yamlconfigwriter_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_config_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_config_mapiterator_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_visualizationframe_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_visualizationmanager_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_displaygroup_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_display_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_viewmanager_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_paneldockwidget_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_tool_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_toolmanager_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_properties_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_properties_property_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_properties_boolproperty_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_common_viewcontroller_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_rendering_ogrelogging_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/rviz_rendering_wrapper.cpp + ${CMAKE_CURRENT_BINARY_DIR}/${shiboken_bindings_library}/ogre_wrapper.cpp +) + +ament_get_recursive_properties(deps_include_dirs _ ${rviz_common_TARGETS}) +list(APPEND shiboken_include_dirs + ${deps_include_dirs}) +message(STATUS "ROS inlcude dirs: ${deps_include_dirs}") +message(STATUS "QT5 inlcude dirs: ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}") +list(APPEND shiboken_include_dirs + ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} + $ + $ +) + +add_library(rviz_python SHARED + src/visualizer_frame_py.cpp +) + +target_include_directories(rviz_python PUBLIC + $ + $) +target_compile_options(rviz_python PUBLIC -Wl,--no-undefined) +ament_target_dependencies(rviz_python + rviz_common + Qt5 +) + +install(TARGETS rviz_python + EXPORT export_rviz_python + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + + +shiboken_generator_ext( + "rviz_shiboken" # TARGET + "${shiboken_include_wrapper}" # INCLUDE WRAPPER + "${shiboken_typesystem}" # TYPSYSTEM + "${CMAKE_CURRENT_BINARY_DIR}" # Working Directory + "${shiboken_generated_sources}" # Generated Sources + "${shiboken_include_dirs}" # INCLUDE DIRS + "${CMAKE_CURRENT_BINARY_DIR}" # BUILD DIR +) + +add_library( + ${shiboken_bindings_library} MODULE + ${shiboken_generated_sources}) + +shiboken_include_directories( + ${shiboken_bindings_library} + "${shiboken_qt_components}") + +target_include_directories( + ${shiboken_bindings_library} PRIVATE + $ + $ + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES} +) + +target_link_libraries( + ${shiboken_bindings_library} + Qt5::Core + Qt5::Gui + Qt5::Widgets + rviz_python +) +ament_target_dependencies( + ${shiboken_bindings_library} + rviz_common +) +shiboken_target_link_libraries(${shiboken_bindings_library} ${shiboken_qt_components}) + + + +set_target_properties(${shiboken_bindings_library} PROPERTIES +PREFIX "") +set_property(TARGET ${shiboken_bindings_library} PROPERTY OUTPUT_NAME +"${shiboken_bindings_library}.${Python3_SOABI}") + +if(WIN32) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set_property(TARGET ${shiboken_bindings_library} PROPERTY SUFFIX "_d.pyd") + else() + set_property(TARGET ${shiboken_bindings_library} PROPERTY SUFFIX ".pyd") + endif() + target_link_libraries( + ${shiboken_bindings_library} + Python3_LIBRARIES + ) +endif() + +ament_python_install_package(rviz) +install(TARGETS ${shiboken_bindings_library} + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PYTHON_INSTALL_DIR}/rviz +) + + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + find_package(ament_cmake_pytest REQUIRED) + # the following line skips the linter which checks for copyrights + # comment the line when a copyright and license is added to all source files + set(ament_cmake_copyright_FOUND TRUE) + # the following line skips cpplint (only works in a git repo) + # comment the line when this package is in a git repo and when + # a copyright and license is added to all source files + set(ament_cmake_cpplint_FOUND TRUE) + + ament_lint_auto_find_test_dependencies() +endif() + +ament_export_include_directories( + include +) +ament_export_libraries( + rviz_python +) +ament_export_targets( + export_rviz_python +) + +ament_package() diff --git a/rviz_python_bindings/README.md b/rviz_python_bindings/README.md new file mode 100644 index 000000000..cf2a67fe0 --- /dev/null +++ b/rviz_python_bindings/README.md @@ -0,0 +1,37 @@ +# RVIZ Pyhon Bindings + +## Usage +To use the python bindings, you need to do the following: + +```python +import os +os.environ["QT_API"] = "pyside2" +from rviz import rviz_common +``` + +Currently only pyside2 is supported. No stubs are generated, so pylance does not work on namespaces. +The following classes are wrapped: +* VisualizationFrame +* Config +* VisualizationManager +* ViewController +* ViewManager +* DisplayGroup +* Display +* PanelDockWidget +* Tool +* ToolManager +* YamlConfigReader +* YamlConfigWriter +* Property +* BoolProperty + +## Windows specifics +On windows you will need to install the following dependencies manually: + +``` +pip install \ + --index-url=https://download.qt.io/official_releases/QtForPython/ \ + --trusted-host download.qt.io \ + shiboken2 pyside2 shiboken2_generator +``` \ No newline at end of file diff --git a/rviz_python_bindings/conf/global.hpp b/rviz_python_bindings/conf/global.hpp new file mode 100644 index 000000000..4779a2f5b --- /dev/null +++ b/rviz_python_bindings/conf/global.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYTHON_GLOBAL_H +#define PYTHON_GLOBAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // PYTHON_GLOBAL_H diff --git a/rviz_python_bindings/conf/typesystem.xml b/rviz_python_bindings/conf/typesystem.xml new file mode 100644 index 000000000..582a8e1bc --- /dev/null +++ b/rviz_python_bindings/conf/typesystem.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rviz_python_bindings/include/rviz_python_bindings/visualizer_frame_py.hpp b/rviz_python_bindings/include/rviz_python_bindings/visualizer_frame_py.hpp new file mode 100644 index 000000000..2a3da8ec8 --- /dev/null +++ b/rviz_python_bindings/include/rviz_python_bindings/visualizer_frame_py.hpp @@ -0,0 +1,31 @@ +#ifndef VISUALIZER_FRAME_PY_HPP +#define VISUALIZER_FRAME_PY_HPP + +#include "rviz_common/visualization_frame.hpp" +#include "rviz_common/ros_integration/ros_client_abstraction.hpp" +#include "rviz_common/ros_integration/ros_node_abstraction.hpp" + +namespace rviz_common +{ + +class VisualizerFramePy : public VisualizationFrame +{ +protected: + std::unique_ptr ros_client_abstraction_; + +public: + explicit VisualizerFramePy( + QWidget * parent = nullptr); + + ~VisualizerFramePy() + { + ros_client_abstraction_->shutdown(); + } + + bool node_ok(); + + void initialize(const QString & display_config_file = ""); +}; +} + +#endif diff --git a/rviz_python_bindings/package.xml b/rviz_python_bindings/package.xml new file mode 100644 index 000000000..e59eaf26a --- /dev/null +++ b/rviz_python_bindings/package.xml @@ -0,0 +1,23 @@ + + + + rviz_python_bindings + 0.0.0 + RViz python bindings + christoph + TODO: License declaration + + ament_cmake + + ament_lint_auto + ament_lint_common + + rviz_common + rviz_rendering + python3_pyside2 + python3_qtpy + + + ament_cmake + + diff --git a/rviz_python_bindings/rviz/__init__.py b/rviz_python_bindings/rviz/__init__.py new file mode 100644 index 000000000..16e98f565 --- /dev/null +++ b/rviz_python_bindings/rviz/__init__.py @@ -0,0 +1,32 @@ +# Python package 'rviz' initialization. +# +# The actual implementations are defined in C++ and wrapped by +# shiboken or sip. This wrapper finds which binding is available and +# presents it as package rviz. + +from qtpy import PYSIDE2 + +if PYSIDE2: + # Import the shared library generated by the shiboken binding-generator. + from . import rviz_shiboken as bindings + + # Expose the contained rviz class as bindings + rviz_common = bindings.rviz_common + VisualizationFrame = bindings.rviz_common.VisualizerFramePy + Config = bindings.rviz_common.Config + VisualizationManager = bindings.rviz_common.VisualizationManager + ViewManager = bindings.rviz_common.ViewManager + DisplayGroup = bindings.rviz_common.DisplayGroup + Display = bindings.rviz_common.Display + PanelDockWidget = bindings.rviz_common.PanelDockWidget + Tool = bindings.rviz_common.Tool + ToolManager = bindings.rviz_common.ToolManager + YamlConfigReader = bindings.rviz_common.YamlConfigReader + YamlConfigWriter = bindings.rviz_common.YamlConfigWriter + Property = bindings.rviz_common.properties.Property + BoolProperty = bindings.rviz_common.properties.BoolProperty +else: + raise ImportError( + "Python Bindings for rviz are only available for PySide2." + "Please set ENV Variable Qt_API to 'pyside2'" + ) diff --git a/rviz_python_bindings/src/visualizer_frame_py.cpp b/rviz_python_bindings/src/visualizer_frame_py.cpp new file mode 100644 index 000000000..43df4cce7 --- /dev/null +++ b/rviz_python_bindings/src/visualizer_frame_py.cpp @@ -0,0 +1,21 @@ +#include "rviz_python_bindings/visualizer_frame_py.hpp" + +using namespace rviz_common; + +VisualizerFramePy::VisualizerFramePy(QWidget * parent) +: VisualizationFrame(parent) +{ + ros_client_abstraction_ = std::make_unique(); + this->rviz_ros_node_ = ros_client_abstraction_->init(0, nullptr, "rviz", false); +} + +bool VisualizerFramePy::node_ok() +{ + return ros_client_abstraction_->ok(); +} + +void VisualizerFramePy::initialize( + const QString & display_config_file) +{ + VisualizationFrame::initialize(rviz_ros_node_, display_config_file); +} diff --git a/rviz_python_bindings/test/python_sample.py b/rviz_python_bindings/test/python_sample.py new file mode 100644 index 000000000..909929ba2 --- /dev/null +++ b/rviz_python_bindings/test/python_sample.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import sys +import os + +os.environ["QT_API"] = "pyside2" + +from rviz import * +from qtpy.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * + + +class SampleWidget(QWidget): + def __init__(self): + QWidget.__init__(self) + + self.grid_display = None + self.props = [] + + layout = QVBoxLayout() + + frame_button = QPushButton("Set Fixed Frame") + frame_button.clicked.connect(self.onFrameButtonClick) + layout.addWidget(frame_button) + + enable_button = QPushButton("Toggle Grid Enable") + enable_button.clicked.connect(self.onEnableButtonClick) + layout.addWidget(enable_button) + + thickness_slider = QSlider(Qt.Horizontal) + thickness_slider.setTracking(True) + thickness_slider.setMinimum(1) + thickness_slider.setMaximum(1000) + thickness_slider.valueChanged.connect(self.onThicknessSliderChanged) + layout.addWidget(thickness_slider) + + fps_button = QPushButton("Switch to FPS") + fps_button.clicked.connect(self.onFpsButtonClick) + layout.addWidget(fps_button) + + h_layout = QHBoxLayout() + + top_button = QPushButton("Top View") + top_button.clicked.connect(self.onTopButtonClick) + h_layout.addWidget(top_button) + + side_button = QPushButton("Side View") + side_button.clicked.connect(self.onSideButtonClick) + h_layout.addWidget(side_button) + + look_button = QPushButton("Look Away") + look_button.clicked.connect(self.onLookButtonClick) + h_layout.addWidget(look_button) + + layout.addLayout(h_layout) + + distance_slider = QSlider(Qt.Horizontal) + distance_slider.setTracking(True) + distance_slider.setMinimum(1) + distance_slider.setMaximum(1000) + distance_slider.valueChanged.connect(self.onDistanceSliderChanged) + layout.addWidget(distance_slider) + + tool_button = QPushButton("Select") + tool_button.clicked.connect(self.onSelectClick) + layout.addWidget(tool_button) + + coolify_button = QPushButton("Coolify Displays") + coolify_button.clicked.connect(self.onCoolifyClick) + layout.addWidget(coolify_button) + + self.setLayout(layout) + + def destruct(self): + """ + Disconnect internal object connections. Without something + like this, it is easy to have a crash when the object goes out + of scope. + """ + self.frame = None + if self.grid_display != None: + self.grid_display.getParent().takeChild(self.grid_display) + self.grid_display = None + for p in self.props: + p.getParent().takeChild(p) + self.props = [] + + def setFrame(self, vis_frame): + self.frame = vis_frame + + def onFrameButtonClick(self): + self.frame.getManager().setFixedFrame("Python") + + def onEnableButtonClick(self): + if self.grid_display == None: + self.grid_display = self.frame.getManager().createDisplay( + "rviz_default_plugins/Grid", "Awesome grid", True + ) + self.grid_display.subProp("Line Style").setValue("Billboards") + else: + self.grid_display.setEnabled(not self.grid_display.isEnabled()) + + def onThicknessSliderChanged(self, new_value): + if self.grid_display != None: + self.grid_display.subProp("Line Style").subProp("Line Width").setValue( + new_value / 1000.0 + ) + prop = Property( + "Prop " + str(new_value), + new_value / 1000.0, + "Bad idea property generation", + ) + self.grid_display.addChild(prop) + self.props.append(prop) + + def onDistanceSliderChanged(self, new_value): + controller = self.frame.getManager().getViewManager().getCurrent() + if controller != None: + controller.subProp("Distance").setValue(new_value / 10.0) + + def onFpsButtonClick(self): + self.frame.getManager().getViewManager().setCurrentViewControllerType( + "rviz_default_plugins/FPS" + ) + + def onTopButtonClick(self): + self.switchToView("Top View") + + def onSideButtonClick(self): + self.switchToView("Side View") + + def onLookButtonClick(self): + controller = self.frame.getManager().getViewManager().getCurrent() + if controller != None: + controller.lookAt(5, 5, 0) + + def switchToView(self, view_name): + view_man = self.frame.getManager().getViewManager() + for i in range(view_man.getNumViews()): + if view_man.getViewAt(i).getName() == view_name: + view_man.setCurrentFrom(view_man.getViewAt(i)) + return + print("Did not find view named %s." % view_name) + + def onSelectClick(self): + tool_man = self.frame.getManager().getToolManager() + for i in range(tool_man.numTools()): + if tool_man.getTool(i).getName() == "Select": + tool_man.setCurrentTool(tool_man.getTool(i)) + return + + def onCoolifyClick(self): + self.coolify(self.frame.getManager().getRootDisplayGroup()) + + def coolify(self, group): + for i in range(group.numDisplays()): + display = group.getDisplayAt(i) + display.setName("Cool " + display.getName()) + subgroup = group.getGroupAt(i) + if subgroup != None: + self.coolify(subgroup) + + +def fun(): + # rviz.OgreLogging.noLog() # (no log is the default) + # rviz.OgreLogging.useStandardOut() + # rviz.OgreLogging.useLogFile( "frame_test.ogre-log" ) + + app = QApplication(sys.argv) + + frame = VisualizationFrame() + frame.setApp(app) + frame.initialize() + frame.show() + + sw = SampleWidget() + sw.setFrame(frame) + sw.show() + frame.addPane("Button", sw) + + app.exec_() + + # Without this destruct() call, this program crashes just after + # the "after exec_()" printout, when "sw" goes out of scope and is + # destroyed. + sw.destruct() + + print("after exec_()") + + +if __name__ == "__main__": + # This fun() function call is just here to demonstrate rviz + # objects going out of scope and being cleaned up correctly. + fun() + print("after fun()") diff --git a/rviz_python_bindings/test/test_imports.py b/rviz_python_bindings/test/test_imports.py new file mode 100644 index 000000000..7e581ac92 --- /dev/null +++ b/rviz_python_bindings/test/test_imports.py @@ -0,0 +1,114 @@ +import os +import pytest + + +def test_import_rviz_common(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import rviz_common # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_visualizationframe(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import VisualizationFrame # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_config(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import Config # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_visualizationmanager(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import VisualizationManager # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_viewmanager(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import ViewManager # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_displaygroup(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import DisplayGroup # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_display(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import Display # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_paneldockwidget(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import PanelDockWidget # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_tool(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import Tool # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_toolmanager(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import ToolManager # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_yamlreader(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import YamlConfigReader # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_yamlwriter(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import YamlConfigWriter # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_property(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import Property # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True) + + +def test_import_boolproperty(): + os.environ["QT_API"] = "pyside2" + try: + from rviz import BoolProperty # noqa: F401 + except Exception as exc: + pytest.fail(exc, pytrace=True)