当前位置: 首页 > news >正文

Open3D解决SceneWidget加入布局中消失的问题

Open3D解决SceneWidget加入布局中消失的问题

  • Open3D解决SceneWidget加入布局中消失的问题
    • 1. 问题
    • 2. 问题代码
    • 3. 解决

Open3D解决SceneWidget加入布局中消失的问题

1. 问题

把SceneWidget加到布局管理其中图形可以展示出来,但是鼠标点击就消失了。

stackoverflow上已经有人提出这个问题了,还是2022年的时候,可是现在好像也没有解决。
https://stackoverflow.com/questions/71706506/why-does-open3d-visualization-disappear-when-i-left-click-the-window

2. 问题代码

在这里插入图片描述

# ----------------------------------------------------------------------------
# -                        Open3D: www.open3d.org                            -
# ----------------------------------------------------------------------------
# Copyright (c) 2018-2024 www.open3d.org
# SPDX-License-Identifier: MIT
# ----------------------------------------------------------------------------

import open3d as o3d
import open3d.visualization.gui as gui
import open3d.visualization.rendering as rendering
import platform
import random
import threading
import time

isMacOS = (platform.system() == "Darwin")


# This example shows two methods of adding geometry to an existing scene.
# 1) add via a UI callback (in this case a menu, but a button would be similar,
#    you would call `button.set_on_clicked(self.on_menu_sphere_)` when
#    configuring the button. See `on_menu_sphere()`.
# 2) add asynchronously by polling from another thread. GUI functions must be
#    called from the UI thread, so use Application.post_to_main_thread().
#    See `on_menu_random()`.
# Running the example will show a simple window with a Debug menu item with the
# two different options. The second method will add random spheres for
# 20 seconds, during which time you can be interacting with the scene, rotating,
# etc.
class SpheresApp:
    MENU_SPHERE = 1
    MENU_RANDOM = 2
    MENU_QUIT = 3

    def __init__(self):
        self._id = 0
        self.window = gui.Application.instance.create_window(
            "Add Spheres Example", 800, 600)


        # The menu is global (because the macOS menu is global), so only create
        # it once, no matter how many windows are created
        if gui.Application.instance.menubar is None:
            if isMacOS:
                app_menu = gui.Menu()
                app_menu.add_item("Quit", SpheresApp.MENU_QUIT)
            debug_menu = gui.Menu()
            debug_menu.add_item("Add Sphere", SpheresApp.MENU_SPHERE)
            debug_menu.add_item("Add Random Spheres", SpheresApp.MENU_RANDOM)
            if not isMacOS:
                debug_menu.add_separator()
                debug_menu.add_item("Quit", SpheresApp.MENU_QUIT)

            menu = gui.Menu()
            if isMacOS:
                # macOS will name the first menu item for the running application
                # (in our case, probably "Python"), regardless of what we call
                # it. This is the application menu, and it is where the
                # About..., Preferences..., and Quit menu items typically go.
                menu.add_menu("Example", app_menu)
                menu.add_menu("Debug", debug_menu)
            else:
                menu.add_menu("Debug", debug_menu)
            gui.Application.instance.menubar = menu

        # The menubar is global, but we need to connect the menu items to the
        # window, so that the window can call the appropriate function when the
        # menu item is activated.
        self.window.set_on_menu_item_activated(SpheresApp.MENU_SPHERE,
                                               self._on_menu_sphere)
        self.window.set_on_menu_item_activated(SpheresApp.MENU_RANDOM,
                                               self._on_menu_random)
        self.window.set_on_menu_item_activated(SpheresApp.MENU_QUIT,
                                               self._on_menu_quit)

        self.scene = gui.SceneWidget()
        self.scene.scene = rendering.Open3DScene(self.window.renderer)
        self.scene.scene.show_axes(True)
        mesh = o3d.geometry.TriangleMesh.create_sphere()
        mesh.compute_vertex_normals()
        material = rendering.MaterialRecord()
        material.shader = "defaultLit"
        self.scene.scene.add_geometry("sphere" + str(self._id), mesh, material)

        em = self.window.theme.font_size

        self.layout = gui.Horiz(0, gui.Margins(0.25*em,0.25*em,0.25*em,0.254*em))
        self.layout.add_child(gui.Label("Model file"))
        self.layout.add_fixed(0.25 * em)

        self.window.set_on_layout(self.on_window_layout)
        self.vlayout = gui.Vert(0, gui.Margins(0.25 * em, 0.25 * em, 0.25 * em, 0.254 * em))
        self.vlayout.add_child(self.scene)
        self.vlayout.add_fixed(0.25 * em)
        self.window.add_child(self.vlayout)
        self.window.add_child(self.layout)

    def on_window_layout(self, layout: gui.LayoutContext) -> None:
        """
        This is called when layout is required for this widgets *immediate children*, like on resize. Only the immediate
        children are *manually* positioned here, by setting the x, y, width, and height of their frames. The
        grandchildren are *not* touched here; they're automatically handled after this.
        """

        # This is the area in this widget available to place child widgets in. The *units* here aren't window screen
        # pixels, like from the width/height here when setting up the window: gui.Application.instance.create_window("Test", width=500, height=600).
        rect = self.window.content_rect
        r = self.window.content_rect
        print(r)

        print(layout.theme.default_layout_spacing)
        print(layout.theme.default_margin)
        print(layout.theme.font_size)

        # Put the layout (containing the controls) on the left 1/3 and the and scene on the right 2/3.
        x_division = rect.width // 3

        # Set the layout (gui.Vert) on left to 1/3 available width, full height.
        # gui.Rect(x, y, width, height)
        # Set the SceneWidget on on right, 2/3 available width, full height
        self.vlayout.frame = gui.Rect(0, 0, r.width - x_division, r.height)
        self.layout.frame = gui.Rect(r.width - x_division, 0, x_division, r.height)

    def add_sphere(self):
        self._id += 1
        mat = rendering.MaterialRecord()
        mat.base_color = [
            random.random(),
            random.random(),
            random.random(), 1.0
        ]
        mat.shader = "defaultLit"
        sphere = o3d.geometry.TriangleMesh.create_sphere(0.5)
        sphere.compute_vertex_normals()
        sphere.translate([
            10.0 * random.uniform(-1.0, 1.0), 10.0 * random.uniform(-1.0, 1.0),
            10.0 * random.uniform(-1.0, 1.0)
        ])

        self.scene.scene.add_geometry("sphere" + str(self._id), sphere, mat)

    def _on_menu_sphere(self):
        # GUI callbacks happen on the main thread, so we can do everything
        # normally here.
        self.scene.scene.clear_geometry()
        self.add_sphere()

    def _on_menu_random(self):
        # This adds spheres asynchronously. This pattern is useful if you have
        # data coming in from another source than user interaction.
        def thread_main():
            for _ in range(0, 20):
                # We can only modify GUI objects on the main thread, so we
                # need to post the function to call to the main thread.
                gui.Application.instance.post_to_main_thread(
                    self.window, self.add_sphere)
                time.sleep(0.5)

        threading.Thread(target=thread_main).start()

    def _on_menu_quit(self):
        gui.Application.instance.quit()


def main():
    gui.Application.instance.initialize()
    SpheresApp()
    gui.Application.instance.run()


if __name__ == "__main__":
    main()

3. 解决

不能放在布局中就直接手动布局吧,
不加到布局中去。。。

在这里插入图片描述

# ----------------------------------------------------------------------------
# -                        Open3D: www.open3d.org                            -
# ----------------------------------------------------------------------------
# Copyright (c) 2018-2024 www.open3d.org
# SPDX-License-Identifier: MIT
# ----------------------------------------------------------------------------

import open3d as o3d
import open3d.visualization.gui as gui
import open3d.visualization.rendering as rendering
import platform
import random
import threading
import time

isMacOS = (platform.system() == "Darwin")


# This example shows two methods of adding geometry to an existing scene.
# 1) add via a UI callback (in this case a menu, but a button would be similar,
#    you would call `button.set_on_clicked(self.on_menu_sphere_)` when
#    configuring the button. See `on_menu_sphere()`.
# 2) add asynchronously by polling from another thread. GUI functions must be
#    called from the UI thread, so use Application.post_to_main_thread().
#    See `on_menu_random()`.
# Running the example will show a simple window with a Debug menu item with the
# two different options. The second method will add random spheres for
# 20 seconds, during which time you can be interacting with the scene, rotating,
# etc.
class SpheresApp:
    MENU_SPHERE = 1
    MENU_RANDOM = 2
    MENU_QUIT = 3

    def __init__(self):
        self._id = 0
        self.window = gui.Application.instance.create_window(
            "Add Spheres Example", 800, 600)


        # The menu is global (because the macOS menu is global), so only create
        # it once, no matter how many windows are created
        if gui.Application.instance.menubar is None:
            if isMacOS:
                app_menu = gui.Menu()
                app_menu.add_item("Quit", SpheresApp.MENU_QUIT)
            debug_menu = gui.Menu()
            debug_menu.add_item("Add Sphere", SpheresApp.MENU_SPHERE)
            debug_menu.add_item("Add Random Spheres", SpheresApp.MENU_RANDOM)
            if not isMacOS:
                debug_menu.add_separator()
                debug_menu.add_item("Quit", SpheresApp.MENU_QUIT)

            menu = gui.Menu()
            if isMacOS:
                # macOS will name the first menu item for the running application
                # (in our case, probably "Python"), regardless of what we call
                # it. This is the application menu, and it is where the
                # About..., Preferences..., and Quit menu items typically go.
                menu.add_menu("Example", app_menu)
                menu.add_menu("Debug", debug_menu)
            else:
                menu.add_menu("Debug", debug_menu)
            gui.Application.instance.menubar = menu

        # The menubar is global, but we need to connect the menu items to the
        # window, so that the window can call the appropriate function when the
        # menu item is activated.
        self.window.set_on_menu_item_activated(SpheresApp.MENU_SPHERE,
                                               self._on_menu_sphere)
        self.window.set_on_menu_item_activated(SpheresApp.MENU_RANDOM,
                                               self._on_menu_random)
        self.window.set_on_menu_item_activated(SpheresApp.MENU_QUIT,
                                               self._on_menu_quit)

        self.scene = gui.SceneWidget()
        self.scene.scene = rendering.Open3DScene(self.window.renderer)
        self.scene.scene.show_axes(True)
        mesh = o3d.geometry.TriangleMesh.create_sphere()
        mesh.compute_vertex_normals()
        material = rendering.MaterialRecord()
        material.shader = "defaultLit"
        self.scene.scene.add_geometry("sphere" + str(self._id), mesh, material)

        em = self.window.theme.font_size
        self.layout = gui.Horiz(0, gui.Margins(0.25*em,0.25*em,0.25*em,0.254*em))
        self.layout.add_child(gui.Label("Model file"))
        self.layout.add_fixed(0.25 * em)

        self.window.set_on_layout(self.on_window_layout)
        self.window.add_child(self.scene)
        self.window.add_child(self.layout)

    def on_window_layout(self, layout: gui.LayoutContext) -> None:
        """
        This is called when layout is required for this widgets *immediate children*, like on resize. Only the immediate
        children are *manually* positioned here, by setting the x, y, width, and height of their frames. The
        grandchildren are *not* touched here; they're automatically handled after this.
        """

        # This is the area in this widget available to place child widgets in. The *units* here aren't window screen
        # pixels, like from the width/height here when setting up the window: gui.Application.instance.create_window("Test", width=500, height=600).
        rect = self.window.content_rect
        r = self.window.content_rect
        print(r)

        print(layout.theme.default_layout_spacing)
        print(layout.theme.default_margin)
        print(layout.theme.font_size)

        # Put the layout (containing the controls) on the left 1/3 and the and scene on the right 2/3.
        x_division = rect.width // 3

        # Set the layout (gui.Vert) on left to 1/3 available width, full height.
        # gui.Rect(x, y, width, height)
        # Set the SceneWidget on on right, 2/3 available width, full height
        self.scene.frame = gui.Rect(0, 0, r.width - x_division, r.height)
        self.layout.frame = gui.Rect(r.width - x_division, 0, x_division, r.height)

    def add_sphere(self):
        self._id += 1
        mat = rendering.MaterialRecord()
        mat.base_color = [
            random.random(),
            random.random(),
            random.random(), 1.0
        ]
        mat.shader = "defaultLit"
        sphere = o3d.geometry.TriangleMesh.create_sphere(0.5)
        sphere.compute_vertex_normals()
        sphere.translate([
            10.0 * random.uniform(-1.0, 1.0), 10.0 * random.uniform(-1.0, 1.0),
            10.0 * random.uniform(-1.0, 1.0)
        ])

        self.scene.scene.add_geometry("sphere" + str(self._id), sphere, mat)

    def _on_menu_sphere(self):
        # GUI callbacks happen on the main thread, so we can do everything
        # normally here.
        self.scene.scene.clear_geometry()
        self.add_sphere()

    def _on_menu_random(self):
        # This adds spheres asynchronously. This pattern is useful if you have
        # data coming in from another source than user interaction.
        def thread_main():
            for _ in range(0, 20):
                # We can only modify GUI objects on the main thread, so we
                # need to post the function to call to the main thread.
                gui.Application.instance.post_to_main_thread(
                    self.window, self.add_sphere)
                time.sleep(0.5)

        threading.Thread(target=thread_main).start()

    def _on_menu_quit(self):
        gui.Application.instance.quit()


def main():
    gui.Application.instance.initialize()
    SpheresApp()
    gui.Application.instance.run()


if __name__ == "__main__":
    main()

相关文章:

  • composer 错误汇总
  • 排序算法(3):
  • Dify Workflows MCP Server (TypeScript)设计与实战
  • 人工智能之数学基础:线性代数中矩阵的运算
  • 极简本地体验deepseek大模型教程
  • 蓝牙接近开关模块感应开锁手机靠近解锁支持HID低功耗
  • Window下Redis的安装和部署详细图文教程(Redis的安装和可视化工具的使用)
  • 爱普生可编程晶振 SG-8101CE 在智能家居领域展现出的优势
  • 【机器学习】逻辑回归(Logistic Regression)
  • 散户如何实现自动化交易下单——篇1:体系介绍与获取同花顺资金账户和持仓信息
  • mysql安装教程,超详细图文教程(附安装包)MySQL8.0安装教程
  • virtualbox安装ubuntu,配置静态ip
  • UnrealEngine UE5 可视化 从地球观察火星 金星 土星 运动轨迹
  • 【Android】安卓付款密码输入框、支付密码输入框
  • 基于PLC的智能窗控制系统设计
  • Spring Boot 拦截器(Interceptor)
  • 目标检测——数据处理
  • 嵌入式晶振细究
  • 《深度揭秘:生成对抗网络如何重塑遥感图像分析精度》
  • 2025年2月个人工作生活总结
  • css网站导航模板/百度投诉中心热线
  • 网站备案 费用/百度官网网页版
  • 万户做网站如何/软件外包公司排名
  • 公司做网站之前要准备什么软件/如何线上推广引流
  • 传统网站建设/seo分析
  • 建设银行缴费网站登录/站长之家音效素材