[PYTHON] Créer une visionneuse de modèle 3D avec PyQt5 et PyQtGraph

introduction

D'une manière ou d'une autre, quand je regardais la documentation PyQt Graph, j'ai remarqué qu'il y avait une fonction 3D Graphics dans l'API. J'étais curieux, j'ai donc essayé de créer une application graphique simple qui affiche un modèle 3D en combinaison avec PyQt5.

Comme j'utilise souvent des imprimantes 3D, le modèle 3D fait ici référence au format de fichier STL.

Ce que j'ai fait

test2.gif

Vous pouvez afficher le fichier STL sous forme filaire en sélectionnant ou en faisant glisser et en déposant le fichier STL. C'est un programme simple qui n'affiche qu'un seul fichier STL à la fois. Le code est également sur GitHub. GitHub:https://github.com/Be4rR/STLViewer

Qu'est-ce que PyQtGraph?

PyQtGraph est une bibliothèque pour dessiner des graphiques et peut être utilisé seul, mais vous pouvez facilement intégrer le graphique créé dans une interface graphique créée par PyQt. Bien que sa fonction soit plus faible que le Matplotlib standard, il est très léger et convient pour tracer des données en temps réel. C'est une bibliothèque peu connue, mais personnellement je la trouve utile. Page officielle: http://www.pyqtgraph.org/ Document officiel: https://pyqtgraph.readthedocs.io/en/latest/index.html

environnement

J'utilise Python3.8, PyQt5, PyQtGraph, PyOpenGL, Numpy et Numpy-STL. PyOpenGL est requis pour utiliser les fonctions graphiques 3D avec PyQtGraph. Lisez également le fichier STL avec Numpy-STL.

conda create -n stlviewer python=3.8 pyqt pyqtgraph numpy numpy-stl pyopengl 

programme

C'est un peu long.

stl-viewer.py
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *  

import numpy as np
from stl import mesh

from pathlib import Path
        
class MyWindow(QMainWindow):
    def __init__(self):
        super(MyWindow, self).__init__()
        self.setGeometry(0, 0, 700, 900) 
        self.setAcceptDrops(True)
        
        self.initUI()
        
        self.currentSTL = None
        self.lastDir = None
        
        self.droppedFilename = None
    
    def initUI(self):
        centerWidget = QWidget()
        self.setCentralWidget(centerWidget)
        
        layout = QVBoxLayout()
        centerWidget.setLayout(layout)
        
        self.viewer = gl.GLViewWidget()
        layout.addWidget(self.viewer, 1)
        
        self.viewer.setWindowTitle('STL Viewer')
        self.viewer.setCameraPosition(distance=40)
        
        g = gl.GLGridItem()
        g.setSize(200, 200)
        g.setSpacing(5, 5)
        self.viewer.addItem(g)

        btn = QPushButton(text="Load STL")
        btn.clicked.connect(self.showDialog)
        btn.setFont(QFont("Ricty Diminished", 14))
        layout.addWidget(btn)
            
    def showDialog(self):
        directory = Path("")
        if self.lastDir:
            directory = self.lastDir
        fname = QFileDialog.getOpenFileName(self, "Open file", str(directory), "STL (*.stl)")
        if fname[0]:
            self.showSTL(fname[0])
            self.lastDir = Path(fname[0]).parent
            
    def showSTL(self, filename):
        if self.currentSTL:
            self.viewer.removeItem(self.currentSTL)

        points, faces = self.loadSTL(filename)
        meshdata = gl.MeshData(vertexes=points, faces=faces)
        mesh = gl.GLMeshItem(meshdata=meshdata, smooth=True, drawFaces=False, drawEdges=True, edgeColor=(0, 1, 0, 1))
        self.viewer.addItem(mesh)
        
        self.currentSTL = mesh
        
    def loadSTL(self, filename):
        m = mesh.Mesh.from_file(filename)
        shape = m.points.shape
        points = m.points.reshape(-1, 3)
        faces = np.arange(points.shape[0]).reshape(-1, 3)
        return points, faces

    def dragEnterEvent(self, e):
        print("enter")
        mimeData = e.mimeData()
        mimeList = mimeData.formats()
        filename = None
        
        if "text/uri-list" in mimeList:
            filename = mimeData.data("text/uri-list")
            filename = str(filename, encoding="utf-8")
            filename = filename.replace("file:///", "").replace("\r\n", "").replace("%20", " ")
            filename = Path(filename)
            
        if filename.exists() and filename.suffix == ".stl":
            e.accept()
            self.droppedFilename = filename
        else:
            e.ignore()
            self.droppedFilename = None
        
    def dropEvent(self, e):
        if self.droppedFilename:
            self.showSTL(self.droppedFilename)

if __name__ == '__main__':
    app = QtGui.QApplication([])
    window = MyWindow()
    window.show()
    app.exec_()

Commentaire

Ce n'est pas trop compliqué, mais je vais vous expliquer quelques points clés.

Widget pour l'affichage 3D GLViewWidget

Divers éléments graphiques sont répertoriés dans le Système graphique 3D de la documentation PyQtGraph.

Le premier GLViewWidget est un widget pour afficher des modèles 3D. Nous ajouterons le deuxième élément graphique et les suivants à ce widget. Par exemple, GLGridItem peut être utilisé pour ajouter un plan de grille, et GLMeshItem peut être utilisé pour ajouter des données de maillage telles qu'un fichier STL. Consultez la documentation officielle pour plus de détails.

Puisque GLViewWidget peut être manipulé exactement de la même manière que le widget de PyQt, il peut être intégré dans l'interface graphique de PyQt tel quel.

Voir le modèle 3D avec GLMeshItem

    def showSTL(self, filename):
        #Si un autre modèle 3D est déjà affiché, supprimez ce modèle 3D.
        if self.currentSTL:
            self.viewer.removeItem(self.currentSTL)

        #Extrayez les points de sommet et les faces des faces du fichier STL.
        points, faces = self.loadSTL(filename)

        #Un widget qui crée un maillage et affiche un modèle 3D(self.viewer)Ajouter à.
        meshdata = gl.MeshData(vertexes=points, faces=faces)
        mesh = gl.GLMeshItem(meshdata=meshdata, smooth=True, drawFaces=False, drawEdges=True, edgeColor=(0, 1, 0, 1))
        self.viewer.addItem(mesh)
        
        self.currentSTL = mesh

La fonction loadSTL extrait les informations de sommet et de face du fichier STL. Les «points» et «faces» sont des tableaux Numpy, où «points» se présente sous la forme de »(nombre de sommets, 3)« et »faces» est sous la forme de «(nombre de faces, 3)».

Dans le programme ci-dessus, les informations sur les sommets et les faces sont transmises à MeshData pour créer meshdata, et sur cette base, gl.GLMeshItem est créé pour déterminer la méthode de dessin (couleurs des faces et des côtés, etc.) Il y a deux étapes.

Ajoutez ensuite le «GLMeshItem» créé au «GLViewWidget» «self.viewer».

self.viewer.addItem(mesh)

Afficher la grille

La grille est également le même élément graphique que GLMeshItem, donc il peut être affiché de la même manière.

Partie de fonction ʻInitUI`.

        g = gl.GLGridItem()
        g.setSize(200, 200)
        g.setSpacing(5, 5)
        self.viewer.addItem(g)

Après avoir créé avec GLGridItem (), la taille est décidée par la fonction setSize, et la taille d'une grille est spécifiée par la fonction setSpacing. Enfin, ajoutez-le à self.viewer de GLViewWidget avec la fonction ʻaddItem`.

Recommended Posts

Créer une visionneuse de modèle 3D avec PyQt5 et PyQtGraph
Implémenter un modèle avec état et comportement
Créez une application graphique native avec Py2app et Tkinter
Créez un lot d'images et gonflez avec ImageDataGenerator
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo Partie 02 Chargement d'images et dessin de sommets
[Python] Comment créer un histogramme bidimensionnel avec Matplotlib
Créer un fichier CAO 2D ".dxf" avec python [ezdxf]
[Linux] Créez un auto-certificat avec Docker et apache
Créer un gif 3D avec python3
Créer une page d'accueil avec django
Créer une visionneuse d'images avec Tkinter
Créer un répertoire avec python
Créez une caméra de surveillance WEB avec Raspberry Pi et OpenCV
[Azure] Créer, déployer et réapprendre un modèle [ML Studio classic]
Créez des applications, enregistrez des données et partagez-les avec un seul e-mail
Créons un diagramme PRML avec Python, Numpy et matplotlib.
Créez des RPG 2D avec Ren'Py (3) - Boutique d'objets et d'outils
Créez un script de déploiement avec fabric et cuisine et réutilisez-le
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo Partie 04 Générer des polygones
Tracez un graphe avec Julia + PyQtGraph (2)
Créons une IA à trois voies avec Pylearn2 --Save and load model -
Créez un fichier temporaire avec django sous forme de zip et renvoyez-le
Résoudre ABC166 A ~ D avec Python
Créez une illusion rayée avec correction gamma pour Python3 et openCV3
Créez un environnement virtuel avec Python!
Tracez un graphique avec Julia + PyQtGraph (1)
J'ai trouvé un moyen de créer un modèle 3D à partir de photos Partie 01 Créer un environnement
Dessinez un graphique avec Julia + PyQtGraph (3)
Créez un DMP privé sans coût initial ni développement avec BigQuery
J'ai essayé de créer des taureaux et des vaches avec un programme shell
Créer un itérateur de modèle avec PySide
Créez un stepper de poisson avec numpy.random
Créer un téléchargeur de fichiers avec Django
Créer et renvoyer un fichier CSV CP932 pour Excel avec Chalice
[AWS] Créez un environnement Python Lambda avec CodeStar et faites Hello World
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo.
Créer une pile avec une file d'attente et une file d'attente avec une pile (à partir de LetCode / Implémenter la pile à l'aide de files d'attente, Implémenter la file d'attente à l'aide de piles)
Créer une application Todo avec Django ④ Implémenter la fonction de création de dossier et de tâche
Créez un environnement Python 3 avec pyenv sur Mac et affichez des graphiques Network X
Implémenter un modèle avec état et comportement (3) - Exemple d'implémentation par décorateur
Créez un arbre de décision à partir de 0 avec Python et comprenez-le (5. Entropie des informations)
Créer un décorateur de fonction Python avec Class
Créez une image factice avec Python + PIL.
Créez un modèle pour votre planning Django
[Python] Créez un environnement virtuel avec Anaconda
Créons un groupe gratuit avec Python
Créer une application graphique avec Tkinter de Python
Un mémo contenant Python2.7 et Python3 dans CentOS
Créer un gros fichier texte avec shellscript
Créer et décrypter du code César avec python
Créez un système stellaire avec le script Blender 2.80
Créer une machine virtuelle avec un fichier YAML (KVM)
Créez une application Web simple avec Flask
Résoudre AtCoder ABC168 avec python (A ~ D)
Créer un compteur de fréquence de mots avec Python 3.4