diff --git a/config/API.config.json b/config/API.config.json index d5f86b4..230fbfe 100644 --- a/config/API.config.json +++ b/config/API.config.json @@ -1,3 +1,3 @@ { - "api_key": "6da85901a47efe9f943b1a40bbc727e5" -} + "api_key": "" +} \ No newline at end of file diff --git a/main.py b/main.py index 7d7fba8..a3135eb 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,21 @@ -import requests -import customtkinter as ctk import threading import json import os import webbrowser +import subprocess +import sys +try: + import customtkinter as ctk +except ImportError: + subprocess.check_call([sys.executable, "-m", "pip", "install", "customtkinter"]) + import customtkinter as ctk +try: + import requests +except ImportError: + subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"]) + import requests app = ctk.CTk() @@ -65,6 +75,8 @@ def toggleFromMetric(): doSearch() + + switchFrame = ctk.CTkFrame(app, fg_color="transparent") switchFrame.pack(anchor="ne") @@ -74,6 +86,9 @@ changeAppearanceMode.pack(anchor="w", padx=10, pady=5) changeFromMetric = ctk.CTkSwitch(switchFrame, text="Change to Imperial System", command=toggleFromMetric) changeFromMetric.pack(anchor="w", padx=10, pady=5) + + + if os.path.exists("config/config.json"): with open("config/config.json", "r") as f: config = json.load(f) @@ -98,6 +113,38 @@ APIKey = None system = "metric" degrees = "°C" +def readFavourite(): + global favouriteVar + fav = "" + favouriteVar = ctk.BooleanVar(value=False) + + with open("config/config.json", "r") as f: + config = json.load(f) + fav = config["fav"] + +def saveFavourite(): + with open("config/config.json", "r") as f: + config = json.load(f) + + if "favourite" not in config: + config["favourite"] = [] + + if favouriteCheck.get() == 1: + if city not in config["favourite"]: + config["favourite"].append(city) + else: + if city in config["favourite"]: + config["favourite"].remove(city) + + with open("config/config.json", "w") as f: + json.dump(config, f, indent=4) + +favouriteCheck = ctk.CTkCheckBox(switchFrame, text="Favourite", command=saveFavourite) + + + + + def glow_effect(widget, steps=25, start_rgb=(239, 68, 68), delay=30): theme_color = ctk.ThemeManager.theme["CTkFrame"]["fg_color"] @@ -126,8 +173,10 @@ def glow_effect(widget, steps=25, start_rgb=(239, 68, 68), delay=30): def returnButtonClick(): resultPage.pack_forget() + favPage.pack_forget() searchPage.pack(fill="both", expand="True") searchPage.pack(pady=70) + favouriteCheck.pack_forget() def get_current_color(widget="CTkFrame"): @@ -140,6 +189,7 @@ def get_current_color(widget="CTkFrame"): return fgColors[0] + def checkSystem(): global degrees @@ -164,6 +214,7 @@ if os.path.exists("config/config.json"): searchPage = ctk.CTkFrame(app, fg_color="transparent") resultPage = ctk.CTkFrame(app, fg_color="transparent") +favPage = ctk.CTkFrame(app, fg_color="transparent") if os.path.exists("config/API.config.json"): with open("config/API.config.json", "r") as f: @@ -196,7 +247,7 @@ def openAPIPopup(): APIlabel = ctk.CTkLabel(popup, text="Enter your API key:") APIlabel.pack(pady=(15, 5)) - APIKeyGet = ctk.CTkLabel(popup, text="How to get free API key", text_color="#3366d6", cursor="hand2") + APIKeyGet = ctk.CTkLabel(popup, text="How to get free API key", text_color="#3366d6", cursor="hand2", font=("Arial", 12, "underline")) APIKeyGet.bind("", openWeatherLink) APIKeyGet.pack(pady=5) @@ -206,6 +257,8 @@ def openAPIPopup(): APIErrorFrame = ctk.CTkFrame(popup, width=450, height=20) APIErrorLabel = ctk.CTkLabel(APIErrorFrame, text="", font=("Arial", 14)) + + def getAPIkey(): global APIKey APIKey = APIEntry.get() @@ -262,84 +315,44 @@ def openAPIPopup(): safeAPI = ctk.CTkButton(popup, text="Safe API key", command=getAPIkey) safeAPI.pack(pady=10) - - - -if not APIKey: - app.after(500, openAPIPopup) - - - -searchPage.pack(fill="both", expand=True) -searchPage.pack(pady=200) - -searchFrame = ctk.CTkFrame(searchPage, width=500) -searchFrame.pack(pady=30, anchor="center") - -searchLabel = ctk.CTkLabel(searchFrame, text="Which city do you want to know the weather?", font=("Arial", 30)) -searchLabel.pack(pady=5, padx=15) - -cityEntry = ctk.CTkEntry(searchPage, placeholder_text="Enter City", font=("Arial", 22, "bold"), width=800, height=70) -cityEntry.pack(pady=10, anchor="center") - -cityEntry.bind("", lambda event: doSearch()) - -resultTitle = ctk.CTkLabel(resultPage, text="Weather: ", font=("Arial", 60, "bold")) -resultTitle.pack(pady=20) - -################################################### -#### WEATHER FRAMES #### -################################################### - -cardsFrame = ctk.CTkFrame(resultPage, fg_color="transparent") -cardsFrame.pack(fill="both", expand=True, padx=20, pady=20) - -weatherTempFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) -weatherTempFrame.pack(side="left", expand=True, fill="both", padx=10) -weatherTempFrame.pack_propagate(False) - -weatherTempTitle = ctk.CTkLabel(weatherTempFrame, text="Temperature", font=("Arial", 45, "bold")) -weatherTempTitle.pack(expand=True, fill="both", pady=(20,10)) - -weatherTempLabel = ctk.CTkLabel(weatherTempFrame, font=("Arial", 25)) -weatherTempLabel.pack(expand=True, anchor="n") - -weatherGeneralFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) -weatherGeneralFrame.pack(side="left", expand=True, fill="both", padx=10) -weatherGeneralFrame.pack_propagate(False) - -weatherGeneralTitle = ctk.CTkLabel(weatherGeneralFrame, text="General:", font=("Arial", 45, "bold")) -weatherGeneralTitle.pack(expand=True, fill="both", pady=(20,10)) - -weatherGeneralLabel = ctk.CTkLabel(weatherGeneralFrame, font=("Arial", 25)) -weatherGeneralLabel.pack(expand=True, anchor = "n") - -weatherWindFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) -weatherWindFrame.pack(side="left", expand=True, fill="both", padx=10) -weatherWindFrame.pack_propagate(False) - -weatherWindTitle = ctk.CTkLabel(weatherWindFrame, text="Wind", font=("Arial", 45, "bold")) -weatherWindTitle.pack(expand=True, fill="both", pady=(20,10)) - -weatherWindLabel = ctk.CTkLabel(weatherWindFrame, font=("Arial", 25)) -weatherWindLabel.pack(expand=True, anchor = "n") - - -################################################## -#### Return Button #### -################################################## - -returnButton = ctk.CTkButton(resultPage, text="Return", command=returnButtonClick) -returnButton.pack(pady=10) - - -ErrorFrame = ctk.CTkFrame(searchPage) -ErrorLabel = ctk.CTkLabel(ErrorFrame, text="") - def doSearch(): - threading.Thread(target=searchWeather, daemon=True).start() + threading.Thread(target=searchWeather, daemon=True).start() + +favFrame = ctk.CTkFrame(favPage) +favFrame.pack(fill="both", expand=True, pady=10, padx=10) + +def switchFav(): + resultPage.pack_forget() + searchPage.pack_forget() + favPage.pack(fill="both", expand=True) + + for widget in favFrame.winfo_children(): + widget.destroy() + + with open("config/config.json", "r") as file: + data = json.load(file) + + favlist = data.get("favourites", []) + + for i, favourite in enumerate(favlist): + row = i // 4 + column = i % 4 + + button = ctk.CTkButton( + favFrame, + text=favourite, + height=50, + font=("Arial", 20, "bold") + ) + + button.grid(row=row, column=column, padx=5, pady=5, sticky="ew") + + for column in range(4): + favFrame.grid_columnconfigure(column, weight=1) + def searchWeather(): + global city city = cityEntry.get() @@ -366,8 +379,6 @@ def searchWeather(): windDirection = data["wind"]["deg"] - print(data) - def show_result(): ErrorLabel.configure(text="") ErrorFrame.pack_forget() @@ -389,6 +400,15 @@ def searchWeather(): ) searchPage.pack_forget() resultPage.pack(fill="both", expand=True) + favouriteCheck.pack(anchor="w", padx=10, pady=5) + + with open("config/config.json", "r") as f: + config = json.load(f) + + if city in config.get("favourite", []): + favouriteCheck.select() + else: + favouriteCheck.deselect() app.after(0, show_result) @@ -433,6 +453,89 @@ def showSearch(): resultPage.pack(fill="both", expand=True) +if not APIKey: + app.after(500, openAPIPopup) + + +favButton = ctk.CTkButton(app, text="Favourites", height=40, width=200, font=("Arial", 20, "bold"), command=switchFav) +favButton.pack(anchor="s", pady=20, padx=20) + +searchPage.pack(fill="both", expand=True) +searchPage.pack(pady=200) + +searchFrame = ctk.CTkFrame(searchPage, width=500) +searchFrame.pack(pady=(30, 80), anchor="center") + +searchLabel = ctk.CTkLabel(searchFrame, text="From which city do you want to know the weather?", font=("Arial", 30)) +searchLabel.pack(pady=(5, 5), padx=15) + +cityEntry = ctk.CTkEntry(searchPage, placeholder_text="Enter City", font=("Arial", 22, "bold"), width=800, height=70) +cityEntry.pack(pady=10, anchor="center") + +cityEntry.bind("", lambda event: doSearch()) + +favItems = json.load(open("config/config.json", "r")) +favlist = favItems.get("favourite") + +favReturnButton = ctk.CTkButton(favPage, text="Return", command=returnButtonClick, width=300, height=50, font=("Arial", 23, "bold")) +favReturnButton.pack(pady=10) + +resultTitle = ctk.CTkLabel(resultPage, text="Weather: ", font=("Arial", 60, "bold")) +resultTitle.pack(pady=20) + +################################################### +#### WEATHER FRAMES #### +################################################### + + +cardsFrame = ctk.CTkFrame(resultPage, fg_color="transparent") +cardsFrame.pack(fill="both", expand=True, padx=20, pady=20) + +weatherTempFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) +weatherTempFrame.pack(side="left", expand=True, fill="both", padx=10) +weatherTempFrame.pack_propagate(False) + +weatherTempTitle = ctk.CTkLabel(weatherTempFrame, text="Temperature", font=("Arial", 45, "bold")) +weatherTempTitle.pack(expand=True, fill="both", pady=(20,10)) + +weatherTempLabel = ctk.CTkLabel(weatherTempFrame, font=("Arial", 25)) +weatherTempLabel.pack(expand=True, anchor="n") + +weatherGeneralFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) +weatherGeneralFrame.pack(side="left", expand=True, fill="both", padx=10) +weatherGeneralFrame.pack_propagate(False) + +weatherGeneralTitle = ctk.CTkLabel(weatherGeneralFrame, text="General:", font=("Arial", 45, "bold")) +weatherGeneralTitle.pack(expand=True, fill="both", pady=(20,10)) + +weatherGeneralLabel = ctk.CTkLabel(weatherGeneralFrame, font=("Arial", 25)) +weatherGeneralLabel.pack(expand=True, anchor = "n") + +weatherWindFrame = ctk.CTkFrame(cardsFrame, width=500, height=350) +weatherWindFrame.pack(side="left", expand=True, fill="both", padx=10) +weatherWindFrame.pack_propagate(False) + +weatherWindTitle = ctk.CTkLabel(weatherWindFrame, text="Wind", font=("Arial", 45, "bold")) +weatherWindTitle.pack(expand=True, fill="both", pady=(20,10)) + +weatherWindLabel = ctk.CTkLabel(weatherWindFrame, font=("Arial", 25)) +weatherWindLabel.pack(expand=True, anchor = "n") + + +################################################## +#### Return Button #### +################################################## + +searchReturnButton = ctk.CTkButton(resultPage, text="Return", command=returnButtonClick, width=300, height=50, font=("Arial", 23, "bold")) + +searchReturnButton.pack(pady=10) + + + +ErrorFrame = ctk.CTkFrame(searchPage) +ErrorLabel = ctk.CTkLabel(ErrorFrame, text="") + + cityEntry.bind("", lambda event: doSearch())