summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkj-sh6042024-08-01 03:20:35 -0400
committerkj-sh6042024-08-01 03:20:35 -0400
commit801c4121f4770bbbe2f77bdbe7b570deb67f6c1f (patch)
treed2076f9896e9faa9a28d658a6c65ed27c752a56c
parent8e2acc3fb6f03738d94667c39eb7a38e86e5e4ff (diff)
kj-gitbot: dateTimeSetter.py
-rw-r--r--dateTimeSetter.py362
1 files changed, 362 insertions, 0 deletions
diff --git a/dateTimeSetter.py b/dateTimeSetter.py
new file mode 100644
index 0000000..5fa9fda
--- /dev/null
+++ b/dateTimeSetter.py
@@ -0,0 +1,362 @@
1#!/usr/bin/python
2
3import tkinter as tk
4from tkinter import ttk, messagebox
5import subprocess
6import requests
7import datetime
8import pytz
9import time
10
11
12class DateTimeSetter(tk.Tk):
13 def __init__(self):
14 super().__init__()
15
16 self.title("dateTimeSetter")
17 self.geometry("640x480")
18 self.resizable(True, True)
19 self.attributes("-type", "dialog") # make the window floating
20
21 self.create_widgets()
22 self.populate_fields()
23
24 def create_widgets(self):
25 # automatic time setting checkbox
26 automatic_time_label = ttk.Label(
27 self, text="Set the date, time, and timezone automatically?")
28 automatic_time_label.pack(pady=5)
29
30 self.automatic_time_check = ttk.Checkbutton(
31 self, text="Yes", command=self.on_automatic_time_toggled)
32 self.automatic_time_check.pack(pady=5)
33
34 # date entry fields
35 date_frame = ttk.LabelFrame(self, text="Date")
36 date_frame.pack(pady=10, padx=10, fill="x")
37
38 ttk.Label(date_frame, text="YYYY:").grid(
39 row=0, column=0, padx=5, pady=5)
40 self.year_var = tk.StringVar()
41 self.year_combo = ttk.Combobox(date_frame, textvariable=self.year_var, values=[str(
42 year) for year in range(1970, 2039)], state="normal")
43 self.year_combo.grid(row=0, column=1, padx=5, pady=5)
44
45 ttk.Label(date_frame, text="MM:").grid(row=0, column=2, padx=5, pady=5)
46 self.month_var = tk.StringVar()
47 self.month_combo = ttk.Combobox(date_frame, textvariable=self.month_var, values=[str(
48 month) for month in range(1, 13)], state="normal")
49 self.month_combo.grid(row=0, column=3, padx=5, pady=5)
50
51 ttk.Label(date_frame, text="DD:").grid(row=0, column=4, padx=5, pady=5)
52 self.day_var = tk.StringVar()
53 self.day_combo = ttk.Combobox(
54 date_frame, textvariable=self.day_var, values=[str(day) for day in range(1, 32)], state="normal")
55 self.day_combo.grid(row=0, column=5, padx=5, pady=5)
56
57 # time entry fields
58 time_frame = ttk.LabelFrame(self, text="Time (24hr)")
59 time_frame.pack(pady=10, padx=10, fill="x")
60
61 ttk.Label(time_frame, text=" HH:").grid(
62 row=0, column=0, padx=5, pady=5)
63 self.hour_var = tk.StringVar()
64 self.hour_combo = ttk.Combobox(time_frame, textvariable=self.hour_var, values=[str(
65 hour) for hour in range(0, 24)], state="normal")
66 self.hour_combo.grid(row=0, column=1, padx=5, pady=5)
67
68 ttk.Label(time_frame, text="MM:").grid(row=0, column=2, padx=5, pady=5)
69 self.minute_var = tk.StringVar()
70 self.minute_combo = ttk.Combobox(time_frame, textvariable=self.minute_var, values=[str(
71 minute) for minute in range(0, 60)], state="normal")
72 self.minute_combo.grid(row=0, column=3, padx=5, pady=5)
73
74 ttk.Label(time_frame, text="SS:").grid(row=0, column=4, padx=5, pady=5)
75 self.second_var = tk.StringVar()
76 self.second_combo = ttk.Combobox(time_frame, textvariable=self.second_var, values=[str(
77 second) for second in range(0, 60)], state="normal")
78 self.second_combo.grid(row=0, column=5, padx=5, pady=5)
79
80 # timezone entry field
81 timezone_frame = ttk.LabelFrame(self, text="Timezone")
82 timezone_frame.pack(pady=10, padx=10, fill="x")
83
84 ttk.Label(timezone_frame, text="Timezone:").grid(
85 row=0, column=0, padx=5, pady=5)
86 self.timezone_var = tk.StringVar()
87 self.timezone_combo = ttk.Combobox(
88 timezone_frame, textvariable=self.timezone_var, values=pytz.all_timezones, state="normal")
89 self.timezone_combo.grid(row=0, column=1, padx=5, pady=5)
90
91 # set-local-rtc checkbox
92 self.local_rtc_check = ttk.Checkbutton(
93 timezone_frame, text="Set local RTC")
94 self.local_rtc_check.grid(
95 row=1, column=0, columnspan=2, padx=5, pady=5)
96
97 # apply button
98 button_frame = ttk.Frame(self)
99 button_frame.pack(pady=20)
100
101 self.apply_button = ttk.Button(
102 button_frame, text="Apply All", command=self.on_apply_clicked)
103 self.apply_button.grid(row=0, column=0, padx=5)
104
105 self.timezone_apply_button = ttk.Button(
106 button_frame, text="Apply Timezone", command=self.on_timezone_apply_clicked)
107 self.timezone_apply_button.grid(row=0, column=1, padx=5)
108
109 self.date_time_apply_button = ttk.Button(
110 button_frame, text="Apply Date & Time", command=self.on_date_time_apply_clicked)
111 self.date_time_apply_button.grid(row=0, column=2, padx=5)
112
113 self.local_rtc_apply_button = ttk.Button(
114 button_frame, text="Apply Local RTC", command=self.on_local_rtc_apply_clicked)
115 self.local_rtc_apply_button.grid(row=0, column=3, padx=5)
116
117 self.update_apply_button_state()
118
119 # Set trace on all variables to call update_apply_button_state when they change
120 self.year_var.trace_add("write", self.update_apply_button_state)
121 self.month_var.trace_add("write", self.update_apply_button_state)
122 self.day_var.trace_add("write", self.update_apply_button_state)
123 self.hour_var.trace_add("write", self.update_apply_button_state)
124 self.minute_var.trace_add("write", self.update_apply_button_state)
125 self.second_var.trace_add("write", self.update_apply_button_state)
126 self.timezone_var.trace_add("write", self.update_apply_button_state)
127
128 def on_automatic_time_toggled(self):
129 if self.automatic_time_check.instate(['selected']):
130 self.year_combo.state(['disabled'])
131 self.month_combo.state(['disabled'])
132 self.day_combo.state(['disabled'])
133 self.hour_combo.state(['disabled'])
134 self.minute_combo.state(['disabled'])
135 self.second_combo.state(['disabled'])
136 self.timezone_combo.state(['disabled'])
137 else:
138 self.year_combo.state(['!disabled'])
139 self.month_combo.state(['!disabled'])
140 self.day_combo.state(['!disabled'])
141 self.hour_combo.state(['!disabled'])
142 self.minute_combo.state(['!disabled'])
143 self.second_combo.state(['!disabled'])
144 self.timezone_combo.state(['!disabled'])
145 self.update_apply_button_state()
146
147 def update_apply_button_state(self, *_):
148 if self.automatic_time_check.instate(['selected']):
149 self.apply_button.state(['!disabled'])
150 self.timezone_apply_button.state(['disabled'])
151 self.date_time_apply_button.state(['disabled'])
152 self.local_rtc_apply_button.state(['disabled'])
153 else:
154 all_date_filled = all([
155 self.year_combo.get(),
156 self.month_combo.get(),
157 self.day_combo.get(),
158 ])
159 all_time_filled = all([
160 self.hour_combo.get(),
161 self.minute_combo.get(),
162 self.second_combo.get(),
163 ])
164 timezone_filled = self.timezone_combo.get()
165 all_filled = all_date_filled and all_time_filled and timezone_filled
166
167 if all_filled:
168 self.apply_button.state(['!disabled'])
169 else:
170 self.apply_button.state(['disabled'])
171
172 if timezone_filled and self.validate_timezone(timezone_filled):
173 self.timezone_apply_button.state(['!disabled'])
174 else:
175 self.timezone_apply_button.state(['disabled'])
176
177 if all_date_filled and all_time_filled:
178 self.date_time_apply_button.state(['!disabled'])
179 else:
180 self.date_time_apply_button.state(['disabled'])
181
182 self.local_rtc_apply_button.state(['!disabled'])
183
184 def on_apply_clicked(self):
185 automatic_time = self.automatic_time_check.instate(['selected'])
186 local_rtc = self.local_rtc_check.instate(['selected'])
187
188 if automatic_time:
189 subprocess.run(["timedatectl", "set-ntp", "true"])
190
191 try:
192 automatic_timezone_output = subprocess.run(
193 ["curl", "--fail", "https://ipinfo.io/timezone"], capture_output=True, text=True)
194 automatic_timezone = automatic_timezone_output.stdout.strip()
195
196 if automatic_timezone:
197 subprocess.run(
198 ["timedatectl", "set-timezone", automatic_timezone])
199 messagebox.showinfo(
200 "Info", "Automatic date, time, and timezone setting complete.")
201 else:
202 messagebox.showwarning(
203 "Warning", "Automatic date, time, and timezone setting failed. Please try again or use timedatectl.")
204 except requests.RequestException:
205 messagebox.showwarning(
206 "Warning", "Failed to fetch date, time, and timezone information. Please try again or use timedatectl.")
207 else:
208 try:
209 date_input = f"{self.year_combo.get()}-{int(self.month_combo.get()):02d}-{int(self.day_combo.get()):02d}"
210 time_input = f"{int(self.hour_combo.get()):02d}:{int(self.minute_combo.get()):02d}:{int(self.second_combo.get()):02d}"
211 timezone_input = self.timezone_combo.get()
212
213 # check for invalid numbers in fields
214 if not all([
215 self.validate_number(self.year_combo.get(), 1970, 2038),
216 self.validate_number(self.month_combo.get(), 1, 12),
217 self.validate_number(self.day_combo.get(), 1, 31),
218 self.validate_number(self.hour_combo.get(), 0, 23),
219 self.validate_number(self.minute_combo.get(), 0, 59),
220 self.validate_number(self.second_combo.get(), 0, 59),
221 self.validate_timezone(timezone_input)
222 ]):
223 messagebox.showerror(
224 "Error", "Invalid number entered in one or more fields.")
225 return
226
227 subprocess.run(["timedatectl", "set-ntp", "false"])
228 time.sleep(1)
229 subprocess.run(["timedatectl", "set-timezone", timezone_input])
230 time.sleep(1)
231 subprocess.run(["timedatectl", "set-time",
232 f"{date_input} {time_input}"])
233 messagebox.showinfo(
234 "Info", "Manual date, time, and timezone setting complete.")
235 except ValueError as e:
236 messagebox.showerror("Error", f"Error:\n{e}\n\nOne or more blank fields!")
237
238 # handle local rtc setting
239 if local_rtc:
240 subprocess.run(["timedatectl", "set-local-rtc", "1"])
241 else:
242 subprocess.run(["timedatectl", "set-local-rtc", "0"])
243
244 def on_timezone_apply_clicked(self):
245 timezone_input = self.timezone_combo.get()
246
247 if not timezone_input or not self.validate_timezone(timezone_input):
248 messagebox.showerror("Error", "Invalid or empty timezone field.")
249 return
250
251 try:
252 subprocess.run(["timedatectl", "set-timezone", timezone_input])
253 messagebox.showinfo(
254 "Info", "Timezone setting complete.")
255 except subprocess.CalledProcessError as e:
256 messagebox.showerror("Error", f"Error setting timezone:\n{e}")
257
258 def on_date_time_apply_clicked(self):
259 try:
260 date_input = f"{self.year_combo.get()}-{int(self.month_combo.get()):02d}-{int(self.day_combo.get()):02d}"
261 time_input = f"{int(self.hour_combo.get()):02d}:{int(self.minute_combo.get()):02d}:{int(self.second_combo.get()):02d}"
262
263 # check for invalid numbers in fields
264 if not all([
265 self.validate_number(self.year_combo.get(), 1970, 2038),
266 self.validate_number(self.month_combo.get(), 1, 12),
267 self.validate_number(self.day_combo.get(), 1, 31),
268 self.validate_number(self.hour_combo.get(), 0, 23),
269 self.validate_number(self.minute_combo.get(), 0, 59),
270 self.validate_number(self.second_combo.get(), 0, 59),
271 ]):
272 messagebox.showerror(
273 "Error", "Invalid number entered in one or more fields.")
274 return
275
276 subprocess.run(["timedatectl", "set-ntp", "false"])
277 time.sleep(1)
278 subprocess.run(["timedatectl", "set-time", f"{date_input} {time_input}"])
279 messagebox.showinfo(
280 "Info", "Manual date and time setting complete.")
281 except ValueError as e:
282 messagebox.showerror("Error", f"Error:\n{e}\n\nOne or more blank fields!")
283
284 def on_local_rtc_apply_clicked(self):
285 local_rtc = self.local_rtc_check.instate(['selected'])
286
287 if local_rtc:
288 subprocess.run(["timedatectl", "set-local-rtc", "1"])
289 messagebox.showinfo("Info", "Local RTC setting enabled.")
290 else:
291 subprocess.run(["timedatectl", "set-local-rtc", "0"])
292 messagebox.showinfo("Info", "Local RTC setting disabled.")
293
294 def validate_number(self, value, min_val, max_val):
295 try:
296 num = int(value)
297 return min_val <= num <= max_val
298 except ValueError:
299 return False
300
301 def validate_timezone(self, timezone):
302 return timezone in pytz.all_timezones
303
304 def populate_fields(self):
305 now = datetime.datetime.now()
306 self.year_combo.set(now.year)
307 self.month_combo.set(now.month)
308 self.day_combo.set(now.day)
309 self.hour_combo.set(now.hour)
310 self.minute_combo.set(now.minute)
311 self.second_combo.set(now.second)
312
313 try:
314 current_timezone = subprocess.check_output(
315 ["timedatectl", "show", "--property=Timezone"]).decode().strip().split("=")[1]
316 self.timezone_combo.set(current_timezone)
317 except subprocess.CalledProcessError:
318 pass
319
320 try:
321 ntp_status = subprocess.check_output(
322 ["timedatectl", "show", "--property=NTP"]).decode().strip().split("=")[1]
323 if ntp_status == "yes":
324 self.automatic_time_check.state(['selected'])
325 self.on_automatic_time_toggled()
326 else:
327 self.automatic_time_check.state(['!selected'])
328 self.on_automatic_time_toggled()
329 except subprocess.CalledProcessError:
330 pass
331
332 try:
333 local_rtc_status = subprocess.check_output(
334 ["timedatectl", "show", "--property=LocalRTC"]).decode().strip().split("=")[1]
335 if local_rtc_status == "yes":
336 self.local_rtc_check.state(['selected'])
337 else:
338 self.local_rtc_check.state(['!selected'])
339 except subprocess.CalledProcessError:
340 pass
341
342 self.year_combo.bind("<<ComboboxSelected>>",
343 self.update_apply_button_state)
344 self.month_combo.bind("<<ComboboxSelected>>",
345 self.update_apply_button_state)
346 self.day_combo.bind("<<ComboboxSelected>>",
347 self.update_apply_button_state)
348 self.hour_combo.bind("<<ComboboxSelected>>",
349 self.update_apply_button_state)
350 self.minute_combo.bind("<<ComboboxSelected>>",
351 self.update_apply_button_state)
352 self.second_combo.bind("<<ComboboxSelected>>",
353 self.update_apply_button_state)
354 self.timezone_combo.bind(
355 "<<ComboboxSelected>>", self.update_apply_button_state)
356 self.timezone_combo.bind(
357 "<KeyRelease>", self.update_apply_button_state)
358
359
360if __name__ == "__main__":
361 app = DateTimeSetter()
362 app.mainloop()