Update pyproject.toml to include Python and Wand dependencies. Modify default.py to enhance profile layout with adjusted positions and improved glass morphism effect. Update profile.py to set modern profile generation as the default style, ensuring a more contemporary user experience.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
9c5924418d
commit
653b16cb5e
4 changed files with 187 additions and 10 deletions
|
@ -208,19 +208,19 @@ def generate_default_profile(
|
|||
|
||||
# Define the stats area with a modern glass effect
|
||||
stats_area = (
|
||||
380, # x1 - Start after profile picture
|
||||
15, # x1 - Start from left edge with small padding
|
||||
15, # y1 - Start near top
|
||||
1020, # x2 - End near right edge
|
||||
1035, # x2 - End near right edge
|
||||
305 # y2 - Adjusted height
|
||||
)
|
||||
|
||||
# Create the stats layer with glass effect
|
||||
stats_layer = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
|
||||
|
||||
# Create a modern glass morphism effect for stats
|
||||
# Create a modern glass morphism effect for entire background
|
||||
glass = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
|
||||
glass_draw = ImageDraw.Draw(glass)
|
||||
glass_draw.rounded_rectangle(stats_area, radius=25, fill=(255, 255, 255, 25)) # Lighter, more modern glass
|
||||
glass_draw.rounded_rectangle(stats_area, radius=25, fill=(0, 0, 0, 100)) # Darker, semi-transparent background
|
||||
|
||||
# Add a subtle gradient overlay for depth
|
||||
gradient = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
|
||||
|
@ -262,13 +262,13 @@ def generate_default_profile(
|
|||
# Add subtle text shadow for depth
|
||||
shadow_offset = 2
|
||||
draw.text(
|
||||
(stats_area[0] + 20 + shadow_offset, level_y + shadow_offset),
|
||||
(stats_area[0] + 340 + shadow_offset, level_y + shadow_offset), # Adjusted x position
|
||||
level_text,
|
||||
font=level_font,
|
||||
fill=(0, 0, 0, 80) # Reduced shadow opacity
|
||||
)
|
||||
draw.text(
|
||||
(stats_area[0] + 20, level_y),
|
||||
(stats_area[0] + 340, level_y), # Adjusted x position
|
||||
level_text,
|
||||
font=level_font,
|
||||
fill=user_color
|
||||
|
@ -282,7 +282,7 @@ def generate_default_profile(
|
|||
spacing = 45 # Increased spacing
|
||||
|
||||
# Starting positions
|
||||
start_x = stats_area[0] + 20
|
||||
start_x = stats_area[0] + 340 # Adjusted x position
|
||||
start_y = level_y + 75
|
||||
|
||||
# Helper function for stat rendering with improved spacing
|
||||
|
|
170
levelup/generator/styles/modern.py
Normal file
170
levelup/generator/styles/modern.py
Normal file
|
@ -0,0 +1,170 @@
|
|||
import typing as t
|
||||
from pathlib import Path
|
||||
from io import BytesIO
|
||||
import logging
|
||||
from wand.image import Image
|
||||
from wand.drawing import Drawing
|
||||
from wand.color import Color
|
||||
from redbot.core.utils.chat_formatting import humanize_number
|
||||
|
||||
try:
|
||||
from .. import imgtools
|
||||
except ImportError:
|
||||
import imgtools
|
||||
|
||||
log = logging.getLogger("red.vrt.levelup.generator.styles.modern")
|
||||
|
||||
def generate_modern_profile(
|
||||
background_bytes: t.Optional[t.Union[bytes, str]] = None,
|
||||
avatar_bytes: t.Optional[t.Union[bytes, str]] = None,
|
||||
username: str = "Spartan117",
|
||||
status: str = "online",
|
||||
level: int = 3,
|
||||
messages: int = 420,
|
||||
voicetime: int = 3600,
|
||||
stars: int = 69,
|
||||
prestige: int = 0,
|
||||
prestige_emoji: t.Optional[t.Union[bytes, str]] = None,
|
||||
balance: int = 0,
|
||||
currency_name: str = "Credits",
|
||||
previous_xp: int = 100,
|
||||
current_xp: int = 125,
|
||||
next_xp: int = 200,
|
||||
position: int = 3,
|
||||
role_icon: t.Optional[t.Union[bytes, str]] = None,
|
||||
blur: bool = False,
|
||||
base_color: t.Tuple[int, int, int] = (255, 255, 255),
|
||||
user_color: t.Optional[t.Tuple[int, int, int]] = None,
|
||||
stat_color: t.Optional[t.Tuple[int, int, int]] = None,
|
||||
level_bar_color: t.Optional[t.Tuple[int, int, int]] = None,
|
||||
font_path: t.Optional[t.Union[str, Path]] = None,
|
||||
render_gif: bool = False,
|
||||
debug: bool = False,
|
||||
**kwargs,
|
||||
) -> t.Tuple[bytes, bool]:
|
||||
"""Generate a modern, sleek profile card using Wand/ImageMagick"""
|
||||
|
||||
# Set default colors for modern theme
|
||||
user_color = user_color or (88, 101, 242) # Discord Blurple
|
||||
stat_color = stat_color or (255, 255, 255) # White
|
||||
level_bar_color = level_bar_color or (88, 101, 242) # Discord Blurple
|
||||
|
||||
# Create base canvas (1050x320)
|
||||
with Image(width=1050, height=320) as card:
|
||||
# Set up background
|
||||
if background_bytes:
|
||||
with Image(blob=background_bytes) as bg:
|
||||
bg.transform(resize='1050x320^')
|
||||
bg.crop(width=1050, height=320, gravity='center')
|
||||
if blur:
|
||||
bg.gaussian_blur(sigma=15)
|
||||
card.composite(bg, 0, 0)
|
||||
else:
|
||||
# Create gradient background
|
||||
card.gradient('linear-gradient', 'navy-darkblue')
|
||||
|
||||
# Add frosted glass effect overlay
|
||||
with Image(width=1050, height=320, background=Color('rgba(255, 255, 255, 0.1)')) as overlay:
|
||||
overlay.gaussian_blur(sigma=50)
|
||||
card.composite(overlay, 0, 0)
|
||||
|
||||
# Create circular avatar mask
|
||||
with Drawing() as draw:
|
||||
# Avatar circle (left side)
|
||||
avatar_size = 200
|
||||
avatar_x = 60
|
||||
avatar_y = 60
|
||||
|
||||
if avatar_bytes:
|
||||
with Image(blob=avatar_bytes) as avatar:
|
||||
# Resize avatar
|
||||
avatar.resize(avatar_size, avatar_size)
|
||||
# Create circular mask
|
||||
with Image(width=avatar_size, height=avatar_size, background=Color('transparent')) as mask:
|
||||
draw.circle((avatar_size/2, avatar_size/2), (0, avatar_size/2))
|
||||
draw.draw(mask)
|
||||
# Apply mask to avatar
|
||||
avatar.composite_channel('alpha', mask, 'copy_alpha', 0, 0)
|
||||
# Add avatar to card
|
||||
card.composite(avatar, avatar_x, avatar_y)
|
||||
|
||||
# Add modern stats panel
|
||||
with Image(width=700, height=280, background=Color('rgba(0, 0, 0, 0.5)')) as stats_panel:
|
||||
stats_panel.gaussian_blur(sigma=2)
|
||||
stats_panel.border_color = Color('rgba(255, 255, 255, 0.1)')
|
||||
stats_panel.border_width = 1
|
||||
card.composite(stats_panel, 310, 20)
|
||||
|
||||
# Add text elements
|
||||
draw.font = str(font_path) if font_path else '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
|
||||
draw.font_size = 40
|
||||
draw.fill_color = Color('rgb({}, {}, {})'.format(*user_color))
|
||||
|
||||
# Level text
|
||||
level_text = f"LEVEL {level}"
|
||||
if prestige > 0:
|
||||
level_text = f"P{prestige} • {level_text}"
|
||||
draw.text(340, 70, level_text)
|
||||
|
||||
# Stats
|
||||
draw.font_size = 25
|
||||
draw.fill_color = Color('white')
|
||||
y_pos = 120
|
||||
|
||||
# Left column
|
||||
draw.text(340, y_pos, f"Messages: {humanize_number(messages)}")
|
||||
draw.text(340, y_pos + 40, f"Voice Time: {imgtools.abbreviate_time(voicetime)}")
|
||||
draw.text(340, y_pos + 80, f"Stars: {humanize_number(stars)}")
|
||||
|
||||
# Right column
|
||||
if balance is not None:
|
||||
draw.text(600, y_pos, f"Balance: {humanize_number(balance)} {currency_name}")
|
||||
draw.text(600, y_pos + 40, f"Rank: #{humanize_number(position)}")
|
||||
|
||||
# Progress bar
|
||||
progress = (current_xp - previous_xp) / (next_xp - previous_xp)
|
||||
bar_width = 620
|
||||
bar_height = 30
|
||||
bar_x = 340
|
||||
bar_y = 240
|
||||
|
||||
# Background bar
|
||||
draw.fill_color = Color('rgba(0, 0, 0, 0.3)')
|
||||
draw.rectangle(bar_x, bar_y, bar_x + bar_width, bar_y + bar_height)
|
||||
|
||||
# Progress bar
|
||||
progress_width = int(bar_width * progress)
|
||||
draw.fill_color = Color('rgb({}, {}, {})'.format(*level_bar_color))
|
||||
if progress_width > 0:
|
||||
draw.rectangle(bar_x, bar_y, bar_x + progress_width, bar_y + bar_height)
|
||||
|
||||
# XP Text
|
||||
draw.font_size = 20
|
||||
draw.fill_color = Color('white')
|
||||
xp_text = f"XP: {humanize_number(current_xp)} / {humanize_number(next_xp)}"
|
||||
draw.text(bar_x + (bar_width - len(xp_text) * 10) / 2, bar_y - 15, xp_text)
|
||||
|
||||
# Progress percentage
|
||||
percent_text = f"{int(progress * 100)}%"
|
||||
draw.text(bar_x + progress_width - 30, bar_y + 5, percent_text)
|
||||
|
||||
# Apply all drawings
|
||||
draw.draw(card)
|
||||
|
||||
# Save the result
|
||||
card.format = 'webp'
|
||||
card.compression_quality = 95
|
||||
|
||||
buffer = BytesIO()
|
||||
card.save(buffer)
|
||||
return buffer.getvalue(), False
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test code
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
test_avatar = (imgtools.ASSETS / "tests" / "default.png").read_bytes()
|
||||
res, _ = generate_modern_profile(
|
||||
avatar_bytes=test_avatar,
|
||||
debug=True
|
||||
)
|
||||
(imgtools.ASSETS / "tests" / "modern_result.webp").write_bytes(res)
|
|
@ -15,7 +15,7 @@ from redbot.core.utils.chat_formatting import box, humanize_number
|
|||
from ..abc import MixinMeta
|
||||
from ..common import formatter, utils
|
||||
from ..common.models import Profile
|
||||
from ..generator.styles import default, runescape
|
||||
from ..generator.styles import default, runescape, modern
|
||||
|
||||
log = logging.getLogger("red.vrt.levelup.shared.profile")
|
||||
_ = Translator("LevelUp", __file__)
|
||||
|
@ -325,12 +325,15 @@ class ProfileFormatting(MixinMeta):
|
|||
except Exception as e:
|
||||
log.error("Failed to fetch profile from internal API", exc_info=e)
|
||||
|
||||
# By default we'll use the bundled generator
|
||||
# By default we'll use the modern generator
|
||||
funcs = {
|
||||
"default": default.generate_default_profile,
|
||||
"modern": modern.generate_modern_profile, # New default modern style
|
||||
"default": default.generate_default_profile, # Old style
|
||||
"runescape": runescape.generate_runescape_profile,
|
||||
}
|
||||
|
||||
profile_style = profile.style if profile.style in funcs else "modern" # Default to modern style
|
||||
|
||||
def _run() -> discord.File:
|
||||
img_bytes, animated = funcs[profile_style](**kwargs)
|
||||
ext = "gif" if animated else "webp"
|
||||
|
|
|
@ -7,3 +7,7 @@
|
|||
line-length = 99
|
||||
target-version = ['py38']
|
||||
include = '\.pyi?$'
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8.1,<4.0"
|
||||
Wand = "^0.6.11" # ImageMagick Python bindings for modern image processing
|
||||
|
|
Loading…
Add table
Reference in a new issue