ONE main.py Python Project 🀯 | Futuristic 3D AI GUI using PyQt5 | FuzzuTech

 Demo :


Click Video πŸ‘‡πŸ‘‡πŸ‘‡























✨FEATURES :

✅ Only ONE Python file (main.py)
✅ No images, no assets, no extra files
3D holographic card animation
✅ Cyberpunk / Neon futuristic UI
✅ AI-style project idea generator
✅ Smooth hover + glow + flip effects
✅ Fully responsive (Laptop / Tablet / Mobile ratio)
✅ Beginner + Advanced friendly
✅ Perfect for:

  • College projects

  • Portfolio

  • YouTube Shorts

  • Instagram Reels


Code :


import sys

import random

import math

from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, 

                             QPushButton, QFrame, QGraphicsDropShadowEffect, 

                             QDesktopWidget, QSizePolicy)

from PyQt5.QtCore import (Qt, QPropertyAnimation, QEasingCurve, QTimer, 

                          pyqtProperty, QRectF, QPointF, QPoint)

from PyQt5.QtGui import (QColor, QFont, QLinearGradient, QBrush, QPainter, 

                         QPen, QTransform, QPolygonF, QPixmap)


# --- DATA ---

PROJECTS = [

    ("Neural Network Visualizer", "Interactive dashboard visualizing neural network learning in real-time."),

    ("AI Voice Assistant", "Local LLM-based voice assistant to control PC and summarize emails."),

    ("Crypto Trading Bot", "Autonomous bot analyzing sentiment to execute crypto trades."),

    ("Smart Home Hub", "IoT dashboard with AI-powered energy prediction and automation."),

    ("Deepfake Detector", "Real-time video analysis tool detecting synthetic media anomalies."),

    ("Code Refactor AI", "Productivity tool suggesting pythonic writes using AST parsing."),

    ("Mood Playlist Gen", "Webcam-based emotion detection feeding a Spotify playlist generator."),

    ("Cyberpunk Terminal", "Retro CRT-style terminal emulator with neon aesthetics."),

    ("Auto-Subtitle Gen", "Stylish animated subtitle generator using OpenAI Whisper."),

    ("Stock Market Oracle", "ML model predicting trends based on social sentiment.")

]


# --- COLORS ---

NEON_CYAN = QColor("#00F3FF")

NEON_PINK = QColor("#BC13FE")

NEON_GREEN = QColor("#00FF9D")

DARK_BG = QColor("#0B0C15")


class TypewriterLabel(QLabel):

    def __init__(self, text="", parent=None, size=14, color="#AAAAAA", bold=False):

        super().__init__(parent)

        self.full_text = text

        self.current_idx = 0

        self.setFont(QFont("Segoe UI", size, QFont.Bold if bold else QFont.Normal))

        self.setStyleSheet(f"color: {color}; background: transparent;")

        self.setAlignment(Qt.AlignCenter)

        self.setWordWrap(True)

        self.timer = QTimer(self)

        self.timer.timeout.connect(self._type_next)

        

    def set_animated_text(self, text):

        self.full_text = text

        self.current_idx = 0

        self.setText("")

        self.timer.start(25) 

        

    def _type_next(self):

        if self.current_idx < len(self.full_text):

            self.current_idx += 1

            self.setText(self.full_text[:self.current_idx] + "█")

        else:

            self.setText(self.full_text)

            self.timer.stop()


class NeonButton(QPushButton):

    def __init__(self, text, parent=None):

        super().__init__(text, parent)

        self.setCursor(Qt.PointingHandCursor)

        self.setFixedSize(260, 60)

        self.setFont(QFont("Segoe UI", 12, QFont.Bold))

        

        self.glow = QGraphicsDropShadowEffect(self)

        self.glow.setBlurRadius(20)

        self.glow.setColor(NEON_CYAN)

        self.glow.setOffset(0, 0)

        self.setGraphicsEffect(self.glow)


        self.anim_timer = QTimer(self)

        self.anim_timer.timeout.connect(self.animate_glow)

        self.glow_val = 0

        self.growing = True


    def animate_glow(self):

        if self.growing:

            self.glow_val += 3

            if self.glow_val >= 60: self.growing = False

        else:

            self.glow_val -= 3

            if self.glow_val <= 20: self.growing = True

        self.glow.setBlurRadius(self.glow_val)


    def enterEvent(self, event):

        self.anim_timer.start(20)

        self.setStyleSheet(f"""

            QPushButton {{

                background-color: rgba(0, 243, 255, 0.2);

                color: white;

                border: 2px solid white;

                border-radius: 12px;

            }}

        """)

        super().enterEvent(event)


    def leaveEvent(self, event):

        self.anim_timer.stop()

        self.glow.setBlurRadius(20)

        self.setStyleSheet(f"""

            QPushButton {{

                background-color: rgba(0, 243, 255, 0.05);

                color: {NEON_CYAN.name()};

                border: 2px solid {NEON_CYAN.name()};

                border-radius: 12px;

            }}

        """)

        super().leaveEvent(event)


class CardContent(QFrame):

    """Hidden widget that renders the actual card layout."""

    def __init__(self, parent=None):

        super().__init__(parent)

        self.setFixedSize(500, 320)

        self.setStyleSheet(f"""

            QFrame {{

                background-color: rgba(20, 25, 40, 0.95);

                border: 2px solid {NEON_PINK.name()};

                border-radius: 20px;

            }}

        """)

        

        layout = QVBoxLayout(self)

        layout.setContentsMargins(30, 30, 30, 30)

        layout.setSpacing(15)


        self.title_lbl = TypewriterLabel("SYSTEM READY", self, 22, NEON_PINK.name(), True)

        self.line = QFrame()

        self.line.setFrameShape(QFrame.HLine)

        self.line.setStyleSheet(f"background: {NEON_CYAN.name()}; max-height: 2px;")

        

        self.desc_lbl = TypewriterLabel("Awaiting initialization sequence...", self, 13, "#DDDDDD")


        layout.addWidget(self.title_lbl)

        layout.addWidget(self.line)

        layout.addWidget(self.desc_lbl)


class ProjectCard3D(QWidget):

    def __init__(self, parent=None):

        super().__init__(parent)

        self.setFixedSize(600, 450)  # Larger area for tilt movement

        

        # The internal widget we will render

        self.internal_card = CardContent(self)

        self.internal_card.hide() # We only paint it

        

        # Tilt state

        self.tilt_x = 0.0 # Mouse X tilt (-1 to 1)

        self.tilt_y = 0.0 # Mouse Y tilt (-1 to 1)

        self.target_tilt_x = 0.0

        self.target_tilt_y = 0.0

        

        # Rotation state (Flip)

        self._rotation_angle = 0.0 # 0 to 180 degrees

        

        # Animation loop for smooth physics

        self.timer = QTimer(self)

        self.timer.timeout.connect(self.update_physics)

        self.timer.start(16)

        

        self.setMouseTracking(True)

        

        # Flip Animation

        self.flip_anim = QPropertyAnimation(self, b"rotationAngle")

        self.flip_anim.setDuration(800)

        self.flip_anim.setEasingCurve(QEasingCurve.OutBack) # Bouncy end

        

        # Helpers

        self.next_title = ""

        self.next_desc = ""


    @pyqtProperty(float)

    def rotationAngle(self):

        return self._rotation_angle


    @rotationAngle.setter

    def rotationAngle(self, val):

        self._rotation_angle = val

        # Swap content at 90 degrees/270 degrees etc.

        # Simple Logic: If we cross 90 or 270, update content

        # But animation goes 0 -> 180. Swap at 90.

        

        # Just check if we are near 90 and have new data

        if 85 < val < 95 and self.next_title:

             self.internal_card.title_lbl.set_animated_text(self.next_title)

             self.internal_card.desc_lbl.set_animated_text(self.next_desc)

             self.next_title = ""


    def flip_to(self, title, desc):

        self.next_title = title

        self.next_desc = desc

        

        # Toggle between 0 and 180

        if self._rotation_angle < 90:

            self.flip_anim.setStartValue(0.0)

            self.flip_anim.setEndValue(180.0)

        else:

            self.flip_anim.setStartValue(180.0)

            self.flip_anim.setEndValue(360.0) # Full circle

            # Reset to 0 after? or keep going

            

        # For simplicity, just ping pong 0 -> 180 -> 0 doesn't look like full rotation usually

        # Let's do 0->180. If at 180, jump to 0 (invisible) then anim to 180?

        # Better: keep increasing angle or ping pong. 

        # Ping Pong 0->180->0 is fine for "Card Flip"

        

        if self._rotation_angle > 90:

             self.flip_anim.setStartValue(180.0)

             self.flip_anim.setEndValue(0.0)

        

        self.flip_anim.start()


    def mouseMoveEvent(self, event):

        # Calculate normalized tilt (-1 to 1)

        w, h = self.width(), self.height()

        mx, my = event.x(), event.y()

        

        self.target_tilt_x = (mx - w/2) / (w/2)

        self.target_tilt_y = (my - h/2) / (h/2)

        super().mouseMoveEvent(event)


    def leaveEvent(self, event):

        self.target_tilt_x = 0

        self.target_tilt_y = 0

        super().leaveEvent(event)


    def update_physics(self):

        # Smooth interpolation (Lerp)

        self.tilt_x += (self.target_tilt_x - self.tilt_x) * 0.1

        self.tilt_y += (self.target_tilt_y - self.tilt_y) * 0.1

        self.update()


    def paintEvent(self, event):

        painter = QPainter(self)

        painter.setRenderHint(QPainter.Antialiasing)

        painter.setRenderHint(QPainter.SmoothPixmapTransform)


        # 1. Grab Card Pixmap

        # We need to render the internal_widget to a pixmap

        pixmap = QPixmap(self.internal_card.size())

        pixmap.fill(Qt.transparent)

        self.internal_card.render(pixmap)


        # 2. Calculate 3D Transform

        # Center of widget

        cw = self.width() / 2

        ch = self.height() / 2

        

        # Card dimensions

        card_w = self.internal_card.width()

        card_h = self.internal_card.height()

        

        # Max tilt amount (pixels)

        MAX_TILT = 30 

        

        # Apply Flip Rotation (around Y axis)

        # We simulate cos/sin scaling

        rad = math.radians(self._rotation_angle)

        cos_a = math.cos(rad) # 1 -> 0 -> -1

        

        # If cos_a is negative, we are seeing the back.

        # We should mirror the texture or show a "Back" texture.

        # For this app, let's just mirror the Front texture (simple).

        

        effective_width = card_w * abs(cos_a)

        if effective_width < 1.0: effective_width = 1.0

        

        # Perspective points

        # Top-Left, Top-Right, Bottom-Right, Bottom-Left

        

        # Base Rect centered

        dx = (self.width() - effective_width) / 2

        dy = (self.height() - card_h) / 2

        

        # TILT OFFSETS

        # When mouse is Top-Left (-1, -1):

        # TL corner goes UP and LEFT

        # BR corner goes DOWN and RIGHT

        

        tx = self.tilt_x * MAX_TILT

        ty = self.tilt_y * MAX_TILT

        

        # Define the 4 corners for the transform

        # Normal 2D rect: (dx, dy), (dx+w, dy), ...

        # Skewed by tilt:

        

        p1 = QPointF(dx - tx,       dy - ty)      # TL

        p2 = QPointF(dx + effective_width + tx, dy - ty)      # TR

        p3 = QPointF(dx + effective_width - tx, dy + card_h + ty) # BR

        p4 = QPointF(dx + tx,       dy + card_h + ty) # BL


        # If rotated > 90 and < 270 (Backside), we might want to flip the image drawing

        # But since we take abs(cos_a), it renders "front" always but squashed.

        # This is fine for a holographic effect. 

        

        # Draw "Thickness" / Shadow to enhance 3D

        # Shifted back layers

        shadow_p1 = p1 + QPointF(15, 15)

        shadow_p2 = p2 + QPointF(15, 15)

        shadow_p3 = p3 + QPointF(15, 15)

        shadow_p4 = p4 + QPointF(15, 15)

        

        shadow_poly = QPolygonF([shadow_p1, shadow_p2, shadow_p3, shadow_p4])

        

        painter.setPen(Qt.NoPen)

        painter.setBrush(QColor(0, 0, 0, 80))

        painter.drawPolygon(shadow_poly)


        # Draw The Card (Texture Mapped) with Robust 2.5D Transform

        

        painter.save()

        

        # Center the coordinate system

        painter.translate(self.width() / 2, self.height() / 2)

        

        # 1. FLIP ANIMATION (Scale X)

        # We simulate 3D rotation around Y axis by scaling local X

        rad = math.radians(self._rotation_angle)

        scale_x = abs(math.cos(rad))

        if scale_x < 0.02: scale_x = 0.02 # Prevent div by zero or invisible

        

        # 2. TILT ANIMATION (Shear & Rotate)

        # Fake 3D perspective using shear

        # tilt_x (-1 to 1) -> Shear X

        # tilt_y (-1 to 1) -> Shear Y

        

        # Apply slight rotation based on movement for "floating" feel

        painter.rotate(self.tilt_x * 2) 

        

        # Apply Shear

        # Note: Order matters. Scale first, then Shear.

        painter.scale(scale_x, 1.0)

        painter.shear(-self.tilt_x * 0.1, -self.tilt_y * 0.1)

        

        # Draw Pixmap centered

        offset_x = -card_w / 2

        offset_y = -card_h / 2

        

        # Draw Shadow manually offset *before* drawing the main card not working well with transforms

        # So we draw shadow first with a different transform? 

        # Simpler: Draw drop shadow on the pixmap itself or use QGraphicsEffect. 

        # But QGraphicsEffect doesn't transform well with painter.

        # Let's just draw a dark rect behind.

        

        painter.setPen(Qt.NoPen)

        painter.setBrush(QColor(0, 0, 0, 100))

        painter.drawRect(QRectF(offset_x + 15, offset_y + 15, card_w, card_h))

        

        # Draw Main Card

        painter.drawPixmap(int(offset_x), int(offset_y), pixmap)

        

        painter.restore()

        

        # Draw Holographic Scan Overlay (Screen Space)

        # Simple scan line moving down

        painter.setCompositionMode(QPainter.CompositionMode_Screen)

        scan_y = (int(self.tilt_x * 100) + int(QTimer.remainingTime(self.timer) * 0.5)) % int(self.height())

        # Actually use a local counter

        if not hasattr(self, 'scan_offset'): self.scan_offset = 0

        self.scan_offset = (self.scan_offset + 2) % (self.height() + 100)

        

        grad = QLinearGradient(0, self.scan_offset, 0, self.scan_offset + 50)

        grad.setColorAt(0, QColor(0, 255, 255, 0))

        grad.setColorAt(0.5, QColor(0, 255, 255, 30))

        grad.setColorAt(1, QColor(0, 255, 255, 0))

        

        painter.fillRect(self.rect(), grad)

        

class MainWindow(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("AI Project Generator 3D")

        self.resize(1200, 800)

        self.center_window()

        

        # Background Animation

        self.grid_offset = 0

        self.bg_timer = QTimer()

        self.bg_timer.timeout.connect(self.animate_bg)

        self.bg_timer.start(16)


        # Layout

        self.main_layout = QVBoxLayout(self)

        self.main_layout.setAlignment(Qt.AlignCenter)

        self.main_layout.setSpacing(0)

        

        self.header = QLabel("HOLOGRAPHIC // PROJECTOR")

        self.header.setFont(QFont("Segoe UI", 16, QFont.Bold))

        self.header.setStyleSheet(f"color: {NEON_GREEN.name()}; letter-spacing: 5px;")

        self.header.setAlignment(Qt.AlignCenter)


        # Container for 3D card

        # We use a container to center it freely

        self.card = ProjectCard3D()

        

        self.btn = NeonButton("INITIATE SEQUENCE")

        self.btn.clicked.connect(self.generate)


        self.main_layout.addStretch(1)

        self.main_layout.addWidget(self.header)

        self.main_layout.addSpacing(40)

        self.main_layout.addWidget(self.card, 0, Qt.AlignCenter)

        self.main_layout.addSpacing(50)

        self.main_layout.addWidget(self.btn, 0, Qt.AlignCenter)

        self.main_layout.addStretch(1)


    def center_window(self):

        qr = self.frameGeometry()

        cp = QDesktopWidget().availableGeometry().center()

        qr.moveCenter(cp)

        self.move(qr.topLeft())


    def animate_bg(self):

        self.grid_offset += 2.0

        if self.grid_offset > 50: self.grid_offset = 0

        self.update()


    def generate(self):

        t, d = random.choice(PROJECTS)

        self.card.flip_to(t, d)


    def paintEvent(self, event):

        """Draws the infinite retro grid."""

        p = QPainter(self)

        p.setRenderHint(QPainter.Antialiasing)

        

        # Background

        p.fillRect(self.rect(), DARK_BG)

        

        # Sky Gradient

        grad = QLinearGradient(0, 0, 0, self.height() / 1.5)

        grad.setColorAt(0, QColor("#020205"))

        grad.setColorAt(1, QColor("#1A0B2E")) # Purpleish horizon

        p.fillRect(0, 0, self.width(), int(self.height() / 1.5), grad)


        # Sun

        sun_grad = QLinearGradient(0, self.height()*0.3, 0, self.height()*0.6)

        sun_grad.setColorAt(0, QColor(255, 0, 128, 0))

        sun_grad.setColorAt(1, QColor(255, 0, 128, 60))

        p.setPen(Qt.NoPen)

        p.setBrush(sun_grad)

        p.drawEllipse(int(self.width()/2 - 150), int(self.height()*0.6 - 150), 300, 300)


        # Grid Floor

        horizon_y = self.height() * 0.6

        bottom_y = self.height()

        

        p.setClipRect(0, int(horizon_y), self.width(), int(bottom_y - horizon_y))

        

        # Grid Lines Color

        grid_pen = QPen(QColor(NEON_PINK.red(), NEON_PINK.green(), NEON_PINK.blue(), 60))

        grid_pen.setWidth(1)

        p.setPen(grid_pen)


        # Vertical Perspective Lines

        cx = self.width() / 2

        for i in range(-25, 26):

            xw = i * 120

            p.drawLine(QPoint(int(cx), int(horizon_y)), 

                       QPoint(int(cx + xw * 4), int(bottom_y)))


        # Horizontal Moving Lines

        # Simple depth formula: y = horizon + scale/z

        # We iterate z from near to far

        for i in range(20):

            # z moves from 10 (close) to 1 (far)

            # phase shift by grid_offset

            

            dist = (i * 50 + self.grid_offset)

            if dist == 0: continue

            

            # y position relative to horizon

            # exponential or reciprocal spacing for 3D look

            # y = horizon + H / (some factor related to i)

            

            # Linear approach for retro synthwave look often works better visually matches

            y = horizon_y + dist

            if y > bottom_y: continue

            

            p.drawLine(0, int(y), self.width(), int(y))


if __name__ == "__main__":

    app = QApplication(sys.argv)

    window = MainWindow()

    window.show()

    sys.exit(app.exec_())


Comments

Popular posts from this blog

Educational File Encryptor GUI (Python AES Project) | FuzzuTech

Is This News Real or Fake? πŸ€– AI Exposes the Truth | FuzzuTech Python App Demo

🚨 Python Intrusion Detection System (IDS) – Real-Time ML + Tkinter GUI Project | FuzzuTech