Open In App

3D Modeling & Animation App Using Python

Last Updated : 27 Nov, 2025
Comments
Improve
Suggest changes
1 Likes
Like
Report

In this article, we will build a simple 3D modeling and animation app using Python. We will use PyQt for the GUI and PyOpenGL to render and rotate a 3D cube. This beginner-friendly guide walks you through the entire setup step-by-step.

Installation

Install the required libraries using pip:

pip install PyOpenGL PyQt5

  • PyOpenGL: Python bindings for OpenGL, used to draw and render 3D geometry.
  • PyQt5: GUI toolkit used to create the application window, layouts and controls.

Now let’s look at the step-by-step implementation of this app.

Step by step Implementation

Step 1: Import the Required Libraries

Before starting the app, we import everything needed for GUI creation, OpenGL rendering, and math calculations.

Python
from PyQt5.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QWidget, QPushButton
from PyQt5.QtOpenGL import QGLWidget
from OpenGL.GL import *
import math
import sys

Explanation:

  • QMainWindow, QApplication, QVBoxLayout, QWidget, QPushButton: provide the main window, event loop, vertical layout, widget container, and a button for the GUI.
  • QGLWidget: supplies an OpenGL surface inside a Qt widget where we render OpenGL content.
  • from OpenGL.GL import *: brings in OpenGL drawing and state functions such as glBegin, glVertex3f, glRotatef, etc.
  • math: used here for math helpers (e.g., math.tan, math.radians) when setting the projection.
  • sys: used to read command-line args and to exit the application cleanly.

Step 2: Create the Main Application Window

Here we create the window, add the OpenGL widget and a button for future actions.

Python
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle('3D Modeling & Animation App')

        layout = QVBoxLayout()
        central_widget = QWidget(self)
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        self.glWidget = GLWidget()
        layout.addWidget(self.glWidget)

        btn_create_object = QPushButton('Create Object', self)
        btn_create_object.clicked.connect(self.create_object)
        layout.addWidget(btn_create_object)

        self.show()

    def create_object(self):
        print('Creating 3D object...')

Explanation

  • setGeometry(...) sets the window position and size
  • setWindowTitle(...) sets the title, the layout and central widget arrange child widgets
  • GLWidget() is added as the OpenGL drawing area
  • The button is wired to create_object() for later use.

Step 3: Initialize OpenGL Settings

Now we prepare the OpenGL environment by setting a background color and enabling depth testing so the cube looks properly 3D.

Python
def initializeGL(self):
    glClearColor(0.0, 0.0, 0.0, 1.0)
    glEnable(GL_DEPTH_TEST)

Explanation:

  • glClearColor(...) sets the background color to black
  • glEnable(GL_DEPTH_TEST) enables depth testing so nearer fragments hide farther ones.

Step 4: Adjust Projection When Window Resizes

Whenever the window size changes, we update the camera view so the cube stays proportionate.

Python
def resizeGL(self, width, height):
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    aspect_ratio = width / height
    fov_degrees = 45.0
    near_clip = 0.1
    far_clip = 100.0

    top = near_clip * math.tan(math.radians(fov_degrees / 2.0))
    bottom = -top
    left = bottom * aspect_ratio
    right = top * aspect_ratio

    glFrustum(left, right, bottom, top, near_clip, far_clip)
    glMatrixMode(GL_MODELVIEW)

Explanation:

  • glViewport(...) sets the drawable region
  • glFrustum(...) constructs a perspective projection based on field-of-view and aspect ratio
  • switching back to GL_MODELVIEW prepares OpenGL for drawing objects.

Step 5: Draw and Animate a Rotating 3D Cube

This method clears the frame, sets camera transforms, draws six faces as quads, and increments the rotation for animation.

Python
def paintGL(self):
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    glTranslatef(0.0, 0.0, -5.0)
    glRotatef(self.rotate_angle, 1, 1, 1)

    glBegin(GL_QUADS)

    glColor3f(1.0, 0.0, 0.0)
    glVertex3f(1.0, 1.0, -1.0)
    glVertex3f(-1.0, 1.0, -1.0)
    glVertex3f(-1.0, 1.0, 1.0)
    glVertex3f(1.0, 1.0, 1.0)

    glColor3f(0.0, 1.0, 0.0)
    glVertex3f(1.0, -1.0, 1.0)
    glVertex3f(-1.0, -1.0, 1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glVertex3f(1.0, -1.0, -1.0)

    glColor3f(0.0, 0.0, 1.0)
    glVertex3f(1.0, 1.0, 1.0)
    glVertex3f(-1.0, 1.0, 1.0)
    glVertex3f(-1.0, -1.0, 1.0)
    glVertex3f(1.0, -1.0, 1.0)

    glColor3f(1.0, 1.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glVertex3f(-1.0, 1.0, -1.0)
    glVertex3f(1.0, 1.0, -1.0)

    glColor3f(0.0, 1.0, 1.0)
    glVertex3f(-1.0, 1.0, 1.0)
    glVertex3f(-1.0, 1.0, -1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glVertex3f(-1.0, -1.0, 1.0)

    glColor3f(1.0, 0.0, 1.0)
    glVertex3f(1.0, 1.0, -1.0)
    glVertex3f(1.0, 1.0, 1.0)
    glVertex3f(1.0, -1.0, 1.0)
    glVertex3f(1.0, -1.0, -1.0)

    glEnd()

    self.rotate_angle += 1

Explanation:

  • glClear(...) clears color and depth buffers
  • glTranslatef(...) moves the scene back so the cube is visible
  • glRotatef(...) applies rotation
  • glBegin(GL_QUADS)/glVertex3f(...) draws each face
  • incrementing rotate_angle creates continuous rotation.

Step 6: Add a Timer for Animation

We use a timer event to call update() repeatedly so paintGL() is called each frame.

Python
def timerEvent(self, event):
    self.update()

Explanation: update() schedules a repaint, producing smooth animation when called repeatedly by the timer.

Output

A new window will open and you will see a 3D cube rotating continuously, similar to the preview shown below:


Explore