Closed pascalisfort closed 3 years ago
Thank you @pascalisfort for reporting. What plotter do you use? Does it happen even for a minimal code snippet like the following:
import pyvista
from pyvistaqt import BackgroundPlotter
plotter = BackgroundPlotter()
plotter.add_mesh(pyvista.Cone())
I remember this happening in the past and I thought it was fixed for good :sweat:
Also, could you share your configuration:
$ python -c "import pyvista; print(pyvista.Report())"
It will be helpful to understand the underlying issue.
Thank you @GuillaumeFavelier I try the minimal code you give me in your message and it works well...
This is my simplified code with the principal function i used for the viewing, can you have an idea where I begin to search to solve the problem. This is not a major problem but I interested to understand and solve it
import sys
# Setting the Qt bindings for QtPy
import os
os.environ["QT_API"] = "pyqt5"
from qtpy import QtWidgets
from qtpy.QtWidgets import QMainWindow, QFileDialog
import laspy#permet de lire les fichiers las
import time
import numpy as np
import pickle #permet de sauvegarder des variables
import easygui #permet d'afficher un message box
import pyvista as pv
from pyvistaqt import QtInteractor
#from pyvista.plotting.theme import rcParams
#from sklearn.neighbors import KDTree
from matplotlib import colors as color_lib
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
from scipy.spatial import cKDTree, ConvexHull
from dbfread import DBF
class MainWindow(QMainWindow):
def __init__(self, parent=None, show=True):
QtWidgets.QMainWindow.__init__(self, parent)
# create the frame
self.frame = QtWidgets.QFrame()
vlayout = QtWidgets.QVBoxLayout()
self.plotter = QtInteractor(shape="1|2")
vlayout.addWidget(self.plotter.interactor)
self.frame.setLayout(vlayout)
self.setCentralWidget(self.frame)
# simple menu to demo functions
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('File')
self.add_point_action = QtWidgets.QAction('ouvrir fichier las', self) #creation du bouton
self.add_point_action.triggered.connect(self.add_point) # creation de l,action a faire par le bouton
exitButton = QtWidgets.QAction('Exit', self)
exitButton.setShortcut('Ctrl+Q')
exitButton.triggered.connect(self.close)
fileMenu.addAction(self.add_point_action) #ajout du bouton dans le menu filemenu
fileMenu.addAction(exitButton)
#initialisation des variables que l'on utilisera tout au long du program
self.nom_output =""
self.rep_base ='c:/data isfort 2/philippe/mffp big data/7601502902/aller/clean_by_R'
self.rep_base ='D:/data isfort 2/philippe/mffp big data'
self.bloc_tranche= pv.MultiBlock()
# preparation du colormap pour les 1023 premiere tige (si plus de 1023 tiges valider
self.list_couleur = ['#FFFFFF']
np.random.seed(0)
aleat= np.random.choice(range(147), 147, replace=False)
aa=plt.cm.get_cmap('gist_rainbow', 147)
dict_color = []
for i in range(0,len(aleat)):
bb = aleat[i]
hex_a = color_lib.rgb2hex(aa(bb))
dict_color.append(hex_a)
self.list_couleur = self.list_couleur+(7*dict_color)
#preparation des bouton et widget
self.plotter.subplot(0, 0)
self.reset_slider_bool = 1 #condition pour ne pas que la fct ch_cluster s'active
self.slide_cluster = self.plotter.add_slider_widget(self.ch_cluster, value = 0.02, rng=[0.001, 0.03], title="distance clustering", pointa=(.025, .1), pointb=(.80, .1))
self.plotter.subplot(1, 0)
self.slide_haut_dhp = self.plotter.add_slider_widget(self.ch_haut_dhp, value = 1.3, rng=[0.85, 2.85], title="Hauteur du DHP", pointa=(.025, .1), pointb=(.95, .1))
self.plotter.subplot(2, 0)
self.write_dhp(0,0)
self.btn_enr=self.plotter.add_checkbox_button_widget(self.save_tree, value= False, position=(10,20), size=30,color_on='green',color_off='red')
self.reset_slider_bool = 0
#bouton pour enregistrer les images activé le au besoin
if show:
self.show()
def add_point(self):
fname, _ = QFileDialog.getOpenFileName(self, 'Open file', self.rep_base,"LAS file (*.las)")# ouvre une fenetre open file et produit un genre de array a 2 valeur dans cette ligne je ne prend que la 1er valeur
if fname:
data = laspy.file.File(fname, mode="r")
#verif si le fichier las a la variable id_tree
for spec in data.point_format:
if spec.name == "id_tree":
self.scalar_las= data.id_tree
break
else:
self.scalar_las= np.array([1]*len(data))
self.xyz = np.c_[data.x, data.y, data.z]
data.close()
self.point_cloud = pv.PolyData(self.xyz)
self.point_cloud["id_arbre"]= self.scalar_las
self.point_cloud["graph"]= np.array([0]*self.point_cloud.n_points)
#self.tree = Cluster(self.point_cloud.points)
self.plotter.subplot(0, 0)#attention il faut remplir les subplot en ordre sinon ils s'efface
couleur=self.list_couleur[:max(self.point_cloud["id_arbre"])]
self.plot_princ = self.plotter.add_mesh(self.point_cloud , scalars="id_arbre", categories=True, point_size=1, cmap=couleur, show_scalar_bar= False)
self.plotter.enable_eye_dome_lighting()
self.plotter.enable_point_picking(self.clicker, use_mesh=True, show_message=False )
def clicker(self, ptc, pid):
try:
self.arbre
if not self.arbre.enregistrement_arbre:
if easygui.ynbox("Vous n'avez pas enregistrer votre dernier arbre voulez-vous continuer?", 'Enregistrement', ('Oui', 'Non')):
pass
else:
return
except:
pass
if ptc["graph"][0]==0:
self.choix_arbre_princ(ptc, pid)
self.dernier_click=0
elif ptc["graph"][0]==1:
self.choix_arbre_sec(ptc, pid)
self.dernier_click=1
def choix_arbre_princ(self, ptc, pid):
self.reset_slider()
if self.point_cloud["id_arbre"][pid]== 1:
pt_cluster = self.tree.Clusterisation(pid, ptc)
id_arbre = max(self.point_cloud["id_arbre"])+1
self.point_cloud["id_arbre"][pt_cluster]= id_arbre
self.redraw_graph("princ")
self.point_cloud_sec = self.point_cloud.extract_cells(pt_cluster)
else:
id_arbre = self.point_cloud["id_arbre"][pid]
self.point_cloud_sec= self.point_cloud.clip_scalar(scalars='id_arbre',invert=False, value=id_arbre)
pt_cluster = self.point_cloud_sec["vtkOriginalCellIds"]#une facon d'avoir les id des points qui sont dans le cluster
pt_depart=ptc.points[pid]
bounds= self.point_cloud_sec.bounds
self.arbre= tree_info(id_arbre, pt_cluster, 1.3, 0.02, bounds, pt_depart) #creation de l'objet arbre contenant les info de l'arbre actif
self.btn_enr.GetRepresentation().SetState(0)
self.point_cloud_sec["graph"]= np.array([1]*self.point_cloud_sec.n_points)# ajout de l'identifiant de quel graphique il s'agit utilisex dans l'action clicker
self.point_cloud_sec['couleur']=np.where(abs(self.point_cloud_sec.points[:,2]-self.arbre.haut_dhp)<0.1, 2, 1)#ajout de la bande rouge du dhp
self.redraw_graph("sec")
self.point_cloud_ter= self.point_cloud_sec.clip_scalar(scalars='couleur',invert=False, value=2)#extraction des points pour creer la galette
self.coord_pourtour = self.arbre.calc_dhp(self.point_cloud_ter)
self.redraw_graph("ter")
def choix_arbre_sec(self, ptc, pid):
#easygui.msgbox(pid, title="arbre secondaire")
pt_cluster = self.tree.Clusterisation_sec(pid, ptc, self.arbre.haut_dhp)
self.arbre.pt_depart=ptc.points[pid]
self.point_cloud_ter = self.point_cloud_sec.extract_cells(pt_cluster)
self.coord_pourtour = self.arbre.calc_dhp(self.point_cloud_ter)
self.redraw_graph("ter")
def redraw_graph(self, graph):
if graph=="princ":
self.plotter.subplot(0, 0)
cpos = self.plotter.camera_position
try:
self.plotter.remove_actor(self.plot_princ)
self.plotter.remove_actor(self.bord_princ)
except:
pass
couleur=self.list_couleur[:max(self.point_cloud["id_arbre"])]
self.plot_princ = self.plotter.add_mesh(self.point_cloud , scalars="id_arbre", categories=True, point_size=1, cmap=self.list_couleur, show_scalar_bar= False)
try:
self.bord_princ = self.plotter.add_mesh(self.bordure , color="red", show_edges=True, style="wireframe", line_width=3)
except:
pass
self.plotter.camera_position = cpos
elif graph=="sec":
self.plotter.subplot(1)
try:
self.plotter.remove_actor(self.plot_sec)
except:
pass
if(len(self.point_cloud_sec.points)<50): return#ne rien afficher et calculé si il reste moin de 50 points
self.plot_sec = self.plotter.add_mesh(self.point_cloud_sec , scalars="couleur", clim=[1,2], point_size=1, cmap=['white','red'], show_scalar_bar= False)
self.plotter.set_focus(self.point_cloud_sec.center)
elif graph=="ter":
self.plotter.subplot(2, 0)
try:
self.plotter.remove_actor(self.plot_ter)
self.plotter.remove_actor(self.ligne_perimetre)
except:
pass
if(len(self.point_cloud_ter.points)<50): return#ne rien afficher et calculé si il reste moin de 50 points
pc_ter = self.point_cloud_ter.copy()
pc_ter.points[:,2]=0 #retire la 3e dimension donc tout les points sont remis sur xy
self.plot_ter = self.plotter.add_mesh(pc_ter , color='red', point_size=3)#affichage des points
self.ligne_perimetre= self.plotter.add_lines(self.coord_pourtour, color='green', width=3)
self.write_dhp(self.arbre.dhp, self.arbre.id)
def reset_slider(self):
self.reset_slider_bool = 1
self.plotter.subplot(0)
try:
self.slide_cluster.Off()
except:
pass
self.slide_cluster = self.plotter.add_slider_widget(self.ch_cluster, value = 0.02, rng=[0.001, 0.03], title="distance clustering", pointa=(.025, .1), pointb=(.80, .1))
self.plotter.subplot(1)
try:
self.slide_haut_dhp.Off()
except:
pass
self.slide_haut_dhp = self.plotter.add_slider_widget(self.ch_haut_dhp, value = 1.3, rng=[0.85, 2.85], title="Hauteur du DHP", pointa=(.025, .1), pointb=(.95, .1))
self.reset_slider_bool = 0
def ch_cluster(self, dist_c):
if hasattr(self, 'tree') and not(self.reset_slider_bool):
#pt_cluster_tranche = self.tree.modif_Clusterisationold(self.point_cloud_ter, dist_c, self.arbre.haut_dhp)
#self.arbre.treshold=dist_c
#if(self.dernier_click):#dernier click vient de l'image originale
self.point_cloud_sec = self.tree.modif_Clusterisation(self.point_cloud_sec, dist_c, self.arbre.treshold, self.arbre.pt_depart, self.arbre.orig_bounds)
#self.temp_sec = self.point_cloud_sec.copy()
self.arbre.treshold=dist_c
self.point_cloud_sec["id_arbre"]= np.array([self.arbre.id]*self.point_cloud_sec.n_points)
self.point_cloud_sec["graph"]= np.array([1]*self.point_cloud_sec.n_points)
self.point_cloud_sec["couleur"]=np.where(abs(self.point_cloud_sec.points[:,2]-self.arbre.haut_dhp)<0.1, 2, 1)#ajout de la bande rouge du dhp
cond=self.point_cloud_sec["couleur"]==2#condition pour extraire la galette
if (not any(cond)): #sortir de la condition si il n'y a pas de points suite au changement de cluster et redessiner le 2e et 3e graph
self.redraw_graph("sec")
self.redraw_graph("ter")
return
self.point_cloud_ter= self.point_cloud_sec.extract_cells(cond)#extraction des points pour creer la galette
self.redraw_graph("sec")#mettre cette commande car la commande précédente fait changer la couleur de tout le nuage de points???
self.coord_pourtour = self.arbre.calc_dhp(self.point_cloud_ter)
self.redraw_graph("ter")
#self.point_cloud_ter_orig= self.point_cloud.extract_cells(pt_cluster)
# try:
# pt_cluster = self.tree.modif_Clusterisation()
# except:
# pass
def ch_haut_dhp(self, haut_dhp):
try:
self.arbre.haut_dhp = haut_dhp
self.point_cloud_sec['couleur']=np.where(abs(self.point_cloud_sec.points[:,2]-haut_dhp)<0.1, 2, 1)
self.redraw_graph("sec")
self.point_cloud_ter= self.point_cloud_sec.clip_scalar(scalars='couleur',invert=False, value=2)
self.coord_pourtour = self.arbre.calc_dhp(self.point_cloud_ter)
self.redraw_graph("ter")
except:
pass
def save_tree(self, arg):
#easygui.msgbox("arbre sauve", title="simple gui")
if self.nom_output=="":
self.nom_output, _ = QFileDialog.getSaveFileName(window, 'Open file', self.rep_base,"txt file (*.txt)")
f = open(self.nom_output, "a")
f.write(str(self.arbre))
f.close()
self.arbre.enregistrement_arbre=True
#ajout des points de la tranche dans un multibloc pour enregistrement futur
self.bloc_tranche.append(self.point_cloud_ter)
def write_dhp(self, dhp,idtree):
self.plotter.subplot(2)
try:
self.plotter.remove_actor(self.txt_dhp)
except:
pass
self.txt_dhp = self.plotter.add_text("DHP= {}cm, ID= {}".format(round(dhp,1),idtree) , position= 'upper_left', font_size=10)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle("LIDAR Identree")# a modifie
#window.resize(700,700)#grandeur de la fenetre
window.showMaximized()
sys.exit(app.exec_())
Date: Mon Apr 12 06:42:50 2021 Est (heure d’été)
OS : Windows
CPU(s) : 6
Machine : AMD64
Architecture : 64bit
RAM : 7.8 GB
Environment : Python
GPU Vendor : Intel
GPU Renderer : Intel(R) UHD Graphics 630
GPU Version : 4.5.0 - Build 26.20.100.6952
Python 3.8.5 (default, Sep 3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
pyvista : 0.27.4
vtk : 9.0.1
numpy : 1.19.5
imageio : 2.9.0
appdirs : 1.4.4
scooby : 0.5.6
meshio : 4.3.8
matplotlib : 3.3.3
pyvistaqt : 0.2.0
PyQt5 : 5.12.3
IPython : 7.19.0
colorcet : 1.0.0
scipy : 1.5.3
itkwidgets : 0.32.0
tqdm : 4.55.2
thank you so much Pascal
Thanks for sharing your code snippet. Hm... I see that you use QtInteractor
and inherits from QMainWindow
:thinking:
Would you give our MainWindow
class a try?
...
from pyvistaqt import MainWindow
class MyMainWindow(MainWindow):
...
It's actually very light and has a builtin signal_close
that can be connected to the interactor as follows:
self.signal_close.connect(self.plotter.close)
I look forward to your reply :)
I try the MainWindow class from pyvistaqt and the same error appear, but when I add self.signal_close.connect(self.plotter.close)
every thing works.
Thank a lot for your help Pascal
Great news! But it means that the doc should be updated then :)
Hello, I use the pyvistaqt to visualize lidar mobil data. If I use the menu item "exit" the program close without any error, but when I use the X to close the window a list of error appear in loop indefinitly until I press ctrl-break.
Do you have an ideas why this appear?
this is a copy of what I see in the command prompt: