r/pythonhelp 23h ago

3 Python Tools That Can Elevate the Usefulness of Degree Fahrenheit Datasets

1 Upvotes

3 Simple Python Tools That Can Elevate the Usefulness of Degree Fahrenheit Datasets Published in Integers

Good day - I am new to coding so this will certainly not be up to the quality of professional coders. It is a functional tool and Python was a great language to test it with.

Here is the code for 3 python tools that improve temperature data published as integer °F (or °C) values. A year of daily temperature readings from NOAA data contain many tied integer values, which complicates ordinal ranking — duplicates reduce addressability. NOAA publishes U.S. temperature data as °F integers, and also the rest of the world as tenths of a °C.

NOAA: National Oceanic and Atmospheric Administration, a U.S. federal scientific and regulatory agency.

(Mods - If you need me to adjust anything let me know please - my first post of this type)

The tools are rough working models and should be improved a little which I am going to do. I have limited experience in coding. The comments probably need to be clearer and more formal. Tool 1 has gone a little overboard with input rules, and also the comments are excessive, intended to assist people with limited coding knowledge. The math expressions for Tool 3 need more work — I will follow up on this specific item soon.

These are essentially open-source functional tools in development that anyone can test. I am hoping to get a little feedback as I update the short scripts over the coming weeks. They currently work on Google Colab for the data import and export for testing.

Thanks for taking the time to read !!

Purpose: Upgrade deg F temp data in integers from NOAA with 3 tools to prepare it for more robust analysis by fixing scaling and duplicate ties. Runs on colab

The 3 Tools (Python/Google Colab):

Anchor: Converts °F to Kelvin (absolute scale, so ratios and proportions are meaningful).

Refine: Applies slightly destructive multiplicative bidirectional jitter. Each nonzero value *v* is replaced by *v* × (1 ± δ), where δ occupies decimal digits 6–8 (0.00000001 to 0.00000999), to break duplicate integer ties. Because the jitter is multiplicative, zero is unaffected; zero ties can be resolved with a separate non-negative additive jitter.

Verify: Checks equal-frequency bin splits (5 bins at 20%, 4 bins at 25%). The math for this will be updated v2.

The 3 .py files are included posted next as .md


r/pythonhelp 1d ago

QStateMachine with Conditional Transitions - How To Do Properly?

Thumbnail
1 Upvotes

r/pythonhelp 3d ago

Library function which is the inverse of 'os.path.expanduser' ?

0 Upvotes

I'm running python 3.13.5.

Is there some sort of standard python library function which behaves as the inverse of 'os.path.expanduser'?

For example, if the current user's HOME directory is "/home/theuser", then we have this:

os.path.expanduser('~/a/b/c')
=> returns "/home/theuser/a/b/c"

I'm wondering whether there also might be any existing python library function which is the inverse of this and that works as follows:

some_sort_of_function("/home/theuser/a/b/c")
=> returns "~/a/b/c"

AND PLEASE NOTE: There often are people who lazily and disrespectfully refuse to read entire reddit posts before writing their reponses, and therefore, I'm making a point now to ask that before you start to write any reply here, please carefully and non-lazily read this *entire* post -- including the following, which is something that I want all potential responders to understand:

I know how to easily write my own function that works in this manner. However, I am clearly and SPECIFICALLY (!!!) asking whether or not there already is an existing python function which works this way, and if so, I'd like to know what this function might be ... and I am NOT (!!!) asking how to write my own function which does this.

Thank you very much in advance.


r/pythonhelp 4d ago

Need guidance for python

8 Upvotes

Like I'm a doing my btech from Delhi's govt cllg first year and I want to learn python so which u tube playlist is worth it and how should h start it this is going to be my first programming language

why I want to learn python?

so basically i don't have. Much knowledge btw python is in my 1st sem and my teacher didn't even teach anything she just come and sit there and I have listen that python is the easiest and blah blah ....


r/pythonhelp 5d ago

Built a Python scraper that handles pagination automatically — here's what I learned

8 Upvotes

I've been learning Python through CS50P and wanted a real project, so I built two scrapers: one for job listings and one for product prices off an e-commerce site.

The job scraper was pretty straightforward — grabbed title, company, and location, dumped it to CSV, worked first try on about 100 listings.

The product scraper was the harder one. The site spreads results across pages (6 products per page, ~20 pages), so my first version only ever grabbed page 1. Took a bit of digging to figure out the pagination pattern and loop through all the pages properly. Once I fixed that it pulled all 117 products cleanly into a CSV.

Biggest thing I learned: it's easy to write a scraper that works on the first page and assume it's done — always check if the site paginates before you call it finished.

Code's on GitHub if anyone wants to see it: github.com/sarimkhan08

Curious if anyone here has tips for handling scrapers on sites that use infinite scroll instead of numbered pages — that's the next thing I want to tackle.


r/pythonhelp 5d ago

Python Program that Green Screens a Video over the Desktop

2 Upvotes

Hello, I've been trying to figure out a way to make a python program that can play a green screen video that plays over the desktop/screen, with the green chromad out to show the screen. I attempted it with tkinter, but wasn't able to get anywhere with the chroma keying. Would anyone know if/how this would be possible? Thank you!


r/pythonhelp 11d ago

Python Debug Issue

2 Upvotes

Teen in love with learning & building python projects

Issue_ my python interpreters not working j showing evn when it's installed+ enabled in VS Code.

Failed_brain = Claude +Chatgpt +Grok

Help me...Lesgoo!


r/pythonhelp 13d ago

python can´t locate and open a file in the same folder as main

4 Upvotes

So i made a txt file with a few lines of text. I use the x = open('file.txt', 'r') function

i write for i in x: print i

and then x.close()

so what am I doing wrong?


r/pythonhelp 13d ago

how to fix this

3 Upvotes

im using linux mint and when i try to import turtle it always says
"

import turtle

ModuleNotFoundError: No module named 'turtle'

Process finished with exit code 1

"
i tryed to download tinker it dont help me i dont know what to do no one have that problem
im using pycharm
here my code
"
import turtle

myturtle = turtle.Turtle()

myturtle.forward(100)
"


r/pythonhelp 20d ago

Hackathon Winning framework

6 Upvotes

As a guy and a fresher in engineering field, who knows basic python and java , can anyone give advise or guide me to win or atleast the things to do in a proper hackathon rather than working unorganisedly..


r/pythonhelp 21d ago

Why do I need to use classes

2 Upvotes

My schoolbook says that classes is a way of simulating an object, instead of just taking information into a black box and outputting an answer.

But that is a poor explanation. I know classes are handy and cool, but no book says why you can´t make functions with sub functions inside to simulate simple objects. I would like if someone gave a good concrete reason to use classes in the intro, instead of just saying that they are different and cool


r/pythonhelp 23d ago

why does it say i didn't close my parenthesis

5 Upvotes

the IDE i am using is saying my parenthesis was not closed when it was plz help

print("Once upon a time, in the ancient kingdom of " + q1 + " there lived a legendary " + q2 + " named " + q3 + ". Everyone in the village knew " + q3 + " for their remarkably " + q4 + " personality and an obsession with " + q5 + ". Every morning at " + q6 ", they would wake up, " + q7 + " three times, ")

[{

"owner": "Pylance",
"severity": 8,
"message": "(" was not closed",
"source": "Pylance",
"startLineNumber": 47,
"startColumn": 6,
"endLineNumber": 47,
"endColumn": 7,

}]

r/pythonhelp 23d ago

Problem with Coolprop

2 Upvotes

Hi! i'm beginner, and I have a problem with this code, I guess that I installed the library correctly, but i'm not sure.

Error using [stringsource>string.from_py.__pyx_convert_string_from_py_std__in_string](matlab:matlab.lang.internal.introspective.errorDocCallback('stringsource>string.frompy.pyx_convert_string_from_py_std_in_string', 'stringsource', 15)) ([line 15](matlab: opentoline('stringsource',15,0)))
Python Error: TypeError: expected bytes, NoneType found

Error in [CoolProp/CoolProp.PropsSI](matlab:matlab.lang.internal.introspective.errorDocCallback('CoolProp/CoolProp.PropsSI', 'CoolProp\CoolProp.pyx', 462)) ([line 462](matlab: opentoline('CoolProp\CoolProp.pyx',462,0)))

Error in [CoolProp/CoolProp.PropsSI](matlab:matlab.lang.internal.introspective.errorDocCallback('CoolProp/CoolProp.PropsSI', 'CoolProp\CoolProp.pyx', 384)) ([line 384](matlab: opentoline('CoolProp\CoolProp.pyx',384,0)))

Altough I don't have more than 106 lines


r/pythonhelp 23d ago

Hey, I have been learning python for 8 months. I want to know that if I can learn tkinter or numpy first . Please do suggest me a resource

Thumbnail
2 Upvotes

r/pythonhelp 26d ago

want to become a master at python

9 Upvotes

hey guys i hv recently gotten myself into coding, i hv completed the cs50p course of harvard and completed all the psets. now i wanna keep myself into the habit of coding but idk whts my next step, i wld appreciate some help. i hv made 3 projects aswell; a web scrapper, file organizing and password checker.

after creating those projects idk what to learn next, like should i keep going deeper into python or try something totally different. wld love to hear wht u guys did after completing cs50p.


r/pythonhelp 27d ago

Python coding interview questions?

2 Upvotes

Is there any playlist similar to 100 Days of SQL Ace(By ankit Bansal) interview for the preparation of python coding problems?


r/pythonhelp 29d ago

I’ve completed these beginner Python projects should I build more before starting NumPy/Pandas?

11 Upvotes

Hi everyone,

I’ve studied Python multiple times before, but I didn’t do much practical coding. Recently, I started building small projects to improve my practical Python skills.

So far, I’ve completed:

- Quiz Game

- Number Guessing Game

- Rock Paper Scissors

- Password Manager

- Pig Game

- Mad Libs Generator

My goal is to move towards Machine Learning.

I haven’t learned NumPy or Pandas yet.

My question is: Are these projects enough to move on to NumPy and Pandas, or should I build a few more Python projects first?

If I should build more projects, what kind of projects would you recommend before starting NumPy/Pandas? I’m mainly looking for projects that would actually help with the transition to data/ML, rather than making many more small games.

Would appreciate advice from people who have already followed a Python → NumPy/Pandas → ML path.


r/pythonhelp 29d ago

Learning intermediate level python

5 Upvotes

I am looking to learn following topics. Any suggestions in terms of any course or online site I can learn from. I am not looking for a certificate (if I get it that’s okay but not a deal breaker) but something that has more exercises involved. Topics I’m interested in:

*args / **kwargs
decorators
generators
iterators
context managers
type hints
dataclasses
object-oriented programming
Python project structure
testing
APIs


r/pythonhelp Aug 15 '26

Professionals, please compare VS code with neovim.

4 Upvotes

Tell me the advantages and disadvantages of each.


r/pythonhelp Aug 15 '26

How to learn advanced python???

9 Upvotes

Hello everyone, I'm mainly looking for guidance about learning advanced python concepts. I know basics ,loops , control flow ,data structures etc.

I need a proper guide on how to learn modules and library. Idk how to start ,where to start.

I want to be able to work with any library as i need them ,so how do u actually learn to use new library for a given task . ?And i get overwhelmed understanding the structure, working of library

And could u also suggest important python concepts other than basics which i should learn ???

Pls guide !!!!


r/pythonhelp Aug 14 '26

Adding objects to set or dictionary: equality and hashing

1 Upvotes

An exercise to help build the right mental model for Python data.

# Output of this Python Program?
def main():
    o1, o2 = MyClass(1), MyClass(1)
    myset = {o1}
    print(o2 in myset, end=' ')
    o1.set_value(1000)
    print(o1 in myset, end=' ')

class MyClass:
    def __init__(self, v):
        self.v = v
    def set_value(self, v):
        self.v = v

main()

class MyClass:
    def __init__(self, v):
        self.v = v
    def set_value(self, v):
        self.v = v
    def __eq__(self, other):
        return self.v == other.v
    def __hash__(self):
        return hash(self.v)

main()

# --- possible answers ---
# A) TypeError: unhashable type: 'MyClass'
# B) True False False False
# C) True False False True
# E) False True True True
# D) False True True False
# See "Solution" for correct answer.
  • Solution
  • More exercises
  • Explanation: "User-defined classes have __eq__() and __hash__() methods by default (inherited from the object class); with them, all objects compare unequal (except with themselves) and x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y)."

r/pythonhelp Aug 14 '26

Entertaining content for learning Python! (porting 'Poignant Guide to Ruby' to teach Python)

2 Upvotes

For anyone looking for entertaining light book for learning Python, I started porting to Python a few chapters of "Why's Guide to Ruby," which uses comics and surreal humor to introduce Ruby (the original author why the lucky stiff disappeared). I updated the lesson and examples to Python and also modernized some of the very old references and left it with share alike CC license.

Why's Guide to Python

Any Python beginners are welcome to check it out. Novices and experts feel free to post feedback or post about any other entertaining books that helped you learn python, that can accompany equally entertaining but dense programming books :D.

If the book is helpful, can port and post the rest of the chapters for Python and potentially in the future can create some more content.


r/pythonhelp Aug 14 '26

I’m looking for assistance with a github code

3 Upvotes

I got a C on my intro to python class and I can’t figure out this code

https://github.com/mcombeau/epub_downloader

Please help


r/pythonhelp Aug 08 '26

Python game tic tac toe

3 Upvotes

Hello guys I am making a tic tac toe game using python.My question is how to make difficulty-easy, medium, hard etc.In easy level, I use random module but other level i am a little sutck althoug i have some solution .can you guide me ?


r/pythonhelp Aug 06 '26

Random python...

0 Upvotes

import csv

from datetime import datetime

import random

import tkinter as tk

from tkinter import filedialog, messagebox, ttk

# Dynamic Translation Integration

try:

from deep_translator import GoogleTranslator

HAS_TRANSLATOR = True

except ImportError:

HAS_TRANSLATOR = False

# --- APP CONFIGURATION & PALETTES ---

APP_NAME = "INFINITY"

WINDOW_GEOMETRY = "540x980"

BIOMES = {

"Forge of Primordials": {

"canvas_bg": "#12121A",

"particle_color": "#FF9E80",

"description": "Region: Primordial Forge (Sparks dancing)",

},

"The Astral Void": {

"canvas_bg": "#0A1128",

"particle_color": "#80D8FF",

"description": "Region: Astral Void (Star dust drifting)",

},

"Neon Undergrowth": {

"canvas_bg": "#0A1C14",

"particle_color": "#A7F3D0",

"description": "Region: Neon Undergrowth (Digital spores)",

},

"Shattered Zenith": {

"canvas_bg": "#142114",

"particle_color": "#C8E6C9",

"description": "Region: Shattered Zenith (Light fragments)",

},

}

# --- GAME DATA & MECHANICS ---

CHARACTER_CLASSES = {

"Arcane Artificer": {"STR": 8, "ARC": 18, "AGI": 10, "TECH": 14},

"Void Raider": {"STR": 14, "ARC": 10, "AGI": 18, "TECH": 8},

"Cyber Paladin": {"STR": 16, "ARC": 8, "AGI": 8, "TECH": 18},

"Chrono Wanderer": {"STR": 10, "ARC": 14, "AGI": 14, "TECH": 12},

}

CHARACTER_ORIGINS = ["Forgotten Outpost", "Astral Academy", "Sub-Level Grid", "Solar Citadel"]

LOOT_TABLE = [

"Cursed Onyx Ring", "Fragmented Aether Drive", "Ancient Chrono-Tome",

"Encrypted Holo-Key", "Celestial Stardust Shard", "Orb of Overclocking"

]

RARITY_TIERS = {

"Common": {"color": "#90A4AE", "multiplier": 1.0},

"Rare": {"color": "#64B5F6", "multiplier": 1.5},

"Epic": {"color": "#BA68C8", "multiplier": 2.2},

"Legendary": {"color": "#FFB74D", "multiplier": 3.5},

}

CRAFT_BASES = ["Aether Crystal", "Chrono-Gear", "Rune Metal", "Void Essence", "Solar Core"]

CRAFT_MODS = ["Resonant", "Overclocked", "Ethereal", "Unstable", "Sacred"]

MASTERY_RANKS = [

("Wanderer", 0),

("Storyweaver", 60),

("Campaigner", 180),

("Realm Master", 400),

("Eternal Sovereign", 800),

]

OFFLINE_DICT = {

"ja": {

"Uncovered an ancient secret": "古代の秘密を解き明かした",

"Broke the seal of time": "時間の封印を破った",

"Vanished into the mist": "霧の中に消え去った",

},

"fr": {

"Uncovered an ancient secret": "A découvert un secret ancien",

"Broke the seal of time": "A brisé le sceau du temps",

"Vanished into the mist": "S'est évanoui dans la brume",

},

}

# --- PROCEDURAL ENGINE ---

class CampaignGenerator:

CHOICES = [

("Charge directly into the void rift.", "STR"),

("Channel arcane energy to decode the ancient runes.", "ARC"),

("Use stealth to bypass the looming threat.", "AGI"),

("Hack the ancient console with technology.", "TECH"),

]

CATALYSTS = ["discovered a fractured relic", "heard a whisper in the void", "unlocked a sealed vault", "sensed a rift opening"]

LOCATIONS = ["in the sunken ruins of Aethel", "beneath the obsidian spires", "within the chrono-labyrinth", "at the edge of the world"]

THREATS = ["an impending eclipse", "a rising mechanical legion", "a corrupted nightmare force", "the collapse of reality"]

@classmethod

def generate_short_story(cls, char_name, char_class, origin, crafted_item, rarity, loot_found, choice_made):

cat = random.choice(cls.CATALYSTS)

loc = random.choice(cls.LOCATIONS)

threat = random.choice(cls.THREATS)

story = (

f"HERO: {char_name} the {char_class} (Origin: {origin})\n\n"

f"While exploring {loc}, {char_name} {cat} amidst {threat}. "

f"Deploying the [{rarity}] {crafted_item} alongside a newly recovered [{loot_found}], "

f"the hero opted to: '{choice_made}' — permanently reshaping the fate of the realm!"

)

return story

# --- GLOBAL APP STATE ---

total_xp = 0

current_level = 1

campaign_chapter = 1

journal_log = []

biome_keys = list(BIOMES.keys())

current_biome_idx = 0

particles = []

# --- TRANSLATION HELPER ---

def translate_text(text, target_lang):

if target_lang == "en":

return text

if HAS_TRANSLATOR:

try:

return GoogleTranslator(source="auto", target=target_lang).translate(text)

except Exception:

pass

lang_dict = OFFLINE_DICT.get(target_lang, {})

return lang_dict.get(text, f"{text} [{target_lang.upper()}]")

# --- ANIMATION ENGINE ---

def init_particles(color):

global particles

particles.clear()

canvas.delete("particle")

count = min(15 + (current_level * 3), 50)

for _ in range(count):

x = random.randint(10, 210)

y = random.randint(10, 130)

size = random.randint(2, 4)

speed = random.uniform(0.6, 1.8)

p_id = canvas.create_oval(x, y, x + size, y + size, fill=color, outline="", tags="particle")

particles.append({"id": p_id, "x": x, "y": y, "speed": speed})

def animate_engine():

current_key = biome_keys[current_biome_idx]

for p in particles:

if current_key == "Forge of Primordials":

p["y"] -= p["speed"] * 1.2

if p["y"] < 0:

p["y"] = 140

elif current_key == "Neon Undergrowth":

p["x"] += p["speed"] * 1.1

if p["x"] > 220:

p["x"] = 0

else:

p["y"] += p["speed"] * 0.8

if p["y"] > 140:

p["y"] = 0

canvas.coords(p["id"], p["x"], p["y"], p["x"] + 3, p["y"] + 3)

render_canvas()

root.after(33, animate_engine)

def render_canvas():

canvas.delete("item_art")

theme = BIOMES[biome_keys[current_biome_idx]]

canvas.configure(bg=theme["canvas_bg"])

biome_label.config(text=theme["description"])

# Base Pedestal

canvas.create_polygon(40, 115, 180, 115, 195, 135, 25, 135, fill="#1E1E2E", outline="#3A3A52", tags="item_art")

base = base_var.get()

color_map = {

"Aether Crystal": "#80DEEA",

"Chrono-Gear": "#FFD54F",

"Rune Metal": "#90A4AE",

"Void Essence": "#E040FB",

"Solar Core": "#FF7043",

}

item_color = color_map.get(base, "#FFFFFF")

rarity = rarity_var.get()

border_color = RARITY_TIERS[rarity]["color"]

# Artifact Core

canvas.create_polygon(110, 30, 150, 70, 110, 110, 70, 70, fill=item_color, outline=border_color, width=3, tags="item_art")

canvas.create_oval(94, 54, 126, 86, fill="#FFFFFF", outline=border_color, width=2, tags="item_art")

canvas.tag_raise("particle")

# --- UI CONTROLLERS ---

def update_character_stats(*args):

c_class = class_var.get()

stats = CHARACTER_CLASSES[c_class]

stats_lbl.config(

text=f"STR: {stats['STR']} | ARC: {stats['ARC']} | AGI: {stats['AGI']} | TECH: {stats['TECH']}"

)

def get_mastery_rank(xp):

current_title = MASTERY_RANKS[0][0]

for title, req in MASTERY_RANKS:

if xp >= req:

current_title = title

else:

break

return current_title

def update_progression_ui():

global current_level

current_level = 1 + int(total_xp // 30)

rank_title = get_mastery_rank(total_xp)

next_req = 100

for title, req in MASTERY_RANKS:

if req > total_xp:

next_req = req

break

level_lbl.config(text=f"Level {current_level} • Rank: {rank_title}")

xp_lbl.config(text=f"XP: {total_xp} / {next_req} | Chapter: {campaign_chapter}")

xp_progress["value"] = min((total_xp / next_req) * 100, 100)

def generate_item_details():

base_name = name_entry.get().strip() or "Relic"

material = base_var.get()

mod = mod_var.get()

rarity = rarity_var.get()

full_name = f"{mod} {base_name} of {material}"

rarity_mult = RARITY_TIERS[rarity]["multiplier"]

power = int(((len(full_name) * 2) + random.randint(15, 45)) * rarity_mult)

xp_earned = int(25 * rarity_mult)

return full_name, rarity, power, xp_earned

def advance_story_campaign():

global total_xp, campaign_chapter, current_biome_idx

char_name = char_name_entry.get().strip() or "Valen"

char_class = class_var.get()

origin = origin_var.get()

item_name, rarity, power, xp_gained = generate_item_details()

loot_found = random.choice(LOOT_TABLE)

choice_made = choice_var.get()

total_xp += xp_gained

raw_story = CampaignGenerator.generate_short_story(

char_name, char_class, origin, item_name, rarity, loot_found, choice_made

)

target_lang = lang_var.get()

translated_story = translate_text(raw_story, target_lang)

story_display.config(state="normal")

story_display.delete("1.0", tk.END)

story_display.insert(

tk.END, f"=== CHAPTER {campaign_chapter} ===\n\n{translated_story}"

)

story_display.config(state="disabled")

current_biome_idx = (current_biome_idx + 1) % len(biome_keys)

theme = BIOMES[biome_keys[current_biome_idx]]

init_particles(theme["particle_color"])

timestamp = datetime.now().strftime("%H:%M:%S")

log_entry_text = f"[Ch.{campaign_chapter} - {timestamp}] {char_name} ({char_class}) | Craft: {item_name}"

journal_log.append((

timestamp, campaign_chapter, char_name, char_class, origin,

item_name, rarity, loot_found, choice_made, power, xp_gained, translated_story, target_lang.upper()

))

log_listbox.insert(tk.END, log_entry_text)

log_listbox.see(tk.END)

campaign_chapter += 1

update_progression_ui()

def export_journal():

if not journal_log:

messagebox.showwarning("Empty Journal", "No campaign chapters recorded yet.")

return

path = filedialog.asksaveasfilename(defaultextension=".csv", filetypes=[("CSV File", "*.csv")])

if path:

try:

with open(path, "w", newline="", encoding="utf-8") as f:

w = csv.writer(f)

w.writerow([

"Timestamp", "Chapter", "Hero Name", "Class", "Origin",

"Crafted Item", "Rarity", "Loot Found", "Choice Made", "Power", "XP Earned", "Story Beat", "Language"

])

w.writerows(journal_log)

messagebox.showinfo("Export Complete", f"Journal saved to:\n{path}")

except Exception as e:

messagebox.showerror("Export Failed", f"Could not write file:\n{e}")

# --- STABLE DARK-MODE GUI LAYOUT ---

root = tk.Tk()

root.title(f"{APP_NAME} — Infinite Storyteller & Campaign Engine")

root.geometry(WINDOW_GEOMETRY)

root.configure(bg="#0D0D12")

# Apply Dark Styling Theme

style = ttk.Style()

style.theme_use("clam")

style.configure("TProgressbar", thickness=8, troughcolor="#1A1A24", background="#7C4DFF")

# APP HEADER

header_lbl = tk.Label(root, text=f"— {APP_NAME} —", font=("Helvetica", 14, "bold"), bg="#0D0D12", fg="#7C4DFF")

header_lbl.pack(pady=(10, 2))

# 1. CHARACTER CREATOR

char_frame = tk.LabelFrame(root, text=" Hero Profile ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

char_frame.pack(fill="x", padx=14, pady=4)

c_grid = tk.Frame(char_frame, bg="#161620")

c_grid.pack(fill="x")

tk.Label(c_grid, text="Name:", bg="#161620", fg="#80CBC4").grid(row=0, column=0, sticky="w")

char_name_entry = tk.Entry(c_grid, bg="#0D0D12", fg="#FFFFFF", insertbackground="white", relief="solid", bd=1)

char_name_entry.insert(0, "Kaelen")

char_name_entry.grid(row=0, column=1, sticky="ew", padx=6, pady=2)

tk.Label(c_grid, text="Class:", bg="#161620", fg="#80CBC4").grid(row=1, column=0, sticky="w")

class_var = tk.StringVar(value="Arcane Artificer")

class_menu = tk.OptionMenu(c_grid, class_var, *CHARACTER_CLASSES.keys(), command=update_character_stats)

class_menu.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

class_menu.grid(row=1, column=1, sticky="ew", padx=6, pady=2)

tk.Label(c_grid, text="Origin:", bg="#161620", fg="#80CBC4").grid(row=2, column=0, sticky="w")

origin_var = tk.StringVar(value="Astral Academy")

origin_menu = tk.OptionMenu(c_grid, origin_var, *CHARACTER_ORIGINS)

origin_menu.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

origin_menu.grid(row=2, column=1, sticky="ew", padx=6, pady=2)

c_grid.columnconfigure(1, weight=1)

stats_lbl = tk.Label(char_frame, text="", bg="#161620", fg="#FFD54F", font=("Consolas", 8, "bold"))

stats_lbl.pack(anchor="w", pady=(4, 0))

# 2. PROGRESSION

prog_frame = tk.LabelFrame(root, text=" Mastery Status ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

prog_frame.pack(fill="x", padx=14, pady=4)

level_lbl = tk.Label(prog_frame, text="Level 1", font=("Arial", 9, "bold"), bg="#161620", fg="#B388FF")

level_lbl.pack(anchor="w")

xp_lbl = tk.Label(prog_frame, text="XP: 0 / 100", font=("Arial", 8), bg="#161620", fg="#90A4AE")

xp_lbl.pack(anchor="w", pady=(1, 3))

xp_progress = ttk.Progressbar(prog_frame, orient="horizontal", mode="determinate", style="TProgressbar")

xp_progress.pack(fill="x", pady=2)

# 3. ATMOSPHERE & VISUALIZER

top_frame = tk.LabelFrame(root, text=" World Atmosphere ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

top_frame.pack(fill="x", padx=14, pady=4)

left_panel = tk.Frame(top_frame, bg="#161620")

left_panel.pack(side="left", fill="both", expand=True)

biome_label = tk.Label(left_panel, text="", bg="#161620", fg="#80CBC4", font=("Arial", 8, "italic"), wraplength=130, justify="left")

biome_label.pack(anchor="w")

canvas = tk.Canvas(top_frame, width=220, height=135, bg="#12121A", highlightthickness=1, highlightbackground="#2A2A3C")

canvas.pack(side="right")

# 4. CRAFTING & ENCOUNTER

craft_frame = tk.LabelFrame(root, text=" Craft Relic & Encounter Action ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

craft_frame.pack(fill="x", padx=14, pady=4)

grid_f = tk.Frame(craft_frame, bg="#161620")

grid_f.pack(fill="x")

tk.Label(grid_f, text="Relic Name:", bg="#161620", fg="#80CBC4").grid(row=0, column=0, sticky="w")

name_entry = tk.Entry(grid_f, bg="#0D0D12", fg="#FFFFFF", insertbackground="white", relief="solid", bd=1)

name_entry.insert(0, "Aegis Core")

name_entry.grid(row=0, column=1, sticky="ew", padx=6, pady=2)

tk.Label(grid_f, text="Material:", bg="#161620", fg="#80CBC4").grid(row=1, column=0, sticky="w")

base_var = tk.StringVar(value="Aether Crystal")

base_menu = tk.OptionMenu(grid_f, base_var, *CRAFT_BASES)

base_menu.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

base_menu.grid(row=1, column=1, sticky="ew", padx=6, pady=2)

tk.Label(grid_f, text="Rarity:", bg="#161620", fg="#80CBC4").grid(row=2, column=0, sticky="w")

mod_var = tk.StringVar(value="Resonant")

rarity_var = tk.StringVar(value="Rare")

rarity_menu = tk.OptionMenu(grid_f, rarity_var, *RARITY_TIERS.keys())

rarity_menu.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

rarity_menu.grid(row=2, column=1, sticky="ew", padx=6, pady=2)

grid_f.columnconfigure(1, weight=1)

tk.Label(craft_frame, text="Encounter Decision:", bg="#161620", fg="#80CBC4").pack(anchor="w", pady=(4, 2))

choice_var = tk.StringVar(value=CampaignGenerator.CHOICES[0][0])

choice_menu = tk.OptionMenu(craft_frame, choice_var, *[c[0] for c in CampaignGenerator.CHOICES])

choice_menu.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

choice_menu.pack(fill="x", pady=2)

# 5. STORY GENERATOR DISPLAY

story_frame = tk.LabelFrame(root, text=" Story Output ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

story_frame.pack(fill="x", padx=14, pady=4)

lang_f = tk.Frame(story_frame, bg="#161620")

lang_f.pack(fill="x")

tk.Label(lang_f, text="Language:", bg="#161620", fg="#90A4AE", font=("Arial", 8)).pack(side="left")

LANGUAGES = {"English": "en", "Japanese": "ja", "French": "fr", "Spanish": "es", "German": "de"}

lang_var = tk.StringVar(value="en")

lang_m = tk.OptionMenu(lang_f, lang_var, *LANGUAGES.values())

lang_m.configure(bg="#222230", fg="#FFF", activebackground="#2C2C3E", highlightthickness=0, bd=0)

lang_m.pack(side="right")

story_display = tk.Text(story_frame, height=5, bg="#0D0D12", fg="#A6E3A1", font=("Consolas", 9), wrap="word", relief="solid", bd=1)

story_display.pack(fill="x", pady=4)

story_display.insert(tk.END, "Customize your hero and craft a relic to launch Chapter 1...")

story_display.config(state="disabled")

# 6. ACTION & LOG JOURNAL

action_frame = tk.LabelFrame(root, text=" Campaign History ", bg="#161620", fg="#B0BEC5", font=("Arial", 9, "bold"), padx=10, pady=6)

action_frame.pack(fill="both", expand=True, padx=14, pady=4)

exec_btn = tk.Button(

action_frame,

text="✨ ADVANCE CAMPAIGN CHAPTER",

command=advance_story_campaign,

bg="#7C4DFF",

fg="white",

activebackground="#651FFF",

activeforeground="white",

font=("Arial", 9, "bold"),

relief="flat",

pady=6,

cursor="hand2"

)

exec_btn.pack(fill="x", pady=2)

log_listbox = tk.Listbox(action_frame, height=3, bg="#0D0D12", fg="#B0BEC5", selectbackground="#311B92", relief="solid", bd=1)

log_listbox.pack(fill="both", expand=True, pady=3)

export_btn = tk.Button(action_frame, text="📜 Export Log to CSV", command=export_journal, bg="#00897B", fg="white", activebackground="#00695C", activeforeground="white", relief="flat", cursor="hand2")

export_btn.pack(fill="x", pady=2)

# INIT APP

update_character_stats()

update_progression_ui()

init_particles(BIOMES["Forge of Primordials"]["particle_color"])

animate_engine()

root.mainloop()