├── .gitignore
├── .vscode
├── PythonImportHelper-v2-Completion.json
├── c_cpp_properties.json
└── settings.json
├── README.md
├── requirements.txt
└── src
├── test_msg
├── CMakeLists.txt
├── msg
│ └── Test.msg
└── package.xml
└── test_plot
├── CMakeLists.txt
├── launch
└── test.launch.py
├── package.xml
└── scripts
├── test_plot.py
├── test_plot1.py
└── test_pub.py
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | install/
3 | log/
4 | venv/
--------------------------------------------------------------------------------
/.vscode/PythonImportHelper-v2-Completion.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "label": "os",
4 | "kind": 6,
5 | "isExtraImport": true,
6 | "importPath": "os",
7 | "description": "os",
8 | "detail": "os",
9 | "documentation": {}
10 | },
11 | {
12 | "label": "getenv",
13 | "importPath": "os",
14 | "description": "os",
15 | "isExtraImport": true,
16 | "detail": "os",
17 | "documentation": {}
18 | },
19 | {
20 | "label": "getenv",
21 | "importPath": "os",
22 | "description": "os",
23 | "isExtraImport": true,
24 | "detail": "os",
25 | "documentation": {}
26 | },
27 | {
28 | "label": "builtins",
29 | "kind": 6,
30 | "isExtraImport": true,
31 | "importPath": "builtins",
32 | "description": "builtins",
33 | "detail": "builtins",
34 | "documentation": {}
35 | },
36 | {
37 | "label": "rosidl_parser.definition",
38 | "kind": 6,
39 | "isExtraImport": true,
40 | "importPath": "rosidl_parser.definition",
41 | "description": "rosidl_parser.definition",
42 | "detail": "rosidl_parser.definition",
43 | "documentation": {}
44 | },
45 | {
46 | "label": "find_packages",
47 | "importPath": "setuptools",
48 | "description": "setuptools",
49 | "isExtraImport": true,
50 | "detail": "setuptools",
51 | "documentation": {}
52 | },
53 | {
54 | "label": "setup",
55 | "importPath": "setuptools",
56 | "description": "setuptools",
57 | "isExtraImport": true,
58 | "detail": "setuptools",
59 | "documentation": {}
60 | },
61 | {
62 | "label": "threading",
63 | "kind": 6,
64 | "isExtraImport": true,
65 | "importPath": "threading",
66 | "description": "threading",
67 | "detail": "threading",
68 | "documentation": {}
69 | },
70 | {
71 | "label": "typing",
72 | "kind": 6,
73 | "isExtraImport": true,
74 | "importPath": "typing",
75 | "description": "typing",
76 | "detail": "typing",
77 | "documentation": {}
78 | },
79 | {
80 | "label": "matplotlib.pyplot",
81 | "kind": 6,
82 | "isExtraImport": true,
83 | "importPath": "matplotlib.pyplot",
84 | "description": "matplotlib.pyplot",
85 | "detail": "matplotlib.pyplot",
86 | "documentation": {}
87 | },
88 | {
89 | "label": "matplotlib.animation",
90 | "kind": 6,
91 | "isExtraImport": true,
92 | "importPath": "matplotlib.animation",
93 | "description": "matplotlib.animation",
94 | "detail": "matplotlib.animation",
95 | "documentation": {}
96 | },
97 | {
98 | "label": "numpy",
99 | "kind": 6,
100 | "isExtraImport": true,
101 | "importPath": "numpy",
102 | "description": "numpy",
103 | "detail": "numpy",
104 | "documentation": {}
105 | },
106 | {
107 | "label": "numpy.typing",
108 | "kind": 6,
109 | "isExtraImport": true,
110 | "importPath": "numpy.typing",
111 | "description": "numpy.typing",
112 | "detail": "numpy.typing",
113 | "documentation": {}
114 | },
115 | {
116 | "label": "rclpy",
117 | "kind": 6,
118 | "isExtraImport": true,
119 | "importPath": "rclpy",
120 | "description": "rclpy",
121 | "detail": "rclpy",
122 | "documentation": {}
123 | },
124 | {
125 | "label": "Test",
126 | "importPath": "test_msg.msg",
127 | "description": "test_msg.msg",
128 | "isExtraImport": true,
129 | "detail": "test_msg.msg",
130 | "documentation": {}
131 | },
132 | {
133 | "label": "Test",
134 | "importPath": "test_msg.msg",
135 | "description": "test_msg.msg",
136 | "isExtraImport": true,
137 | "detail": "test_msg.msg",
138 | "documentation": {}
139 | },
140 | {
141 | "label": "Test",
142 | "importPath": "test_msg.msg",
143 | "description": "test_msg.msg",
144 | "isExtraImport": true,
145 | "detail": "test_msg.msg",
146 | "documentation": {}
147 | },
148 | {
149 | "label": "Test",
150 | "importPath": "test_msg.msg",
151 | "description": "test_msg.msg",
152 | "isExtraImport": true,
153 | "detail": "test_msg.msg",
154 | "documentation": {}
155 | },
156 | {
157 | "label": "Test",
158 | "importPath": "test_msg.msg",
159 | "description": "test_msg.msg",
160 | "isExtraImport": true,
161 | "detail": "test_msg.msg",
162 | "documentation": {}
163 | },
164 | {
165 | "label": "Test",
166 | "importPath": "test_msg.msg",
167 | "description": "test_msg.msg",
168 | "isExtraImport": true,
169 | "detail": "test_msg.msg",
170 | "documentation": {}
171 | },
172 | {
173 | "label": "Subscription",
174 | "importPath": "rclpy.subscription",
175 | "description": "rclpy.subscription",
176 | "isExtraImport": true,
177 | "detail": "rclpy.subscription",
178 | "documentation": {}
179 | },
180 | {
181 | "label": "Subscription",
182 | "importPath": "rclpy.subscription",
183 | "description": "rclpy.subscription",
184 | "isExtraImport": true,
185 | "detail": "rclpy.subscription",
186 | "documentation": {}
187 | },
188 | {
189 | "label": "Subscription",
190 | "importPath": "rclpy.subscription",
191 | "description": "rclpy.subscription",
192 | "isExtraImport": true,
193 | "detail": "rclpy.subscription",
194 | "documentation": {}
195 | },
196 | {
197 | "label": "Subscription",
198 | "importPath": "rclpy.subscription",
199 | "description": "rclpy.subscription",
200 | "isExtraImport": true,
201 | "detail": "rclpy.subscription",
202 | "documentation": {}
203 | },
204 | {
205 | "label": "Node",
206 | "importPath": "rclpy.node",
207 | "description": "rclpy.node",
208 | "isExtraImport": true,
209 | "detail": "rclpy.node",
210 | "documentation": {}
211 | },
212 | {
213 | "label": "Node",
214 | "importPath": "rclpy.node",
215 | "description": "rclpy.node",
216 | "isExtraImport": true,
217 | "detail": "rclpy.node",
218 | "documentation": {}
219 | },
220 | {
221 | "label": "Node",
222 | "importPath": "rclpy.node",
223 | "description": "rclpy.node",
224 | "isExtraImport": true,
225 | "detail": "rclpy.node",
226 | "documentation": {}
227 | },
228 | {
229 | "label": "Node",
230 | "importPath": "rclpy.node",
231 | "description": "rclpy.node",
232 | "isExtraImport": true,
233 | "detail": "rclpy.node",
234 | "documentation": {}
235 | },
236 | {
237 | "label": "Node",
238 | "importPath": "rclpy.node",
239 | "description": "rclpy.node",
240 | "isExtraImport": true,
241 | "detail": "rclpy.node",
242 | "documentation": {}
243 | },
244 | {
245 | "label": "Node",
246 | "importPath": "rclpy.node",
247 | "description": "rclpy.node",
248 | "isExtraImport": true,
249 | "detail": "rclpy.node",
250 | "documentation": {}
251 | },
252 | {
253 | "label": "time",
254 | "kind": 6,
255 | "isExtraImport": true,
256 | "importPath": "time",
257 | "description": "time",
258 | "detail": "time",
259 | "documentation": {}
260 | },
261 | {
262 | "label": "rclpy.callback_groups",
263 | "kind": 6,
264 | "isExtraImport": true,
265 | "importPath": "rclpy.callback_groups",
266 | "description": "rclpy.callback_groups",
267 | "detail": "rclpy.callback_groups",
268 | "documentation": {}
269 | },
270 | {
271 | "label": "Client",
272 | "importPath": "rclpy.client",
273 | "description": "rclpy.client",
274 | "isExtraImport": true,
275 | "detail": "rclpy.client",
276 | "documentation": {}
277 | },
278 | {
279 | "label": "Client",
280 | "importPath": "rclpy.client",
281 | "description": "rclpy.client",
282 | "isExtraImport": true,
283 | "detail": "rclpy.client",
284 | "documentation": {}
285 | },
286 | {
287 | "label": "launch.actions",
288 | "kind": 6,
289 | "isExtraImport": true,
290 | "importPath": "launch.actions",
291 | "description": "launch.actions",
292 | "detail": "launch.actions",
293 | "documentation": {}
294 | },
295 | {
296 | "label": "get_package_share_directory",
297 | "importPath": "ament_index_python.packages",
298 | "description": "ament_index_python.packages",
299 | "isExtraImport": true,
300 | "detail": "ament_index_python.packages",
301 | "documentation": {}
302 | },
303 | {
304 | "label": "get_package_share_directory",
305 | "importPath": "ament_index_python.packages",
306 | "description": "ament_index_python.packages",
307 | "isExtraImport": true,
308 | "detail": "ament_index_python.packages",
309 | "documentation": {}
310 | },
311 | {
312 | "label": "LaunchDescription",
313 | "importPath": "launch",
314 | "description": "launch",
315 | "isExtraImport": true,
316 | "detail": "launch",
317 | "documentation": {}
318 | },
319 | {
320 | "label": "LaunchDescription",
321 | "importPath": "launch",
322 | "description": "launch",
323 | "isExtraImport": true,
324 | "detail": "launch",
325 | "documentation": {}
326 | },
327 | {
328 | "label": "Node",
329 | "importPath": "launch_ros.actions",
330 | "description": "launch_ros.actions",
331 | "isExtraImport": true,
332 | "detail": "launch_ros.actions",
333 | "documentation": {}
334 | },
335 | {
336 | "label": "Node",
337 | "importPath": "launch_ros.actions",
338 | "description": "launch_ros.actions",
339 | "isExtraImport": true,
340 | "detail": "launch_ros.actions",
341 | "documentation": {}
342 | },
343 | {
344 | "label": "argparse",
345 | "kind": 6,
346 | "isExtraImport": true,
347 | "importPath": "argparse",
348 | "description": "argparse",
349 | "detail": "argparse",
350 | "documentation": {}
351 | },
352 | {
353 | "label": "OrderedDict",
354 | "importPath": "collections",
355 | "description": "collections",
356 | "isExtraImport": true,
357 | "detail": "collections",
358 | "documentation": {}
359 | },
360 | {
361 | "label": "OrderedDict",
362 | "importPath": "collections",
363 | "description": "collections",
364 | "isExtraImport": true,
365 | "detail": "collections",
366 | "documentation": {}
367 | },
368 | {
369 | "label": "Path",
370 | "importPath": "pathlib",
371 | "description": "pathlib",
372 | "isExtraImport": true,
373 | "detail": "pathlib",
374 | "documentation": {}
375 | },
376 | {
377 | "label": "Path",
378 | "importPath": "pathlib",
379 | "description": "pathlib",
380 | "isExtraImport": true,
381 | "detail": "pathlib",
382 | "documentation": {}
383 | },
384 | {
385 | "label": "sys",
386 | "kind": 6,
387 | "isExtraImport": true,
388 | "importPath": "sys",
389 | "description": "sys",
390 | "detail": "sys",
391 | "documentation": {}
392 | },
393 | {
394 | "label": "Metaclass_Test",
395 | "kind": 6,
396 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
397 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
398 | "peekOfCode": "class Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None\n _TYPE_SUPPORT = None\n __constants = {\n }\n @classmethod",
399 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
400 | "documentation": {}
401 | },
402 | {
403 | "label": "Test",
404 | "kind": 6,
405 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
406 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
407 | "peekOfCode": "class Test(metaclass=Metaclass_Test):\n \"\"\"Message class 'Test'.\"\"\"\n __slots__ = [\n '_num',\n '_check_fields',\n ]\n _fields_and_field_types = {\n 'num': 'int64',\n }\n # This attribute is used to store an rosidl_parser.definition variable",
408 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
409 | "documentation": {}
410 | },
411 | {
412 | "label": "ros_python_check_fields",
413 | "kind": 5,
414 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
415 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
416 | "peekOfCode": "ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')\n# Import statements for member types\nimport builtins # noqa: E402, I100\nimport rosidl_parser.definition # noqa: E402, I100\nclass Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None",
417 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test",
418 | "documentation": {}
419 | },
420 | {
421 | "label": "Metaclass_Test",
422 | "kind": 6,
423 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
424 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
425 | "peekOfCode": "class Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None\n _TYPE_SUPPORT = None\n __constants = {\n }\n @classmethod",
426 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
427 | "documentation": {}
428 | },
429 | {
430 | "label": "Test",
431 | "kind": 6,
432 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
433 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
434 | "peekOfCode": "class Test(metaclass=Metaclass_Test):\n \"\"\"Message class 'Test'.\"\"\"\n __slots__ = [\n '_num',\n '_check_fields',\n ]\n _fields_and_field_types = {\n 'num': 'int64',\n }\n # This attribute is used to store an rosidl_parser.definition variable",
435 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
436 | "documentation": {}
437 | },
438 | {
439 | "label": "ros_python_check_fields",
440 | "kind": 5,
441 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
442 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
443 | "peekOfCode": "ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')\n# Import statements for member types\nimport builtins # noqa: E402, I100\nimport rosidl_parser.definition # noqa: E402, I100\nclass Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None",
444 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test",
445 | "documentation": {}
446 | },
447 | {
448 | "label": "Example_Node",
449 | "kind": 6,
450 | "importPath": "install.test_plot.lib.test_plot.test_plot",
451 | "description": "install.test_plot.lib.test_plot.test_plot",
452 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"",
453 | "detail": "install.test_plot.lib.test_plot.test_plot",
454 | "documentation": {}
455 | },
456 | {
457 | "label": "main",
458 | "kind": 2,
459 | "importPath": "install.test_plot.lib.test_plot.test_plot",
460 | "description": "install.test_plot.lib.test_plot.test_plot",
461 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()",
462 | "detail": "install.test_plot.lib.test_plot.test_plot",
463 | "documentation": {}
464 | },
465 | {
466 | "label": "Example_Node",
467 | "kind": 6,
468 | "importPath": "install.test_plot.lib.test_plot.test_plot1",
469 | "description": "install.test_plot.lib.test_plot.test_plot1",
470 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"",
471 | "detail": "install.test_plot.lib.test_plot.test_plot1",
472 | "documentation": {}
473 | },
474 | {
475 | "label": "main",
476 | "kind": 2,
477 | "importPath": "install.test_plot.lib.test_plot.test_plot1",
478 | "description": "install.test_plot.lib.test_plot.test_plot1",
479 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()",
480 | "detail": "install.test_plot.lib.test_plot.test_plot1",
481 | "documentation": {}
482 | },
483 | {
484 | "label": "TestPub",
485 | "kind": 6,
486 | "importPath": "install.test_plot.lib.test_plot.test_pub",
487 | "description": "install.test_plot.lib.test_plot.test_pub",
488 | "peekOfCode": "class TestPub(Node):\n def __init__(self) -> None:\n super().__init__(\"test_pub\")\n self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()\n self.pub = self.create_publisher(Test, \"test\", 10, callback_group=self.cbg)\n self.counter = 4\n time.sleep(5)\n self.timer = self.create_timer(1, self.loop_pub)\n def loop_pub(self) -> None:\n self.counter += 1",
489 | "detail": "install.test_plot.lib.test_plot.test_pub",
490 | "documentation": {}
491 | },
492 | {
493 | "label": "main",
494 | "kind": 2,
495 | "importPath": "install.test_plot.lib.test_plot.test_pub",
496 | "description": "install.test_plot.lib.test_plot.test_pub",
497 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = TestPub()\n rclpy.spin(node)\n rclpy.shutdown()\nif __name__ == \"__main__\":\n main()",
498 | "detail": "install.test_plot.lib.test_plot.test_pub",
499 | "documentation": {}
500 | },
501 | {
502 | "label": "generate_launch_description",
503 | "kind": 2,
504 | "importPath": "install.test_plot.share.test_plot.launch.test.launch",
505 | "description": "install.test_plot.share.test_plot.launch.test.launch",
506 | "peekOfCode": "def generate_launch_description():\n test_plotter = Node(\n package=\"test_plot\",\n executable=\"test_plot1.py\",\n name=\"test_plotter\",\n output=\"screen\",\n emulate_tty=True,\n )\n test_pub = Node(\n package=\"test_plot\",",
507 | "detail": "install.test_plot.share.test_plot.launch.test.launch",
508 | "documentation": {}
509 | },
510 | {
511 | "label": "main",
512 | "kind": 2,
513 | "importPath": "install._local_setup_util_ps1",
514 | "description": "install._local_setup_util_ps1",
515 | "peekOfCode": "def main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',\n help='The additional file extension to be considered')",
516 | "detail": "install._local_setup_util_ps1",
517 | "documentation": {}
518 | },
519 | {
520 | "label": "get_packages",
521 | "kind": 2,
522 | "importPath": "install._local_setup_util_ps1",
523 | "description": "install._local_setup_util_ps1",
524 | "peekOfCode": "def get_packages(prefix_path, merged_install):\n \"\"\"\n Find packages based on colcon-specific files created during installation.\n :param Path prefix_path: The install prefix path of all packages\n :param bool merged_install: The flag if the packages are all installed\n directly in the prefix or if each package is installed in a subdirectory\n named after the package\n :returns: A mapping from the package name to the set of runtime\n dependencies\n :rtype: dict",
525 | "detail": "install._local_setup_util_ps1",
526 | "documentation": {}
527 | },
528 | {
529 | "label": "add_package_runtime_dependencies",
530 | "kind": 2,
531 | "importPath": "install._local_setup_util_ps1",
532 | "description": "install._local_setup_util_ps1",
533 | "peekOfCode": "def add_package_runtime_dependencies(path, packages):\n \"\"\"\n Check the path and if it exists extract the packages runtime dependencies.\n :param Path path: The resource file containing the runtime dependencies\n :param dict packages: A mapping from package names to the sets of runtime\n dependencies to add to\n \"\"\"\n content = path.read_text()\n dependencies = set(content.split(os.pathsep) if content else [])\n packages[path.name] = dependencies",
534 | "detail": "install._local_setup_util_ps1",
535 | "documentation": {}
536 | },
537 | {
538 | "label": "order_packages",
539 | "kind": 2,
540 | "importPath": "install._local_setup_util_ps1",
541 | "description": "install._local_setup_util_ps1",
542 | "peekOfCode": "def order_packages(packages):\n \"\"\"\n Order packages topologically.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies\n :returns: The package names\n :rtype: list\n \"\"\"\n # select packages with no dependencies in alphabetical order\n to_be_ordered = list(packages.keys())",
543 | "detail": "install._local_setup_util_ps1",
544 | "documentation": {}
545 | },
546 | {
547 | "label": "reduce_cycle_set",
548 | "kind": 2,
549 | "importPath": "install._local_setup_util_ps1",
550 | "description": "install._local_setup_util_ps1",
551 | "peekOfCode": "def reduce_cycle_set(packages):\n \"\"\"\n Reduce the set of packages to the ones part of the circular dependency.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies which is modified in place\n \"\"\"\n last_depended = None\n while len(packages) > 0:\n # get all remaining dependencies\n depended = set()",
552 | "detail": "install._local_setup_util_ps1",
553 | "documentation": {}
554 | },
555 | {
556 | "label": "get_commands",
557 | "kind": 2,
558 | "importPath": "install._local_setup_util_ps1",
559 | "description": "install._local_setup_util_ps1",
560 | "peekOfCode": "def get_commands(pkg_name, prefix, primary_extension, additional_extension):\n commands = []\n package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv')\n if os.path.exists(package_dsv_path):\n commands += process_dsv_file(\n package_dsv_path, prefix, primary_extension, additional_extension)\n return commands\ndef process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):",
561 | "detail": "install._local_setup_util_ps1",
562 | "documentation": {}
563 | },
564 | {
565 | "label": "process_dsv_file",
566 | "kind": 2,
567 | "importPath": "install._local_setup_util_ps1",
568 | "description": "install._local_setup_util_ps1",
569 | "peekOfCode": "def process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):\n commands = []\n if _include_comments():\n commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path}))\n with open(dsv_path, 'r') as h:\n content = h.read()\n lines = content.splitlines()\n basenames = OrderedDict()",
570 | "detail": "install._local_setup_util_ps1",
571 | "documentation": {}
572 | },
573 | {
574 | "label": "handle_dsv_types_except_source",
575 | "kind": 2,
576 | "importPath": "install._local_setup_util_ps1",
577 | "description": "install._local_setup_util_ps1",
578 | "peekOfCode": "def handle_dsv_types_except_source(type_, remainder, prefix):\n commands = []\n if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET):\n try:\n env_name, value = remainder.split(';', 1)\n except ValueError:\n raise RuntimeError(\n \"doesn't contain a semicolon separating the environment name \"\n 'from the value')\n try_prefixed_value = os.path.join(prefix, value) if value else prefix",
579 | "detail": "install._local_setup_util_ps1",
580 | "documentation": {}
581 | },
582 | {
583 | "label": "FORMAT_STR_COMMENT_LINE",
584 | "kind": 5,
585 | "importPath": "install._local_setup_util_ps1",
586 | "description": "install._local_setup_util_ps1",
587 | "peekOfCode": "FORMAT_STR_COMMENT_LINE = '# {comment}'\nFORMAT_STR_SET_ENV_VAR = 'Set-Item -Path \"Env:{name}\" -Value \"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'",
588 | "detail": "install._local_setup_util_ps1",
589 | "documentation": {}
590 | },
591 | {
592 | "label": "FORMAT_STR_SET_ENV_VAR",
593 | "kind": 5,
594 | "importPath": "install._local_setup_util_ps1",
595 | "description": "install._local_setup_util_ps1",
596 | "peekOfCode": "FORMAT_STR_SET_ENV_VAR = 'Set-Item -Path \"Env:{name}\" -Value \"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'",
597 | "detail": "install._local_setup_util_ps1",
598 | "documentation": {}
599 | },
600 | {
601 | "label": "FORMAT_STR_USE_ENV_VAR",
602 | "kind": 5,
603 | "importPath": "install._local_setup_util_ps1",
604 | "description": "install._local_setup_util_ps1",
605 | "peekOfCode": "FORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'",
606 | "detail": "install._local_setup_util_ps1",
607 | "documentation": {}
608 | },
609 | {
610 | "label": "FORMAT_STR_INVOKE_SCRIPT",
611 | "kind": 5,
612 | "importPath": "install._local_setup_util_ps1",
613 | "description": "install._local_setup_util_ps1",
614 | "peekOfCode": "FORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103",
615 | "detail": "install._local_setup_util_ps1",
616 | "documentation": {}
617 | },
618 | {
619 | "label": "FORMAT_STR_REMOVE_LEADING_SEPARATOR",
620 | "kind": 5,
621 | "importPath": "install._local_setup_util_ps1",
622 | "description": "install._local_setup_util_ps1",
623 | "peekOfCode": "FORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(",
624 | "detail": "install._local_setup_util_ps1",
625 | "documentation": {}
626 | },
627 | {
628 | "label": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR",
629 | "kind": 5,
630 | "importPath": "install._local_setup_util_ps1",
631 | "description": "install._local_setup_util_ps1",
632 | "peekOfCode": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '",
633 | "detail": "install._local_setup_util_ps1",
634 | "documentation": {}
635 | },
636 | {
637 | "label": "DSV_TYPE_APPEND_NON_DUPLICATE",
638 | "kind": 5,
639 | "importPath": "install._local_setup_util_ps1",
640 | "description": "install._local_setup_util_ps1",
641 | "peekOfCode": "DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')",
642 | "detail": "install._local_setup_util_ps1",
643 | "documentation": {}
644 | },
645 | {
646 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE",
647 | "kind": 5,
648 | "importPath": "install._local_setup_util_ps1",
649 | "description": "install._local_setup_util_ps1",
650 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(",
651 | "detail": "install._local_setup_util_ps1",
652 | "documentation": {}
653 | },
654 | {
655 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS",
656 | "kind": 5,
657 | "importPath": "install._local_setup_util_ps1",
658 | "description": "install._local_setup_util_ps1",
659 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',",
660 | "detail": "install._local_setup_util_ps1",
661 | "documentation": {}
662 | },
663 | {
664 | "label": "DSV_TYPE_SET",
665 | "kind": 5,
666 | "importPath": "install._local_setup_util_ps1",
667 | "description": "install._local_setup_util_ps1",
668 | "peekOfCode": "DSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')",
669 | "detail": "install._local_setup_util_ps1",
670 | "documentation": {}
671 | },
672 | {
673 | "label": "DSV_TYPE_SET_IF_UNSET",
674 | "kind": 5,
675 | "importPath": "install._local_setup_util_ps1",
676 | "description": "install._local_setup_util_ps1",
677 | "peekOfCode": "DSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(",
678 | "detail": "install._local_setup_util_ps1",
679 | "documentation": {}
680 | },
681 | {
682 | "label": "DSV_TYPE_SOURCE",
683 | "kind": 5,
684 | "importPath": "install._local_setup_util_ps1",
685 | "description": "install._local_setup_util_ps1",
686 | "peekOfCode": "DSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',",
687 | "detail": "install._local_setup_util_ps1",
688 | "documentation": {}
689 | },
690 | {
691 | "label": "env_state",
692 | "kind": 5,
693 | "importPath": "install._local_setup_util_ps1",
694 | "description": "install._local_setup_util_ps1",
695 | "peekOfCode": "env_state = {}\ndef _append_unique_value(name, value):\n global env_state\n if name not in env_state:\n if os.environ.get(name):\n env_state[name] = set(os.environ[name].split(os.pathsep))\n else:\n env_state[name] = set()\n # append even if the variable has not been set yet, in case a shell script sets the\n # same variable without the knowledge of this Python script.",
696 | "detail": "install._local_setup_util_ps1",
697 | "documentation": {}
698 | },
699 | {
700 | "label": "main",
701 | "kind": 2,
702 | "importPath": "install._local_setup_util_sh",
703 | "description": "install._local_setup_util_sh",
704 | "peekOfCode": "def main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',\n help='The additional file extension to be considered')",
705 | "detail": "install._local_setup_util_sh",
706 | "documentation": {}
707 | },
708 | {
709 | "label": "get_packages",
710 | "kind": 2,
711 | "importPath": "install._local_setup_util_sh",
712 | "description": "install._local_setup_util_sh",
713 | "peekOfCode": "def get_packages(prefix_path, merged_install):\n \"\"\"\n Find packages based on colcon-specific files created during installation.\n :param Path prefix_path: The install prefix path of all packages\n :param bool merged_install: The flag if the packages are all installed\n directly in the prefix or if each package is installed in a subdirectory\n named after the package\n :returns: A mapping from the package name to the set of runtime\n dependencies\n :rtype: dict",
714 | "detail": "install._local_setup_util_sh",
715 | "documentation": {}
716 | },
717 | {
718 | "label": "add_package_runtime_dependencies",
719 | "kind": 2,
720 | "importPath": "install._local_setup_util_sh",
721 | "description": "install._local_setup_util_sh",
722 | "peekOfCode": "def add_package_runtime_dependencies(path, packages):\n \"\"\"\n Check the path and if it exists extract the packages runtime dependencies.\n :param Path path: The resource file containing the runtime dependencies\n :param dict packages: A mapping from package names to the sets of runtime\n dependencies to add to\n \"\"\"\n content = path.read_text()\n dependencies = set(content.split(os.pathsep) if content else [])\n packages[path.name] = dependencies",
723 | "detail": "install._local_setup_util_sh",
724 | "documentation": {}
725 | },
726 | {
727 | "label": "order_packages",
728 | "kind": 2,
729 | "importPath": "install._local_setup_util_sh",
730 | "description": "install._local_setup_util_sh",
731 | "peekOfCode": "def order_packages(packages):\n \"\"\"\n Order packages topologically.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies\n :returns: The package names\n :rtype: list\n \"\"\"\n # select packages with no dependencies in alphabetical order\n to_be_ordered = list(packages.keys())",
732 | "detail": "install._local_setup_util_sh",
733 | "documentation": {}
734 | },
735 | {
736 | "label": "reduce_cycle_set",
737 | "kind": 2,
738 | "importPath": "install._local_setup_util_sh",
739 | "description": "install._local_setup_util_sh",
740 | "peekOfCode": "def reduce_cycle_set(packages):\n \"\"\"\n Reduce the set of packages to the ones part of the circular dependency.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies which is modified in place\n \"\"\"\n last_depended = None\n while len(packages) > 0:\n # get all remaining dependencies\n depended = set()",
741 | "detail": "install._local_setup_util_sh",
742 | "documentation": {}
743 | },
744 | {
745 | "label": "get_commands",
746 | "kind": 2,
747 | "importPath": "install._local_setup_util_sh",
748 | "description": "install._local_setup_util_sh",
749 | "peekOfCode": "def get_commands(pkg_name, prefix, primary_extension, additional_extension):\n commands = []\n package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv')\n if os.path.exists(package_dsv_path):\n commands += process_dsv_file(\n package_dsv_path, prefix, primary_extension, additional_extension)\n return commands\ndef process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):",
750 | "detail": "install._local_setup_util_sh",
751 | "documentation": {}
752 | },
753 | {
754 | "label": "process_dsv_file",
755 | "kind": 2,
756 | "importPath": "install._local_setup_util_sh",
757 | "description": "install._local_setup_util_sh",
758 | "peekOfCode": "def process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):\n commands = []\n if _include_comments():\n commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path}))\n with open(dsv_path, 'r') as h:\n content = h.read()\n lines = content.splitlines()\n basenames = OrderedDict()",
759 | "detail": "install._local_setup_util_sh",
760 | "documentation": {}
761 | },
762 | {
763 | "label": "handle_dsv_types_except_source",
764 | "kind": 2,
765 | "importPath": "install._local_setup_util_sh",
766 | "description": "install._local_setup_util_sh",
767 | "peekOfCode": "def handle_dsv_types_except_source(type_, remainder, prefix):\n commands = []\n if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET):\n try:\n env_name, value = remainder.split(';', 1)\n except ValueError:\n raise RuntimeError(\n \"doesn't contain a semicolon separating the environment name \"\n 'from the value')\n try_prefixed_value = os.path.join(prefix, value) if value else prefix",
768 | "detail": "install._local_setup_util_sh",
769 | "documentation": {}
770 | },
771 | {
772 | "label": "FORMAT_STR_COMMENT_LINE",
773 | "kind": 5,
774 | "importPath": "install._local_setup_util_sh",
775 | "description": "install._local_setup_util_sh",
776 | "peekOfCode": "FORMAT_STR_COMMENT_LINE = '# {comment}'\nFORMAT_STR_SET_ENV_VAR = 'export {name}=\"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'",
777 | "detail": "install._local_setup_util_sh",
778 | "documentation": {}
779 | },
780 | {
781 | "label": "FORMAT_STR_SET_ENV_VAR",
782 | "kind": 5,
783 | "importPath": "install._local_setup_util_sh",
784 | "description": "install._local_setup_util_sh",
785 | "peekOfCode": "FORMAT_STR_SET_ENV_VAR = 'export {name}=\"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'",
786 | "detail": "install._local_setup_util_sh",
787 | "documentation": {}
788 | },
789 | {
790 | "label": "FORMAT_STR_USE_ENV_VAR",
791 | "kind": 5,
792 | "importPath": "install._local_setup_util_sh",
793 | "description": "install._local_setup_util_sh",
794 | "peekOfCode": "FORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'",
795 | "detail": "install._local_setup_util_sh",
796 | "documentation": {}
797 | },
798 | {
799 | "label": "FORMAT_STR_INVOKE_SCRIPT",
800 | "kind": 5,
801 | "importPath": "install._local_setup_util_sh",
802 | "description": "install._local_setup_util_sh",
803 | "peekOfCode": "FORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103",
804 | "detail": "install._local_setup_util_sh",
805 | "documentation": {}
806 | },
807 | {
808 | "label": "FORMAT_STR_REMOVE_LEADING_SEPARATOR",
809 | "kind": 5,
810 | "importPath": "install._local_setup_util_sh",
811 | "description": "install._local_setup_util_sh",
812 | "peekOfCode": "FORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(",
813 | "detail": "install._local_setup_util_sh",
814 | "documentation": {}
815 | },
816 | {
817 | "label": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR",
818 | "kind": 5,
819 | "importPath": "install._local_setup_util_sh",
820 | "description": "install._local_setup_util_sh",
821 | "peekOfCode": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '",
822 | "detail": "install._local_setup_util_sh",
823 | "documentation": {}
824 | },
825 | {
826 | "label": "DSV_TYPE_APPEND_NON_DUPLICATE",
827 | "kind": 5,
828 | "importPath": "install._local_setup_util_sh",
829 | "description": "install._local_setup_util_sh",
830 | "peekOfCode": "DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')",
831 | "detail": "install._local_setup_util_sh",
832 | "documentation": {}
833 | },
834 | {
835 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE",
836 | "kind": 5,
837 | "importPath": "install._local_setup_util_sh",
838 | "description": "install._local_setup_util_sh",
839 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(",
840 | "detail": "install._local_setup_util_sh",
841 | "documentation": {}
842 | },
843 | {
844 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS",
845 | "kind": 5,
846 | "importPath": "install._local_setup_util_sh",
847 | "description": "install._local_setup_util_sh",
848 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',",
849 | "detail": "install._local_setup_util_sh",
850 | "documentation": {}
851 | },
852 | {
853 | "label": "DSV_TYPE_SET",
854 | "kind": 5,
855 | "importPath": "install._local_setup_util_sh",
856 | "description": "install._local_setup_util_sh",
857 | "peekOfCode": "DSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')",
858 | "detail": "install._local_setup_util_sh",
859 | "documentation": {}
860 | },
861 | {
862 | "label": "DSV_TYPE_SET_IF_UNSET",
863 | "kind": 5,
864 | "importPath": "install._local_setup_util_sh",
865 | "description": "install._local_setup_util_sh",
866 | "peekOfCode": "DSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(",
867 | "detail": "install._local_setup_util_sh",
868 | "documentation": {}
869 | },
870 | {
871 | "label": "DSV_TYPE_SOURCE",
872 | "kind": 5,
873 | "importPath": "install._local_setup_util_sh",
874 | "description": "install._local_setup_util_sh",
875 | "peekOfCode": "DSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',",
876 | "detail": "install._local_setup_util_sh",
877 | "documentation": {}
878 | },
879 | {
880 | "label": "env_state",
881 | "kind": 5,
882 | "importPath": "install._local_setup_util_sh",
883 | "description": "install._local_setup_util_sh",
884 | "peekOfCode": "env_state = {}\ndef _append_unique_value(name, value):\n global env_state\n if name not in env_state:\n if os.environ.get(name):\n env_state[name] = set(os.environ[name].split(os.pathsep))\n else:\n env_state[name] = set()\n # append even if the variable has not been set yet, in case a shell script sets the\n # same variable without the knowledge of this Python script.",
885 | "detail": "install._local_setup_util_sh",
886 | "documentation": {}
887 | },
888 | {
889 | "label": "generate_launch_description",
890 | "kind": 2,
891 | "importPath": "src.test_plot.launch.test.launch",
892 | "description": "src.test_plot.launch.test.launch",
893 | "peekOfCode": "def generate_launch_description():\n test_plotter = Node(\n package=\"test_plot\",\n executable=\"test_plot1.py\",\n name=\"test_plotter\",\n output=\"screen\",\n emulate_tty=True,\n )\n test_pub = Node(\n package=\"test_plot\",",
894 | "detail": "src.test_plot.launch.test.launch",
895 | "documentation": {}
896 | },
897 | {
898 | "label": "Example_Node",
899 | "kind": 6,
900 | "importPath": "src.test_plot.scripts.test_plot",
901 | "description": "src.test_plot.scripts.test_plot",
902 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"",
903 | "detail": "src.test_plot.scripts.test_plot",
904 | "documentation": {}
905 | },
906 | {
907 | "label": "main",
908 | "kind": 2,
909 | "importPath": "src.test_plot.scripts.test_plot",
910 | "description": "src.test_plot.scripts.test_plot",
911 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()",
912 | "detail": "src.test_plot.scripts.test_plot",
913 | "documentation": {}
914 | },
915 | {
916 | "label": "Example_Node",
917 | "kind": 6,
918 | "importPath": "src.test_plot.scripts.test_plot1",
919 | "description": "src.test_plot.scripts.test_plot1",
920 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"",
921 | "detail": "src.test_plot.scripts.test_plot1",
922 | "documentation": {}
923 | },
924 | {
925 | "label": "main",
926 | "kind": 2,
927 | "importPath": "src.test_plot.scripts.test_plot1",
928 | "description": "src.test_plot.scripts.test_plot1",
929 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()",
930 | "detail": "src.test_plot.scripts.test_plot1",
931 | "documentation": {}
932 | },
933 | {
934 | "label": "TestPub",
935 | "kind": 6,
936 | "importPath": "src.test_plot.scripts.test_pub",
937 | "description": "src.test_plot.scripts.test_pub",
938 | "peekOfCode": "class TestPub(Node):\n def __init__(self) -> None:\n super().__init__(\"test_pub\")\n self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()\n self.pub = self.create_publisher(Test, \"test\", 10, callback_group=self.cbg)\n self.counter = 4\n time.sleep(5)\n self.timer = self.create_timer(1, self.loop_pub)\n def loop_pub(self) -> None:\n self.counter += 1",
939 | "detail": "src.test_plot.scripts.test_pub",
940 | "documentation": {}
941 | },
942 | {
943 | "label": "main",
944 | "kind": 2,
945 | "importPath": "src.test_plot.scripts.test_pub",
946 | "description": "src.test_plot.scripts.test_pub",
947 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = TestPub()\n rclpy.spin(node)\n rclpy.shutdown()\nif __name__ == \"__main__\":\n main()",
948 | "detail": "src.test_plot.scripts.test_pub",
949 | "documentation": {}
950 | }
951 | ]
--------------------------------------------------------------------------------
/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "browse": {
5 | "databaseFilename": "${default}",
6 | "limitSymbolsToIncludedHeaders": false
7 | },
8 | "includePath": [
9 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/include/**",
10 | "/home/tim-ub-pc/Documents/code/SA/install/power_energy_propogation/include/**",
11 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/include/**",
12 | "/opt/ros/jazzy/include/**",
13 | "include/**",
14 | "/usr/include/**"
15 | ],
16 | "name": "ROS",
17 | "intelliSenseMode": "gcc-x64",
18 | "compilerPath": "/usr/bin/gcc",
19 | "cStandard": "gnu11",
20 | "cppStandard": "c++14"
21 | }
22 | ],
23 | "version": 4
24 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.autoComplete.extraPaths": [
3 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/lib/python3.12/site-packages",
4 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/lib/python3.12/site-packages",
5 | "/opt/ros/jazzy/lib/python3.12/site-packages"
6 | ],
7 | "python.analysis.extraPaths": [
8 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/lib/python3.12/site-packages",
9 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/lib/python3.12/site-packages",
10 | "/opt/ros/jazzy/lib/python3.12/site-packages"
11 | ]
12 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Using MatplotLib with threading in Python to allow for Seamless Real Time Data Visualization
2 |
3 | ## Explanation of Threading and Matplotlib
4 | Matplotlib requires synchronous plotting functionality to occur on the main thread. Within ROS anything that is executed in a timer or callback is executed on a secondary thread. This means if a user is attempting to plot regularly from information that is retrieved from either service-client, or publisher-subscriber requires manual pushing of plotting to the main thread to allow for the images to be displayed.
5 |
6 | To explain how to do this two examples are shown. The first example shows how the threading can be achieved without ROS to explain what is happening on the Python side. The second example follows similar logic to the first example but is implemented in ROS2 to show what changes are specific to ROS.
7 |
8 | # Example of Threading Without ROS
9 |
10 | ```python
11 | import numpy as np
12 | import matplotlib.pyplot as plt
13 | import matplotlib.animation as anim
14 | import threading
15 | import time
16 |
17 |
18 | class Tester:
19 | """Example of how to use matplotlib with threading.
20 |
21 | Attributes:
22 | fig: Figure object for matplotlib
23 | ax: Axes object for matplotlib
24 | x: x values for matplotlib
25 | y: y values for matplotlib
26 | counter: counter for class
27 | lock: lock for matplotlib
28 | """
29 |
30 | def __init__(self):
31 | # Initialize figure and axes and save to class
32 | self.fig, self.ax = plt.subplots()
33 | # create initial values to plot
34 | self.plt_x = [i for i in range(5)]
35 | self.plt_width = [0.5 for i in range(5)]
36 | self.plt_counter = 0
37 | self._lock = threading.Lock()
38 |
39 | def plt_func(self, _):
40 | """Function for matplotlib animation.
41 |
42 | Args:
43 | _: Dummy variable for matplotlib animation
44 |
45 | Returns:
46 | Axes object for matplotlib
47 | """
48 | # lock thread
49 | with self._lock:
50 | x = np.array(self.plt_x)
51 | y = np.array(self.plt_width)
52 | self.ax.barh(y, 0.5, left=x, color="red")
53 | return self.ax
54 |
55 | def loop_logic(self):
56 | """Looping logic for adding data to plot."""
57 | time.sleep(3)
58 | print("init")
59 | while True:
60 | self.plt_add()
61 | time.sleep(1)
62 |
63 | def plt_add(self):
64 | """"Add data to class arrays."""
65 | with self._lock:
66 | if self.plt_counter > 4:
67 | self.plt_x.append(self.plt_counter)
68 | self.plt_width.append(0.5)
69 | self.plt_counter += 1
70 | print(self.plt_counter)
71 |
72 | def _plt(self):
73 | print("INIT PLOT")
74 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000)
75 | print("showing")
76 | plt.show()
77 |
78 |
79 | if __name__ == "__main__":
80 | test = Tester()
81 | thread = threading.Thread(target=test.loop_logic)
82 | thread.start()
83 | test._plt()
84 | ```
85 |
86 | # Explanation of Example 1
87 |
88 | In this example, the class Tester is used to simulate a node class when used in ROS. First, in the init function, a Matplotlib figure and axis are saved to the class. Both will be continually updated throughout the lifecycle of the class. The x, and y member variables are initialized with data to show how to update existing data. A threading lock must also be created to prevent the simultaneous access of the data that is to be plotted.
89 |
90 | The loop logic function begins by adding data to the x and y member variables every three seconds. This loop logic calls a second function ```plt_add``` that uses the threading lock ```_lock``` to add data to the member variables x and y.
91 |
92 | To allow for continuous updating of the axis the ```FuncAnimation``` function is used which updates the figures with a given callable at the frequency given by the keyword argument interval. Any callable used by FuncAnimation must return the axis that will update the given figure.
93 |
94 | The function ```plt_func``` follows this convention using the threading lock to retrieve the data from the member variables x and y and adding them to the axis and returning it.
95 |
96 | In the main function, the class is initialized and the ```loop_logic``` function is placed into a second thread and the thread is started. The ```_plt``` function is started in the main thread.
97 |
98 | # Example Using ROS
99 |
100 | Using this same logic the next example implements this plotting in a ROS NODE.
101 |
102 | ```python
103 | #! /usr/bin/env python3
104 |
105 | import threading
106 | import typing
107 |
108 | import matplotlib.pyplot as plt
109 | import matplotlib.animation as anim
110 | import numpy as np
111 | import numpy.typing as npt
112 | import rclpy
113 | from test_msg.msg import Test
114 | from rclpy.subscription import Subscription
115 | from rclpy.node import Node
116 |
117 |
118 | class Example_Node(Node):
119 | """Example Node for showing how to use matplotlib within ros 2 node
120 |
121 | Attributes:
122 | fig: Figure object for matplotlib
123 | ax: Axes object for matplotlib
124 | x: x values for matplotlib
125 | y: y values for matplotlib
126 | lock: lock for threading
127 | _sub: Subscriber for node
128 | """
129 |
130 | def __init__(self):
131 | """Initialize."""
132 | super().__init__("example_node")
133 | # Initialize figure and axes and save to class
134 | self.fig, self.ax = plt.subplots()
135 | # create Thread lock to prevent multiaccess threading errors
136 | self._lock = threading.Lock()
137 | # create initial values to plot
138 | self.x = [i for i in range(5)]
139 | self.width = [0.5 for i in range(5)]
140 | # create subscriber
141 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()
142 | self._sub: Subscription = self.create_subscription(
143 | Test, "test", self._callback, 10, callback_group=self.cbg
144 | )
145 |
146 | def _callback(self, msg: Test):
147 | """Callback for subscriber
148 |
149 | Args:
150 | msg: message from subscriber
151 | Message format
152 | ----------------
153 | int32 num
154 | """
155 | # lock thread
156 | with self._lock:
157 | # update values
158 | number: int = msg.num
159 | self.x.append(number)
160 | self.width.append(0.5)
161 |
162 | def plt_func(self, _):
163 | """Function for for adding data to axis.
164 |
165 | Args:
166 | _ : Dummy variable that is required for matplotlib animation.
167 |
168 | Returns:
169 | Axes object for matplotlib
170 | """
171 | # lock thread
172 | with self._lock:
173 | x = np.array(self.x)
174 | y = np.array(self.width)
175 | self.ax.barh(y, 0.5, left=x, color="red")
176 |
177 | return self.ax
178 |
179 | def _plt(self):
180 | """Function for initializing and showing matplotlib animation."""
181 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000)
182 | plt.show()
183 |
184 |
185 | def main(args=None):
186 | rclpy.init(args=args)
187 | node = Example_Node()
188 | executor = rclpy.executors.MultiThreadedExecutor()
189 | executor.add_node(node)
190 | thread = threading.Thread(target=executor.spin, daemon=True)
191 | thread.start()
192 | node._plt()
193 |
194 |
195 | if __name__ == "__main__":
196 | main()
197 |
198 | ```
199 |
200 | # Major Differences Between Example 1 and 2
201 |
202 | The main change is the loop_logic function from the previous example becomes a callback for the class's subscriber.
203 |
204 | Then in the main function, a multi-threaded executor is created and the example node is added to the executor. The spinning of this executor is then placed into the thread and an extra keyword is added ```daemon=True``` to allow for the ROS2 daemon to execute. Otherwise, the layout is the same.
205 |
206 | # Location of Examples and How to Run
207 |
208 | The Example Code can be found within the ```src``` folder of this repository. To run the examples, first, clone the repository and then build the package using colcon. To build this repository you must be using ROS2 Foxy or newer. The following commands will install the required dependencies, build the package, and run the example.
209 |
210 | ```bash
211 | cd "repository"
212 | source /opt/ros/$ROSDISTRO/setup.bash
213 | pip install -r requirements.txt
214 | colcon build
215 | . install/setup.bash
216 | ros2 launch test_plot test.launch.py
217 | ```
218 |
219 | # Boiler Plate Code
220 |
221 | # Boiler Plate Code
222 |
223 | Boiler plate code for plotting can be found on my profile and can be cloned with the following command.
224 |
225 | ```bash
226 | git clone https://github.com/timdodge54/boiler_plate_plotting.git
227 | ```
228 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib
2 | numpy
--------------------------------------------------------------------------------
/src/test_msg/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(test_msg)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 |
11 | find_package(std_msgs REQUIRED)
12 | find_package(rosidl_default_generators REQUIRED)
13 | find_package(builtin_interfaces REQUIRED)
14 |
15 | set(msg_files
16 | "msg/Test.msg"
17 | )
18 |
19 | rosidl_generate_interfaces(${PROJECT_NAME}
20 | ${msg_files}
21 | DEPENDENCIES builtin_interfaces std_msgs
22 | )
23 | ament_export_dependencies(rosidl_default_runtime)
24 | ament_export_dependencies(builtin_interfaces)
25 | ament_export_dependencies(std_msgs)
26 |
27 | include_directories(
28 | ${${PROJECT_NAME}_INCLUDE_DIRS}
29 |
30 | # ${std_msgs_INCLUDE_DIRS}
31 | )
32 |
33 | if(BUILD_TESTING)
34 | find_package(ament_lint_auto REQUIRED)
35 |
36 | # the following line skips the linter which checks for copyrights
37 | # uncomment the line when a copyright and license is not present in all source files
38 | # set(ament_cmake_copyright_FOUND TRUE)
39 | # the following line skips cpplint (only works in a git repo)
40 | # uncomment the line when this package is not in a git repo
41 | # set(ament_cmake_cpplint_FOUND TRUE)
42 | ament_lint_auto_find_test_dependencies()
43 | endif()
44 |
45 | ament_package()
46 |
--------------------------------------------------------------------------------
/src/test_msg/msg/Test.msg:
--------------------------------------------------------------------------------
1 | int64 num
--------------------------------------------------------------------------------
/src/test_msg/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | test_msg
5 | 0.0.0
6 | TODO: Package description
7 | Timothy Dodge
8 | Apache License 2.0
9 |
10 | ament_cmake
11 | rosidl_default_generators
12 |
13 | rosidl_default_generators
14 |
15 | rosidl_default_runtime
16 |
17 | rosidl_interface_packages
18 |
19 |
20 | builtin_interfaces
21 | std_msgs
22 |
23 | ament_lint_auto
24 | ament_lint_common
25 |
26 |
27 | ament_cmake
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/test_plot/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(test_plot)
3 |
4 | # Default to C99
5 | if(NOT CMAKE_C_STANDARD)
6 | set(CMAKE_C_STANDARD 99)
7 | endif()
8 |
9 | # Default to C++14
10 | if(NOT CMAKE_CXX_STANDARD)
11 | set(CMAKE_CXX_STANDARD 14)
12 | endif()
13 |
14 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
15 | add_compile_options(-Wall -Wextra -Wpedantic)
16 | endif()
17 |
18 | add_compile_options(-std=c++14)
19 |
20 | # add_compile_options(-g)
21 | add_compile_options(-march=native -O3 -DNBUG -omit-frame-pointer)
22 |
23 | # # Find catkin macros and libraries
24 | # # if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
25 | # # is used, also find other catkin packages
26 | find_package(ament_cmake REQUIRED)
27 | find_package(ament_cmake_python REQUIRED)
28 | find_package(rclpy REQUIRED)
29 | find_package(std_msgs REQUIRED)
30 | find_package(common_interfaces REQUIRED)
31 | find_package(test_msg REQUIRED)
32 |
33 | # find_package(Boost REQUIRED COMPONENTS system)
34 | # include_directories(
35 | # include
36 | # ${YAML_CPP_INCLUDE_DIRS}
37 | # )
38 |
39 | # install(TARGETS
40 | # threshold_operator_planner
41 | # DESTINATION lib/${PROJECT_NAME})
42 |
43 | # ament_python_install_package(${PROJECT_NAME})
44 | # ament_python_install_package(UTA_Bus_Tracking)
45 | install(PROGRAMS
46 | scripts/test_plot.py
47 | scripts/test_plot1.py
48 | scripts/test_pub.py
49 | DESTINATION lib/${PROJECT_NAME})
50 |
51 | install(DIRECTORY
52 | launch
53 | DESTINATION share/${PROJECT_NAME}/
54 | )
55 |
56 | ament_export_include_directories(include)
57 | ament_export_dependencies(std_msgs)
58 |
59 | if(BUILD_TESTING)
60 | find_package(ament_lint_auto REQUIRED)
61 |
62 | # # the following line skips the linter which checks for copyrights
63 | # # uncomment the line when a copyright and license is not present in all source files
64 | # # set(ament_cmake_copyright_FOUND TRUE)
65 | # # the following line skips cpplint (only works in a git repo)
66 | # # uncomment the line when this package is not in a git repo
67 | # # set(ament_cmake_cpplint_FOUND TRUE)
68 | ament_lint_auto_find_test_dependencies()
69 | endif()
70 |
71 | ament_package()
72 |
--------------------------------------------------------------------------------
/src/test_plot/launch/test.launch.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import launch.actions as actions
4 | from ament_index_python.packages import get_package_share_directory
5 | from launch import LaunchDescription
6 | from launch_ros.actions import Node
7 |
8 |
9 | def generate_launch_description():
10 | test_plotter = Node(
11 | package="test_plot",
12 | executable="test_plot1.py",
13 | name="test_plotter",
14 | output="screen",
15 | emulate_tty=True,
16 | )
17 | test_pub = Node(
18 | package="test_plot",
19 | executable="test_pub.py",
20 | name="test_pub",
21 | output="screen",
22 | emulate_tty=True,
23 | )
24 |
25 | ld = LaunchDescription()
26 | ld.add_action(test_pub)
27 | ld.add_action(test_plotter)
28 | return ld
29 |
--------------------------------------------------------------------------------
/src/test_plot/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | test_plot
5 | 0.0.0
6 | TODO: Package description
7 | Timothy Dodge
8 | Apache License 2.0
9 |
10 | ament_cmake
11 | rclpy
12 | test_msg
13 |
14 | ament_lint_auto
15 | ament_lint_common
16 | rclpy
17 |
18 |
19 |
20 | ament_cmake
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/test_plot/scripts/test_plot.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | import threading
4 | import typing
5 |
6 | import matplotlib.pyplot as plt
7 | import matplotlib.animation as anim
8 | import numpy as np
9 | import numpy.typing as npt
10 | import rclpy
11 | from test_msg.msg import Test
12 | from rclpy.subscription import Subscription
13 | from rclpy.node import Node
14 |
15 |
16 | class Example_Node(Node):
17 | """Example Node for showing how to use matplotlib within ros 2 node
18 |
19 | Attributes:
20 | fig: Figure object for matplotlib
21 | ax: Axes object for matplotlib
22 | x: x values for matplotlib
23 | y: y values for matplotlib
24 | lock: lock for threading
25 | _sub: Subscriber for node
26 | """
27 |
28 | def __init__(self):
29 | """Initialize."""
30 | super().__init__("example_node")
31 | # Initialize figure and axes and save to class
32 | self.fig, self.ax = plt.subplots()
33 | # create Thread lock to prevent multiaccess threading errors
34 | self._lock = threading.Lock()
35 | # create initial values to plot
36 | self.x = [i for i in range(5)]
37 | self.width = [0.5 for i in range(5)]
38 | # create subscriber
39 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()
40 | self._sub: Subscription = self.create_subscription(
41 | Test, "test", self._callback, 10, callback_group=self.cbg
42 | )
43 |
44 | def _callback(self, msg: Test):
45 | """Callback for subscriber
46 |
47 | Args:
48 | msg: message from subscriber
49 | Message format
50 | ----------------
51 | int32 num
52 | """
53 | # lock thread
54 | with self._lock:
55 | # update values
56 | number: int = msg.num
57 | self.x.append(number)
58 | self.width.append(0.5)
59 | # update counter
60 |
61 | def plt_func(self, _):
62 | """Function for for adding data to axis.
63 |
64 | Args:
65 | _ : Dummy variable that is required for matplotlib animation.
66 |
67 | Returns:
68 | Axes object for matplotlib
69 | """
70 | # lock thread
71 | with self._lock:
72 | x = np.array(self.x)
73 | y = np.array(self.width)
74 | self.ax.barh(y, 0.5, left=x, color="red")
75 |
76 | return self.ax
77 |
78 | def _plt(self):
79 | """Function for initializing and showing matplotlib animation."""
80 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000)
81 | plt.show()
82 |
83 |
84 | def main(args=None):
85 | rclpy.init(args=args)
86 | node = Example_Node()
87 | executor = rclpy.executors.MultiThreadedExecutor()
88 | executor.add_node(node)
89 | thread = threading.Thread(target=executor.spin, daemon=True)
90 | thread.start()
91 | node._plt()
92 |
93 |
94 | if __name__ == "__main__":
95 | main()
96 |
--------------------------------------------------------------------------------
/src/test_plot/scripts/test_plot1.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 |
3 | import threading
4 | import typing
5 |
6 | import matplotlib.pyplot as plt
7 | import matplotlib.animation as anim
8 | import numpy as np
9 | import numpy.typing as npt
10 | import rclpy
11 | from test_msg.msg import Test
12 | from rclpy.subscription import Subscription
13 | from rclpy.node import Node
14 |
15 |
16 | class Example_Node(Node):
17 | """Example Node for showing how to use matplotlib within ros 2 node
18 |
19 | Attributes:
20 | fig: Figure object for matplotlib
21 | ax: Axes object for matplotlib
22 | x: x values for matplotlib
23 | y: y values for matplotlib
24 | lock: lock for threading
25 | _sub: Subscriber for node
26 | """
27 |
28 | def __init__(self):
29 | """Initialize."""
30 | super().__init__("example_node")
31 | # Initialize figure and axes and save to class
32 | self.fig, self.ax = plt.subplots()
33 | # create Thread lock to prevent multiaccess threading errors
34 | self._lock = threading.Lock()
35 | # create initial values to plot
36 | self.x = [i for i in range(5)]
37 | self.current_x = 5
38 | self.width = [0.5 for i in range(5)]
39 | # create subscriber
40 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()
41 | self._sub: Subscription = self.create_subscription(
42 | Test, "test", self._callback, 10, callback_group=self.cbg
43 | )
44 |
45 | def _callback(self, msg: Test):
46 | """Callback for subscriber
47 |
48 | Args:
49 | msg: message from subscriber
50 | Message format
51 | ----------------
52 | int32 num
53 | """
54 | # lock thread
55 | with self._lock:
56 | # update values
57 | number: int = msg.num
58 | self.x.append(self.current_x)
59 | self.width.append(number)
60 | self.current_x += 1
61 | # update counter
62 |
63 | def plt_func(self, _):
64 | """Function for for adding data to axis.
65 |
66 | Args:
67 | _ : Dummy variable that is required for matplotlib animation.
68 |
69 | Returns:
70 | Axes object for matplotlib
71 | """
72 | # lock thread
73 | with self._lock:
74 | x = np.array(self.x)
75 | y = np.array(self.width)
76 | self.ax.plot(x, y, color="red")
77 |
78 | return self.ax
79 |
80 | def _plt(self):
81 | """Function for initializing and showing matplotlib animation."""
82 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000)
83 | plt.show()
84 |
85 |
86 | def main(args=None):
87 | rclpy.init(args=args)
88 | node = Example_Node()
89 | executor = rclpy.executors.MultiThreadedExecutor()
90 | executor.add_node(node)
91 | thread = threading.Thread(target=executor.spin, daemon=True)
92 | thread.start()
93 | node._plt()
94 |
95 |
96 | if __name__ == "__main__":
97 | main()
98 |
--------------------------------------------------------------------------------
/src/test_plot/scripts/test_pub.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | import time
3 |
4 | import rclpy
5 | import rclpy.callback_groups
6 | from test_msg.msg import Test
7 | from rclpy.client import Client
8 | from rclpy.node import Node
9 | import numpy as np
10 |
11 |
12 | class TestPub(Node):
13 | def __init__(self) -> None:
14 | super().__init__("test_pub")
15 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()
16 | self.pub = self.create_publisher(Test, "test", 10, callback_group=self.cbg)
17 | self.counter = 4
18 | time.sleep(5)
19 | self.timer = self.create_timer(1, self.loop_pub)
20 |
21 | def loop_pub(self) -> None:
22 | self.counter += 1
23 | msg = Test()
24 | msg.num = np.random.randint(0, 30)
25 | self.pub.publish(msg)
26 |
27 |
28 | def main(args=None):
29 | rclpy.init(args=args)
30 | node = TestPub()
31 | rclpy.spin(node)
32 | rclpy.shutdown()
33 |
34 |
35 | if __name__ == "__main__":
36 | main()
37 |
--------------------------------------------------------------------------------