From 801c4121f4770bbbe2f77bdbe7b570deb67f6c1f Mon Sep 17 00:00:00 2001 From: kj-sh604 Date: Thu, 1 Aug 2024 03:20:35 -0400 Subject: kj-gitbot: dateTimeSetter.py --- dateTimeSetter.py | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 dateTimeSetter.py diff --git a/dateTimeSetter.py b/dateTimeSetter.py new file mode 100644 index 0000000..5fa9fda --- /dev/null +++ b/dateTimeSetter.py @@ -0,0 +1,362 @@ +#!/usr/bin/python + +import tkinter as tk +from tkinter import ttk, messagebox +import subprocess +import requests +import datetime +import pytz +import time + + +class DateTimeSetter(tk.Tk): + def __init__(self): + super().__init__() + + self.title("dateTimeSetter") + self.geometry("640x480") + self.resizable(True, True) + self.attributes("-type", "dialog") # make the window floating + + self.create_widgets() + self.populate_fields() + + def create_widgets(self): + # automatic time setting checkbox + automatic_time_label = ttk.Label( + self, text="Set the date, time, and timezone automatically?") + automatic_time_label.pack(pady=5) + + self.automatic_time_check = ttk.Checkbutton( + self, text="Yes", command=self.on_automatic_time_toggled) + self.automatic_time_check.pack(pady=5) + + # date entry fields + date_frame = ttk.LabelFrame(self, text="Date") + date_frame.pack(pady=10, padx=10, fill="x") + + ttk.Label(date_frame, text="YYYY:").grid( + row=0, column=0, padx=5, pady=5) + self.year_var = tk.StringVar() + self.year_combo = ttk.Combobox(date_frame, textvariable=self.year_var, values=[str( + year) for year in range(1970, 2039)], state="normal") + self.year_combo.grid(row=0, column=1, padx=5, pady=5) + + ttk.Label(date_frame, text="MM:").grid(row=0, column=2, padx=5, pady=5) + self.month_var = tk.StringVar() + self.month_combo = ttk.Combobox(date_frame, textvariable=self.month_var, values=[str( + month) for month in range(1, 13)], state="normal") + self.month_combo.grid(row=0, column=3, padx=5, pady=5) + + ttk.Label(date_frame, text="DD:").grid(row=0, column=4, padx=5, pady=5) + self.day_var = tk.StringVar() + self.day_combo = ttk.Combobox( + date_frame, textvariable=self.day_var, values=[str(day) for day in range(1, 32)], state="normal") + self.day_combo.grid(row=0, column=5, padx=5, pady=5) + + # time entry fields + time_frame = ttk.LabelFrame(self, text="Time (24hr)") + time_frame.pack(pady=10, padx=10, fill="x") + + ttk.Label(time_frame, text=" HH:").grid( + row=0, column=0, padx=5, pady=5) + self.hour_var = tk.StringVar() + self.hour_combo = ttk.Combobox(time_frame, textvariable=self.hour_var, values=[str( + hour) for hour in range(0, 24)], state="normal") + self.hour_combo.grid(row=0, column=1, padx=5, pady=5) + + ttk.Label(time_frame, text="MM:").grid(row=0, column=2, padx=5, pady=5) + self.minute_var = tk.StringVar() + self.minute_combo = ttk.Combobox(time_frame, textvariable=self.minute_var, values=[str( + minute) for minute in range(0, 60)], state="normal") + self.minute_combo.grid(row=0, column=3, padx=5, pady=5) + + ttk.Label(time_frame, text="SS:").grid(row=0, column=4, padx=5, pady=5) + self.second_var = tk.StringVar() + self.second_combo = ttk.Combobox(time_frame, textvariable=self.second_var, values=[str( + second) for second in range(0, 60)], state="normal") + self.second_combo.grid(row=0, column=5, padx=5, pady=5) + + # timezone entry field + timezone_frame = ttk.LabelFrame(self, text="Timezone") + timezone_frame.pack(pady=10, padx=10, fill="x") + + ttk.Label(timezone_frame, text="Timezone:").grid( + row=0, column=0, padx=5, pady=5) + self.timezone_var = tk.StringVar() + self.timezone_combo = ttk.Combobox( + timezone_frame, textvariable=self.timezone_var, values=pytz.all_timezones, state="normal") + self.timezone_combo.grid(row=0, column=1, padx=5, pady=5) + + # set-local-rtc checkbox + self.local_rtc_check = ttk.Checkbutton( + timezone_frame, text="Set local RTC") + self.local_rtc_check.grid( + row=1, column=0, columnspan=2, padx=5, pady=5) + + # apply button + button_frame = ttk.Frame(self) + button_frame.pack(pady=20) + + self.apply_button = ttk.Button( + button_frame, text="Apply All", command=self.on_apply_clicked) + self.apply_button.grid(row=0, column=0, padx=5) + + self.timezone_apply_button = ttk.Button( + button_frame, text="Apply Timezone", command=self.on_timezone_apply_clicked) + self.timezone_apply_button.grid(row=0, column=1, padx=5) + + self.date_time_apply_button = ttk.Button( + button_frame, text="Apply Date & Time", command=self.on_date_time_apply_clicked) + self.date_time_apply_button.grid(row=0, column=2, padx=5) + + self.local_rtc_apply_button = ttk.Button( + button_frame, text="Apply Local RTC", command=self.on_local_rtc_apply_clicked) + self.local_rtc_apply_button.grid(row=0, column=3, padx=5) + + self.update_apply_button_state() + + # Set trace on all variables to call update_apply_button_state when they change + self.year_var.trace_add("write", self.update_apply_button_state) + self.month_var.trace_add("write", self.update_apply_button_state) + self.day_var.trace_add("write", self.update_apply_button_state) + self.hour_var.trace_add("write", self.update_apply_button_state) + self.minute_var.trace_add("write", self.update_apply_button_state) + self.second_var.trace_add("write", self.update_apply_button_state) + self.timezone_var.trace_add("write", self.update_apply_button_state) + + def on_automatic_time_toggled(self): + if self.automatic_time_check.instate(['selected']): + self.year_combo.state(['disabled']) + self.month_combo.state(['disabled']) + self.day_combo.state(['disabled']) + self.hour_combo.state(['disabled']) + self.minute_combo.state(['disabled']) + self.second_combo.state(['disabled']) + self.timezone_combo.state(['disabled']) + else: + self.year_combo.state(['!disabled']) + self.month_combo.state(['!disabled']) + self.day_combo.state(['!disabled']) + self.hour_combo.state(['!disabled']) + self.minute_combo.state(['!disabled']) + self.second_combo.state(['!disabled']) + self.timezone_combo.state(['!disabled']) + self.update_apply_button_state() + + def update_apply_button_state(self, *_): + if self.automatic_time_check.instate(['selected']): + self.apply_button.state(['!disabled']) + self.timezone_apply_button.state(['disabled']) + self.date_time_apply_button.state(['disabled']) + self.local_rtc_apply_button.state(['disabled']) + else: + all_date_filled = all([ + self.year_combo.get(), + self.month_combo.get(), + self.day_combo.get(), + ]) + all_time_filled = all([ + self.hour_combo.get(), + self.minute_combo.get(), + self.second_combo.get(), + ]) + timezone_filled = self.timezone_combo.get() + all_filled = all_date_filled and all_time_filled and timezone_filled + + if all_filled: + self.apply_button.state(['!disabled']) + else: + self.apply_button.state(['disabled']) + + if timezone_filled and self.validate_timezone(timezone_filled): + self.timezone_apply_button.state(['!disabled']) + else: + self.timezone_apply_button.state(['disabled']) + + if all_date_filled and all_time_filled: + self.date_time_apply_button.state(['!disabled']) + else: + self.date_time_apply_button.state(['disabled']) + + self.local_rtc_apply_button.state(['!disabled']) + + def on_apply_clicked(self): + automatic_time = self.automatic_time_check.instate(['selected']) + local_rtc = self.local_rtc_check.instate(['selected']) + + if automatic_time: + subprocess.run(["timedatectl", "set-ntp", "true"]) + + try: + automatic_timezone_output = subprocess.run( + ["curl", "--fail", "https://ipinfo.io/timezone"], capture_output=True, text=True) + automatic_timezone = automatic_timezone_output.stdout.strip() + + if automatic_timezone: + subprocess.run( + ["timedatectl", "set-timezone", automatic_timezone]) + messagebox.showinfo( + "Info", "Automatic date, time, and timezone setting complete.") + else: + messagebox.showwarning( + "Warning", "Automatic date, time, and timezone setting failed. Please try again or use timedatectl.") + except requests.RequestException: + messagebox.showwarning( + "Warning", "Failed to fetch date, time, and timezone information. Please try again or use timedatectl.") + else: + try: + date_input = f"{self.year_combo.get()}-{int(self.month_combo.get()):02d}-{int(self.day_combo.get()):02d}" + time_input = f"{int(self.hour_combo.get()):02d}:{int(self.minute_combo.get()):02d}:{int(self.second_combo.get()):02d}" + timezone_input = self.timezone_combo.get() + + # check for invalid numbers in fields + if not all([ + self.validate_number(self.year_combo.get(), 1970, 2038), + self.validate_number(self.month_combo.get(), 1, 12), + self.validate_number(self.day_combo.get(), 1, 31), + self.validate_number(self.hour_combo.get(), 0, 23), + self.validate_number(self.minute_combo.get(), 0, 59), + self.validate_number(self.second_combo.get(), 0, 59), + self.validate_timezone(timezone_input) + ]): + messagebox.showerror( + "Error", "Invalid number entered in one or more fields.") + return + + subprocess.run(["timedatectl", "set-ntp", "false"]) + time.sleep(1) + subprocess.run(["timedatectl", "set-timezone", timezone_input]) + time.sleep(1) + subprocess.run(["timedatectl", "set-time", + f"{date_input} {time_input}"]) + messagebox.showinfo( + "Info", "Manual date, time, and timezone setting complete.") + except ValueError as e: + messagebox.showerror("Error", f"Error:\n{e}\n\nOne or more blank fields!") + + # handle local rtc setting + if local_rtc: + subprocess.run(["timedatectl", "set-local-rtc", "1"]) + else: + subprocess.run(["timedatectl", "set-local-rtc", "0"]) + + def on_timezone_apply_clicked(self): + timezone_input = self.timezone_combo.get() + + if not timezone_input or not self.validate_timezone(timezone_input): + messagebox.showerror("Error", "Invalid or empty timezone field.") + return + + try: + subprocess.run(["timedatectl", "set-timezone", timezone_input]) + messagebox.showinfo( + "Info", "Timezone setting complete.") + except subprocess.CalledProcessError as e: + messagebox.showerror("Error", f"Error setting timezone:\n{e}") + + def on_date_time_apply_clicked(self): + try: + date_input = f"{self.year_combo.get()}-{int(self.month_combo.get()):02d}-{int(self.day_combo.get()):02d}" + time_input = f"{int(self.hour_combo.get()):02d}:{int(self.minute_combo.get()):02d}:{int(self.second_combo.get()):02d}" + + # check for invalid numbers in fields + if not all([ + self.validate_number(self.year_combo.get(), 1970, 2038), + self.validate_number(self.month_combo.get(), 1, 12), + self.validate_number(self.day_combo.get(), 1, 31), + self.validate_number(self.hour_combo.get(), 0, 23), + self.validate_number(self.minute_combo.get(), 0, 59), + self.validate_number(self.second_combo.get(), 0, 59), + ]): + messagebox.showerror( + "Error", "Invalid number entered in one or more fields.") + return + + subprocess.run(["timedatectl", "set-ntp", "false"]) + time.sleep(1) + subprocess.run(["timedatectl", "set-time", f"{date_input} {time_input}"]) + messagebox.showinfo( + "Info", "Manual date and time setting complete.") + except ValueError as e: + messagebox.showerror("Error", f"Error:\n{e}\n\nOne or more blank fields!") + + def on_local_rtc_apply_clicked(self): + local_rtc = self.local_rtc_check.instate(['selected']) + + if local_rtc: + subprocess.run(["timedatectl", "set-local-rtc", "1"]) + messagebox.showinfo("Info", "Local RTC setting enabled.") + else: + subprocess.run(["timedatectl", "set-local-rtc", "0"]) + messagebox.showinfo("Info", "Local RTC setting disabled.") + + def validate_number(self, value, min_val, max_val): + try: + num = int(value) + return min_val <= num <= max_val + except ValueError: + return False + + def validate_timezone(self, timezone): + return timezone in pytz.all_timezones + + def populate_fields(self): + now = datetime.datetime.now() + self.year_combo.set(now.year) + self.month_combo.set(now.month) + self.day_combo.set(now.day) + self.hour_combo.set(now.hour) + self.minute_combo.set(now.minute) + self.second_combo.set(now.second) + + try: + current_timezone = subprocess.check_output( + ["timedatectl", "show", "--property=Timezone"]).decode().strip().split("=")[1] + self.timezone_combo.set(current_timezone) + except subprocess.CalledProcessError: + pass + + try: + ntp_status = subprocess.check_output( + ["timedatectl", "show", "--property=NTP"]).decode().strip().split("=")[1] + if ntp_status == "yes": + self.automatic_time_check.state(['selected']) + self.on_automatic_time_toggled() + else: + self.automatic_time_check.state(['!selected']) + self.on_automatic_time_toggled() + except subprocess.CalledProcessError: + pass + + try: + local_rtc_status = subprocess.check_output( + ["timedatectl", "show", "--property=LocalRTC"]).decode().strip().split("=")[1] + if local_rtc_status == "yes": + self.local_rtc_check.state(['selected']) + else: + self.local_rtc_check.state(['!selected']) + except subprocess.CalledProcessError: + pass + + self.year_combo.bind("<>", + self.update_apply_button_state) + self.month_combo.bind("<>", + self.update_apply_button_state) + self.day_combo.bind("<>", + self.update_apply_button_state) + self.hour_combo.bind("<>", + self.update_apply_button_state) + self.minute_combo.bind("<>", + self.update_apply_button_state) + self.second_combo.bind("<>", + self.update_apply_button_state) + self.timezone_combo.bind( + "<>", self.update_apply_button_state) + self.timezone_combo.bind( + "", self.update_apply_button_state) + + +if __name__ == "__main__": + app = DateTimeSetter() + app.mainloop() -- cgit v1.2.3