Open3D模型编辑窗口按Q关闭时主GUI退出的问题解决请求
问题解决:关闭Open3D编辑窗口不影响主GUI
问题原因
o3d.visualization.draw_geometries_with_editing属于Open3D的旧版阻塞式可视化接口,它会接管整个应用的事件循环。当按下Q关闭该窗口时,会触发全局应用退出信号,导致主GUI窗口也被关闭。
修改方案
通过子进程隔离旧版可视化窗口的运行,子进程的退出不会影响主进程的GUI事件循环。具体逻辑:
- 用临时文件传递点云数据(Open3D对象无法直接跨进程传递)
- 将裁剪窗口的逻辑放到子进程中执行,主进程继续维持GUI运行
修改后的代码
import open3d.visualization.gui as gui import open3d.visualization.rendering as rendering import os import numpy as np import multiprocessing import tempfile class Open3dApp: def __init__(self): self.window = gui.Application.instance.create_window("3D Full Body Human Viewer", 1400, 900) win = self.window self.current_file = None self.Clean3DPLY = None # Check if file exists if os.path.isfile('Clean body.ply'): self.Clean3DPLY = 'Clean body.ply' # member variables self.model_dir = "" self.model_name = "" self.pcd = None # to store the loaded .ply file self._widget3d = None # to store the 3D widget self.file_path = "" # 补充初始化缺失变量 em = win.theme.font_size # 3D Widget self.test = gui.Combobox() self.test.add_item("First") self._widget3d = gui.SceneWidget() self._widget3d.scene = rendering.Open3DScene(win.renderer) # 修正原代码中错误的变量引用 self._widget3d.set_view_controls(gui.SceneWidget.Controls.ROTATE_CAMERA) # create a frame that encapsulates the SceneWidget self._widget3d.frame = gui.Rect(500, win.content_rect.y, 900, win.content_rect.height) mesh = o3d.geometry.TriangleMesh.create_sphere() mesh.compute_vertex_normals() material = rendering.MaterialRecord() material.shader = "defaultLit" self._widget3d.scene.add_geometry('mesh', mesh, material) self._widget3d.scene.set_background([200, 0, 0, 200]) self._widget3d.scene.camera.look_at([0, 0, 0], [1, 1, 1], [0, 0, 1]) # gui layout gui_layout = gui.Vert(0, gui.Margins(0.5 * em, 0.5 * em, 0.5 * em, 0.5 * em)) # create frame that encapsulates the gui gui_layout.frame = gui.Rect(win.content_rect.x, win.content_rect.y, 500, win.content_rect.height) # File-chooser widget self._fileedit = gui.TextEdit() filedlgbutton = gui.Button("...") filedlgbutton.horizontal_padding_em = 0.5 filedlgbutton.vertical_padding_em = 0 filedlgbutton.set_on_clicked(self._on_filedlg_button) # Add a fixed height empty layout for spacing empty_layout = gui.Vert(0) empty_layout.add_fixed(1 * em) fileedit_layout = gui.Horiz() fileedit_layout.add_child(gui.Label("Model file")) fileedit_layout.add_child(self._fileedit) fileedit_layout.add_fixed(0.25 * em) fileedit_layout.add_child(filedlgbutton) # Add to the top-level (vertical) layout gui_layout.add_child(fileedit_layout) # Add a fixed height empty layout for spacing empty_layout = gui.Vert(0) empty_layout.add_fixed(1 * em) gui_layout.add_child(empty_layout) # start pre processing collapse = gui.CollapsableVert("Pre Processing Data", 0.33 * em, gui.Margins(em, 0, 0, 0)) # Additional feature buttons feature_button1 = gui.Button("Geometry Cropping") feature_button1.set_on_clicked(self.demo_3d) feature_button2 = gui.Button("Segmentation") feature_button2.set_on_clicked(self.clean3d) geometry_button4 = gui.Button("Exit") geometry_button4.set_on_clicked(self._on_exit_click) gui_layout.add_child(geometry_button4) gui_layout.add_child(empty_layout) win.add_child(gui_layout) win.add_child(self._widget3d) def _on_filedlg_button(self): # 补充文件选择对话框实现 dlg = gui.FileDialog(gui.FileDialog.OPEN, "Select Model File", self.window.theme) dlg.add_filter(".ply", "PLY files") def on_file_selected(filename): if filename: self.file_path = filename self._fileedit.text = filename dlg.set_on_cancel(lambda: None) dlg.set_on_done(on_file_selected) self.window.show_dialog(dlg) def clean3d(self): # 补充缺失的方法实现 pass def _on_exit_click(self): gui.Application.instance.quit() def demo_3d(self): if not self.file_path: print("Error: Please select a file") else: # Load the point cloud data from the selected file indi = o3d.io.read_point_cloud(self.file_path) # 保存点云到临时文件,供子进程读取 with tempfile.NamedTemporaryFile(suffix=".ply", delete=False) as temp_file: temp_path = temp_file.name o3d.io.write_point_cloud(temp_path, indi) # 启动子进程打开裁剪窗口,不阻塞主GUI p = multiprocessing.Process(target=self._run_crop_geometry, args=(temp_path,)) p.start() self.current_file = self.Clean3DPLY @staticmethod def _run_crop_geometry(temp_path): # 子进程中读取临时文件并打开编辑窗口 indi = o3d.io.read_point_cloud(temp_path) print("Demo for manual geometry cropping") print( "1) Press 'Y' twice to align geometry with negative direction of y-axis" ) print("2) Press 'K' to lock screen and to switch to selection mode") print("3) Drag for rectangle selection,") print(" or use ctrl + left click for polygon selection") print("4) Press 'C' to get a selected geometry and to save it") print("5) Press 'F' to switch to freeview mode") o3d.visualization.draw_geometries_with_editing([indi], width=800, height=900, left=1050, top=100) # 子进程结束后删除临时文件 os.unlink(temp_path) if __name__ == "__main__": # 必须在main函数中启动GUI,避免多进程冲突 gui.Application.instance.initialize() app = Open3dApp() gui.Application.instance.run()
关键修改说明
- 新增
multiprocessing和tempfile模块,实现进程隔离与跨进程数据传递 - 将裁剪窗口逻辑移到静态方法
_run_crop_geometry,作为子进程执行目标 - 在
demo_3d中通过临时文件传递点云,启动子进程后立即返回,不阻塞主GUI - 修复原代码中的变量引用错误、缺失方法实现等问题
内容的提问来源于stack exchange,提问作者Ali Habib Haidar
相关产品推荐
相关产品推荐

