import tkinter as tk from tkinter import colorchooser, filedialog from PIL import Image, ImageDraw, ImageTk from tkinter import filedialog, simpledialog import math import sys import os import subprocess from tkinter import messagebox class ImagePreviewGenerator: def __init__(self, root): self.root = root self.root.title("MOE creator") # Controls for stripes self.stripe_height = tk.IntVar(value=260) self.stripe_width = tk.IntVar(value=40) self.stripe_color = "#FF0000" self.stripe_border_color = "#000000" self.stripe_border_size = tk.IntVar(value=3) self.stripe_image_path = None self.use_stripe_image = tk.BooleanVar(value=False) # Controls for shapes self.shape_type = tk.StringVar(value="No Shape") self.shape_size = tk.IntVar(value=35) self.shape_color = "#0000FF" self.shape_border_color = "#000000" self.shape_border_size = tk.IntVar(value=2) # Background Color self.bg_color = "#FFFFFF" self.use_transparent_bg = tk.BooleanVar(value=False) self.bg_image_path = None self.use_bg_image = tk.BooleanVar(value=False) # dirt Effect self.dirt_intensity = tk.IntVar(value=0) self.texture_image_path = None self.apply_dirt_effect = tk.BooleanVar(value=False) # Wear Intensity Variable self.wear_intensity = tk.IntVar(value=0) self.wear_texture_path = None self.apply_wear_effect_var = tk.BooleanVar(value=False) # Shape image path self.shape_image_path = None self.retain_aspect_ratio = tk.BooleanVar(value=True) self.enable_image = tk.BooleanVar(value=False) # Shape options shape_options = ["No Shape", "Circle", "Square", "4-Point Star", "5-Point Star", "6-Point Star", "7-Point Star", "8-Point Star", "Diamond"] #Folder path for mod output_dir = os.path.join("res", "gui", "maps", "vehicles", "decals", "insignia") os.makedirs(output_dir, exist_ok=True) # Create frames controls_frame = tk.Frame(root) controls_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew") stripe_frame = tk.LabelFrame(controls_frame, text="Stripe Settings", padx=10, pady=10) stripe_frame.grid(row=0, column=0, padx=10, pady=10, sticky="n") shape_frame = tk.LabelFrame(controls_frame, text="Shape Settings", padx=10, pady=10) shape_frame.grid(row=0, column=1, padx=10, pady=10, sticky="n") bg_frame = tk.LabelFrame(controls_frame, text="Background Settings", padx=10, pady=10) bg_frame.grid(row=0, column=2, padx=10, pady=10, sticky="n") dirt_frame = tk.LabelFrame(controls_frame, text="Dirt Settings", padx=10, pady=10) dirt_frame.grid(row=1, column=0, padx=10, pady=10, sticky="wn") wear_frame = tk.LabelFrame(controls_frame, text="Wear Settings", padx=10, pady=10) wear_frame.grid(row=1, column=1, padx=10, pady=10, sticky="wn") create_mod_frame = tk.LabelFrame(controls_frame, text="Create Set", padx=10, pady=10) create_mod_frame.grid(row=1, column=2, padx=10, pady=10, sticky="n") run_frame = tk.LabelFrame(controls_frame, text="Create MOE from Set's", padx=10, pady=10) run_frame.grid(row=1, column=3, padx=10, pady=10, sticky="n") # Stripe Controls tk.Label(stripe_frame, text="Stripe Height:").grid(row=0, column=0, sticky="w") tk.Scale(stripe_frame, from_=0, to=260, orient=tk.HORIZONTAL, variable=self.stripe_height, command=self.generate_previews).grid(row=0, column=1, sticky="e") tk.Label(stripe_frame, text="Stripe Width:").grid(row=1, column=0, sticky="w") tk.Scale(stripe_frame, from_=0, to=86, orient=tk.HORIZONTAL, variable=self.stripe_width, command=self.generate_previews).grid(row=1, column=1, sticky="e") tk.Button(stripe_frame, text="Stripe Color", command=lambda: self.choose_color('stripe_color')).grid(row=2, column=0, sticky="w") tk.Button(stripe_frame, text="Stripe Border Color", command=lambda: self.choose_color('stripe_border_color')).grid(row=2, column=1, sticky="e") tk.Label(stripe_frame, text="Stripe Border Size:").grid(row=3, column=0, sticky="w") tk.Scale(stripe_frame, from_=0, to=10, orient=tk.HORIZONTAL, variable=self.stripe_border_size, command=self.generate_previews).grid(row=3, column=1, sticky="e") tk.Button(stripe_frame, text="Stripe Image", command=self.choose_stripe_image).grid(row=4, column=0, sticky="w") tk.Checkbutton(stripe_frame, text="Use Stripe Image", variable=self.use_stripe_image, command=self.generate_previews).grid(row=4, column=1, sticky="w") # Shape Controls tk.Label(shape_frame, text="Shape:").grid(row=0, column=0, sticky="w") tk.OptionMenu(shape_frame, self.shape_type, *shape_options, command=self.generate_previews).grid(row=0, column=1, sticky="e") tk.Label(shape_frame, text="Shape Size:").grid(row=1, column=0, sticky="w") tk.Scale(shape_frame, from_=10, to=55, orient=tk.HORIZONTAL, variable=self.shape_size, command=self.generate_previews).grid(row=1, column=1, sticky="e") tk.Button(shape_frame, text="Shape Color", command=lambda: self.choose_color('shape_color')).grid(row=2, column=0, sticky="w") tk.Button(shape_frame, text="Shape Border Color", command=lambda: self.choose_color('shape_border_color')).grid(row=2, column=1, sticky="e") tk.Label(shape_frame, text="Shape Border Size:").grid(row=3, column=0, sticky="w") tk.Scale(shape_frame, from_=0, to=10, orient=tk.HORIZONTAL, variable=self.shape_border_size, command=self.generate_previews).grid(row=3, column=1, sticky="e") tk.Button(shape_frame, text="Shape Image", command=self.choose_shape_image).grid(row=4, column=0, sticky="w") tk.Checkbutton(shape_frame, text="Retain Aspect Ratio", variable=self.retain_aspect_ratio, command=self.generate_previews).grid(row=5, column=1, sticky="e") tk.Checkbutton(shape_frame, text="Use Image as Shape", variable=self.enable_image, command=self.generate_previews).grid(row=4, column=1, columnspan=2, sticky="w") # Background Settings tk.Button(bg_frame, text="Background Color", command=lambda: self.choose_color('bg_color')).grid(row=0, column=0, sticky="w") tk.Checkbutton(bg_frame, text="Use Transparent Background", variable=self.use_transparent_bg, command=self.generate_previews).grid(row=1, column=0, sticky="e") tk.Button(bg_frame, text="Background Image", command=self.choose_bg_image).grid(row=2, column=0, sticky="w") tk.Checkbutton(bg_frame, text="Use Background Image", variable=self.use_bg_image, command=self.generate_previews).grid(row=3, column=0, sticky="w") # dirt Settings tk.Label(dirt_frame, text="Dirt Intensity:").grid(row=0, column=0, sticky="w", padx=5, pady=5) tk.Scale(dirt_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.dirt_intensity, command=self.generate_previews).grid(row=0, column=1, sticky="e", padx=5, pady=5) tk.Button(dirt_frame, text="Dirt Texture", command=self.choose_texture_image).grid(row=1, column=0, sticky="w", padx=5, pady=5) tk.Checkbutton(dirt_frame, text="Apply Dirt Effect", variable=self.apply_dirt_effect, command=self.generate_previews).grid(row=2, column=0, sticky="w", padx=5, pady=5) # Wear controls tk.Label(wear_frame, text="Wear Intensity:").grid(row=0, column=2, sticky="w", padx=5, pady=5) tk.Scale(wear_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.wear_intensity, command=self.generate_previews).grid(row=0, column=3, sticky="e", padx=5, pady=5) tk.Button(wear_frame, text="Wear Texture", command=self.choose_wear_texture).grid(row=1, column=2, sticky="w", padx=5, pady=5) tk.Checkbutton(wear_frame, text="Apply Wear Effect", variable=self.apply_wear_effect_var, command=self.generate_previews).grid(row=2, column=2, sticky="w", padx=5, pady=5) #Insignia run tk.Label(run_frame, text="There are 67 in game \nMOE to choose from here. \nOr if you put an image set into \n\\images\\insignia\\, \nthe widget will add them to the list \nand be created as a mod.\n Mod will be prefixed with MOE-").grid(row=0, column=0, padx=5, pady=5) tk.Button(run_frame, text="MOE Set Widget", command=self.run_insignia_script).grid(row=3, column=0, padx=5, pady=5) # Create Mod frame tk.Label(create_mod_frame, text="This will create an image set \nfor creating the mod. \nGo to MOE Set Widget to create \ncomplete mod.").grid(row=0, column=0, padx=5, pady=5) tk.Button(create_mod_frame, text="Create Set", command=self.create_mod).grid(row=3, column=0, padx=5, pady=5) # Preview Canvas self.canvas = tk.Canvas(root, width=(256 + 10) * 3, height=256) self.canvas.grid(row=3, column=0, padx=10, pady=10) #generate previews on startup self.generate_previews() def choose_stripe_image(self): """Open a file dialog to select a stripe texture image.""" self.stripe_image_path = filedialog.askopenfilename( title="Select Stripe Image", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")] ) self.generate_previews() def create_image_with_effects(self, columns): # Create base image img = self.create_image(columns) # Apply dirt if self.apply_dirt_effect.get(): img = self.apply_dirted_effect(img) # Apply wear if self.apply_wear_effect_var.get(): img = self.apply_wear_effect(img) return img def run_insignia_script(self): """Function to run the insignia.py script.""" try: # Check if running inside an .exe or as a script base_path = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__)) insignia_script_path = os.path.join(base_path, 'insignia.py') # Path to insignia.py subprocess.run(['python', insignia_script_path], check=True) except subprocess.CalledProcessError as e: print(f"Error running insignia.py: {e}") def choose_color(self, color_var): color_code = colorchooser.askcolor(title="Choose Color") if color_code[1]: # Ensure a color was selected setattr(self, color_var, color_code[1]) self.generate_previews() def choose_texture_image(self): self.texture_image_path = filedialog.askopenfilename( title="Select Texture Image", filetypes=[("Image Files", "*.png")] ) self.generate_previews() def choose_shape_image(self): """Open a file dialog to select an image for the shape.""" self.shape_image_path = filedialog.askopenfilename( title="Select Shape Image", filetypes=[("Image Files", "*.png")] ) self.generate_previews() def choose_bg_image(self): self.bg_image_path = filedialog.askopenfilename( title="Select Background Image", filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")] ) self.generate_previews() def generate_previews(self, *args): self.canvas.delete("all") #create images with effects img_1 = self.create_image_with_effects(1) img_2 = self.create_image_with_effects(2) img_3 = self.create_image_with_effects(3) #images PhotoImage img_1_tk = ImageTk.PhotoImage(img_1) img_2_tk = ImageTk.PhotoImage(img_2) img_3_tk = ImageTk.PhotoImage(img_3) self.canvas.create_image(0, 0, anchor=tk.NW, image=img_1_tk) self.canvas.create_image(256 + 10, 0, anchor=tk.NW, image=img_2_tk) self.canvas.create_image((256 + 10) * 2, 0, anchor=tk.NW, image=img_3_tk) # Keep references to avoid garbage collection self.canvas.image1 = img_1_tk self.canvas.image2 = img_2_tk self.canvas.image3 = img_3_tk def create_image(self, columns): if self.use_transparent_bg.get(): img = Image.new("RGBA", (256, 256), (0, 0, 0, 0)) else: img = Image.new("RGBA", (256, 256), self.bg_color) if self.use_bg_image.get() and self.bg_image_path and os.path.exists(self.bg_image_path): bg_image = Image.open(self.bg_image_path).resize(img.size, Image.LANCZOS) img.paste(bg_image, (0, 0)) draw = ImageDraw.Draw(img) column_width = img.width // 3 centers = [(column_width // 2), (img.width // 2), (img.width - (column_width // 2))] for i in range(columns): x_center = centers[i] self.draw_stripe(img, draw, x_center, img.height) # Pass img and draw to draw_stripe # Draw image if selected, otherwise draw shape if self.enable_image.get() and self.shape_image_path and os.path.exists(self.shape_image_path): self.draw_shape_image(img, x_center, img.height // 2) elif self.shape_type.get() != "No Shape": self.draw_shape(draw, x_center, img.height // 2) return img def draw_stripe(self, img, draw, x_center, canvas_height): stripe_height = self.stripe_height.get() stripe_width = self.stripe_width.get() border_size = self.stripe_border_size.get() # Only use the stripe image if the checkbox is selected if self.use_stripe_image.get() and self.stripe_image_path and os.path.exists(self.stripe_image_path): stripe_image = Image.open(self.stripe_image_path).convert("RGBA") # Resize the stripe image stripe_image = stripe_image.resize((stripe_width, stripe_height), Image.LANCZOS) # Create a mask for the stripe area (only the stripe will be visible) mask = Image.new("L", (stripe_width, stripe_height), 0) mask.paste(255, [0, 0, stripe_width, stripe_height]) # White (visible) inside the stripe # Apply the mask to the stripe image stripe_image.putalpha(mask) # Calculate the stripe's bounding box x1 = x_center - stripe_width // 2 x2 = x_center + stripe_width // 2 y1 = canvas_height // 2 - stripe_height // 2 y2 = canvas_height // 2 + stripe_height // 2 # Paste the stripe image with the mask into the main image img.paste(stripe_image, (x1, y1), stripe_image) # Use the alpha channel as mask else: # Draw a solid stripe if no image is chosen or if the checkbox is off x1 = x_center - stripe_width // 2 x2 = x_center + stripe_width // 2 y1 = canvas_height // 2 - stripe_height // 2 y2 = canvas_height // 2 + stripe_height // 2 draw.rectangle([x1, y1, x2, y2], fill=self.stripe_color, outline=self.stripe_border_color, width=border_size) def draw_shape(self, draw, x_center, y_center): size = self.shape_size.get() shape = self.shape_type.get() border_size = self.shape_border_size.get() if shape == "Circle": draw.ellipse([x_center - size, y_center - size, x_center + size, y_center + size], fill=self.shape_color, outline=self.shape_border_color, width=border_size) elif shape == "Square": draw.rectangle([x_center - size, y_center - size, x_center + size, y_center + size], fill=self.shape_color, outline=self.shape_border_color, width=border_size) elif shape == "Diamond": points = [(x_center, y_center - size), (x_center + size, y_center), (x_center, y_center + size), (x_center - size, y_center)] draw.polygon(points, fill=self.shape_color, outline=self.shape_border_color, width=border_size) elif "Point Star" in shape: points = int(shape.split("-Point Star")[0]) self.draw_star(draw, x_center, y_center, size, points, self.shape_color, self.shape_border_color, border_size) else: print(f"Shape '{shape}' is not recognized or not implemented.") def draw_shape_image(self, img, x_center, y_center): """Draw the shape image at the center of the column.""" if self.shape_image_path and os.path.exists(self.shape_image_path): shape_image = Image.open(self.shape_image_path).convert("RGBA") original_width, original_height = shape_image.size if self.retain_aspect_ratio.get(): # Calculate the new size while retaining aspect ratio aspect_ratio = original_width / original_height new_size = (self.shape_size.get() * 2, int((self.shape_size.get() * 2) / aspect_ratio)) else: new_size = (self.shape_size.get() * 2, self.shape_size.get() * 2) shape_image = shape_image.resize(new_size, Image.LANCZOS) x_pos = x_center - shape_image.width // 2 y_pos = y_center - shape_image.height // 2 img.paste(shape_image, (x_pos, y_pos), shape_image) # Use paste with mask def draw_star(self, draw, cx, cy, radius, points, color, border_color, border_size): outer_radius = radius inner_radius = radius / 2 angle_offset = -math.pi / 2 angle = 2 * math.pi / (points * 2) star_points = [] for i in range(points * 2): r = outer_radius if i % 2 == 0 else inner_radius x = cx + r * math.cos(i * angle + angle_offset) y = cy + r * math.sin(i * angle + angle_offset) star_points.append((x, y)) draw.polygon(star_points, fill=color, outline=border_color, width=border_size) def apply_dirted_effect(self, img): intensity = self.dirt_intensity.get() if self.texture_image_path and os.path.exists(self.texture_image_path): texture = Image.open(self.texture_image_path).convert("RGBA") texture = texture.resize(img.size, Image.LANCZOS) texture = texture.point(lambda p: p * (intensity / 100)) img = Image.alpha_composite(img.convert("RGBA"), texture) return img def choose_wear_texture(self): """Open a file dialog to select a wear texture image.""" self.wear_texture_path = filedialog.askopenfilename( title="Select Wear Texture Image", filetypes=[("Image Files", "*.png")] ) self.generate_previews() # Regenerate previews after choosing a new image def apply_wear_effect(self, img): intensity = self.wear_intensity.get() # Load the wear texture if self.wear_texture_path and os.path.exists(self.wear_texture_path): wear_texture = Image.open(self.wear_texture_path).convert("RGBA") wear_texture = wear_texture.resize(img.size, Image.LANCZOS) # Ensure RGBA format img = img.convert("RGBA") # Create a new image for the final output output_img = Image.new("RGBA", img.size) for x in range(img.width): for y in range(img.height): # Get pixel values from both images img_pixel = img.getpixel((x, y)) wear_pixel = wear_texture.getpixel((x, y)) # Use the wear texture's alpha to determine transparency wear_alpha = wear_pixel[3] # Get alpha from wear texture wear_intensity_adjusted = int(wear_alpha * (intensity / 100)) # Scale alpha based on intensity # Calculate new alpha value for the blended pixel new_alpha = max(0, img_pixel[3] - wear_intensity_adjusted) # Create new pixel with the blended values blended_pixel = ( img_pixel[0], # Keep original color values img_pixel[1], img_pixel[2], new_alpha # Apply new alpha value ) output_img.putpixel((x, y), blended_pixel) return output_img return img def create_mod(self): # Ask the user for a base filename base_filename = simpledialog.askstring("File Name", "Enter a Set name:", parent=None) if not base_filename: # If the user cancels or enters an empty name, exit the method messagebox.showwarning("No Filename", "No filename entered. Exiting...") return # Generate images for each column and save them for i in range(1, 4): # Create the image with effects img = self.create_image_with_effects(i) # Define the output filename filename = os.path.join(f"{base_filename}_{i}.png") # Save the image as PNG img.save(filename, "PNG") print(f"Saved {filename}") # After saving the images, run the second script try: base_path = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__)) dds_script_path = os.path.join(base_path, 'wot-dds.py') # Path to wot-dds.py subprocess.run(["python", dds_script_path], check=True) except subprocess.CalledProcessError as e: print(f"Error running wot-dds.py: {e}") # Inform mod images have been created messagebox.showinfo("Set Created", "Set added to the Insignia widget.") if __name__ == "__main__": root = tk.Tk() app = ImagePreviewGenerator(root) root.mainloop()