Skip to content
This repository was archived by the owner on Aug 16, 2025. It is now read-only.

Commit e9a4c16

Browse files
authored
Add CTkHyperlink class
1 parent e9a7d30 commit e9a4c16

File tree

2 files changed

+40
-33
lines changed

2 files changed

+40
-33
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import customtkinter as ctk
2+
import webbrowser
3+
4+
5+
class CTkHyperlink(ctk.CTkLabel):
6+
color: str | tuple[str, str] = ("#0000EE", "#2fa8ff")
7+
hover_color: str | tuple[str, str] = ("#0000CC", "#58bbff")
8+
9+
def __init__(self, master, url: str, text: str | None = None, fg_color: str | tuple[str, str] = "transparent"):
10+
super().__init__(master, text=text or url, cursor="hand2", fg_color=fg_color, text_color=self.color)
11+
12+
self.bind("<Button-1>", lambda _: webbrowser.open_new_tab(url))
13+
self.bind("<Enter>", lambda _: self.configure(text_color=self.hover_color))
14+
self.bind("<Leave>", lambda _: self.configure(text_color=self.color))

modules/menu/sections/mod_generator.py

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import re
33
from tkinter import messagebox
44
from threading import Thread
5-
import webbrowser
65
from tkinter import filedialog
76
from typing import Literal
87
import uuid
@@ -12,6 +11,7 @@
1211
from modules import filesystem
1312
from modules.filesystem import Directory, restore_from_meipass
1413
from modules.functions.interface.image import load as load_image, load_from_image
14+
from modules.functions.interface.CTkHyperlink import CTkHyperlink
1515
from modules.config import settings
1616
from modules import mod_generator
1717
from modules.mod_generator.get_mask import get_mask
@@ -72,7 +72,7 @@ def _destroy(self) -> None:
7272
def _load_title(self) -> None:
7373
frame: ctk.CTkFrame = ctk.CTkFrame(self.container, fg_color="transparent")
7474
frame.grid_columnconfigure(0, weight=1)
75-
frame.grid(column=0, row=0, sticky="nsew", pady=(0,16))
75+
frame.grid(column=0, row=0, sticky="nsew", pady=(0,8))
7676

7777
ctk.CTkLabel(frame, text=self.Constants.SECTION_TITLE, anchor="w", font=self.Fonts.title).grid(column=0, row=0, sticky="nsew")
7878
ctk.CTkLabel(frame, text=self.Constants.SECTION_DISCLAIMER, anchor="w", font=self.Fonts.large).grid(column=0, row=1, sticky="nsew")
@@ -85,9 +85,24 @@ def _load_content(self) -> None:
8585
container.grid_columnconfigure(0, weight=1)
8686
container.grid(column=0, row=1, sticky="nsew", padx=(0,4))
8787

88+
# Run
89+
run_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
90+
run_frame.grid(column=0, row=0, sticky="nsew", pady=(0, 16))
91+
ctk.CTkLabel(run_frame, text="Run", anchor="w", font=self.Fonts.bold).grid(column=0, row=0, sticky="nw")
92+
93+
run_icon: Path = (Directory.RESOURCES / "menu" / "common" / "image-run").with_suffix(".png")
94+
if not run_icon.is_file():
95+
restore_from_meipass(run_icon)
96+
run_image = load_image(run_icon)
97+
98+
ctk.CTkButton(run_frame, text="Generate mod", image=run_image, command=self._run, width=1, anchor="w", compound=ctk.LEFT).grid(column=0, row=1, sticky="w")
99+
100+
# Progress label
101+
ctk.CTkLabel(run_frame, textvariable=self.progress_variable, anchor="w", font=self.Fonts.bold).grid(column=1, row=1, sticky="w", padx=(4, 0))
102+
88103
# name input
89104
name_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
90-
name_frame.grid(column=0, row=0, sticky="nsew")
105+
name_frame.grid(column=0, row=1, sticky="nsew")
91106
ctk.CTkLabel(name_frame, text="Mod name", anchor="w", font=self.Fonts.bold).grid(column=0, row=0, sticky="nw")
92107
self.mod_name_entry = ctk.CTkEntry(
93108
name_frame, width=256, height=40, validate="key",
@@ -97,7 +112,7 @@ def _load_content(self) -> None:
97112

98113
# color/angle inputs
99114
color_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
100-
color_frame.grid(column=0, row=1, sticky="nsew", pady=(16, 0))
115+
color_frame.grid(column=0, row=2, sticky="nsew", pady=(16, 0))
101116

102117
color1_frame: ctk.CTkFrame = ctk.CTkFrame(color_frame, fg_color="transparent")
103118
color1_frame.grid(column=0, row=1)
@@ -129,21 +144,18 @@ def _load_content(self) -> None:
129144
self.angle_entry.bind("<Control-s>", lambda _: self.root.focus())
130145
self.angle_entry.bind("<FocusOut>", lambda _: self._generate_preview())
131146

147+
# Possible colors
132148
possible_colors_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
133-
possible_colors_frame.grid(column=0, row=2, sticky="w", pady=(4, 0))
149+
possible_colors_frame.grid(column=0, row=3, sticky="w", pady=(4, 0))
134150
ctk.CTkLabel(possible_colors_frame, text="A list of available color formats can be found at ").grid(column=0, row=0)
135151
url: str = r"https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names"
136-
unhover_color: str | tuple[str ,str] = ("#0000EE", "#2fa8ff")
137-
hover_color: str | tuple[str ,str] = ("#0000CC", "#58bbff")
138-
hyperlink: ctk.CTkLabel = ctk.CTkLabel(possible_colors_frame, text=url, cursor="hand2", text_color=unhover_color)
139-
hyperlink.bind("<Button-1>", lambda _: self._open_in_browser(url))
152+
153+
hyperlink: CTkHyperlink = CTkHyperlink(possible_colors_frame, url)
140154
hyperlink.grid(column=1, row=0)
141-
hyperlink.bind("<Enter>", lambda _: hyperlink.configure(text_color=hover_color))
142-
hyperlink.bind("<Leave>", lambda _: hyperlink.configure(text_color=unhover_color))
143155

144-
# Preview
156+
# Color Preview
145157
preview_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
146-
preview_frame.grid(column=0, row=3, sticky="nsew", pady=(16, 0))
158+
preview_frame.grid(column=0, row=4, sticky="nsew", pady=(12, 0))
147159

148160
ctk.CTkLabel(preview_frame, text="Preview", anchor="w", font=self.Fonts.bold).grid(column=0, row=0, sticky="w")
149161
self.preview_image = ctk.CTkLabel(preview_frame, text="", fg_color="#000", width=self.Constants.PREVIEW_SIZE, height=self.Constants.PREVIEW_SIZE)
@@ -153,7 +165,7 @@ def _load_content(self) -> None:
153165
# User selected files
154166
user_selected_files_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
155167
user_selected_files_frame.grid_columnconfigure(0, weight=1)
156-
user_selected_files_frame.grid(column=0, row=4, pady=(16, 0), sticky="nsew")
168+
user_selected_files_frame.grid(column=0, row=5, pady=(16, 0), sticky="nsew")
157169
ctk.CTkLabel(user_selected_files_frame, text="Additional files", anchor="w", font=self.Fonts.bold).grid(column=0, row=0, sticky="nw")
158170
ctk.CTkLabel(user_selected_files_frame, text="Select additional files to be generated on top of the default ones. (only PNG files are supported)", anchor="w").grid(column=0, row=1, sticky="nw")
159171

@@ -169,21 +181,6 @@ def _load_content(self) -> None:
169181
self.user_selected_files_container = ctk.CTkFrame(user_selected_files_frame)
170182
self.user_selected_files_container.grid_columnconfigure(0, weight=1)
171183
self._load_user_selected_files()
172-
173-
# Run
174-
run_frame: ctk.CTkFrame = ctk.CTkFrame(container, fg_color="transparent")
175-
run_frame.grid(column=0, row=5, sticky="nsew", pady=(32, 0))
176-
ctk.CTkLabel(run_frame, text="Run", anchor="w", font=self.Fonts.bold).grid(column=0, row=0, sticky="nw")
177-
178-
run_icon: Path = (Directory.RESOURCES / "menu" / "common" / "image-run").with_suffix(".png")
179-
if not run_icon.is_file():
180-
restore_from_meipass(run_icon)
181-
run_image = load_image(run_icon)
182-
183-
ctk.CTkButton(run_frame, text="Generate mod", image=run_image, command=self._run, width=1, anchor="w", compound=ctk.LEFT).grid(column=0, row=1, sticky="w")
184-
185-
# Progress label
186-
ctk.CTkLabel(container, textvariable=self.progress_variable, anchor="w", font=self.Fonts.bold).grid(column=0, row=6, sticky="w", pady=(4, 0))
187184
# endregion
188185

189186

@@ -346,10 +343,6 @@ def _load_user_selected_files(self) -> None:
346343
self.user_selected_files_container.grid(column=0, row=3, sticky="nsew", pady=(8, 0))
347344

348345

349-
def _open_in_browser(self, url: str) -> None:
350-
webbrowser.open_new_tab(url)
351-
352-
353346
def _generate_preview(self, default: bool = False) -> None:
354347
if default:
355348
self.default_image: Image.Image = get_mask(ImageColor.getcolor("black", "RGBA"), None, 0, size=(self.Constants.PREVIEW_SIZE, self.Constants.PREVIEW_SIZE))

0 commit comments

Comments
 (0)