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
Post a Comment