Skip to content
Snippets Groups Projects
Commit ab8d72c0 authored by Jeffrey Fisher's avatar Jeffrey Fisher
Browse files

WIP rewrite with Python rqt

parent e800a3f0
No related branches found
No related tags found
No related merge requests found
cmake_minimum_required(VERSION 3.8)
project(qubo_gui)
find_package(ament_cmake REQUIRED)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(rclcpp REQUIRED)
find_package(qt_gui_cpp REQUIRED)
find_package(rqt_gui_cpp REQUIRED)
find_package(Qt5Widgets REQUIRED)
set(qubo_gui_SRCS
src/gui.cpp
)
set(qubo_gui_HEADERS
include/qubo_gui/gui.h
)
set(qubo_gui_UIS
src/gui.ui
)
if(BUILD_TESTING)
find_package(ament_lint_auto 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()
qt5_wrap_cpp(qubo_gui_MOCS ${qubo_gui_HEADERS})
qt5_wrap_ui(qubo_gui_UIS_H ${qubo_gui_UIS})
add_library(${PROJECT_NAME} SHARED
${qubo_gui_SRCS}
${qubo_gui_MOCS}
${qubo_gui_UIS_H}
)
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
)
target_link_libraries(${PROJECT_NAME} PUBLIC
${rclcpp_TARGETS}
${qt_gui_cpp_TARGETS}
${rqt_gui_cpp_TARGETS}
Qt5::Widgets
)
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}
LIBRARY DESTINATION lib/${PROJECT_NAME}
RUNTIME DESTINATION bin/${PROJECT_NAME}
)
install(
DIRECTORY include/
DESTINATION include/${PROJECT_NAME}
)
install(FILES plugin.xml
DESTINATION share/${PROJECT_NAME}
)
pluginlib_export_plugin_description_file(qubo_gui "plugin.xml")
ament_export_targets(${PROJECT_NAME})
ament_package()
#if !defined(QUBO_GUI_H)
#define QUBO_GUI_H
#include <rqt_qui_cpp/plugin.h>
#include <QWidget>
class QuboWidget : public rqt_gui_cpp::Plugin
{
Q_OBJECT
public:
QuboWidget();
virtual void initPlugin(qt_gui_cpp::PluginContext& context);
virtual void shutdownPlugin();
// virtual void saveSetttings
}
#endif
......@@ -4,27 +4,20 @@
<name>qubo_gui</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="team@ram.umd.edu">R@M</maintainer>
<maintainer email="jegfish@todo.todo">jegfish</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<build_depend>qtbase5-dev</build_depend>
<build_depend>rqt_gui</build_depend>
<build_depend>rqt_gui_cpp</build_depend>
<build_depend>qt_gui_cpp</build_depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<exec_depend>rqt_gui</exec_depend>
<exec_depend>rqt_gui_cpp</exec_depend>
<exec_depend>qt_gui_cpp</exec_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<exec_depend>rqt_gui_py</exec_depend>
<exec_depend>rqt_py_common</exec_depend>
<export>
<build_type>ament_cmake</build_type>
<rqt_gui plugi="${prefix}/plugin.xml"/>
<build_type>ament_python</build_type>
<rqt_gui plugin="${prefix}/plugin.xml"/>
</export>
</package>
<library path="qubo_gui">
<class name="qubo_gui/QuboWidget" type="qubo_gui::QuboWidget" base_class_type="rqt_gui_cpp::Plugin">
<class name="ThrustersPlugin" type="qubo_gui.thrusters.Thrusters" base_class_type="rqt_gui_py::Plugin">
<description>
A GUI plugin for Qubo.
An example Python GUI plugin to create a great user interface.
</description>
<qtgui>
<group>
<label>Qubo</label>
<icon type="theme">folder</icon>
<statustip>Plugins related to Qubo.</statustip>
<label>Visualization</label>
</group>
<label>Qubo Widget</label>
<icon type="theme">image-x-generic</icon>
<statustip>A GUI plugin for Qubo.</statustip>
<label>My first Python Plugin</label>
<icon type="theme">system-help</icon>
<statustip>Great user interface to provide real value.</statustip>
</qtgui>
</class>
<class name="ExamplePlugin" type="qubo_gui.plugin2.Plugin2" base_class_type="rqt_gui_py::Plugin">
<description>
An example Python GUI plugin to create a great user interface.
</description>
<qtgui>
<group>
<label>Visualization</label>
</group>
<label>another plugin of mine</label>
<icon type="theme">system-help</icon>
<statustip>Great user interface to provide real value.</statustip>
</qtgui>
</class>
</library>
File moved
import random
from qt_gui.plugin import Plugin
from python_qt_binding import QtCore
# from python_qt_binding.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
import python_qt_binding.QtWidgets as QtWidgets
# https://stackoverflow.com/questions/77934193/how-can-i-create-a-simple-gui-using-an-rqt-plugin
class Plugin2(Plugin):
def __init__(self, context):
super(Plugin2, self).__init__(context)
self._context = context
self.setObjectName("ThrustersPlugin")
self._widget = MyWidget()
self._widget.show()
context.add_widget(self._widget)
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
self.button = QtWidgets.QPushButton("Click on this button, me!")
self.text = QtWidgets.QLabel("Hello World",
alignment=QtCore.Qt.AlignCenter)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.button.clicked.connect(self.magic)
@QtCore.Slot()
def magic(self):
self.text.setText(random.choice(self.hello))
# class MyWidget(QWidget):
# def __init__(self):
# super(MyWidget, self).__init__()
# print("MyWidget constructor called...")
# self.setStyleSheet("background-color: white;")
# # Create a layout
# layout = QVBoxLayout(self)
# # Create a button
# self.button = QPushButton('Click Me!', self)
# self.button.clicked.connect(self.on_button_click)
# # Create a label for text display
# self.label = QLabel('Hello, World!', self)
# # Add the button and label to the layout
# layout.addWidget(self.button)
# layout.addWidget(self.label)
# # Set the layout for the widget
# self.setLayout(layout)
# def on_button_click(self):
# self.label.setText('Button Clicked!')
\ No newline at end of file
from .thrusters import Thrusters
def main():
print('Hi from qubo_gui.')
if __name__ == '__main__':
main()
import random
from qt_gui.plugin import Plugin
from python_qt_binding import QtCore
# from python_qt_binding.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
import python_qt_binding.QtWidgets as QtWidgets
# https://stackoverflow.com/questions/77934193/how-can-i-create-a-simple-gui-using-an-rqt-plugin
class Thrusters(Plugin):
def __init__(self, context):
super(Thrusters, self).__init__(context)
self._context = context
self.setObjectName("ThrustersPlugin")
self._widget = MyWidget()
self._widget.show()
context.add_widget(self._widget)
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
self.button = QtWidgets.QPushButton("Click me!")
self.text = QtWidgets.QLabel("Hello World",
alignment=QtCore.Qt.AlignCenter)
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.button.clicked.connect(self.magic)
@QtCore.Slot()
def magic(self):
self.text.setText(random.choice(self.hello))
# class MyWidget(QWidget):
# def __init__(self):
# super(MyWidget, self).__init__()
# print("MyWidget constructor called...")
# self.setStyleSheet("background-color: white;")
# # Create a layout
# layout = QVBoxLayout(self)
# # Create a button
# self.button = QPushButton('Click Me!', self)
# self.button.clicked.connect(self.on_button_click)
# # Create a label for text display
# self.label = QLabel('Hello, World!', self)
# # Add the button and label to the layout
# layout.addWidget(self.button)
# layout.addWidget(self.label)
# # Set the layout for the widget
# self.setLayout(layout)
# def on_button_click(self):
# self.label.setText('Button Clicked!')
\ No newline at end of file
[develop]
script-dir=$base/lib/qubo_gui
[install]
install-scripts=$base/lib/qubo_gui
setup.py 0 → 100644
from setuptools import setup
package_name = 'qubo_gui'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml', 'plugin.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='jegfish',
maintainer_email='jegfish@todo.todo',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'qubo_gui = qubo_gui.qubo_gui:main'
],
},
)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QuboWidget</class>
<widget class="QWidget" name="QuboWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>425</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Image View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="toolbar_widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="topics_combo_box">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refresh_topics_push_button">
<property name="toolTip">
<string>Refresh topics</string>
</property>
<property name="icon">
<iconset theme="view-refresh">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="zoom_1_push_button">
<property name="toolTip">
<string>Original zoom</string>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="zoom-original">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="num_gridlines_spin_box">
<property name="toolTip">
<string># of gridlines to overlay</string>
</property>
<property name="minimum">
<double>0</double>
</property>
<property name="maximum">
<double>20</double>
</property>
<property name="value">
<double>0</double>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="dynamic_range_check_box">
<property name="toolTip">
<string>Dynamic depth range</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="max_range_double_spin_box">
<property name="toolTip">
<string>Max depth</string>
</property>
<property name="suffix">
<string>m</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save_as_image_push_button">
<property name="toolTip">
<string>Save as image</string>
</property>
<property name="icon">
<iconset theme="image-x-generic">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="publish_click_location_check_box">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="publish_click_location_topic_line_edit">
<property name="toolTip">
<string>Click location topic (leave empty for auto-naming)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="smooth_image_check_box">
<property name="toolTip">
<string>Dynamic depth range</string>
</property>
<property name="text">
<string>Smooth scaling</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rotate_left_push_button">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="object-rotate-left">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rotate_label">
<property name="text">
<string></string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rotate_right_push_button">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="object-rotate-right">
<normaloff/>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="color_scheme_combo_box">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="image_layout" stretch="1,0">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>767</width>
<height>650</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="rqt_image_view::RatioLayoutedFrame" name="image_frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>60</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
</widget>
</item>
<property name="margin">
<number>1</number>
</property>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>rqt_image_view::RatioLayoutedFrame</class>
<extends>QFrame</extends>
<header>rqt_image_view/ratio_layouted_frame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_copyright.main import main
import pytest
@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'
# Copyright 2017 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_flake8.main import main_with_errors
import pytest
@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc, errors = main_with_errors(argv=[])
assert rc == 0, \
'Found %d code style errors / warnings:\n' % len(errors) + \
'\n'.join(errors)
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from ament_pep257.main import main
import pytest
@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found code style errors / warnings'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment