1
+ import tkinter as tk
2
+ from tkinter import messagebox
3
+ import requests
4
+ import threading
5
+ import time
6
+ from dotenv import load_dotenv
7
+ load_dotenv ()
8
+ from geopy .geocoders import Nominatim
9
+ import os
10
+
11
+ API_KEY = os .getenv ('OPENWEATHER_API_KEY' )
12
+ if API_KEY is None :
13
+ raise ValueError ("API key not found. Please set the OPENWEATHER_API_KEY in the .env file." )
14
+
15
+ def calculate_lat_long (city ):
16
+ geolocator = Nominatim (user_agent = "weather_notifier" )
17
+ location = geolocator .geocode (city )
18
+ if location :
19
+ return location .latitude , location .longitude
20
+ return None ,None
21
+ def get_weather (lat ,long ):
22
+ try :
23
+ print (lat ,long )
24
+ url = f"https://api.openweathermap.org/data/2.5/weather?lat={ lat } &lon={ long } &units=metric&appid={ API_KEY } "
25
+ res = requests .get (url )
26
+ data = res .json ()
27
+ if data ['cod' ] != 404 :
28
+ weather_info = {
29
+ "City" : data .get ("name" , "N/A" ),
30
+ "Latitude" : data ['coord' ]['lat' ],
31
+ "Longitude" : data ['coord' ]['lon' ],
32
+ "Temperature" : data ['main' ]['temp' ],
33
+ "Feels Like" : data ['main' ]['feels_like' ],
34
+ "Min Temp" : data ['main' ]['temp_min' ],
35
+ "Max Temp" : data ['main' ]['temp_max' ],
36
+ "Pressure" : data ['main' ]['pressure' ],
37
+ "Humidity" : data ['main' ]['humidity' ],
38
+ "Wind Speed" : data ['wind' ]['speed' ],
39
+ "Wind Degree" : data ['wind' ]['deg' ],
40
+ "Weather" : data ['weather' ][0 ]['description' ].capitalize (),
41
+ "Clouds" : f"{ data ['clouds' ]['all' ]} %" ,
42
+ "Visibility" : data .get ('visibility' , "N/A" ),
43
+ "Sunrise" : time .strftime ('%Y-%m-%d %H:%M:%S' , time .gmtime (data ['sys' ]['sunrise' ] + data ['timezone' ])),
44
+ "Sunset" : time .strftime ('%Y-%m-%d %H:%M:%S' , time .gmtime (data ['sys' ]['sunset' ] + data ['timezone' ])),
45
+ }
46
+ return weather_info
47
+ else :
48
+ return None
49
+
50
+ except Exception as e :
51
+ messagebox .showerror ("Error" , f"Error fetching weather data: { str (e )} " )
52
+ return None
53
+
54
+ # updating the weather
55
+ def update_weather ():
56
+ city = city_entry .get ()
57
+ if city :
58
+ lat , lon = calculate_lat_long (city )
59
+ if lat and lon :
60
+ weather_info = get_weather (lat , lon )
61
+ if weather_info :
62
+ weather_info_in_str_to_display = covert_the_info_to_display (weather_info )
63
+ weather_label .config (text = weather_info_in_str_to_display )
64
+ stop_button .pack (pady = 5 )
65
+ city_label .pack_forget ()
66
+ city_entry .pack_forget ()
67
+ manual_radio .pack_forget ()
68
+ auto_radio .pack_forget ()
69
+ start_button .pack_forget ()
70
+ else :
71
+ weather_label .config (text = "Unable to find coordinates!" )
72
+ stop_button .pack_forget ()
73
+ else :
74
+ weather_label .config (text = "Unable to find coordinates!" )
75
+ stop_button .pack_forget ()
76
+ else :
77
+ messagebox .showerror ("Error" , "Please enter a valid city name." )
78
+ stop_button .pack_forget ()
79
+ # displaying the info in the tkinter created box
80
+ def covert_the_info_to_display (weather_info ):
81
+ # Clear the previous text
82
+ weather_info_in_str_to_display = f'''
83
+ City: { weather_info ['City' ]} \n
84
+ Coordinates: ({ weather_info ['Latitude' ]} , { weather_info ['Longitude' ]} )\n
85
+ Temperature: { weather_info ['Temperature' ]} °C (Feels like { weather_info ['Feels Like' ]} °C)\n
86
+ Min Temp: { weather_info ['Min Temp' ]} °C, Max Temp: { weather_info ['Max Temp' ]} °C\n
87
+ Pressure: { weather_info ['Pressure' ]} hPa\n
88
+ Humidity: { weather_info ['Humidity' ]} %\n
89
+ Wind: { weather_info ['Wind Speed' ]} m/s, { weather_info ['Wind Degree' ]} °\n
90
+ Clouds: { weather_info ['Clouds' ]} \n
91
+ Visibility: { weather_info ['Visibility' ]} meters\n
92
+ Weather: { weather_info ['Weather' ]} \n
93
+ Sunrise: { weather_info ['Sunrise' ]} \n
94
+ Sunset: { weather_info ['Sunset' ]} \n
95
+ '''
96
+ return weather_info_in_str_to_display
97
+
98
+ # run_in_background logic
99
+ def run_in_background (interval ):
100
+ while auto_mode .get ():
101
+ update_weather ()
102
+ time .sleep (interval )
103
+
104
+ # Function to handle click
105
+ def start_notifier ():
106
+ if auto_mode .get ():
107
+ interval_str = interval_entry .get ().strip ()
108
+ if not interval_str :
109
+ messagebox .showerror ("Error" , "Please enter a valid interval (in seconds)." )
110
+ return
111
+ try :
112
+ interval = int (interval_str )
113
+ if interval <= 0 :
114
+ messagebox .showerror ("Error" , "Please enter a valid interval (in seconds)." )
115
+ return
116
+ except ValueError as e :
117
+ messagebox .showerror ("Error" , "Please enter a valid number." )
118
+ return
119
+ start_button .config (state = tk .DISABLED )
120
+
121
+ threading .Thread (target = run_in_background , args = (interval ,), daemon = True ).start ()
122
+ else :
123
+ update_weather ()
124
+
125
+ # Function to stop auto-updating
126
+ def stop_notifier ():
127
+ auto_mode .set (False )
128
+ start_button .config (state = tk .NORMAL )
129
+ stop_button .pack_forget ()
130
+ go_back ()
131
+
132
+ def go_back ():
133
+ weather_label .config (text = "" )
134
+ city_label .pack (pady = 10 )
135
+ city_entry .pack (pady = 5 )
136
+ manual_radio .pack (anchor = tk .W , padx = 20 )
137
+ auto_radio .pack (anchor = tk .W , padx = 20 )
138
+ start_button .pack (pady = 10 )
139
+ interval_label .pack_forget ()
140
+ interval_entry .pack_forget ()
141
+ stop_button .pack_forget ()
142
+
143
+ # gui setup
144
+ def show_interval_entry ():
145
+ if auto_mode .get ():
146
+ interval_label .pack (pady = 5 )
147
+ interval_entry .pack (pady = 5 )
148
+ else :
149
+ interval_label .pack_forget ()
150
+ interval_entry .pack_forget ()
151
+
152
+ def toggle_stop_button ():
153
+ if auto_mode .get ():
154
+ stop_button .pack (pady = 5 )
155
+ else :
156
+ stop_button .pack_forget ()
157
+
158
+ if __name__ == '__main__' :
159
+ city = "Surat"
160
+ lat ,long = calculate_lat_long (city )
161
+ if lat == None or long == None :
162
+ print ('No city found' )
163
+ exit (0 )
164
+
165
+ root = tk .Tk ()
166
+ root .title ("Weather Notifier" )
167
+ root .geometry ("550x500" )
168
+ root .resizable (False , False )
169
+
170
+ # City Label and Entry
171
+ city_label = tk .Label (root , text = "Enter your city:" )
172
+ city_label .pack (pady = 10 )
173
+ city_entry = tk .Entry (root , width = 30 ) # Define city_entry here
174
+ city_entry .pack (pady = 5 )
175
+
176
+ # Weather Info Label
177
+ weather_label = tk .Label (root , text = "" , font = ("Helvetica" , 10 ),justify = "left" )
178
+ weather_label .pack (pady = 20 )
179
+
180
+ # Mode Selection: Manual or Automatic
181
+ auto_mode = tk .BooleanVar ()
182
+
183
+ manual_radio = tk .Radiobutton (root , text = "On-click only" , variable = auto_mode , value = False )
184
+ manual_radio .pack (anchor = tk .W , padx = 20 )
185
+
186
+ auto_radio = tk .Radiobutton (root , text = "Run after a fixed interval" , variable = auto_mode , value = True )
187
+ auto_radio .pack (anchor = tk .W , padx = 20 )
188
+
189
+ # Interval Entry (only visible when interval mode is selected)
190
+ interval_label = tk .Label (root , text = "Enter interval (seconds):" )
191
+ interval_entry = tk .Entry (root , width = 10 )
192
+
193
+
194
+
195
+ auto_mode .trace_add ("write" , lambda * args : show_interval_entry ())
196
+
197
+ # Start Button
198
+ start_button = tk .Button (root , text = "Start Notifier" , command = start_notifier )
199
+ start_button .pack (pady = 10 )
200
+
201
+ # Stop Button (visible only when auto mode is active)
202
+ stop_button = tk .Button (root , text = "Stop Notifier" , command = stop_notifier )
203
+ stop_button .pack_forget ()
204
+
205
+ auto_mode .trace_add ("write" , lambda * args : toggle_stop_button ())
206
+
207
+ # Run the GUI loop
208
+ root .mainloop ()
0 commit comments