Skip to content

Commit 435c261

Browse files
Optimised Code
1 parent d433372 commit 435c261

File tree

1 file changed

+110
-99
lines changed

1 file changed

+110
-99
lines changed

SteamRoulette.py

Lines changed: 110 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from io import BytesIO
1111
import vdf
1212
import sys
13-
import concurrent.futures
13+
from concurrent.futures import ThreadPoolExecutor
1414
import time
1515
import winreg
1616
import threading
@@ -53,14 +53,16 @@ def find_steam_path_fallback():
5353
return None
5454

5555
def resource_path(relative_path):
56-
""" Get the absolute path to the resource, works for both development and PyInstaller. """
56+
"""Get the absolute path to the resource, works for both development and PyInstaller."""
5757
try:
58-
# PyInstaller creates a temp folder and stores the path to the bundled app
59-
base_path = sys._MEIPASS
58+
base_path = sys._MEIPASS # PyInstaller temp directory
6059
except Exception:
61-
base_path = os.path.abspath(".") # Use the current directory in development
60+
base_path = os.path.dirname(os.path.abspath(__file__)) # Development mode
61+
62+
resolved_path = os.path.join(base_path, relative_path)
6263

63-
return os.path.join(base_path, relative_path)
64+
print(f"Resolved path for {relative_path}: {resolved_path}") # Debugging output
65+
return resolved_path
6466

6567
# Constants
6668
STEAM_PATH = get_steam_install_path() or find_steam_path_fallback()
@@ -100,24 +102,21 @@ def fetch_header_image(app_id, cache_dir, timeout=10):
100102
"""Fetch game header image from Steam or return a placeholder."""
101103
cache_file_path = os.path.join(cache_dir, f"{app_id}.jpg")
102104

103-
# If the image is already cached, return the cached image
104105
if os.path.exists(cache_file_path):
105106
print(f"Using cached image for app_id {app_id}")
106-
return Image.open(cache_file_path)
107+
return Image.open(cache_file_path) # Open the cached image
107108

108-
# If not cached, fetch the image from Steam and save it to the cache
109+
# If not cached, fetch from Steam
109110
urls = [
110111
f"https://cdn.cloudflare.steamstatic.com/steam/apps/{app_id}/header.jpg",
111112
f"https://cdn.cloudflare.steamstatic.com/steam/apps/{app_id}/page_bg.jpg",
112113
]
113114
for url in urls:
114115
try:
115-
print(f"Attempting to fetch image for app_id {app_id} from URL: {url}")
116116
response = requests.get(url, timeout=timeout)
117117
if response.status_code == 200:
118118
img = Image.open(BytesIO(response.content))
119-
# Save the image to the cache
120-
img.save(cache_file_path, "JPEG")
119+
img.save(cache_file_path, "JPEG") # Save it to the cache
121120
return img
122121
except Exception as e:
123122
print(f"Error fetching image for app_id {app_id}: {e}")
@@ -329,7 +328,7 @@ def __init__(self, root, installed_games, drives):
329328
self.frame_delay = 16 # Controls the speed of the animation (time between frames)
330329

331330
self.root.title("Steam Roulette")
332-
self.root.geometry("600x700")
331+
self.root.geometry("600x750")
333332
self.root.resizable(False, False)
334333

335334
# Define the relative path to the 'header_images' folder
@@ -347,14 +346,25 @@ def __init__(self, root, installed_games, drives):
347346
# Display a random header image on startup
348347
self.display_random_header_image()
349348

350-
# Label displaying "Games Found on Drives" (bottom right corner)
349+
# Label displaying "Games Found on Drives"
351350
self.label_game_count = tk.Label(root, text=self.generate_games_found_text(), font=("Arial", 10))
352-
self.label_game_count.place(relx=0.0, rely=0.0, anchor='nw', x=5, y=60)
351+
352+
# Get the width of the window and place the label in the top-right corner
353+
window_width = root.winfo_width() # Get current width of the window
354+
self.label_game_count.place(x=window_width - 5, y=5, anchor='ne') # 10px from the right and 10px from the top
353355

354356
# Label displaying copyright notice in the top-left corner
355357
self.copyright_notice = tk.Label(root, text="© Streetbackguy 2024", font=("Arial", 8))
356358
self.copyright_notice.place(relx=0.0, rely=0.0, anchor='nw', x=5, y=5)
357359

360+
# Welcome label
361+
self.label_welcome = tk.Label(frame, text="Welcome to Steam Roulette!", font=("Arial", 16), bg=self.light_mode_bg)
362+
self.label_welcome.grid(row=1, pady=5)
363+
364+
# Game name label
365+
self.label_game_name = tk.Label(frame, text="", wraplength=600, font=("Arial", 20), bg=self.light_mode_bg)
366+
self.label_game_name.grid(row=2, pady=5)
367+
358368
# Initial theme mode (light mode by default)
359369
self.is_dark_mode = False
360370

@@ -420,8 +430,8 @@ def __init__(self, root, installed_games, drives):
420430
self.yes_no_frame.place(relx=1.0, rely=1.0, anchor='se', x=-2, y=-2) # Padding for the frame
421431

422432
# Label for Selecting whether to include uninstalled games or not
423-
self.label_number_of_games = tk.Label(self.yes_no_frame, text="Include Uninstalled Games?", font=("Arial", 8))
424-
self.label_number_of_games.grid(row=0, column=0, columnspan=2, pady=2)
433+
self.label_include_uninstalled_games = tk.Label(self.yes_no_frame, text="Include Uninstalled Games?", font=("Arial", 8))
434+
self.label_include_uninstalled_games.grid(row=0, column=0, columnspan=2, pady=2)
425435

426436
# Yes button
427437
self.button_yes = tk.Button(self.yes_no_frame, text="Yes", command=self.on_yes_click)
@@ -436,29 +446,49 @@ def __init__(self, root, installed_games, drives):
436446
self.selected_game_item = None
437447
self.animation_id = None
438448

439-
# Use resource_path to get the correct logo path
440-
logo_path = resource_path("SteamRouletteLogo.png")
441449
try:
450+
# Use resource_path to get the correct logo path
451+
logo_path = resource_path("SteamRouletteLogo.png")
452+
print(f"Trying to load logo from: {logo_path}")
453+
454+
if not os.path.exists(logo_path):
455+
raise FileNotFoundError(f"Logo file not found at: {logo_path}")
456+
457+
# Load the logo image
442458
logo_image = Image.open(logo_path)
443-
logo_image = ImageTk.PhotoImage(logo_image)
459+
logo_image_tk = ImageTk.PhotoImage(logo_image)
444460

445-
self.label_logoimage = tk.Label(frame, image=logo_image, bg=self.light_mode_bg)
446-
self.label_logoimage.image = logo_image # Keep a reference to the image
461+
# Display logo
462+
self.label_logoimage = tk.Label(frame, image=logo_image_tk, bg=self.light_mode_bg)
463+
self.label_logoimage.image = logo_image_tk # Keep reference to avoid garbage collection
447464
self.label_logoimage.grid(row=0, pady=5)
448465

449-
# Welcome label
450-
self.label_welcome = tk.Label(frame, text="Welcome to Steam Roulette!", font=("Arial", 16), bg=self.light_mode_bg)
451-
self.label_welcome.grid(row=1, pady=5)
466+
except FileNotFoundError as fnfe:
467+
print(fnfe)
468+
# In case logo or icon is missing, display a fallback message
469+
self.label_logoimage = tk.Label(frame, text="Logo Missing", font=("Arial", 16), bg=self.light_mode_bg)
470+
self.label_logoimage.grid(row=0, pady=5)
452471

453-
# Remove text from label_game_name after logo is displayed
454-
self.label_game_name = tk.Label(frame, text="", wraplength=600, font=("Arial", 20), bg=self.light_mode_bg)
455-
self.label_game_name.grid(row=2, pady=5)
472+
except Exception as e:
473+
print(f"Error: {e}")
474+
475+
try:
476+
# Get the path for the .ico file
477+
icon_path = resource_path("SteamRouletteIcon.ico")
478+
print(f"Trying to load icon from: {icon_path}")
456479

457-
# Ensure the window updates after packing elements
458-
self.root.update_idletasks()
480+
# Ensure the icon file exists
481+
if not os.path.exists(icon_path):
482+
raise FileNotFoundError(f"Icon file not found at: {icon_path}")
459483

484+
# Set the window icon
485+
self.root.iconbitmap(icon_path)
486+
print("Window icon successfully set.")
487+
488+
except FileNotFoundError as fnfe:
489+
print(fnfe)
460490
except Exception as e:
461-
print(f"Error loading logo image: {e}")
491+
print(f"Error setting window icon: {e}")
462492

463493
# Set Light Mode
464494
self.set_light_mode()
@@ -467,32 +497,18 @@ def __init__(self, root, installed_games, drives):
467497
self.display_random_header_image()
468498

469499
def preload_images(self):
470-
"""Preload images for both installed and uninstalled games."""
471-
# Get the directory of the executable or script
472-
base_dir = os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else __file__)
473-
cache_dir = os.path.join(base_dir, "image_cache")
474-
os.makedirs(cache_dir, exist_ok=True)
475-
476-
for game in self.installed_games + getattr(self, 'uninstalled_games', []):
500+
"""Preload images for both installed and uninstalled games using threading."""
501+
def preload_image(game):
477502
app_id = game.get("app_id")
478-
if not app_id:
479-
continue
480-
481-
# Check if image already exists in the cache
482-
cache_file_path = os.path.join(cache_dir, f"{app_id}.jpg")
483-
if os.path.exists(cache_file_path):
484-
print(f"Using cached image for app_id {app_id}")
485-
continue # Skip if the image is already cached
486-
487-
print(f"Fetching image for app_id {app_id}...")
488-
img = fetch_header_image(app_id, self.cache_dir) # Replace with actual image fetching logic
489-
503+
if not app_id or app_id in self.preloaded_images:
504+
return
505+
img = fetch_header_image(app_id, self.cache_dir)
490506
if img:
491-
# Save the image to the cache
492-
img.save(cache_file_path, "JPEG")
493-
print(f"Image cached for app_id {app_id}")
494-
else:
495-
print(f"Failed to fetch image for app_id {app_id}")
507+
self.preloaded_images[app_id] = img
508+
509+
# Preload images in parallel
510+
with ThreadPoolExecutor(max_workers=10) as executor:
511+
executor.map(preload_image, self.installed_games + getattr(self, 'uninstalled_games', []))
496512

497513
def generate_games_found_text(self):
498514
"""Generate a summary of games found on each drive."""
@@ -645,16 +661,13 @@ def on_no_click(self):
645661
del self.uninstalled_games # Clear the uninstalled games list
646662
messagebox.showinfo("Uninstalled Games Removed", "Uninstalled games have been removed from the cycle.")
647663

648-
def load_images_in_parallel(self):
649-
"""Preload images for uninstalled games in the background."""
650-
self.is_images_preloaded = False # Set flag to indicate images are not preloaded yet
651-
652-
# Preload images for all uninstalled games
653-
for game in self.uninstalled_games:
654-
app_id = game["app_id"]
655-
fetch_header_image(app_id, self.cache_dir) # Replace with your actual image loading function
656-
657-
# After all images are preloaded, re-enable the button on the main thread
664+
def load_images_in_parallel(self, batch_size=10):
665+
"""Preload images for uninstalled games in batches."""
666+
games_to_load = self.uninstalled_games
667+
for i in range(0, len(games_to_load), batch_size):
668+
batch = games_to_load[i:i + batch_size]
669+
with ThreadPoolExecutor(max_workers=5) as executor:
670+
executor.map(lambda game: fetch_header_image(game["app_id"], self.cache_dir), batch)
658671
self.root.after(0, self.on_images_preloaded)
659672

660673
def on_images_preloaded(self):
@@ -698,18 +711,35 @@ def start_animation(self):
698711

699712
def display_random_header_image(self):
700713
"""Display a random header image on the canvas initially."""
701-
random_app_id = random.choice(self.installed_games)["app_id"] # Choose a random installed game
702-
random_image = fetch_header_image(random_app_id, self.cache_dir)
714+
random_game = random.choice(self.installed_games)
715+
random_app_id = random_game["app_id"] # Get the app_id of the selected game
716+
print(f"Fetching image for app_id {random_app_id}")
703717

718+
random_image = fetch_header_image(random_app_id, self.cache_dir)
719+
720+
if random_image:
721+
print(f"Image fetched successfully for app_id {random_app_id}")
722+
704723
# Resize the image to match the canvas size
705724
canvas_width = self.canvas.winfo_width()
706725
canvas_height = self.canvas.winfo_height()
726+
print(f"Canvas width: {canvas_width}, Canvas height: {canvas_height}")
727+
707728
random_image_resized = random_image.resize((canvas_width, canvas_height), Image.Resampling.LANCZOS)
708729

709730
random_image_tk = ImageTk.PhotoImage(random_image_resized)
731+
732+
# Make sure to keep a reference to the image
710733
self.canvas.create_image(canvas_width // 2, canvas_height // 2, image=random_image_tk, anchor=tk.CENTER)
711-
self.active_images = [(random_image_tk, random_image)] # Store for later reference
712-
print("Random header image displayed.")
734+
735+
# Store the image reference to prevent it from being garbage collected
736+
self.active_images = [(random_image_tk, random_image)]
737+
738+
print("Random header image displayed on canvas.")
739+
740+
# Force Tkinter to process the events and refresh the canvas
741+
self.canvas.update_idletasks()
742+
self.root.update()
713743

714744
def display_image_from_url(self, image_url):
715745
"""Download and display the image from a URL on the canvas."""
@@ -810,7 +840,7 @@ def submit_number_of_games(self):
810840
self.selected_num_games = num_games_to_spin # Store the selected number of games
811841

812842
# Update the label to show the selected number of games
813-
self.label_number_of_games.config(text=f"Number of games selected: {num_games_to_spin}")
843+
self.label_number_of_games.config(text=f"Number selected:\n {num_games_to_spin}")
814844

815845
# Close the popup window after submitting
816846
self.popup.destroy()
@@ -941,33 +971,14 @@ def prepare_images(self, games):
941971
def load_image(self, app_id):
942972
"""Load and resize an image, checking the cache if not in preloaded_images."""
943973
img = self.preloaded_images.get(app_id)
944-
if img is None:
945-
# Fallback to load from the cache if not found in preloaded_images
946-
cache_dir = os.path.join(os.path.dirname(sys.executable), "image_cache")
947-
948-
# Check if the directory exists, create it if it doesn't
949-
if not os.path.exists(cache_dir):
950-
os.makedirs(cache_dir)
951-
print(f"Cache directory created at: {cache_dir}")
952-
else:
953-
print(f"Cache directory already exists at: {cache_dir}")
954-
955-
cache_file_path = os.path.join(cache_dir, f"{app_id}.jpg")
956-
if os.path.exists(cache_file_path):
957-
print(f"Loading image from cache for app_id: {app_id}")
958-
try:
959-
img = Image.open(cache_file_path)
960-
img = img.resize((600, 300), Image.Resampling.LANCZOS) # Resize for consistency
961-
self.preloaded_images[app_id] = img
962-
except Exception as e:
963-
print(f"Error loading image for app_id {app_id}: {e}")
964-
return None # Return None if there was an error loading the image
965-
else:
966-
print(f"Warning: No image found for app_id {app_id}")
967-
return None # Return None if image isn't found in the cache
968-
969-
return img
970-
974+
if not img:
975+
img = fetch_header_image(app_id, self.cache_dir)
976+
if img:
977+
self.preloaded_images[app_id] = img
978+
if img:
979+
return img.resize((600, 300), Image.Resampling.LANCZOS)
980+
return create_placeholder_image("Image Unavailable")
981+
971982
def cycle_images(self, selected_games):
972983
"""Cycle through the images as part of the spinning effect, with no padding and images resized to fit the canvas."""
973984
self.active_images = []
@@ -1037,7 +1048,7 @@ def animate_images(self):
10371048
total_distance = len(self.active_images) * canvas_width
10381049

10391050
# Set the desired duration (in milliseconds)
1040-
desired_duration = 6800 if include_uninstalled_games else 7400 # 7.4 seconds for animation
1051+
desired_duration = 7600 # 8 seconds for animation
10411052
self.frame_delay = 16 # Default frame delay in ms (60 FPS)
10421053
frames = desired_duration // self.frame_delay # Total number of frames
10431054
self.animation_speed = max(20, total_distance // frames) # Pixels per frame
@@ -1048,7 +1059,7 @@ def slide():
10481059
self.canvas.move(image_item, -self.animation_speed, 0) # Move images to the left
10491060

10501061
# Start slowing down earlier if uninstalled games are included
1051-
slowdown_start_index = -12 if include_uninstalled_games else -4 # Start earlier if uninstalled games are included
1062+
slowdown_start_index = -3
10521063

10531064
# Check if the current image is in the last 10 images (based on whether uninstalled games are included)
10541065
for i, (image_item, img_tk) in enumerate(self.active_images[slowdown_start_index:]):

0 commit comments

Comments
 (0)