?本文方法來自:PYQT5內(nèi)嵌外部exe程序(win7)_pyqt5嵌入外部窗口_這杯可樂有點(diǎn)甜的博客-CSDN博客
open3d在繪制點(diǎn)云等圖形時(shí),通常需要?jiǎng)?chuàng)建一個(gè)窗口。本文實(shí)現(xiàn)了將open3d創(chuàng)建的窗口顯示在Qt窗口內(nèi),以便于后續(xù)通過Qt控件和槽函數(shù)調(diào)用open3d強(qiáng)大的繪圖和處理功能。
運(yùn)行結(jié)果如下圖所示:
?
實(shí)現(xiàn)過程:
1.首先,導(dǎo)入需要的庫(kù):
import sys
import open3d as o3d
from PyQt5.QtWidgets import QApplication,QMainWindow,QWidget
from PyQt5.QtGui import QWindow
from PyQt5.QtCore import QTimer
import win32gui
其中win32gui為Pywin32庫(kù)。
2.設(shè)計(jì)界面:
用Qt?Designer繪制界面,如圖:
?保存并編譯.ui文件,得到對(duì)應(yīng)的.py文件。
3.嵌入窗口:
新建一個(gè)python文件,繼承剛剛所寫的界面的類。
創(chuàng)建一個(gè)open3d的Visualizer類的實(shí)例vis,并創(chuàng)建窗口:
class MainWindow(QMainWindow):
def __init__(self,parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_display_test.Ui_MainWindow()
self.ui.setupUi(self)
self.vis = o3d.visualization.Visualizer()
self.vis.create_window()
添加斷點(diǎn),或者使用vis.run()使窗口保持顯示,在Visual Studio安裝目錄的Community\Common7\Tools找到并打開Spy++,按Ctrl+F,將“查找程序工具”拖動(dòng)到打開的open3d窗口上,如圖:
得到窗體類名為GLFW30。
通過Pywin32和PyQt5.QtGui?找到這個(gè)類對(duì)應(yīng)的窗口,并獲得對(duì)應(yīng)WinId:
self.winid = win32gui.FindWindow('GLFW30',None)
self.sub_window = QWindow.fromWinId(self.winid)
創(chuàng)建WindowContainer,將窗口放入其中,在將WindowContainer添加到Qt布局中:
self.displayer = QWidget.createWindowContainer(self.sub_window)
self.ui.grid_display.addWidget(self.displayer)
此時(shí)open3d窗口已經(jīng)嵌入Qt窗口中。
4.加載點(diǎn)云,測(cè)試效果:
根據(jù)open3d文檔,o3d.visualization.draw_geometries([pcd])方法可以用以下代碼替代實(shí)現(xiàn):
def custom_draw_geometry(pcd):
# The following code achieves the same effect as:
# o3d.visualization.draw_geometries([pcd])
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(pcd)
vis.run()
vis.destroy_window()
因此,添加函數(shù):
def draw_test(self):
pcd = o3d.io.read_point_cloud(r'../material/bun000.pcd') #點(diǎn)云路徑
self.vis.add_geometry(pcd)
self.vis.run()
并在析構(gòu)函數(shù)添加
self.vis.destroy_window()
5.修改為非阻塞顯示方式:
上面的代碼雖然能正常顯示點(diǎn)云,但是由于vis.run()是阻塞方式運(yùn)行的,所以在窗口關(guān)閉時(shí)destroy_window()并不能被執(zhí)行,此時(shí)會(huì)導(dǎo)致GLFW一直報(bào)錯(cuò),程序無法停止運(yùn)行。
在open3d文檔中,有:
代碼的下一部分是本教程的核心。update_geometry通知vis相關(guān)的幾何圖形已經(jīng)更新。最后,可視化器通過調(diào)用poll_events和update_renderer渲染一個(gè)新幀。在任何for循環(huán)迭代之后,destroy_window將關(guān)閉窗口。
?將vis.run()替換為:
while True:
self.vis.poll_events()
self.vis.update_renderer()
實(shí)現(xiàn)的效果與vis.run()相同。
添加一個(gè)定時(shí)器,設(shè)置定時(shí)器為20毫秒,將這兩行代碼寫到定時(shí)器信號(hào)對(duì)應(yīng)的槽函數(shù)中:
def __init__:
…………
self.clock = QTimer(self)
self.clock.timeout.connect(self.draw_update)
self.clock.start(20)
…………
def draw_update(self):
self.vis.poll_events()
self.vis.update_renderer()
就實(shí)現(xiàn)了每20ms刷新一次顯示,同時(shí)不阻塞程序運(yùn)行。
完整代碼:
display_test.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>828</width>
<height>608</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3">
<item>
<layout class="QGridLayout" name="grid_display">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>828</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
Ui_display_test.py,由display_test.ui編譯生成:文章來源:http://www.zghlxwxcb.cn/news/detail-461173.html
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'd:\personal programs\gradesign\CSDN文章用\display_test.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(828, 608)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.grid_display = QtWidgets.QGridLayout()
self.grid_display.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.grid_display.setObjectName("grid_display")
self.horizontalLayout.addLayout(self.grid_display)
self.horizontalLayout.setStretch(0, 3)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 828, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
displaytest.py:文章來源地址http://www.zghlxwxcb.cn/news/detail-461173.html
import sys
import open3d as o3d
from PyQt5.QtWidgets import QApplication,QMainWindow,QWidget
from PyQt5.QtGui import QWindow
from PyQt5.QtCore import QTimer
import win32gui
import Ui_display_test
class MainWindow(QMainWindow):
def __init__(self,parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_display_test.Ui_MainWindow()
self.ui.setupUi(self)
self.vis = o3d.visualization.Visualizer()
self.vis.create_window(visible=False) #visible=False窗口不顯示,避免啟動(dòng)時(shí)一閃而過
self.winid = win32gui.FindWindow('GLFW30',None)
self.sub_window = QWindow.fromWinId(self.winid)
self.displayer = QWidget.createWindowContainer(self.sub_window)
self.ui.grid_display.addWidget(self.displayer)
self.clock = QTimer(self)
self.clock.timeout.connect(self.draw_update)
self.clock.start(20)
self.draw_test()
def draw_test(self):
pcd = o3d.io.read_point_cloud(r'../material/bun000.pcd') #點(diǎn)云路徑
self.vis.add_geometry(pcd)
self.vis.update_geometry(pcd)
def draw_update(self):
self.vis.poll_events()
self.vis.update_renderer()
def __del__(self):
#self.clock.stop() #這一行其實(shí)并不需要
self.vis.destroy_window()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
到了這里,關(guān)于在PyQt5窗口中嵌入open3d窗口顯示點(diǎn)云圖形的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!