Skip to content

Instantly share code, notes, and snippets.

@notaTeapot
Last active July 11, 2024 08:29
Show Gist options
  • Save notaTeapot/5feea53dccc921ea6af88a4d222e1e2b to your computer and use it in GitHub Desktop.
Save notaTeapot/5feea53dccc921ea6af88a4d222e1e2b to your computer and use it in GitHub Desktop.
Open 3d Widget with editing
# Partially Working Error on Exit
from PySide2 import QtWidgets, QtGui,QtCore
import open3d as o3d
import win32gui
import sys
import threading
import time
class Worker(QtCore.QObject):
def run(self,vis):
vis.run()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
widget = QtWidgets.QWidget()
layout = QtWidgets.QGridLayout(widget)
self.setCentralWidget(widget)
file_path = "C:\\test\test.pcd"
self.pcd = o3d.io.read_point_cloud(file_path)
self.vis = o3d.visualization.VisualizerWithEditing()
self.vis.create_window()
self.vis.add_geometry(self.pcd)
hwnd = win32gui.FindWindowEx(0, 0, None, "Open3D - free view")
self.window = QtGui.QWindow.fromWinId(hwnd)
self.windowcontainer = self.createWindowContainer(self.window, widget)
layout.addWidget(self.windowcontainer, 0, 0)
self.thread = QtCore.QThread()
self.worker = Worker()
self.thread.started.connect(lambda: self.worker.run(self.vis))
self.thread.start()
timer = QtCore.QTimer(self)
timer.timeout.connect(self.blub)
timer.start(100)
#print("starting vis")
btn = QtWidgets.QPushButton(text="test")
btn.clicked.connect(lambda: print("Button pressed!"))
layout.addWidget(btn)
def blub(self):
#Function to keep PySide eventloop running
pass
def start_vis(self):
print("thread start")
self.vis.run()
print("thread end")
def update_vis(self):
#self.vis.update_geometry()
self.vis.poll_events()
self.vis.update_renderer()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
form = MainWindow()
form.setWindowTitle('o3d Embed')
form.setGeometry(100, 100, 600, 500)
form.show()
sys.exit(app.exec_())
@notaTeapot
Copy link
Author

notaTeapot commented Mar 21, 2022

No problem, the blub function is just a placeholder to keep the eventloop running, it should work now.

@antwal
Copy link

antwal commented Apr 3, 2022

not working on mac or linux, if use hwnd = widget.winId() working partially

@wpyq
Copy link

wpyq commented Apr 14, 2022

I want to press btn to add another geometry, but run() will block the current thread. So I didn't succeed, Do you know how to solve it, thank you very much!

@SouChtioui
Copy link

Overriding the closeEvent of your MainWindow could fix your Error on Exit

def closeEvent(self, event):
    self.vis.destroy_window()
    self.thread.quit()
    event.accept()

@guirnab
Copy link

guirnab commented Aug 5, 2023

Overriding the closeEvent of your MainWindow could fix your Error on Exit

def closeEvent(self, event):
    self.vis.destroy_window()
    self.thread.quit()
    event.accept()

yes, this works 👍

And you can add visible=False as parameter to self.vis.create_window(visible=False) to get rid of the open3D window flash on startup.

@Thanatossan
Copy link

I want to press btn to add another geometry, but run() will block the current thread. So I didn't succeed, Do you know how to solve it, thank you very much!

I have the same problem, did you find the solution yet?

@zealX
Copy link

zealX commented Dec 22, 2023

Thanks for your help.

@M-A-X-P-Y
Copy link

M-A-X-P-Y commented Apr 29, 2024

Could anyone hint me on how this code could be changed to visualize a .OBJ or .PLY file with texture? (still using VisualizerWithEditing() within a Qt widget)
Here is what I have been able to achieve (and not):

  • using o3d.io.read_triangle_mesh() on a .PLY file instead of o3d.io.read_point_cloud() for a .PCD brings the object with texture in the visualizer, but then clicking does not do anything.

  • using o3d.io.read_triangle_mesh() on a .OBJ brings the object but does not load the texture, and again clicking does not do anything.

  • using o3d.io.read_triangle_model() yields an error as self.vis.add_geometry() does not support "triangle_model" object type. self.vis Class only has add_geometry(), and does not support add_model() as some other open3d classes have.

I am thinking about finding a way to convert the OBJ or PLY object to a point-cloud (with texture) in open3d and then loading it into the visualizer as a workaround if no other way exists (and need to figure out how to perform that conversion).
[Edit] I converted a textured mesh (.PLY) to point-count simply with the following:
filepath1 = 'my_filepath_to_my_ply_file'
obj1 = o3d.io.read_triangle_mesh(filepath1)
pcd = o3d.geometry.PointCloud()
pcd.points = obj1.vertices
pcd.colors = obj1.vertex_colors
pcd.normals = obj1.vertex_normals
It is not ideal as clicking between points results in "no click", or may hit a point located on a face behind the intended face.
It also seems that VisualizerWithEditing() ignores further add_geometry() calls, so it is not possible to have more than one object in the visualizer.

I am thinking it may then be best to use the standard open3d visualizer and use ray_casting to identify the location where the user clicked.
https://www.open3d.org/docs/release/tutorial/geometry/ray_casting.html#Creating-a-virtual-point-cloud
Anyone tried doing that?

@KazyaGlaedwine
Copy link

I want to press btn to add another geometry, but run() will block the current thread. So I didn't succeed, Do you know how to solve it, thank you very much!

I solved it by using open3d.visualization.Visualizer() instead of self.vis = o3d.visualization.VisualizerWithEditing()
VisualizerWithEditing() can only render one geometry at a time even though you succeeded in adding it whereas Visualizer can only render more than one geometry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment