Compare commits

..
11 Commits
Author SHA1 Message Date
Demmdo faabb7c82a added a favourite tab, but its still a bit buggy 2026-08-10 12:10:34 +02:00
Demmdo 71ffae0026 finished the last two weather cards 2026-07-26 15:09:06 +02:00
Demmdo 4fb574fe52 test 2026-07-26 14:13:40 +02:00
Demmdo e0034821bc test 2026-07-26 14:13:00 +02:00
Demmdo eed4849a52 changed the saving 2026-07-26 14:11:47 +02:00
Demmdo aaafa1017d Stop tracking config file 2026-07-26 14:10:43 +02:00
Demmdo 0fdac97936 Changed the saving for API.config.json 2026-07-26 14:09:23 +02:00
Demmdo d4f2de73a6 Remove API config from git 2026-07-26 14:06:19 +02:00
DemmdoandGitHub 9831930b60 Update API.config.json 2026-07-26 13:58:08 +02:00
Demmdo d882c794fc Added a Label to get API 2026-07-26 13:56:54 +02:00
Demmdo 936178df04 Initial commit 2026-07-26 13:18:27 +02:00
5 changed files with 235 additions and 99 deletions
View File
-5
View File
@@ -1,5 +0,0 @@
{
"api_key": "",
"appearance": "",
"system": ""
}
+3
View File
@@ -0,0 +1,3 @@
{
"api_key": ""
}
+4
View File
@@ -0,0 +1,4 @@
{
"appearance": "dark",
"system": "metric"
}
+228 -94
View File
@@ -1,13 +1,26 @@
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()
def toggleAppearanceMode():
if changeAppearanceMode.get() == 0:
mode = "dark"
@@ -20,14 +33,14 @@ def toggleAppearanceMode():
try:
with open("config.json", "r") as f:
with open("config/config.json", "r") as f:
config = json.load(f)
except:
config = {}
config["appearance"] = mode
with open("config.json", "w") as f:
with open("config/config.json", "w") as f:
json.dump(config, f, indent=4)
@@ -48,20 +61,22 @@ def toggleFromMetric():
checkSystem()
try:
with open("config.json", "r") as f:
with open("config/config.json", "r") as f:
config = json.load(f)
except:
config = {}
config["system"] = system
with open("config.json", "w") as f:
with open("config/config.json", "w") as f:
json.dump(config, f, indent=4)
if resultPage.winfo_ismapped():
doSearch()
switchFrame = ctk.CTkFrame(app, fg_color="transparent")
switchFrame.pack(anchor="ne")
@@ -71,8 +86,11 @@ 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.json"):
with open("config.json", "r") as f:
if os.path.exists("config/config.json"):
with open("config/config.json", "r") as f:
config = json.load(f)
mode = config.get("appearance")
if mode == "light":
@@ -95,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"]
@@ -123,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"):
@@ -137,38 +189,41 @@ def get_current_color(widget="CTkFrame"):
return fgColors[0]
def checkSystem():
global degrees
global speed
if system == "metric":
degrees = "°C"
speed = "m/s"
changeFromMetric.set(False)
elif system == "imperial":
degrees = "°F"
speed = "mph"
changeFromMetric.set(True)
if os.path.exists("config.json"):
with open("config.json", "r") as f:
if os.path.exists("config/config.json"):
with open("config/config.json", "r") as f:
config = json.load(f)
system = config.get("system", "metric")
checkSystem()
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.json"):
with open("config.json", "r") as f:
if os.path.exists("config/API.config.json"):
with open("config/API.config.json", "r") as f:
config = json.load(f)
APIKey = config.get("api_key")
def openWeatherLink(event):
webbrowser.open("https://openweathermap.org/api")
def openAPIPopup():
global popup
@@ -190,14 +245,20 @@ def openAPIPopup():
APIlabel = ctk.CTkLabel(popup, text="Enter your API key:")
APIlabel.pack(pady=20)
APIlabel.pack(pady=(15, 5))
APIKeyGet = ctk.CTkLabel(popup, text="How to get free API key", text_color="#3366d6", cursor="hand2", font=("Arial", 12, "underline"))
APIKeyGet.bind("<Button-1>", openWeatherLink)
APIKeyGet.pack(pady=5)
APIEntry = ctk.CTkEntry(popup, placeholder_text="API key...")
APIEntry.pack(pady=10)
APIEntry.pack(pady=0)
APIErrorFrame = ctk.CTkFrame(popup, width=450, height=20)
APIErrorLabel = ctk.CTkLabel(APIErrorFrame, text="", font=("Arial", 14))
def getAPIkey():
global APIKey
APIKey = APIEntry.get()
@@ -212,19 +273,18 @@ def openAPIPopup():
APIKey = APIEntry.get()
try:
with open("config.json", "r") as f:
with open("config/API.config.json", "r") as f:
config = json.load(f)
except:
config = {}
config["api_key"] = APIKey
with open("config.json", "w") as f:
with open("config/API.config.json", "w") as f:
json.dump(config, f, indent=4)
if APIcode == 401:
def show_error():
print("401")
APIErrorFrame.pack()
APIErrorLabel.configure(text="Couldn't access the API key!")
APIErrorLabel.pack(padx=10)
@@ -255,75 +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("<Return>", 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)
weatherWindFrame = ctk.CTkFrame(cardsFrame, width=500, height=350)
weatherWindFrame.pack(side="left", expand=True, fill="both", padx=10)
weatherWindFrame.pack_propagate(False)
##################################################
#### 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()
@@ -332,8 +361,7 @@ def searchWeather():
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={APIKey}&units={system}&lang=en"
print(system)
print(url)
try:
response = requests.get(url, timeout=5)
APIcode = response.status_code
@@ -345,19 +373,42 @@ def searchWeather():
feels_like = data["main"]["feels_like"]
maxTemp = data["main"]["temp_max"]
minTemp = data["main"]["temp_min"]
pressure = data["main"]["grnd_level"] / 1000
humidity = data["main"]["humidity"]
windSpeed = data["wind"]["speed"]
windDirection = data["wind"]["deg"]
def show_result():
ErrorLabel.configure(text="")
ErrorFrame.pack_forget()
weatherTempLabel.configure(
text=f"It is {temp}{degrees} in {city}. \n\n"
f"It feels like {feels_like}{degrees} and the \n\n"
f"max Temperature is {maxTemp}{degrees}, while\n\n"
f"the minimum Temperature is {minTemp}{degrees}"
text=f"The current temperature in {city}\n\n is {temp}{degrees}. \n\n"
f"It actually feels like {feels_like}{degrees} and today's \n\n"
f"temperature will drop to a minimum of {minTemp}{degrees},\n\n"
f"while the maximum temperature expected \n\n today is {maxTemp}{degrees}"
)
weatherGeneralLabel.configure(
text=f"The atmospheric pressure measured at ground \n\n"
f"level is {pressure} bar. \n\n"
f"The current relative air humidity stands at {humidity}%"
)
weatherWindLabel.configure(
text=f"The wind is currently blowing at a \n\n speed of {windSpeed}{speed}.\n\n"
f"The wind direction is coming from {windDirection}°."
)
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)
@@ -402,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("<Return>", 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("<Return>", lambda event: doSearch())