Skip to content

Commit c3902cf

Browse files
authored
Merge pull request #963 from Davda-James/script/weather_notifier
Script for "Weather Notifier" added
2 parents a28959d + 59ee5e2 commit c3902cf

File tree

8 files changed

+340
-8
lines changed

8 files changed

+340
-8
lines changed

video_cropper/video_cropper.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,41 @@ def to_int(a, rel_to):
2525
parser = argparse.ArgumentParser()
2626

2727
parser.add_argument('-i', '--input', type=str, help='the file to crop')
28-
parser.add_argument('-c', '--crop', type=str, help='the amount to crop in the format "TOP,BOTTOM,LEFT,RIGHT"')
29-
parser.add_argument('-t', '--top', type=str, help='the amount to crop off the top of the video')
30-
parser.add_argument('-b', '--bottom', type=str, help='the amount to crop off the bottom of the video')
31-
parser.add_argument('-l', '--left', type=str, help='the amount to crop off the left of the video')
32-
parser.add_argument('-r', '--right', type=str, help='the amount to crop off the right of the video')
33-
parser.add_argument('-o', '--output', type=str, help='the file to output to (cannot be the same as input file)')
34-
parser.add_argument('-y', '--yes', action='store_true', help='skip the prompt to confirm overwriting a file')
28+
parser.add_argument(
29+
'-c',
30+
'--crop',
31+
type=str,
32+
help='the amount to crop in the format "TOP,BOTTOM,LEFT,RIGHT"')
33+
parser.add_argument(
34+
'-t',
35+
'--top',
36+
type=str,
37+
help='the amount to crop off the top of the video')
38+
parser.add_argument(
39+
'-b',
40+
'--bottom',
41+
type=str,
42+
help='the amount to crop off the bottom of the video')
43+
parser.add_argument(
44+
'-l',
45+
'--left',
46+
type=str,
47+
help='the amount to crop off the left of the video')
48+
parser.add_argument(
49+
'-r',
50+
'--right',
51+
type=str,
52+
help='the amount to crop off the right of the video')
53+
parser.add_argument(
54+
'-o',
55+
'--output',
56+
type=str,
57+
help='the file to output to (cannot be the same as input file)')
58+
parser.add_argument(
59+
'-y',
60+
'--yes',
61+
action='store_true',
62+
help='skip the prompt to confirm overwriting a file')
3563

3664
args = parser.parse_args()
3765

@@ -107,7 +135,11 @@ def to_int(a, rel_to):
107135
sys.exit(1)
108136

109137
# call ffmpeg with the required args
110-
cmd = 'ffmpeg -hide_banner -loglevel error -i "{}" -c:a copy -filter:v "crop={}:{}:{}:{}" {}{}'
138+
cmd = (
139+
'ffmpeg -hide_banner -loglevel error -i "{}" -c:a copy '
140+
'-filter:v "crop={}:{}:{}:{}" {}{}'
141+
)
142+
111143
cmd = cmd.format(
112144
args.input,
113145
width,

weather_notifier/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Weather Notifier - README
2+
3+
# Overview
4+
The ** Weather Notifier ** is a Python - based GUI application built with `Tkinter`, which allows users to retrieve weather information for any city using the OpenWeatherMap API. It provides two modes of operation:
5+
- **Manual Mode**: Fetch the weather on - demand with a single click.
6+
- **Auto Mode**: Fetch the weather at regular intervals and can also run in the background.
7+
8+
The app displays current weather details such as temperature, humidity, wind speed, and more in a user - friendly interface.
9+
10+
# Features
11+
- Fetch current weather for any city globally.
12+
- Switch between manual and automatic weather updates.
13+
- Display detailed weather information, including temperature, humidity, wind, and more.
14+
- Option to stop the automatic weather updates and return to the main city selection screen.
15+
16+
# Prerequisites
17+
1. Python 3.x
18+
2. The following Python libraries:
19+
- `Tkinter`
20+
- `requests`
21+
- `threading`
22+
- `geopy`
23+
- `dotenv`
24+
25+
To install the required libraries, use the following command:
26+
```bash
27+
pip install - r requirements.txt
28+
```
29+
30+
# Screenshots
31+
![Main Screen](. / images / box.jpg)
32+
![Error](. / images / error_box.jpg)
33+
![Error](. / images / automated_gui.jpg)
34+
![Error](. / images / data_automated.jpg)
23.7 KB
Loading

weather_notifier/images/box.jpg

20.6 KB
Loading
47.8 KB
Loading

weather_notifier/images/error_box.jpg

42.4 KB
Loading

weather_notifier/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
requests=2.28.2
2+
geopy=2.4.1
3+
dotenv=1.0.1

weather_notifier/weather_notifier.py

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

0 commit comments

Comments
 (0)