diff --git a/Under Construction/Date and Time.app/Date and Time b/Under Construction/Date and Time.app/Date and Time
new file mode 100755
index 000000000..d873bfbab
--- /dev/null
+++ b/Under Construction/Date and Time.app/Date and Time
@@ -0,0 +1,6 @@
+#!/bin/sh
+HERE="$(dirname "$(readlink -f "${0}")")"
+#SUDO_EDITOR_TMP=$HERE | sed 's/\ /\\ /g'
+#export VISUAL="${SUDO_EDITOR_TMP}/Resources/date_and_time.py"
+#exec "sudoedit" "/etc/timezone" "/etc/ntpd.conf"
+exec "${HERE}/Resources/date_and_time.py" "$@"
\ No newline at end of file
diff --git a/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations.png b/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations.png
new file mode 100644
index 000000000..661073113
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations.png differ
diff --git a/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations_disable.png b/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations_disable.png
new file mode 100644
index 000000000..cab31a7ae
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/1280px-World_map_with_nations_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations.png b/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations.png
new file mode 100644
index 000000000..f7ab19330
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations.png differ
diff --git a/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations_disable.png b/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations_disable.png
new file mode 100644
index 000000000..47f1aae85
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/2560px-World_map_with_nations_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations.png b/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations.png
new file mode 100644
index 000000000..491fcb1a7
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations.png differ
diff --git a/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations_disable.png b/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations_disable.png
new file mode 100644
index 000000000..57c661816
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/320px-World_map_with_nations_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations.png b/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations.png
new file mode 100644
index 000000000..a14c97f3f
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations.png differ
diff --git a/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations_disable.png b/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations_disable.png
new file mode 100644
index 000000000..504521ebc
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/640px-World_map_with_nations_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/Date and Time.png b/Under Construction/Date and Time.app/Resources/Date and Time.png
new file mode 100644
index 000000000..e51e25e2d
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/Date and Time.png differ
diff --git a/Under Construction/Date and Time.app/Resources/LICENSE b/Under Construction/Date and Time.app/Resources/LICENSE
new file mode 100644
index 000000000..e1b0eeb29
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2023, Jérôme Ornech alias Hierosme
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Under Construction/Date and Time.app/Resources/clock.state b/Under Construction/Date and Time.app/Resources/clock.state
new file mode 100644
index 000000000..2f4f85975
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/clock.state
@@ -0,0 +1,61 @@
+general back scale 200
+general back colour rgba(255, 255, 255, 0)
+general back faceColour rgba(255, 255, 255, 1)
+general back edgeColour rgba(0, 0, 0, 1)
+general back edgeWidth 2
+general markers displayTicks on
+general markers tickColour rgba(35, 69, 117, 1)
+general markers tickLength 0.06
+general markers tickThickness 7
+general markers displaySubticks on
+general markers subtickColour rgba(35, 69, 117, 1)
+general markers subtickLength 0.04
+general markers subtickThickness 7
+num inner display on
+num inner type latin
+num inner order numeric
+num inner pos 0.68
+num inner rotation none
+num inner font Nimbus Sans
+num inner fontWeight bold
+num inner fontStyle
+num inner fontSize 21
+num inner colour rgba(52, 105, 158, 1)
+num minute display
+num minute interval 5
+num minute pos 0.5
+num minute rotation none
+num minute font Tahoma
+num minute fontWeight bold
+num minute fontStyle
+num minute fontSize 8
+num minute colour rgba(0, 0, 0, 1)
+hand hour display
+hand hour value 10
+hand hour lineShape rectangle
+hand hour arrow 0
+hand hour colour rgba(53, 76, 112, 1)
+hand hour length 0.5
+hand hour backLength 17
+hand hour lineWidth 0.105
+hand minute display
+hand minute value 9
+hand minute lineShape rectangle
+hand minute arrow 0
+hand minute colour rgba(53, 76, 112, 1)
+hand minute length 0.85
+hand minute backLength 18
+hand minute lineWidth 0.05
+hand second display
+hand second value 0
+hand second lineShape rectangle
+hand second arrow 3
+hand second colour rgba(255, 162, 0, 1)
+hand second length 0.95
+hand second backLength 17
+hand second lineWidth 0.04
+middle circle display
+middle circle radius 10
+middle circle borderWidth 3.3
+middle circle backgroundColour rgba(53, 76, 112, 1)
+middle circle borderColour rgba(53, 76, 112, 1)
\ No newline at end of file
diff --git a/Under Construction/Date and Time.app/Resources/clock_face.png b/Under Construction/Date and Time.app/Resources/clock_face.png
new file mode 100644
index 000000000..f3ff6acf7
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/clock_face.png differ
diff --git a/Under Construction/Date and Time.app/Resources/clock_face2.png b/Under Construction/Date and Time.app/Resources/clock_face2.png
new file mode 100644
index 000000000..f591b6792
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/clock_face2.png differ
diff --git a/Under Construction/Date and Time.app/Resources/clock_face2_disable.png b/Under Construction/Date and Time.app/Resources/clock_face2_disable.png
new file mode 100644
index 000000000..d2990c581
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/clock_face2_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/clock_face_disable.png b/Under Construction/Date and Time.app/Resources/clock_face_disable.png
new file mode 100644
index 000000000..37da53de0
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/clock_face_disable.png differ
diff --git a/Under Construction/Date and Time.app/Resources/date_and_time.py b/Under Construction/Date and Time.app/Resources/date_and_time.py
new file mode 100755
index 000000000..03d40117e
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/date_and_time.py
@@ -0,0 +1,588 @@
+#!/usr/bin/env python3
+
+# FIXME: The spinner is not shown; we need to fix this by using threading? Is there a better way?
+
+import subprocess
+import sys
+import os
+
+from PyQt5.QtCore import Qt, QDateTime, QTimer, QDate, QUrl, QEvent, QByteArray, QSettings, QThread, QThreadPool
+from PyQt5.QtWidgets import QApplication, QDateTimeEdit, QGridLayout, QGroupBox
+from PyQt5.QtWidgets import QMainWindow, QMessageBox, QPushButton, QVBoxLayout, QWidget
+from PyQt5.QtGui import QKeyEvent, QPixmap
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
+
+from date_and_time_ui import Ui_MainWindow
+from property_date_time_auto import DateTimeAutomatically
+from property_timezone import TimeZoneProperty
+from worker_ntp_client import NtpClientWorker
+from worker_ip_api import IpApiWorker
+
+
+class DateTimeWindow(QMainWindow, Ui_MainWindow, DateTimeAutomatically, TimeZoneProperty):
+
+ def __init__(self):
+ super().__init__()
+ DateTimeAutomatically.__init__(self)
+ TimeZoneProperty.__init__(self)
+
+ self.initialized = False
+ # Worker
+ self.threads_worker_ntp_client = []
+ self.threads_ip_api = []
+ self.threadpool = QThreadPool()
+
+ self.ntp_client_request_count = 0
+ self.ntp_client_request_count_max = 1
+
+ self.error_dialog = None
+ self.timer = None
+
+ self.system_date = None
+ self.system_time = None
+ self.system_timezone = None
+
+ self.date = None
+ self.time = None
+
+ self.timezone_file_path = None
+ self.timezone_file = None
+
+ self.settings = None
+
+ self.__ntp_servers = {
+ 0: "africa.pool.ntp.org",
+ 1: "antarctica.pool.ntp.org",
+ 2: "asia.pool.ntp.org",
+ 3: "europe.pool.ntp.org",
+ 4: "north-america.pool.ntp.org",
+ 5: "oceania.pool.ntp.org",
+ 6: "south-america.pool.ntp.org",
+ }
+
+ self.timer = QTimer()
+ self.timer_ntp_client = QTimer()
+
+ self.setupUi(self)
+ self.initial_state()
+ self.signalsConnect()
+ self.read_settings()
+ self.initialized = True
+
+ def initial_state(self):
+ self.timer.start(1000)
+ self.timer_ntp_client.start(5000)
+
+ self.dat_timeedit_widget.setDateTime(self.get_current_datetime())
+ self.dat_dateedit_widget.setDate(self.get_current_date())
+
+ self.__timezone_closest_city_changed([self.get_timezone_file_content()])
+
+ self.settings = QSettings("helloSystem", "Date and Time.app")
+
+ def signalsConnect(self):
+ self.timer.timeout.connect(self.refresh)
+ self.timer_ntp_client.timeout.connect(self.refresh_ntp_client)
+
+ self.actionHelpAbout.triggered.connect(self.show_about)
+ self.DateTimeAutomaticallyChanged.connect(self.__checkbox_set_date_and_time_automatically_changed)
+ self.date_and_time_auto_checkbox.toggled.connect(self.__checkbox_set_date_and_time_automatically_changed)
+
+ # Date and Time
+ self.dat_calendar_widget.selectionChanged.connect(self.__dat_calendar_widget_changed)
+ self.dat_dateedit_widget.dateTimeChanged.connect(self.__dat_dateedit_widget_changed)
+ self.dat_timeedit_widget.dateTimeChanged.connect(self.__dat_timeedit_widget_changed)
+ self.ntp_servers_comboBox.currentIndexChanged.connect(self.__ntp_servers_comboBox_index_changed)
+
+ # Time Zone
+ self.tz_closest_city_combobox.currentIndexChanged.connect(self.__timezone_combobox_index_changed)
+ self.tz_time_zone_world_map_widget.TimeZoneClosestCityChanged.connect(self.__timezone_closest_city_changed)
+ self.checkbox_set_time_zone_automatically_using_current_location.toggled.connect(
+ self.__checkbox_set_time_zone_automatically_using_current_location_changed
+ )
+
+ # Undo, cut, copy, paste, and delete actions. Undo is disabled.
+ # They have the default shortcuts. When invoked, the corresponding
+ # shortcut is sent to the currently focused widget.
+ self.action_cut.triggered.connect(lambda: self.send_shortcut(Qt.Key_X, Qt.ControlModifier))
+ self.action_copy.triggered.connect(lambda: self.send_shortcut(Qt.Key_C, Qt.ControlModifier))
+ self.action_paste.triggered.connect(lambda: self.send_shortcut(Qt.Key_V, Qt.ControlModifier))
+ self.action_delete.triggered.connect(lambda: self.send_shortcut(Qt.Key_Delete, Qt.NoModifier))
+ self.action_set_date_and_time_automatically.changed.connect(
+ self.__action_set_date_and_time_automatically_changed
+ )
+ self.action_set_time_zone_automatically.changed.connect(self.__action_set_time_zone_automatically_changed)
+
+ def createNtpClientThread(self):
+ thread = QThread()
+ worker = NtpClientWorker(
+ host=self.__ntp_servers[self.ntp_servers_comboBox.currentIndex()],
+ version=3,
+ port=123,
+ timeout=5,
+ )
+ worker.moveToThread(thread)
+ thread.started.connect(lambda: worker.refresh())
+
+ # Icons Cache
+ worker.updated_datetime.connect(self.__worker_ntp_client_send_updated_datetime)
+ worker.updated_date.connect(self.__worker_ntp_client_send_updated_date)
+ worker.error.connect(self.__worker_ntp_client_send_error)
+
+ worker.finished.connect(thread.quit)
+ worker.finished.connect(worker.deleteLater)
+ thread.finished.connect(thread.deleteLater)
+
+ return thread
+
+ def createIpApiThread(self):
+ thread = QThread()
+ worker = IpApiWorker()
+ worker.moveToThread(thread)
+ thread.started.connect(lambda: worker.refresh())
+
+ # Icons Cache
+ worker.updated_timezone.connect(self.__worker_ip_api_send_updated_timezone)
+ worker.error.connect(self.__worker_ip_api_send_error)
+
+ worker.finished.connect(thread.quit)
+ worker.finished.connect(worker.deleteLater)
+ thread.finished.connect(thread.deleteLater)
+
+ return thread
+
+ def refresh_ntp_client(self):
+ # if self.ntp_client_request_count > self.ntp_client_request_count_max:
+ # self.error_message_label.setText("")
+ if self.date_and_time_auto_checkbox.isChecked():
+ if self.ntp_client_request_count <= self.ntp_client_request_count_max:
+ self.error_message_label.setText(
+ f""
+ f"
"
+ f""
+ f""
+ f"NTP request ({self.ntp_client_request_count}/{self.ntp_client_request_count_max}) "
+ f"{self.__ntp_servers[self.ntp_servers_comboBox.currentIndex()]} ..."
+ f""
+ f"
"
+ f""
+ f""
+ )
+ elif self.ntp_client_request_count > self.ntp_client_request_count_max:
+ self.error_message_label.setText(
+ f""
+ f""
+ f""
+ f""
+ f" "
+ f""
+ f"
"
+ f""
+ f""
+ )
+ self.threads_worker_ntp_client.clear()
+ self.threads_worker_ntp_client = [
+ self.createNtpClientThread(),
+ ]
+ for thread in self.threads_worker_ntp_client:
+ thread.start()
+
+ if not self.ntp_client_request_count > self.ntp_client_request_count_max:
+ self.ntp_client_request_count += 1
+
+ def refresh(self):
+ self.dat_timeedit_widget.setDateTime(self.dat_timeedit_widget.dateTime().addSecs(1))
+ # self.dat_date_widget.setDate(self.dat_date_widget.date().addSecs(1))
+ self.dat_clock_widget.time = self.dat_timeedit_widget.time()
+
+ # # Help menu
+ # self.help_menu = self.menuBar().addMenu("Help")
+ # about_action = self.help_menu.addAction("About", self.show_about)
+ # about_action.setShortcut("Ctrl+?")
+ #
+ # # Create spinner
+ # self.spinner = QLabel(self)
+ # # Generated using http://ajaxload.info/
+ # self.spinner.setMovie(
+ # QMovie(os.path.dirname(__file__) + "/Resources/spinner.gif"))
+ # self.spinner.movie().start()
+ # self.spinner.hide()
+ # self.spinner.setAlignment(Qt.AlignCenter)
+ # # self.spinner.setFixedSize(16, 16)
+ # self.spinner.move(0, self.height() - self.spinner.height())
+ # # Connect to the resizeEvent of the window to update the position of the spinner
+ # self.resizeEvent = lambda event: self.spinner.move(
+ # -20, self.height() - self.spinner.height() - 10)
+ if not self.timer.isActive():
+ self.timer.start()
+
+ def send_shortcut(self, key_code, modifier):
+ # Send shortcut to currently focused widget like Ctrl+C, Ctrl+V, etc.
+ widget = self.focusWidget()
+ key_event = QKeyEvent(QEvent.KeyPress, key_code, modifier)
+ QApplication.postEvent(widget, key_event)
+
+ def update_date_time(self):
+ self.dt_set_date_time_manual.setDateTime(self.get_current_datetime())
+ self.dt_set_date_manual.setDateTime(self.get_current_date())
+
+ @staticmethod
+ def get_current_datetime():
+ return QDateTime.currentDateTime()
+
+ @staticmethod
+ def get_current_date():
+ return QDate.currentDate()
+
+ def set_date_time_manual(self):
+ self.spinner.show()
+ # 202304151224.10 = Sa. 15 Apr. 2023 12:24:10 CEST
+ new_date_time = self.dt_set_date_time_manual.dateTime().toString("yyyyMMddhhmm.ss")
+ try:
+ # Set the new date and time
+ subprocess.run(["sudo", "date", new_date_time], check=True)
+ self.dt_set_date_time_manual.setDateTime(self.get_current_datetime())
+ except subprocess.CalledProcessError as e:
+ self.show_error_dialog(
+ f"Error setting Date and Time "
+ f"An error occurred while setting the Date and Time: {e.stderr}"
+ )
+ self.spinner.hide()
+
+ def set_time_zone_auto(self):
+ self.threads_ip_api.clear()
+ self.threads_ip_api = [
+ self.createIpApiThread(),
+ ]
+ for thread in self.threads_ip_api:
+ thread.start()
+
+ # def handleResponse(reply):
+ # er = reply.error()
+ #
+ # if er == QNetworkReply.NoError:
+ # self.setTimeZone(bytes(reply.readAll()).decode("utf-8").strip("\n"))
+ #
+ # self.tz_closest_city_combobox.clear()
+ # self.tz_closest_city_combobox.addItem(self.TimeZone)
+ # self.__timezone_combobox_index_changed()
+ #
+ # else:
+ # self.show_error_dialog(
+ # message=f"Error occurred: {er}
{'
'.join(reply.errorString().split(' - '))}"
+ # )
+ # self.checkbox_set_time_zone_automatically_using_current_location.setChecked(False)
+ #
+ # if self.checkbox_set_time_zone_automatically_using_current_location.isChecked():
+ # req = QNetworkRequest(QUrl("http://ip-api.com/line?fields=timezone"))
+ #
+ # self.nam = QNetworkAccessManager()
+ # self.nam.finished.connect(handleResponse)
+ # self.nam.get(req)
+
+ # TODO: Set language, keyboard,, etc. automatically based on geolocation if user allows
+
+ def get_timezone_file_content(self):
+ try:
+ return self.TimeZone
+ except IOError as error:
+ self.show_error_dialog("Problem reading file %s" % self.timezone_file_path)
+
+ @staticmethod
+ def show_error_dialog(message):
+ msg_box = QMessageBox()
+ msg_box.setIcon(QMessageBox.Critical)
+ msg_box.setWindowTitle(" ")
+ msg_box.setText(message)
+ msg_box.exec_()
+
+ @staticmethod
+ def show_about():
+ msg = QMessageBox()
+ msg.setWindowTitle("About")
+ msg.setIconPixmap(
+ QPixmap(os.path.join(os.path.dirname(__file__), "Date and Time.png")).scaled(
+ 64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation
+ )
+ )
+
+ candidates = ["COPYRIGHT", "COPYING", "LICENSE"]
+ for candidate in candidates:
+ if os.path.exists(os.path.join(os.path.dirname(__file__), candidate)):
+ with open(os.path.join(os.path.dirname(__file__), candidate), "r") as file:
+ data = file.read()
+ msg.setDetailedText(data)
+ msg.setText("Date and Time
")
+ msg.setInformativeText(
+ "A simple preferences application to set date and time using "
+ "ntplib "
+ "and date
"
+ "Visit "
+ ""
+ "https://github.com/helloSystem/Utilities/ "
+ "for more information or to report bug and/or suggest a new feature."
+ )
+ msg.exec()
+
+ def __checkbox_set_date_and_time_automatically_changed(self):
+ if self.date_and_time_auto_checkbox.isChecked():
+ self.ntp_servers_comboBox.setEnabled(True)
+
+ self.dat_timeedit_widget.setEnabled(False)
+ self.dat_clock_widget.setEnabled(False)
+
+ self.dat_dateedit_widget.setEnabled(False)
+ self.dat_calendar_widget.setEnabled(False)
+
+ self.ntp_client_request_count = 1
+
+ # Prevent loop with the action menu
+ if not self.action_set_date_and_time_automatically.isChecked():
+ self.action_set_date_and_time_automatically.setChecked(True)
+
+ else:
+ self.ntp_servers_comboBox.setEnabled(False)
+
+ self.dat_timeedit_widget.setEnabled(True)
+ self.dat_clock_widget.setEnabled(True)
+
+ self.dat_dateedit_widget.setEnabled(True)
+ self.dat_calendar_widget.setEnabled(True)
+
+ # Prevent loop with the action menu
+ if self.action_set_date_and_time_automatically.isChecked():
+ self.action_set_date_and_time_automatically.setChecked(False)
+
+ def __action_set_date_and_time_automatically_changed(self):
+ if self.action_set_date_and_time_automatically.isChecked():
+
+ # Prevent loop with the action menu
+ if not self.date_and_time_auto_checkbox.isChecked():
+ self.date_and_time_auto_checkbox.setChecked(True)
+ else:
+
+ # Prevent loop with the action menu
+ if self.date_and_time_auto_checkbox.isChecked():
+ self.date_and_time_auto_checkbox.setChecked(False)
+
+ def __checkbox_set_time_zone_automatically_using_current_location_changed(self):
+ if self.checkbox_set_time_zone_automatically_using_current_location.isChecked():
+ self.tz_closest_city_label.setEnabled(False)
+ self.tz_closest_city_combobox.setEnabled(False)
+ self.tz_time_zone_world_map_widget.setEnabled(False)
+ self.tz_time_zone_label.setEnabled(False)
+ self.tz_time_zone_value.setEnabled(False)
+
+ # Prevent loop with the action menu
+ if not self.action_set_time_zone_automatically.isChecked():
+ self.action_set_time_zone_automatically.setChecked(True)
+
+ else:
+ self.tz_closest_city_label.setEnabled(True)
+ self.tz_closest_city_combobox.setEnabled(True)
+ self.tz_time_zone_world_map_widget.setEnabled(True)
+ self.tz_time_zone_label.setEnabled(True)
+ self.tz_time_zone_value.setEnabled(True)
+
+ # Prevent loop with the action menu
+ if self.action_set_time_zone_automatically.isChecked():
+ self.action_set_time_zone_automatically.setChecked(False)
+
+ found_the_city_in_zone1970_db = False
+ lng = 0
+ lat = 0
+ for key, value in self.tz_time_zone_world_map_widget.zone1970_db.items():
+ if key == self.TimeZone:
+ found_the_city_in_zone1970_db = True
+ lng = float(value["longitude"])
+ lat = float(value["latitude"])
+
+ if found_the_city_in_zone1970_db:
+ tol = 0
+ found = False
+ closest = []
+ while not found:
+ for key, item in self.tz_time_zone_world_map_widget.zone1970_db.items():
+ if (
+ lng - tol <= item["longitude"] <= lng + tol
+ and lat - tol <= item["latitude"] <= lat + tol
+ ):
+ if key not in closest and not len(closest) > 15:
+ closest.append(key)
+
+ if len(closest) > 15:
+ found = True
+ else:
+ tol += 0.1
+
+ if closest and len(closest) >= 1:
+ self.__timezone_closest_city_changed(closest)
+
+ def __action_set_time_zone_automatically_changed(self):
+ if self.action_set_time_zone_automatically.isChecked():
+ # Prevent loop with the action menu
+ if not self.checkbox_set_time_zone_automatically_using_current_location.isChecked():
+ self.set_time_zone_automatically_checkbox.setChecked(True)
+ self.set_time_zone_auto()
+ else:
+ # Prevent loop with the action menu
+ if self.checkbox_set_time_zone_automatically_using_current_location.isChecked():
+ self.set_time_zone_automatically_checkbox.setChecked(False)
+
+ # Store the setting
+ self.settings.setValue(
+ "checkbox_set_time_zone_automatically_using_current_location",
+ self.checkbox_set_time_zone_automatically_using_current_location.isChecked(),
+ )
+
+ def __dat_calendar_widget_changed(self):
+ self.dat_dateedit_widget.setDate(self.dat_calendar_widget.selectedDate())
+
+ def __dat_dateedit_widget_changed(self):
+ self.dat_calendar_widget.setSelectedDate(self.dat_dateedit_widget.date())
+
+ def __dat_timeedit_widget_changed(self):
+ pass
+ # self.dat_clock_widget.setTime(self.dat_timeedit_widget.time())
+
+ def __ntp_servers_comboBox_index_changed(self):
+ self.ntp_client_request_count = 1
+ if self.initialized:
+ self.refresh_ntp_client()
+
+ def __timezone_closest_city_changed(self, value):
+ self.tz_closest_city_combobox.clear()
+ self.tz_closest_city_combobox.addItems(sorted(value))
+ index = self.tz_closest_city_combobox.findText(self.TimeZone, Qt.MatchFixedString)
+ if index >= 0:
+ self.tz_closest_city_combobox.setCurrentIndex(index)
+ self.__timezone_combobox_index_changed()
+
+ def __timezone_combobox_index_changed(self):
+ if self.tz_closest_city_combobox.currentText():
+ code = ", ".join(
+ self.tz_time_zone_world_map_widget.zone1970_db[self.tz_closest_city_combobox.currentText()]["code"]
+ )
+
+ if (
+ "comments"
+ in self.tz_time_zone_world_map_widget.zone1970_db[self.tz_closest_city_combobox.currentText()]
+ ):
+ comments = self.tz_time_zone_world_map_widget.zone1970_db[self.tz_closest_city_combobox.currentText()][
+ "comments"
+ ]
+ self.tz_time_zone_value.setText(f"{code} - {comments}")
+ else:
+ self.tz_time_zone_value.setText(f"{code}")
+
+ def __worker_ntp_client_send_updated_date(self, qdate):
+ if isinstance(qdate, QDate):
+ self.dat_dateedit_widget.setDate(qdate)
+
+ def __worker_ntp_client_send_updated_datetime(self, qdatetime):
+ if isinstance(qdatetime, QDateTime):
+ self.dat_timeedit_widget.setDateTime(qdatetime)
+
+ def __worker_ntp_client_send_error(self, error):
+ self.error_message_label.setText(error)
+
+ def __worker_ip_api_send_updated_timezone(self, timezone):
+ self.setTimeZone(timezone)
+ self.tz_closest_city_combobox.clear()
+ self.tz_closest_city_combobox.addItem(self.TimeZone)
+ self.__timezone_combobox_index_changed()
+ self.checkbox_set_time_zone_automatically_using_current_location.setChecked(False)
+
+ def __worker_ip_api_send_error(self, error):
+ self.error_message_label.setText(error)
+
+ def closeEvent(self, event):
+ self.write_settings()
+ super(DateTimeWindow, self).closeEvent(event)
+ event.accept()
+
+ def write_settings(self):
+ # Entire Application Window
+ self.settings.setValue("geometry", self.saveGeometry())
+ self.settings.setValue("windowState", self.saveState())
+
+ # Tab
+ self.settings.setValue("tabWidget", self.tabWidget.currentIndex())
+
+ # Date and Time Tab
+ self.settings.setValue("date_and_time_auto", self.date_and_time_auto_checkbox.isChecked())
+
+ self.settings.setValue("ntp_servers", self.ntp_servers_comboBox.currentIndex())
+
+ # Time Zone Tab
+ self.settings.setValue(
+ "checkbox_set_time_zone_automatically_using_current_location",
+ self.checkbox_set_time_zone_automatically_using_current_location.isChecked(),
+ )
+
+ self.settings.setValue("tz_closest_city", self.tz_closest_city_combobox.currentText())
+
+ # Clock Tab
+ self.settings.setValue("show_the_date_and_time", self.groupbox_show_the_date_and_time.isChecked())
+ self.settings.setValue("display_the_time_with_seconds", self.checkbox_display_the_time_with_seconds.isChecked())
+ self.settings.setValue("show_the_day_of_the_week", self.checkbox_show_the_day_of_the_week.isChecked())
+ self.settings.setValue("show_am_pm", self.checkbox_show_am_pm.isChecked())
+ self.settings.setValue("show_am_pm", self.checkbox_show_am_pm.isChecked())
+ self.settings.setValue("flash_time_separator", self.checkbox_flash_time_separator.isChecked())
+ self.settings.setValue("use_24h_clock", self.checkbox_use_24h_clock.isChecked())
+
+ def read_settings(self):
+ # Entire Application Window
+ self.restoreGeometry(self.settings.value("geometry", QByteArray()))
+ self.restoreState(self.settings.value("windowState", QByteArray()))
+
+ # Tab
+ self.tabWidget.setCurrentIndex(self.settings.value("tabWidget", defaultValue=0, type=int))
+ # Date and Time Tab
+ self.date_and_time_auto_checkbox.setChecked(
+ self.settings.value("date_and_time_auto", defaultValue=False, type=bool)
+ )
+
+ self.ntp_servers_comboBox.setCurrentIndex(self.settings.value("ntp_servers", defaultValue=3, type=int))
+
+ # Time Zone Tab
+ self.checkbox_set_time_zone_automatically_using_current_location.setChecked(
+ self.settings.value(
+ "checkbox_set_time_zone_automatically_using_current_location", defaultValue=False, type=bool
+ )
+ )
+
+ self.checkbox_set_time_zone_automatically_using_current_location.setChecked(
+ self.settings.value(
+ "checkbox_set_time_zone_automatically_using_current_location", defaultValue=False, type=bool
+ )
+ )
+ self.tz_closest_city_combobox.clear()
+ self.tz_closest_city_combobox.addItem(self.settings.value("tz_closest_city", defaultValue="", type=str))
+
+ self.__checkbox_set_time_zone_automatically_using_current_location_changed()
+
+ # Clock Tab
+ self.groupbox_show_the_date_and_time.setChecked(
+ self.settings.value("show_the_date_and_time", defaultValue=True, type=bool)
+ )
+ self.checkbox_display_the_time_with_seconds.setChecked(
+ self.settings.value("display_the_time_with_seconds", defaultValue=True, type=bool)
+ )
+ self.checkbox_show_the_day_of_the_week.setChecked(
+ self.settings.value("show_the_day_of_the_week", defaultValue=True, type=bool)
+ )
+ self.checkbox_show_am_pm.setChecked(self.settings.value("show_am_pm", defaultValue=True, type=bool))
+ self.checkbox_flash_time_separator.setChecked(
+ self.settings.value("flash_time_separator", defaultValue=False, type=bool)
+ )
+ self.checkbox_use_24h_clock.setChecked(self.settings.value("use_24h_clock", defaultValue=False, type=bool))
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ window = DateTimeWindow()
+ window.show()
+ sys.exit(app.exec_())
diff --git a/Under Construction/Date and Time.app/Resources/date_and_time.ui b/Under Construction/Date and Time.app/Resources/date_and_time.ui
new file mode 100644
index 000000000..72f164081
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/date_and_time.ui
@@ -0,0 +1,900 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 756
+ 573
+
+
+
+
+ 0
+ 0
+
+
+
+ Date and Time
+
+
+
+ Date and Time.pngDate and Time.png
+
+
+
+
+ 0
+
+
+ 13
+
+
+ 13
+
+
+ 13
+
+
+ 13
+
+ -
+
+
+
+ Nimbus Sans
+
+
+
+ QTabWidget::tab-bar {
+ alignment: center;
+ }
+ QTabWidget::pane { /* The tab widget frame */
+ position: absolute;
+ top: -0.9em;
+ }
+
+
+
+ 1
+
+
+
+ 32
+ 32
+
+
+
+ Qt::ElideNone
+
+
+ false
+
+
+
+ Date and Time
+
+
+
+ 23
+
+
+ 34
+
+
+ 23
+
+
+ 23
+
+
-
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 23
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ H:mm:ss AP
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 20
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ false
+
+
+ QCalendarWidget::SingleLetterDayNames
+
+
+ QCalendarWidget::NoVerticalHeader
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ 0
+
+
-
+
+ Africa (africa.pool.ntp.org)
+
+
+ -
+
+ Antartica (antarctica.pool.ntp.org)
+
+
+ -
+
+ Asia (asia.pool.ntp.org)
+
+
+ -
+
+ Europe (europe.pool.ntp.org)
+
+
+ -
+
+ Nort America (north-america.pool.ntp.org)
+
+
+ -
+
+ Oceania (oceania.pool.ntp.org)
+
+
+ -
+
+ South America (south-america.pool.ntp.org)
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ Set date and time automatically
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ 26
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p><span style=" font-size:14pt; vertical-align:sub;">To set date and time formats, use International prefrences</span></p></body></html>
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ Open International
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+ Time Zone
+
+
+
+ 23
+
+
+ 34
+
+
+ 23
+
+
+ 23
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ <html><head/><body><p align="center"><span style=" font-size:10pt;">To select a time zone, click the map near your location and choose a city from the Closetes City Menu.<br/>You can also the time zone change automatically, if possible, based on your current location.</span></p></body></html>
+
+
+
+ -
+
+
+ Set time zone automatically using current location
+
+
+
+ -
+
+
+ 11
+
+
+ 11
+
+
+ 11
+
+
+ 11
+
+
+ 23
+
+
+ 13
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ Nimbus Sans
+
+
+
+ Time Zone:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Closest City:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 400
+ 200
+
+
+
+
+
+
+
+
+
+
+ Clock
+
+
+
+ 23
+
+
+ 34
+
+
+ 23
+
+
+ 23
+
+ -
+
+
+ false
+
+
+ Show the date and time
+
+
+ false
+
+
+ true
+
+
+
+ 23
+
+
+ 23
+
+
+ 23
+
+
+ 23
+
+
-
+
+
+ Display the time with seconds
+
+
+
+ -
+
+
+ Show the day of the week
+
+
+
+ -
+
+
+ Show AM/PM
+
+
+
+ -
+
+
+ Flash time separator
+
+
+
+ -
+
+
+
+ Nimbus Sans
+
+
+
+ Use 24-hour clock
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 20
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 23
+
+
+ 0
+
+
+ 13
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 🔒
+
+
+
+ -
+
+
+ <html><head/><body><p><span style=" font-size:14pt; vertical-align:sub;"/></p></body></html>
+
+
+ Qt::RichText
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+ Quit
+
+
+ Ctrl+Q
+
+
+
+
+ About Date and Time
+
+
+
+
+ false
+
+
+ Undo
+
+
+ Ctrl+Z
+
+
+
+
+ Cut
+
+
+ Ctrl+X
+
+
+
+
+ Copy
+
+
+ Ctrl+C
+
+
+
+
+ Paste
+
+
+ Ctrl+V
+
+
+
+
+ Delete
+
+
+ Del
+
+
+
+
+ true
+
+
+ Set date and time automatically
+
+
+
+
+ true
+
+
+ Set time zone automatically
+
+
+
+
+
+ AnalogClock
+ QWidget
+
+ 1
+
+
+ TimeZoneWorldMap
+ QWidget
+ widget_timezone_world_map
+ 1
+
+
+
+
+
+ actionQuit
+ triggered()
+ MainWindow
+ close()
+
+
+ -1
+ -1
+
+
+ 336
+ 261
+
+
+
+
+
diff --git a/Under Construction/Date and Time.app/Resources/date_and_time_ui.py b/Under Construction/Date and Time.app/Resources/date_and_time_ui.py
new file mode 100644
index 000000000..4a4544222
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/date_and_time_ui.py
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file './date_and_time.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.10
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(756, 573)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
+ MainWindow.setSizePolicy(sizePolicy)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap("./Date and Time.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ MainWindow.setWindowIcon(icon)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
+ self.verticalLayout.setContentsMargins(13, 13, 13, 13)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.tabWidget.setFont(font)
+ self.tabWidget.setStyleSheet("QTabWidget::tab-bar {\n"
+" alignment: center;\n"
+" }\n"
+" QTabWidget::pane { /* The tab widget frame */\n"
+" position: absolute;\n"
+" top: -0.9em;\n"
+" }\n"
+" ")
+ self.tabWidget.setIconSize(QtCore.QSize(32, 32))
+ self.tabWidget.setElideMode(QtCore.Qt.ElideNone)
+ self.tabWidget.setTabBarAutoHide(False)
+ self.tabWidget.setObjectName("tabWidget")
+ self.DateAndTimeTab = QtWidgets.QWidget()
+ self.DateAndTimeTab.setObjectName("DateAndTimeTab")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.DateAndTimeTab)
+ self.verticalLayout_3.setContentsMargins(23, 34, 23, 23)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.gridLayout_4 = QtWidgets.QGridLayout()
+ self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout_4.setHorizontalSpacing(23)
+ self.gridLayout_4.setVerticalSpacing(6)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_4.addItem(spacerItem, 1, 0, 5, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_4.addItem(spacerItem1, 1, 4, 5, 1)
+ spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem2, 2, 1, 1, 1)
+ self.dat_timeedit_widget = QtWidgets.QTimeEdit(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.dat_timeedit_widget.sizePolicy().hasHeightForWidth())
+ self.dat_timeedit_widget.setSizePolicy(sizePolicy)
+ self.dat_timeedit_widget.setMinimumSize(QtCore.QSize(120, 0))
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.dat_timeedit_widget.setFont(font)
+ self.dat_timeedit_widget.setWrapping(False)
+ self.dat_timeedit_widget.setAccelerated(False)
+ self.dat_timeedit_widget.setProperty("showGroupSeparator", False)
+ self.dat_timeedit_widget.setCalendarPopup(False)
+ self.dat_timeedit_widget.setObjectName("dat_timeedit_widget")
+ self.gridLayout_4.addWidget(self.dat_timeedit_widget, 3, 3, 1, 1, QtCore.Qt.AlignHCenter)
+ spacerItem3 = QtWidgets.QSpacerItem(20, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
+ self.gridLayout_4.addItem(spacerItem3, 6, 1, 1, 3)
+ self.dat_calendar_widget = QtWidgets.QCalendarWidget(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.dat_calendar_widget.sizePolicy().hasHeightForWidth())
+ self.dat_calendar_widget.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.dat_calendar_widget.setFont(font)
+ self.dat_calendar_widget.setGridVisible(False)
+ self.dat_calendar_widget.setHorizontalHeaderFormat(QtWidgets.QCalendarWidget.SingleLetterDayNames)
+ self.dat_calendar_widget.setVerticalHeaderFormat(QtWidgets.QCalendarWidget.NoVerticalHeader)
+ self.dat_calendar_widget.setNavigationBarVisible(True)
+ self.dat_calendar_widget.setDateEditEnabled(True)
+ self.dat_calendar_widget.setObjectName("dat_calendar_widget")
+ self.gridLayout_4.addWidget(self.dat_calendar_widget, 5, 1, 1, 1, QtCore.Qt.AlignHCenter)
+ self.dat_clock_widget = AnalogClock(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.dat_clock_widget.sizePolicy().hasHeightForWidth())
+ self.dat_clock_widget.setSizePolicy(sizePolicy)
+ self.dat_clock_widget.setObjectName("dat_clock_widget")
+ self.gridLayout_4.addWidget(self.dat_clock_widget, 5, 3, 1, 1)
+ self.ntp_servers_comboBox = QtWidgets.QComboBox(self.DateAndTimeTab)
+ self.ntp_servers_comboBox.setEnabled(False)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.ntp_servers_comboBox.sizePolicy().hasHeightForWidth())
+ self.ntp_servers_comboBox.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.ntp_servers_comboBox.setFont(font)
+ self.ntp_servers_comboBox.setObjectName("ntp_servers_comboBox")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.ntp_servers_comboBox.addItem("")
+ self.gridLayout_4.addWidget(self.ntp_servers_comboBox, 1, 3, 1, 1)
+ self.dat_dateedit_widget = QtWidgets.QDateEdit(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.dat_dateedit_widget.sizePolicy().hasHeightForWidth())
+ self.dat_dateedit_widget.setSizePolicy(sizePolicy)
+ self.dat_dateedit_widget.setMinimumSize(QtCore.QSize(120, 0))
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.dat_dateedit_widget.setFont(font)
+ self.dat_dateedit_widget.setObjectName("dat_dateedit_widget")
+ self.gridLayout_4.addWidget(self.dat_dateedit_widget, 3, 1, 1, 1, QtCore.Qt.AlignHCenter)
+ self.date_and_time_auto_checkbox = QtWidgets.QCheckBox(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.date_and_time_auto_checkbox.sizePolicy().hasHeightForWidth())
+ self.date_and_time_auto_checkbox.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.date_and_time_auto_checkbox.setFont(font)
+ self.date_and_time_auto_checkbox.setObjectName("date_and_time_auto_checkbox")
+ self.gridLayout_4.addWidget(self.date_and_time_auto_checkbox, 1, 1, 1, 1)
+ spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem4, 0, 1, 1, 3)
+ spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.gridLayout_4.addItem(spacerItem5, 1, 2, 5, 1)
+ spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem6, 4, 1, 1, 1)
+ spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem7, 2, 3, 1, 1)
+ spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout_4.addItem(spacerItem8, 4, 3, 1, 1)
+ self.verticalLayout_3.addLayout(self.gridLayout_4)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setSpacing(26)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_2 = QtWidgets.QLabel(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
+ self.label_2.setSizePolicy(sizePolicy)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout_2.addWidget(self.label_2)
+ self.dt_set_custom_format = QtWidgets.QPushButton(self.DateAndTimeTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.dt_set_custom_format.sizePolicy().hasHeightForWidth())
+ self.dt_set_custom_format.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.dt_set_custom_format.setFont(font)
+ self.dt_set_custom_format.setObjectName("dt_set_custom_format")
+ self.horizontalLayout_2.addWidget(self.dt_set_custom_format)
+ spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem9)
+ self.verticalLayout_3.addLayout(self.horizontalLayout_2)
+ self.tabWidget.addTab(self.DateAndTimeTab, "")
+ self.TimeZoneTab = QtWidgets.QWidget()
+ self.TimeZoneTab.setObjectName("TimeZoneTab")
+ self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.TimeZoneTab)
+ self.verticalLayout_4.setContentsMargins(23, 34, 23, 23)
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
+ self.label_5 = QtWidgets.QLabel(self.TimeZoneTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
+ self.label_5.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.label_5.setFont(font)
+ self.label_5.setObjectName("label_5")
+ self.verticalLayout_4.addWidget(self.label_5, 0, QtCore.Qt.AlignHCenter)
+ self.checkbox_set_time_zone_automatically_using_current_location = QtWidgets.QCheckBox(self.TimeZoneTab)
+ self.checkbox_set_time_zone_automatically_using_current_location.setObjectName("checkbox_set_time_zone_automatically_using_current_location")
+ self.verticalLayout_4.addWidget(self.checkbox_set_time_zone_automatically_using_current_location, 0, QtCore.Qt.AlignHCenter)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setContentsMargins(11, 11, 11, 11)
+ self.gridLayout.setHorizontalSpacing(23)
+ self.gridLayout.setVerticalSpacing(13)
+ self.gridLayout.setObjectName("gridLayout")
+ self.tz_time_zone_label = QtWidgets.QLabel(self.TimeZoneTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tz_time_zone_label.sizePolicy().hasHeightForWidth())
+ self.tz_time_zone_label.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.tz_time_zone_label.setFont(font)
+ self.tz_time_zone_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.tz_time_zone_label.setObjectName("tz_time_zone_label")
+ self.gridLayout.addWidget(self.tz_time_zone_label, 1, 0, 1, 1)
+ self.tz_closest_city_label = QtWidgets.QLabel(self.TimeZoneTab)
+ self.tz_closest_city_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.tz_closest_city_label.setObjectName("tz_closest_city_label")
+ self.gridLayout.addWidget(self.tz_closest_city_label, 2, 0, 1, 1)
+ self.tz_closest_city_combobox = QtWidgets.QComboBox(self.TimeZoneTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tz_closest_city_combobox.sizePolicy().hasHeightForWidth())
+ self.tz_closest_city_combobox.setSizePolicy(sizePolicy)
+ self.tz_closest_city_combobox.setObjectName("tz_closest_city_combobox")
+ self.gridLayout.addWidget(self.tz_closest_city_combobox, 2, 1, 1, 1)
+ self.tz_time_zone_value = QtWidgets.QLabel(self.TimeZoneTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tz_time_zone_value.sizePolicy().hasHeightForWidth())
+ self.tz_time_zone_value.setSizePolicy(sizePolicy)
+ self.tz_time_zone_value.setText("")
+ self.tz_time_zone_value.setObjectName("tz_time_zone_value")
+ self.gridLayout.addWidget(self.tz_time_zone_value, 1, 1, 1, 1)
+ self.tz_time_zone_world_map_widget = TimeZoneWorldMap(self.TimeZoneTab)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.tz_time_zone_world_map_widget.sizePolicy().hasHeightForWidth())
+ self.tz_time_zone_world_map_widget.setSizePolicy(sizePolicy)
+ self.tz_time_zone_world_map_widget.setMinimumSize(QtCore.QSize(400, 200))
+ self.tz_time_zone_world_map_widget.setObjectName("tz_time_zone_world_map_widget")
+ self.gridLayout.addWidget(self.tz_time_zone_world_map_widget, 0, 0, 1, 2)
+ self.verticalLayout_4.addLayout(self.gridLayout)
+ self.tabWidget.addTab(self.TimeZoneTab, "")
+ self.ClockTab = QtWidgets.QWidget()
+ self.ClockTab.setObjectName("ClockTab")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.ClockTab)
+ self.verticalLayout_5.setContentsMargins(23, 34, 23, 23)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.groupbox_show_the_date_and_time = QtWidgets.QGroupBox(self.ClockTab)
+ self.groupbox_show_the_date_and_time.setAutoFillBackground(False)
+ self.groupbox_show_the_date_and_time.setFlat(False)
+ self.groupbox_show_the_date_and_time.setCheckable(True)
+ self.groupbox_show_the_date_and_time.setObjectName("groupbox_show_the_date_and_time")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupbox_show_the_date_and_time)
+ self.verticalLayout_2.setContentsMargins(23, 23, 23, 23)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.checkbox_display_the_time_with_seconds = QtWidgets.QCheckBox(self.groupbox_show_the_date_and_time)
+ self.checkbox_display_the_time_with_seconds.setObjectName("checkbox_display_the_time_with_seconds")
+ self.verticalLayout_2.addWidget(self.checkbox_display_the_time_with_seconds)
+ self.checkbox_show_the_day_of_the_week = QtWidgets.QCheckBox(self.groupbox_show_the_date_and_time)
+ self.checkbox_show_the_day_of_the_week.setObjectName("checkbox_show_the_day_of_the_week")
+ self.verticalLayout_2.addWidget(self.checkbox_show_the_day_of_the_week)
+ self.checkbox_show_am_pm = QtWidgets.QCheckBox(self.groupbox_show_the_date_and_time)
+ self.checkbox_show_am_pm.setObjectName("checkbox_show_am_pm")
+ self.verticalLayout_2.addWidget(self.checkbox_show_am_pm)
+ self.checkbox_flash_time_separator = QtWidgets.QCheckBox(self.groupbox_show_the_date_and_time)
+ self.checkbox_flash_time_separator.setObjectName("checkbox_flash_time_separator")
+ self.verticalLayout_2.addWidget(self.checkbox_flash_time_separator)
+ self.checkbox_use_24h_clock = QtWidgets.QCheckBox(self.groupbox_show_the_date_and_time)
+ font = QtGui.QFont()
+ font.setFamily("Nimbus Sans")
+ self.checkbox_use_24h_clock.setFont(font)
+ self.checkbox_use_24h_clock.setChecked(True)
+ self.checkbox_use_24h_clock.setObjectName("checkbox_use_24h_clock")
+ self.verticalLayout_2.addWidget(self.checkbox_use_24h_clock)
+ spacerItem10 = QtWidgets.QSpacerItem(20, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
+ self.verticalLayout_2.addItem(spacerItem10)
+ self.verticalLayout_5.addWidget(self.groupbox_show_the_date_and_time)
+ self.tabWidget.addTab(self.ClockTab, "")
+ self.verticalLayout.addWidget(self.tabWidget)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setContentsMargins(0, 13, 0, 0)
+ self.horizontalLayout.setSpacing(23)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.sudo_toolbutton = QtWidgets.QToolButton(self.centralwidget)
+ self.sudo_toolbutton.setObjectName("sudo_toolbutton")
+ self.horizontalLayout.addWidget(self.sudo_toolbutton)
+ self.error_message_label = QtWidgets.QLabel(self.centralwidget)
+ self.error_message_label.setTextFormat(QtCore.Qt.RichText)
+ self.error_message_label.setObjectName("error_message_label")
+ self.horizontalLayout.addWidget(self.error_message_label)
+ spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem11)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 756, 28))
+ self.menubar.setObjectName("menubar")
+ self.menuFile = QtWidgets.QMenu(self.menubar)
+ self.menuFile.setObjectName("menuFile")
+ self.menuEdit = QtWidgets.QMenu(self.menubar)
+ self.menuEdit.setObjectName("menuEdit")
+ self.menuHelp = QtWidgets.QMenu(self.menubar)
+ self.menuHelp.setObjectName("menuHelp")
+ MainWindow.setMenuBar(self.menubar)
+ self.actionQuit = QtWidgets.QAction(MainWindow)
+ self.actionQuit.setObjectName("actionQuit")
+ self.actionHelpAbout = QtWidgets.QAction(MainWindow)
+ self.actionHelpAbout.setObjectName("actionHelpAbout")
+ self.action_undo = QtWidgets.QAction(MainWindow)
+ self.action_undo.setEnabled(False)
+ self.action_undo.setObjectName("action_undo")
+ self.action_cut = QtWidgets.QAction(MainWindow)
+ self.action_cut.setObjectName("action_cut")
+ self.action_copy = QtWidgets.QAction(MainWindow)
+ self.action_copy.setObjectName("action_copy")
+ self.action_paste = QtWidgets.QAction(MainWindow)
+ self.action_paste.setObjectName("action_paste")
+ self.action_delete = QtWidgets.QAction(MainWindow)
+ self.action_delete.setObjectName("action_delete")
+ self.action_set_date_and_time_automatically = QtWidgets.QAction(MainWindow)
+ self.action_set_date_and_time_automatically.setCheckable(True)
+ self.action_set_date_and_time_automatically.setObjectName("action_set_date_and_time_automatically")
+ self.action_set_time_zone_automatically = QtWidgets.QAction(MainWindow)
+ self.action_set_time_zone_automatically.setCheckable(True)
+ self.action_set_time_zone_automatically.setObjectName("action_set_time_zone_automatically")
+ self.menuFile.addAction(self.actionQuit)
+ self.menuEdit.addAction(self.action_undo)
+ self.menuEdit.addSeparator()
+ self.menuEdit.addAction(self.action_cut)
+ self.menuEdit.addAction(self.action_copy)
+ self.menuEdit.addAction(self.action_paste)
+ self.menuEdit.addAction(self.action_delete)
+ self.menuEdit.addSeparator()
+ self.menuEdit.addAction(self.action_set_date_and_time_automatically)
+ self.menuEdit.addAction(self.action_set_time_zone_automatically)
+ self.menuHelp.addAction(self.actionHelpAbout)
+ self.menubar.addAction(self.menuFile.menuAction())
+ self.menubar.addAction(self.menuEdit.menuAction())
+ self.menubar.addAction(self.menuHelp.menuAction())
+
+ self.retranslateUi(MainWindow)
+ self.tabWidget.setCurrentIndex(0)
+ self.ntp_servers_comboBox.setCurrentIndex(0)
+ self.actionQuit.triggered.connect(MainWindow.close) # type: ignore
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "Date and Time"))
+ self.dat_timeedit_widget.setDisplayFormat(_translate("MainWindow", "H:mm:ss AP"))
+ self.ntp_servers_comboBox.setItemText(0, _translate("MainWindow", "Africa (africa.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(1, _translate("MainWindow", "Antartica (antarctica.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(2, _translate("MainWindow", "Asia (asia.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(3, _translate("MainWindow", "Europe (europe.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(4, _translate("MainWindow", "Nort America (north-america.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(5, _translate("MainWindow", "Oceania (oceania.pool.ntp.org)"))
+ self.ntp_servers_comboBox.setItemText(6, _translate("MainWindow", "South America (south-america.pool.ntp.org)"))
+ self.date_and_time_auto_checkbox.setText(_translate("MainWindow", "Set date and time automatically"))
+ self.label_2.setText(_translate("MainWindow", "To set date and time formats, use International prefrences
"))
+ self.dt_set_custom_format.setText(_translate("MainWindow", "Open International"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.DateAndTimeTab), _translate("MainWindow", "Date and Time"))
+ self.label_5.setText(_translate("MainWindow", "To select a time zone, click the map near your location and choose a city from the Closetes City Menu.
You can also the time zone change automatically, if possible, based on your current location.
"))
+ self.checkbox_set_time_zone_automatically_using_current_location.setText(_translate("MainWindow", "Set time zone automatically using current location"))
+ self.tz_time_zone_label.setText(_translate("MainWindow", "Time Zone:"))
+ self.tz_closest_city_label.setText(_translate("MainWindow", "Closest City:"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.TimeZoneTab), _translate("MainWindow", "Time Zone"))
+ self.groupbox_show_the_date_and_time.setTitle(_translate("MainWindow", "Show the date and time"))
+ self.checkbox_display_the_time_with_seconds.setText(_translate("MainWindow", "Display the time with seconds"))
+ self.checkbox_show_the_day_of_the_week.setText(_translate("MainWindow", "Show the day of the week"))
+ self.checkbox_show_am_pm.setText(_translate("MainWindow", "Show AM/PM"))
+ self.checkbox_flash_time_separator.setText(_translate("MainWindow", "Flash time separator"))
+ self.checkbox_use_24h_clock.setText(_translate("MainWindow", "Use 24-hour clock"))
+ self.tabWidget.setTabText(self.tabWidget.indexOf(self.ClockTab), _translate("MainWindow", "Clock"))
+ self.sudo_toolbutton.setText(_translate("MainWindow", "🔒"))
+ self.error_message_label.setText(_translate("MainWindow", "
"))
+ self.menuFile.setTitle(_translate("MainWindow", "File"))
+ self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
+ self.menuHelp.setTitle(_translate("MainWindow", "Help"))
+ self.actionQuit.setText(_translate("MainWindow", "Quit"))
+ self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
+ self.actionHelpAbout.setText(_translate("MainWindow", "About Date and Time"))
+ self.action_undo.setText(_translate("MainWindow", "Undo"))
+ self.action_undo.setShortcut(_translate("MainWindow", "Ctrl+Z"))
+ self.action_cut.setText(_translate("MainWindow", "Cut"))
+ self.action_cut.setShortcut(_translate("MainWindow", "Ctrl+X"))
+ self.action_copy.setText(_translate("MainWindow", "Copy"))
+ self.action_copy.setShortcut(_translate("MainWindow", "Ctrl+C"))
+ self.action_paste.setText(_translate("MainWindow", "Paste"))
+ self.action_paste.setShortcut(_translate("MainWindow", "Ctrl+V"))
+ self.action_delete.setText(_translate("MainWindow", "Delete"))
+ self.action_delete.setShortcut(_translate("MainWindow", "Del"))
+ self.action_set_date_and_time_automatically.setText(_translate("MainWindow", "Set date and time automatically"))
+ self.action_set_time_zone_automatically.setText(_translate("MainWindow", "Set time zone automatically"))
+from widget_analogclock import AnalogClock
+from widget_timezone_world_map import TimeZoneWorldMap
diff --git a/Under Construction/Date and Time.app/Resources/docs/README.md b/Under Construction/Date and Time.app/Resources/docs/README.md
new file mode 100644
index 000000000..9623e3bb2
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/docs/README.md
@@ -0,0 +1,75 @@
+# Date & Time
+
+The Date & Time preferences applications includes three tabs for setting the system time and clock options:
+
+- **Date & Time** (Figure 22) enables you to manually set the date and time.
+
+
+
+- **Time Zone** (Figure 25) enables you to set your time zone.
+
+
+
+- **Clock** (Figure 27) enables you to set options for the appearance of the clock.
+
+
+
+## Manually Set the Date & Time
+
+1. In the Date & Time preferences pane, click the Date & Time button ().
+2. If necessary, turn off the Set date & time automatically check box.
+3. To change the date, click the part of the date you want to change (), then type a new value or use the tiny arrow buttons beside the date to change the value.
+4. To change the time, click the part of the time that you want to change and type a new value or use the tiny arrow buttons beside the time to change the value.
+5. Click Save.
+
+**Tips:**
+- You can't manually change the date or time if you have enabled the network time server feature; more about that on the next page.
+- Another way to change the time in step 4 is to drag the hands of the analog clock so they display the correct time.
+
+## Automatically Set the Date & Time
+
+1. In the Date & Time preferences pane, click the Date & Time button ().
+2. Turn on the Set date & time automatically check box.
+3. Choose the closest time server from the drop-down list ().
+
+**Tip:**
+With the network time server feature enabled, your computer will use its Internet connection to periodically get the date and time from a time server and update the system clock automatically. This ensures that your computer's clock is always correct.
+
+## Set the Time Zone
+
+1. In the Date & Time preferences pane, click the Time Zone button ().
+2. Click your approximate location on the map. A white bar indicates the time zone area ().
+3. If necessary, choose the name of your time zone from the Closest City drop-down list beneath the map ().
+
+**Tips:**
+In step 3, only those time zones within the white bar on the map are listed in the drop-down list (). If your time zone does not appear, make sure you clicked the correct area in the map in step 2.
+It's a good idea to choose the correct time zone, as Mac OS uses this information with the network time server (if utilized) and to properly change the clock for daylight saving time.
+
+## Set Clock Options
+
+1. In the Date & Time preferences pane, click the Clock button ().
+2. To enable the clock, turn on the Show the date and time check box.
+3. Select a View in radio button:
+ - **Menu Bar** puts the clock in the menu bar, which is the default location.
+ - **Window** puts the clock in a floating window ().
+
+
+
+4. Select a View as radio button:
+ - **Digital** displays the date and time with letters and numbers.
+ - **Analog** displays the time on an analog clock. If you select this option, you cannot set options in step 5.
+
+5. Toggle check boxes to customize the clock's appearance:
+ - **Display the time with seconds** displays the seconds as part of the time.
+ - **Show AM/PM** displays AM or PM after the time.
+ - **Show the day of the week** displays the three-letter abbreviation for the day of the week before the time.
+ - **Flash the time separators** blinks the colon(s) in the time every second.
+ - **Use a 24-hour clock** displays the time as a 24-hour (rather than 12-hour) clock.
+
+6. If you selected Window in step 3, you can use the Transparency slider to set the transparency of the clock window.
+7. To instruct your computer to vocally announce the time periodically, turn on the Announce the time check box and choose a frequency option from the pop-up menu.
+
+**Tip:**
+The menu bar clock is also a menu that displays the full date and time and offers options for changing the clock display ().
+
+
diff --git a/Under Construction/Date and Time.app/Resources/ntplib.py b/Under Construction/Date and Time.app/Resources/ntplib.py
new file mode 100644
index 000000000..c8cc82afc
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/ntplib.py
@@ -0,0 +1,491 @@
+###############################################################################
+# The MIT License (MIT)
+#
+# Copyright (C) 2009-2015 Charles-Francois Natali
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+###############################################################################
+"""Python NTP library.
+
+Implementation of client-side NTP (RFC-1305), and useful NTP-related
+functions.
+"""
+
+
+import datetime
+import socket
+import struct
+import time
+
+
+class NTPException(Exception):
+ """Exception raised by this module."""
+
+
+class NTPRolloverException(NTPException):
+ """Exception raised when the system time is beyond the NTPv3 rollover. """
+ # See https://en.wikipedia.org/wiki/Network_Time_Protocol#Timestamps
+
+
+class NTP: # pylint: disable=no-init
+ """Helper class defining constants."""
+
+ _SYSTEM_EPOCH = datetime.datetime(*time.gmtime(0)[0:3])
+ """system epoch"""
+ _NTP_EPOCH = datetime.datetime(1900, 1, 1)
+ """NTP epoch"""
+ NTP_DELTA = int((_SYSTEM_EPOCH - _NTP_EPOCH).total_seconds())
+ """delta between system and NTP time"""
+
+ REF_ID_TABLE = {
+ "GOES": "Geostationary Orbit Environment Satellite",
+ "GPS": "Global Position System",
+ "GAL": "Galileo Positioning System",
+ "PPS": "Generic pulse-per-second",
+ "IRIG": "Inter-Range Instrumentation Group",
+ "WWVB": "LF Radio WWVB Ft. Collins, CO 60 kHz",
+ "DCF": "LF Radio DCF77 Mainflingen, DE 77.5 kHz",
+ "HBG": "LF Radio HBG Prangins, HB 75 kHz",
+ "MSF": "LF Radio MSF Anthorn, UK 60 kHz",
+ "JJY": "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz",
+ "LORC": "MF Radio LORAN C station, 100 kHz",
+ "TDF": "MF Radio Allouis, FR 162 kHz",
+ "CHU": "HF Radio CHU Ottawa, Ontario",
+ "WWV": "HF Radio WWV Ft. Collins, CO",
+ "WWVH": "HF Radio WWVH Kauai, HI",
+ "NIST": "NIST telephone modem",
+ "ACTS": "NIST telephone modem",
+ "USNO": "USNO telephone modem",
+ "PTB": "European telephone modem",
+ "LOCL": "uncalibrated local clock",
+ "CESM": "calibrated Cesium clock",
+ "RBDM": "calibrated Rubidium clock",
+ "OMEG": "OMEGA radionavigation system",
+ "DCN": "DCN routing protocol",
+ "TSP": "TSP time protocol",
+ "DTS": "Digital Time Service",
+ "ATOM": "Atomic clock (calibrated)",
+ "VLF": "VLF radio (OMEGA,, etc.)",
+ "1PPS": "External 1 PPS input",
+ "FREE": "(Internal clock)",
+ "INIT": "(Initialization)",
+ "ROA": "Real Observatorio de la Armada",
+ "": "NULL",
+ }
+ """reference identifier table"""
+
+ STRATUM_TABLE = {
+ 0: "unspecified or invalid (%s)",
+ 1: "primary reference (%s)",
+ }
+ """stratum table"""
+
+ MODE_TABLE = {
+ 0: "reserved",
+ 1: "symmetric active",
+ 2: "symmetric passive",
+ 3: "client",
+ 4: "server",
+ 5: "broadcast",
+ 6: "reserved for NTP control messages",
+ 7: "reserved for private use",
+ }
+ """mode table"""
+
+ LEAP_TABLE = {
+ 0: "no warning",
+ 1: "last minute of the day has 61 seconds",
+ 2: "last minute of the day has 59 seconds",
+ 3: "unknown (clock unsynchronized)",
+ }
+ """leap indicator table"""
+
+
+class NTPPacket(object):
+ """NTP packet class.
+
+ This represents an NTP packet.
+ """
+
+ _PACKET_FORMAT = "!B B B b 11I"
+ """packet format to pack/unpack"""
+
+ def __init__(self, version=2, mode=3, tx_timestamp=0):
+ """Constructor.
+
+ Parameters:
+ version -- NTP version
+ mode -- packet mode (client, server)
+ tx_timestamp -- packet transmit timestamp
+ """
+ self.leap = 0
+ """leap second indicator"""
+ self.version = version
+ """version"""
+ self.mode = mode
+ """mode"""
+ self.stratum = 0
+ """stratum"""
+ self.poll = 0
+ """poll interval"""
+ self.precision = 0
+ """precision"""
+ self.root_delay = 0
+ """root delay"""
+ self.root_dispersion = 0
+ """root dispersion"""
+ self.ref_id = 0
+ """reference clock identifier"""
+ self.ref_timestamp = 0
+ """reference timestamp"""
+ self.orig_timestamp = 0
+ """originate timestamp"""
+ self.recv_timestamp = 0
+ """receive timestamp"""
+ self.tx_timestamp = tx_timestamp
+ """transmit timestamp"""
+
+ def to_data(self):
+ """Convert this NTPPacket to a buffer that can be sent over a socket.
+
+ Returns:
+ buffer representing this packet
+
+ Raises:
+ NTPException -- in case of invalid field
+ """
+ try:
+ packed = struct.pack(
+ NTPPacket._PACKET_FORMAT,
+ (self.leap << 6 | self.version << 3 | self.mode),
+ self.stratum,
+ self.poll,
+ self.precision,
+ _to_int(self.root_delay) << 16 | _to_frac(self.root_delay, 16),
+ _to_int(self.root_dispersion) << 16 |
+ _to_frac(self.root_dispersion, 16),
+ self.ref_id,
+ _to_int(self.ref_timestamp),
+ _to_frac(self.ref_timestamp),
+ _to_int(self.orig_timestamp),
+ _to_frac(self.orig_timestamp),
+ _to_int(self.recv_timestamp),
+ _to_frac(self.recv_timestamp),
+ _to_int(self.tx_timestamp),
+ _to_frac(self.tx_timestamp))
+ except struct.error:
+ raise NTPException("Invalid NTP packet fields.")
+ return packed
+
+ def from_data(self, data):
+ """Populate this instance from a NTP packet payload received from
+ the network.
+
+ Parameters:
+ data -- buffer payload
+
+ Raises:
+ NTPException -- in case of invalid packet format
+ """
+ try:
+ unpacked = struct.unpack(
+ NTPPacket._PACKET_FORMAT,
+ data[0:struct.calcsize(NTPPacket._PACKET_FORMAT)]
+ )
+ except struct.error:
+ raise NTPException("Invalid NTP packet.")
+
+ self.leap = unpacked[0] >> 6 & 0x3
+ self.version = unpacked[0] >> 3 & 0x7
+ self.mode = unpacked[0] & 0x7
+ self.stratum = unpacked[1]
+ self.poll = unpacked[2]
+ self.precision = unpacked[3]
+ self.root_delay = float(unpacked[4])/2**16
+ self.root_dispersion = float(unpacked[5])/2**16
+ self.ref_id = unpacked[6]
+ self.ref_timestamp = _to_time(unpacked[7], unpacked[8])
+ self.orig_timestamp = _to_time(unpacked[9], unpacked[10])
+ self.recv_timestamp = _to_time(unpacked[11], unpacked[12])
+ self.tx_timestamp = _to_time(unpacked[13], unpacked[14])
+
+
+class NTPStats(NTPPacket):
+ """NTP statistics.
+
+ Wrapper for NTPPacket, offering additional statistics like offset and
+ delay, and timestamps converted to system time.
+ """
+
+ def __init__(self):
+ """Constructor."""
+ super(NTPStats, self).__init__()
+ self.dest_timestamp = 0
+ """destination timestamp"""
+
+ @property
+ def offset(self):
+ """offset"""
+ return ((self.recv_timestamp - self.orig_timestamp) +
+ (self.tx_timestamp - self.dest_timestamp))/2
+
+ @property
+ def delay(self):
+ """round-trip delay"""
+ return ((self.dest_timestamp - self.orig_timestamp) -
+ (self.tx_timestamp - self.recv_timestamp))
+
+ @property
+ def tx_time(self):
+ """Transmit timestamp in system time."""
+ return ntp_to_system_time(self.tx_timestamp)
+
+ @property
+ def recv_time(self):
+ """Receive timestamp in system time."""
+ return ntp_to_system_time(self.recv_timestamp)
+
+ @property
+ def orig_time(self):
+ """Originate timestamp in system time."""
+ return ntp_to_system_time(self.orig_timestamp)
+
+ @property
+ def ref_time(self):
+ """Reference timestamp in system time."""
+ return ntp_to_system_time(self.ref_timestamp)
+
+ @property
+ def dest_time(self):
+ """Destination timestamp in system time."""
+ return ntp_to_system_time(self.dest_timestamp)
+
+
+class NTPClient(object):
+ """NTP client session."""
+
+ def __init__(self):
+ """Constructor."""
+
+ def request(self, host, version=2, port=123, timeout=5, address_family=socket.AF_UNSPEC): # pylint: disable=no-self-use,too-many-arguments
+ """Query a NTP server.
+
+ Parameters:
+ host -- server name/address
+ version -- NTP version to use
+ port -- server port
+ timeout -- timeout on socket operations
+ address_family -- socket address family
+
+ Returns:
+ NTPStats object
+ """
+
+ # lookup server address
+ addrinfo = socket.getaddrinfo(host, port, address_family)[0]
+ family, sockaddr = addrinfo[0], addrinfo[4]
+
+ # create the socket
+ sock = socket.socket(family, socket.SOCK_DGRAM)
+
+ try:
+ sock.settimeout(timeout)
+
+ # create the request packet - mode 3 is client
+ query_packet = NTPPacket(
+ mode=3,
+ version=version,
+ tx_timestamp=system_to_ntp_time(time.time())
+ )
+
+ # send the request
+ sock.sendto(query_packet.to_data(), sockaddr)
+
+ # wait for the response - check the source address
+ src_addr = (None,)
+ while src_addr[0] != sockaddr[0]:
+ response_packet, src_addr = sock.recvfrom(256)
+
+ # build the destination timestamp
+ dest_timestamp = system_to_ntp_time(time.time())
+ except socket.timeout:
+ raise NTPException("No response received from %s." % host)
+ finally:
+ sock.close()
+
+ # construct corresponding statistics
+ stats = NTPStats()
+ stats.from_data(response_packet)
+ stats.dest_timestamp = dest_timestamp
+
+ return stats
+
+
+def _to_int(timestamp):
+ """Return the integral part of a timestamp.
+
+ Parameters:
+ timestamp -- NTP timestamp
+
+ Returns:
+ integral part
+ """
+ return int(timestamp)
+
+
+def _to_frac(timestamp, bits=32):
+ """Return the fractional part of a timestamp.
+
+ Parameters:
+ timestamp -- NTP timestamp
+ bits -- number of bits of the fractional part
+
+ Returns:
+ fractional part
+ """
+ return int(abs(timestamp - _to_int(timestamp)) * 2**bits)
+
+
+def _to_time(integ, frac, bits=32):
+ """Return a timestamp from an integral and fractional part.
+
+ Parameters:
+ integ -- integral part
+ frac -- fractional part
+ bits -- number of bits of the fractional part
+
+ Returns:
+ timestamp
+ """
+ return integ + float(frac)/2**bits
+
+
+def ntp_to_system_time(timestamp):
+ """Convert a NTP time to system time.
+
+ Parameters:
+ timestamp -- timestamp in NTP time
+
+ Returns:
+ corresponding system time
+ """
+ return timestamp - NTP.NTP_DELTA
+
+
+def system_to_ntp_time(timestamp):
+ """Convert a system time to a NTP time.
+
+ Parameters:
+ timestamp -- timestamp in system time
+
+ Returns:
+ corresponding NTP time
+ """
+ ntp_time = timestamp + NTP.NTP_DELTA
+ if ntp_time >= 2 ** 32:
+ raise NTPRolloverException("Timestamp %s is beyond NTPv3 rollover" %
+ timestamp)
+ return ntp_time
+
+
+def leap_to_text(leap):
+ """Convert a leap indicator to text.
+
+ Parameters:
+ leap -- leap indicator value
+
+ Returns:
+ corresponding message
+
+ Raises:
+ NTPException -- in case of invalid leap indicator
+ """
+ if leap in NTP.LEAP_TABLE:
+ return NTP.LEAP_TABLE[leap]
+ else:
+ raise NTPException("Invalid leap indicator.")
+
+
+def mode_to_text(mode):
+ """Convert a NTP mode value to text.
+
+ Parameters:
+ mode -- NTP mode
+
+ Returns:
+ corresponding message
+
+ Raises:
+ NTPException -- in case of invalid mode
+ """
+ if mode in NTP.MODE_TABLE:
+ return NTP.MODE_TABLE[mode]
+ else:
+ raise NTPException("Invalid mode.")
+
+
+def stratum_to_text(stratum):
+ """Convert a stratum value to text.
+
+ Parameters:
+ stratum -- NTP stratum
+
+ Returns:
+ corresponding message
+
+ Raises:
+ NTPException -- in case of invalid stratum
+ """
+ if stratum in NTP.STRATUM_TABLE:
+ return NTP.STRATUM_TABLE[stratum] % stratum
+ elif 1 < stratum < 16:
+ return "secondary reference (%s)" % stratum
+ elif stratum == 16:
+ return "unsynchronized (%s)" % stratum
+ else:
+ raise NTPException("Invalid stratum or reserved.")
+
+
+def ref_id_to_text(ref_id, stratum=2):
+ """Convert a reference clock identifier to text according to its stratum.
+
+ Parameters:
+ ref_id -- reference clock identifier
+ stratum -- NTP stratum
+
+ Returns:
+ corresponding message
+
+ Raises:
+ NTPException -- in case of invalid stratum
+ """
+ fields = (ref_id >> 24 & 0xff, ref_id >> 16 & 0xff,
+ ref_id >> 8 & 0xff, ref_id & 0xff)
+
+ # return the result as a string or dot-formatted IP address
+ if 0 <= stratum <= 1:
+ text = ("%c%c%c%c" % fields).rstrip("\00")
+ if text in NTP.REF_ID_TABLE:
+ return NTP.REF_ID_TABLE[text]
+ else:
+ return "Unidentified reference source '%s'" % text
+ elif 2 <= stratum < 255:
+ return "%d.%d.%d.%d" % fields
+ else:
+ raise NTPException("Invalid stratum.")
\ No newline at end of file
diff --git a/Under Construction/Date and Time.app/Resources/property_date_time_auto.py b/Under Construction/Date and Time.app/Resources/property_date_time_auto.py
new file mode 100644
index 000000000..0949f2983
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/property_date_time_auto.py
@@ -0,0 +1,28 @@
+from PyQt5.QtCore import (
+ pyqtProperty, pyqtSignal
+)
+
+
+class DateTimeAutomatically(object):
+ DateTimeAutomaticallyChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+
+ self.__date_time_auto = None
+ self.DateTimeAutomatically = None
+
+ @pyqtProperty(int)
+ def DateTimeAutomatically(self):
+ return self.__date_time_auto
+
+ @DateTimeAutomatically.setter
+ def DateTimeAutomatically(self, value):
+ if value is None:
+ value = False
+ if self.__date_time_auto != value:
+ self.__date_time_auto = value
+ self.DateTimeAutomaticallyChanged.emit()
+
+ def setDateTimeAutomatically(self, value):
+ self.DateTimeAutomatically = value
diff --git a/Under Construction/Date and Time.app/Resources/property_time.py b/Under Construction/Date and Time.app/Resources/property_time.py
new file mode 100644
index 000000000..595fd348f
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/property_time.py
@@ -0,0 +1,28 @@
+from PyQt5.QtCore import (
+ pyqtProperty, pyqtSignal, QTime
+)
+
+
+class Time(object):
+ TimeChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+
+ self.__time = None
+ self.time = None
+
+ @pyqtProperty(QTime)
+ def time(self):
+ return self.__time
+
+ @time.setter
+ def time(self, value):
+ if value is None:
+ value = QTime.currentTime()
+ if self.__time != value:
+ self.__time = value
+ self.TimeChanged.emit()
+
+ def setTime(self, value):
+ self.time = value
diff --git a/Under Construction/Date and Time.app/Resources/property_timezone.py b/Under Construction/Date and Time.app/Resources/property_timezone.py
new file mode 100644
index 000000000..8f1558c9d
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/property_timezone.py
@@ -0,0 +1,51 @@
+import os
+
+from PyQt5.QtCore import QFile, QFileInfo, QTextCodec
+from PyQt5.QtCore import pyqtProperty, pyqtSignal
+
+
+class TimeZoneProperty(object):
+ TimeZoneChanged = pyqtSignal()
+
+ def __init__(self, *args, **kwargs):
+
+ self.__timezone_default_path = "/etc/timezone"
+ self.__timezone = None
+ self.TimeZone = None
+ self.timezone_file = None
+
+ @pyqtProperty(str)
+ def TimeZone(self):
+ return self.__timezone
+
+ @TimeZone.setter
+ def TimeZone(self, value):
+ if value is None:
+ info1 = QFileInfo(self.__timezone_default_path)
+ if os.getenv("TZ"):
+ value = os.environ.get("TZ")
+ elif info1.exists() and (info1.isFile() or info1.isSymLink()):
+ if info1.isSymLink():
+ info1 = QFileInfo(info1.symLinkTarget())
+ if info1.isFile() and info1.isReadable():
+ try:
+ file_handle = QFile(info1.absoluteFilePath())
+ file_handle.open(QFile.ReadOnly)
+ data = file_handle.readAll()
+ codec = QTextCodec.codecForUtfText(data)
+ value = codec.toUnicode(data).strip("\n")
+
+ # for line in QTextCodec.codecForUtfText(data):
+ # if line.startswith("#"):
+ # pass
+ # else:
+ # value = line.strip("\n")
+ except (Exception, BaseException):
+ raise IOError("Problem reading file %s" % info1.absoluteFilePath())
+
+ if self.__timezone != value:
+ self.__timezone = value
+ self.TimeZoneChanged.emit()
+
+ def setTimeZone(self, value):
+ self.TimeZone = value
diff --git a/Under Construction/Date and Time.app/Resources/property_timezone_offset.py b/Under Construction/Date and Time.app/Resources/property_timezone_offset.py
new file mode 100644
index 000000000..c3cd11204
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/property_timezone_offset.py
@@ -0,0 +1,28 @@
+from PyQt5.QtCore import (
+ pyqtProperty, pyqtSignal
+)
+
+
+class TimeZoneOffset(object):
+ TimeZoneOffsetChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+
+ self.__timezone_offset = None
+ self.TimeZoneOffset = None
+
+ @pyqtProperty(int)
+ def TimeZoneOffset(self):
+ return self.__timezone_offset
+
+ @TimeZoneOffset.setter
+ def TimeZoneOffset(self, value):
+ if value is None:
+ value = 0
+ if self.__timezone_offset != value:
+ self.__timezone_offset = value
+ self.TimeZoneOffsetChanged.emit()
+
+ def setTimeZoneOffset(self, value):
+ self.TimeZoneOffset = value
diff --git a/Under Construction/Date and Time.app/Resources/property_timezone_selection.py b/Under Construction/Date and Time.app/Resources/property_timezone_selection.py
new file mode 100644
index 000000000..fe43ef1b5
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/property_timezone_selection.py
@@ -0,0 +1,28 @@
+from PyQt5.QtCore import (
+ pyqtProperty, pyqtSignal
+)
+
+
+class TimeZoneSelection(object):
+ TimeZoneSelectionChanged = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+
+ self.__timezone_selection = None
+ self.TimeZoneSelection = None
+
+ @pyqtProperty(int)
+ def TimeZoneSelection(self):
+ return self.__timezone_selection
+
+ @TimeZoneSelection.setter
+ def TimeZoneSelection(self, value):
+ if value is None:
+ value = 0
+ if self.__timezone_selection != value:
+ self.__timezone_selection = value
+ self.TimeZoneSelectionChanged.emit()
+
+ def setTimeZoneSelection(self, value):
+ self.TimeZoneSelection = value
diff --git a/Under Construction/Date and Time.app/Resources/spinner.gif b/Under Construction/Date and Time.app/Resources/spinner.gif
new file mode 100644
index 000000000..3288d1035
Binary files /dev/null and b/Under Construction/Date and Time.app/Resources/spinner.gif differ
diff --git a/Under Construction/Date and Time.app/Resources/widget_analogclock.py b/Under Construction/Date and Time.app/Resources/widget_analogclock.py
new file mode 100644
index 000000000..6e54668b2
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/widget_analogclock.py
@@ -0,0 +1,354 @@
+# Thanks a lot rakshitarora who provide the template code
+# https://www.geeksforgeeks.org/pyqt5-qdateedit-getting-input-date/
+# Clock face generate via https://www.oliverboorman.biz/projects/tools/clocks.php
+
+# importing libraries
+from PyQt5.QtWidgets import (
+ QWidget, QApplication
+)
+from PyQt5.QtCore import (
+ Qt, pyqtSignal, QTime, QTimer, QRect, QTime
+)
+from PyQt5.QtGui import (
+ QPolygon, QColor, QPainter, QImage, QBrush, QPen, QFont
+)
+import sys
+import os
+
+from property_time import Time
+
+
+# creating a clock class
+class AnalogClock(QWidget, Time):
+ timeChanged = pyqtSignal(QTime)
+ timeZoneChanged = pyqtSignal(int)
+
+ # constructor
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
+ Time.__init__(self)
+
+ self.qp = None
+
+ # background image
+ self.background_image_path = None
+ self.background_image_disable_path = None
+ self.background_image = None
+ self.background_image_disable = None
+
+ # Hands Pointers
+ self.hour_pointer = None
+ self.minute_pointer = None
+ self.second_pointer = None
+
+ # colors
+ self.hour_pointer_color = None
+ self.hour_pointer_disable_color = None
+ self.minute_pointer_color = None
+ self.minute_pointer_disable_color = None
+ self.second_pointer_color = None
+ self.second_pointer_disable_color = None
+
+ # color for number clock face
+ self.clock_face_number_color = None
+ self.clock_face_number_disable_color = None
+
+ self.clock_face_hour_color = None
+ self.clock_face_hour_disable_color = None
+
+ self.font_face_clock_hours = None
+
+ self.clock_face_minute_color = None
+ self.clock_face_minute_disable_color = None
+
+ self.clock_face_am_pm_color = None
+ self.clock_face_am_pm_disable_color = None
+
+ self.pen_square = None
+ self.pen_round = None
+
+ self.pen_am_pm = None
+ self.pen_am_pm_disable = None
+ self.font_face_clock_am_pm = None
+
+ self.pen_square_disable = None
+ self.pen_round_disable = None
+
+ self.pen_clock_face_number = None
+ self.pen_clock_face_number_disable = None
+
+ self.pen_dot_illusion = None
+ self.pen_dot_illusion_disable = None
+
+ self.brush_dot_illusion = None
+ self.brush_dot_illusion_disable = None
+
+ self.setupUi()
+ self.setup()
+
+ def setupUi(self):
+ # setting window title
+ self.setWindowTitle('Clock')
+
+ # setting window geometry
+ self.setGeometry(200, 200, 300, 300)
+
+ def setup(self):
+ # creating hour hand
+ self.hour_pointer = QPolygon(QRect(-2, 10, 4, -58))
+
+ # creating minute hand
+ self.minute_pointer = QPolygon(QRect(-1, 10, 3, -95))
+
+ # creating second hand
+ self.second_pointer = QPolygon(QRect(-1, 20, 2, -107))
+
+ self.font_face_clock_hours = QFont("Nimbus Sans", 13, QFont.Bold)
+
+ self.font_face_clock_am_pm = QFont('Nimbus Sans', 19)
+
+ self.background_image_path = os.path.join(
+ os.path.dirname(__file__),
+ "clock_face2.png"
+ )
+ self.background_image_disable_path = os.path.join(
+ os.path.dirname(__file__),
+ "clock_face2_disable.png"
+ )
+
+ self.background_image = QImage(self.background_image_path)
+ self.background_image_disable = QImage(self.background_image_disable_path)
+
+ # set time one file for the first time (indirect system ask)
+ self.setTime(QTime.currentTime())
+
+ self.qp = QPainter()
+
+ # color for minute and hour hand
+ self.hour_pointer_color = QColor(53, 76, 112, 255)
+ self.hour_pointer_disable_color = QColor(146, 146, 146, 255)
+
+ # color for minute and hour hand
+ self.minute_pointer_color = QColor(53, 76, 112, 255)
+ self.minute_pointer_disable_color = QColor(146, 146, 146, 255)
+
+ # color for second hand
+ self.second_pointer_color = QColor(255, 162, 0, 255)
+ self.second_pointer_disable_color = QColor(146, 146, 146, 255)
+
+ self.clock_face_number_color = QColor(0, 124, 202, 255)
+ self.clock_face_number_disable_color = QColor(146, 146, 146, 255)
+
+ self.clock_face_hour_color = QColor(29, 103, 188, 255)
+ self.clock_face_hour_disable_color = QColor(146, 146, 146, 255)
+
+ self.clock_face_minute_color = QColor(74, 120, 157, 255)
+ self.clock_face_minute_disable_color = QColor(146, 146, 146, 255)
+
+ self.clock_face_am_pm_color = Qt.darkGray
+ self.clock_face_am_pm_disable_color = Qt.darkGray
+
+ self.pen_am_pm = QPen(self.clock_face_am_pm_color, 2, Qt.SolidLine)
+ self.pen_am_pm_disable = QPen(self.clock_face_am_pm_disable_color, 2, Qt.SolidLine)
+
+ self.pen_square = QPen(self.clock_face_hour_color, 2, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin)
+ self.pen_round = QPen(self.clock_face_minute_color, 3.2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
+
+ self.pen_square_disable = QPen(Qt.darkGray, 2, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin)
+ self.pen_round_disable = QPen(Qt.darkGray, 3.2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
+
+ self.pen_clock_face_number = QPen(self.clock_face_number_color, 2, Qt.SolidLine)
+ self.pen_clock_face_number_disable = QPen(self.clock_face_number_disable_color, 2, Qt.SolidLine)
+
+ self.pen_dot_illusion = QPen(self.minute_pointer_color, 2, Qt.SolidLine)
+ self.pen_dot_illusion_disable = QPen(self.minute_pointer_disable_color, 2, Qt.SolidLine)
+
+ self.brush_dot_illusion = QBrush(self.minute_pointer_color, Qt.SolidPattern)
+ self.brush_dot_illusion_disable = QBrush(self.minute_pointer_disable_color, Qt.SolidPattern)
+
+ # method for paint event
+ def paintEvent(self, event):
+
+ self.qp.begin(self)
+
+ # tune up painter
+ self.qp.setRenderHint(QPainter.Antialiasing)
+
+ # # drawing background
+ self.__draw_background_image()
+
+ # translating the painter
+ self.qp.translate(self.width() / 2, self.height() / 2)
+ # # scale the painter
+ self.qp.scale(min(self.width(), self.height()) / 200, min(self.width(), self.height()) / 200)
+
+ # drawing clock face
+ self.__draw_face_clock()
+
+ # AM / PM
+ self.__draw_face_clock_am_pm()
+
+ # Clock Face Number in hard for take less CPU
+ self.__draw_face_clock_numbers()
+
+ # Pointers
+ self.__draw_pointers()
+
+ # the small circle for center illusion
+ self.__draw_small_center_illusion()
+
+ # ending the painter
+ self.qp.end()
+
+ def __draw_pointers(self):
+ # set current pen as no pen
+ self.qp.setPen(Qt.NoPen)
+ if self.isEnabled():
+ hour_pointer_color = self.hour_pointer_color
+ minute_pointer_color = self.minute_pointer_color
+ second_pointer_color = self.second_pointer_color
+ else:
+ hour_pointer_color = Qt.darkGray
+ minute_pointer_color = Qt.darkGray
+ second_pointer_color = Qt.gray
+ self.__draw_pointer(
+ second_pointer_color,
+ (6 * self.time.second()),
+ self.second_pointer
+ )
+ self.__draw_pointer(
+ hour_pointer_color,
+ (30 * (self.time.hour() + self.time.minute() / 60)),
+ self.hour_pointer
+ )
+ self.__draw_pointer(
+ minute_pointer_color,
+ (6 * (self.time.minute() + self.time.second() / 60)),
+ self.minute_pointer
+ )
+
+ def __draw_background_image(self):
+ rec = min(self.width(), self.height())
+
+ if self.isEnabled():
+ bg = self.background_image.scaled(rec, rec, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ else:
+ bg = self.background_image_disable.scaled(rec, rec, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+
+ # drawing background
+ self.qp.drawImage(int((self.width() - bg.width()) / 2),
+ int((self.height() - bg.height()) / 2),
+ bg
+ )
+
+ def __draw_pointer(self, color, rotation, pointer):
+
+ # setting brush
+ self.qp.setBrush(QBrush(color))
+
+ # saving painter
+ self.qp.save()
+
+ # rotating painter
+ self.qp.rotate(rotation)
+
+ # draw the polygon i.e hand
+ self.qp.drawConvexPolygon(pointer)
+
+ # restore the painter
+ self.qp.restore()
+
+ def __draw_face_clock(self):
+
+ if self.isEnabled():
+ pen_square = self.pen_square
+ pen_round = self.pen_round
+ else:
+ pen_square = self.pen_square_disable
+ pen_round = self.pen_round_disable
+
+ # Draw the clock face
+ for i in range(0, 60):
+ # drawing background lines
+ if (i % 5) == 0:
+ self.qp.setPen(pen_square)
+ self.qp.drawLine(76, 0, 84, 0)
+ elif (i % 1) == 0:
+ self.qp.setPen(pen_round)
+ self.qp.drawPoint(80, 0)
+ # rotating the painter
+ self.qp.rotate(6)
+
+ def __draw_face_clock_am_pm(self):
+ # AM / PM
+ self.qp.setPen(Qt.NoPen)
+ self.qp.setPen(self.pen_am_pm)
+ self.qp.setFont(self.font_face_clock_am_pm)
+
+ if self.time.hour() <= 0 <= 12:
+ self.qp.drawText(-15, 45, "AM")
+ else:
+ self.qp.drawText(-15, 45, "PM")
+
+ def __draw_small_center_illusion(self):
+ # the small circle for center illusion
+ self.qp.setPen(Qt.NoPen)
+ if self.isEnabled():
+ self.qp.setPen(self.pen_dot_illusion)
+ self.qp.setBrush(self.brush_dot_illusion)
+ else:
+ self.qp.setPen(self.pen_dot_illusion_disable)
+ self.qp.setBrush(self.brush_dot_illusion_disable)
+ self.qp.drawEllipse(-3, -3, 6, 6)
+
+ def __draw_face_clock_numbers(self):
+ self.qp.setPen(Qt.NoPen)
+ if self.isEnabled():
+ self.qp.setPen(self.pen_clock_face_number)
+ else:
+ self.qp.setPen(self.pen_clock_face_number_disable)
+
+ self.qp.setFont(self.font_face_clock_hours)
+
+ # Clock Face Number location is set in hard for take less CPU (Yes i know, me too ...)
+ self.qp.drawText(-9, -62, "12")
+ self.qp.drawText(29, -52, "1")
+ self.qp.drawText(55, -29, "2")
+ self.qp.drawText(65, 7, "3")
+ self.qp.drawText(54, 40, "4")
+ self.qp.drawText(30, 63, "5")
+ self.qp.drawText(-4, 72, "6")
+ self.qp.drawText(-38, 64, "7")
+ self.qp.drawText(-64, 40, "8")
+ self.qp.drawText(-73, 6, "9")
+ self.qp.drawText(-66, -25, "10")
+ self.qp.drawText(-42, -50, "11")
+
+ def updateTime(self):
+ self.timeChanged.emit(QTime.currentTime())
+
+
+# Driver code
+if __name__ == '__main__':
+ def update_time():
+ clock.setTime(QTime.currentTime())
+ clock.update()
+
+
+ app = QApplication(sys.argv)
+
+ # creating a clock object
+ clock = AnalogClock()
+
+ # creating a timer object
+ timer = QTimer()
+ # adding action to the timer
+ # update the whole code
+ timer.timeout.connect(update_time)
+ # setting start time of timer i.e 1 second
+ timer.start(1000)
+
+ # show
+ clock.show()
+
+ exit(app.exec_())
diff --git a/Under Construction/Date and Time.app/Resources/widget_timezone_world_map.py b/Under Construction/Date and Time.app/Resources/widget_timezone_world_map.py
new file mode 100644
index 000000000..d2692f7c9
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/widget_timezone_world_map.py
@@ -0,0 +1,298 @@
+import os
+import sys
+
+from PyQt5.QtCore import pyqtSignal, pyqtProperty, Qt
+from PyQt5.QtGui import QPainter, QImage, QColor, QPen
+from PyQt5.QtWidgets import QWidget, QApplication
+
+
+class TimeZoneWorldMap(QWidget):
+ timeZoneChanged = pyqtSignal(int)
+ TimeZoneSelectionChanged = pyqtSignal()
+ TimeZoneClosestCityChanged = pyqtSignal(list)
+
+ # constructor
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
+
+ # setting window title
+ self.setWindowTitle('TimeZone World Map')
+
+ self.setMouseTracking(True)
+ self.bg = None
+ self.zone_location = None
+ self.latitude_location = None
+ self.longitude_location = None
+
+ self.state_over = {
+ "-12": False,
+ "-11": False,
+ "-10": False,
+ "-9": False,
+ "-8": False,
+ "-7": False,
+ "-6": False,
+ "-5": False,
+ "-4": False,
+ "-3": False,
+ "-2": False,
+ "-1": False,
+ "utc": False,
+ "+1": False,
+ "+2": False,
+ "+3": False,
+ "+4": False,
+ "+5": False,
+ "+6": False,
+ "+7": False,
+ "+8": False,
+ "+9": False,
+ "+10": False,
+ "+11": False,
+ "+12": False,
+ }
+
+ self.__timezone_selection = None
+ self.TimeZoneSelection = None
+
+ self.zone1970_db = None
+ self.prime_meridian = None
+ self.equator_location = None
+ self.north_location = None
+ self.south_location = None
+ self.west_location = None
+ self.east_location = None
+ self.lat_ratio = None
+ self.lng_ratio = None
+
+ self.import_zone1970_db()
+
+ def import_zone1970_db(self):
+ # Start by reset the internal db
+ self.zone1970_db = {}
+
+ # This location is the default on of the project, can be more system with TZInfo python module
+ with open("/usr/share/zoneinfo/zone1970.tab") as zone1970_file_descriptor:
+ imported_data = zone1970_file_descriptor.readlines()
+
+ # codes coordinates TZ comments
+ for line in imported_data:
+ # Ignore line it start by a #
+ if line.startswith("#"):
+ continue
+
+ # For each line, we create a list os elements split by TAB
+ data = line.strip("\n").split("\t")
+
+ # it have 2 formats, we just care about degree, and not use minutes coordinates
+ # ±DDMM±DDDMM or ±DDMMSS±DDDMMSS,
+ if len(data[1]) == len("±DDMM±DDDMM"):
+ lat = int(f"{data[1][0]}{data[1][1]}{data[1][2]}")
+ lng = int(f"{data[1][5]}{data[1][6]}{data[1][7]}")
+ else:
+ lat = int(f"{data[1][0]}{data[1][1]}{data[1][2]}")
+ lng = int(f"{data[1][7]}{data[1][8]}{data[1][9]}{data[1][10]}")
+
+ # Here for debug
+ # print(f"lat:{lat} lng:{lng}")
+
+ # Update the internal DB where the keyname is the TimeZone as use by TZ var
+ self.zone1970_db[f"{data[2]}"] = {
+ "code": data[0].split(","),
+ "latitude": lat,
+ "longitude": lng,
+ }
+
+ # some time it have a commentary, the use of try is for speed up
+ try:
+ self.zone1970_db[f"{data[2]}"]["comments"] = data[3]
+ except IndexError:
+ pass
+
+ @property
+ def TimeZoneSelection(self):
+ return self.__timezone_selection
+
+ @TimeZoneSelection.setter
+ def TimeZoneSelection(self, value):
+ if value is None:
+ value = 0
+ if self.__timezone_selection != value:
+ self.__timezone_selection = value
+ self.TimeZoneSelectionChanged.emit()
+
+ def setTimeZoneSelection(self, value):
+ self.TimeZoneSelection = value
+
+ # method for paint event
+ def paintEvent(self, event):
+ # start by reset all our value
+ self.zone_location = {}
+ self.longitude_location = {}
+ self.latitude_location = {}
+
+ # getting minimum of width and height
+ rec = max(self.width(), self.height())
+
+ # creating a painter object
+ painter = QPainter(self)
+
+ # Load image dinamically
+ if rec >= 1280:
+ map_file = "2560px-World_map_with_nations"
+ elif rec >= 640:
+ map_file = "1280px-World_map_with_nations"
+ elif rec:
+ map_file = "640px-World_map_with_nations"
+ else:
+ map_file = "320px-World_map_with_nations"
+
+ if self.isEnabled():
+ map_file += ".png"
+ else:
+ map_file += "_disable.png"
+
+ # The image is scale to fit allowed size
+ self.bg = QImage(os.path.join(
+ os.path.dirname(__file__),
+ map_file
+ )).scaled(self.width(), self.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ # The location image is important that is the center subtract by the halt image size
+ painter.drawImage(int(self.width() / 2 - self.bg.width() / 2),
+ int(self.height() / 2 - self.bg.height() / 2),
+ self.bg
+ )
+
+ # That is the ideal TimeZone size
+ timezone_grid_size = self.bg.width() / 24
+
+ # We can predict they cardinal position
+ self.north_location = int(self.height() / 2 - self.bg.height() / 2)
+ self.south_location = self.north_location + self.bg.width()
+ self.west_location = int(self.width() / 2 - self.bg.width() / 2)
+ self.east_location = self.west_location + self.bg.width()
+
+ self.prime_meridian = self.west_location + int(self.bg.width() / 2)
+ self.equator_location = self.north_location + int(self.bg.height() / 2)
+
+ self.lat_ratio = 180 / self.bg.height()
+ self.lng_ratio = 360 / self.bg.width()
+
+ # Trace UTC
+ self.zone_location["utc"] = {}
+ self.zone_location["utc"]['left'] = self.prime_meridian - int(timezone_grid_size / 2)
+ self.zone_location["utc"]['right'] = self.prime_meridian + int(timezone_grid_size / 2)
+ utc_start = self.prime_meridian - int(timezone_grid_size / 2)
+ utc_stop = self.prime_meridian + int(timezone_grid_size / 2)
+
+ # Time Zone Area
+ for utc_plus in range(1, 13):
+ # keep a memory of Time zone location
+ x = int(utc_stop + (timezone_grid_size * utc_plus))
+
+ self.zone_location[f"+{utc_plus}"] = {}
+ self.zone_location[f"+{utc_plus}"]['left'] = x - timezone_grid_size
+ self.zone_location[f"+{utc_plus}"]['right'] = x
+
+ for utc_minus in range(1, 12):
+ # Key a memory of Tome zone location
+ x = int(utc_start - (timezone_grid_size * utc_minus))
+
+ self.zone_location[f"-{utc_minus}"] = {}
+ self.zone_location[f"-{utc_minus}"]['left'] = x
+ self.zone_location[f"-{utc_minus}"]['right'] = x + timezone_grid_size
+
+ # Trace the selection the MouseOver
+ for key, value in self.state_over.items():
+ if value is True:
+ painter.setPen(QPen(QColor(255, 255, 255, 255), 1, Qt.SolidLine))
+ painter.setBrush(QColor(255, 255, 255, 150))
+ painter.drawRect(
+ int(self.zone_location[key]['left'] + 1),
+ int(self.north_location + 1),
+ 32,
+ 32,
+ )
+ try:
+ if self.TimeZoneSelection == f"{key}":
+ painter.setPen(QPen(QColor(0, 39, 60, 255), 1, Qt.SolidLine))
+ painter.setBrush(QColor(0, 39, 60, 127))
+ painter.drawRect(
+ int(self.zone_location[key]['left'] + 1),
+ int(self.north_location + 1),
+ int(timezone_grid_size - 2),
+ int(self.bg.height() - 1),
+ )
+
+ except KeyError:
+ # YES it is correct,
+ # you should show me how you do with IF statement... are you sure of the performance ?
+ pass
+
+ # ending the painter
+ painter.end()
+
+ def mousePressEvent(self, event):
+ if self.west_location <= event.pos().x() < self.east_location:
+ for key, value in self.zone_location.items():
+ if self.zone_location[key]['left'] <= event.pos().x() < self.zone_location[key]['right']:
+ self.setTimeZoneSelection(f"{key}")
+ self.update()
+ super().mousePressEvent(event)
+
+ #
+ def mouseMoveEvent(self, event):
+ if self.west_location <= event.pos().x() < self.east_location:
+ for key, value in self.zone_location.items():
+ if self.zone_location[key]['left'] <= event.pos().x() < self.zone_location[key]['right']:
+ self.state_over[key] = True
+ self.update()
+ else:
+ self.state_over[key] = False
+ self.update()
+
+ super().mouseMoveEvent(event)
+
+ def mouseReleaseEvent(self, event):
+ if self.west_location <= event.pos().x() < self.east_location:
+ lng = (event.pos().x() - self.prime_meridian) * self.lng_ratio
+ lat = (event.pos().y() - self.equator_location) * self.lat_ratio
+ lat = lat * -1
+ # print(f"lat:{lat} lng:{lng}")
+
+ tol = 5
+ found = False
+ closest = []
+ while not found:
+ for key, item in self.zone1970_db.items():
+ if int(lng) - tol <= item["longitude"] <= int(lng) + tol and \
+ int(lat) - tol <= item["latitude"] <= int(lat) + tol:
+ if key not in closest:
+ closest.append(key)
+ if len(closest) > 10:
+ found = True
+ else:
+ tol += 1
+
+ if closest and len(closest) >= 1:
+ self.TimeZoneClosestCityChanged.emit(closest)
+
+ for key, value in self.zone_location.items():
+ if self.zone_location[key]['left'] <= event.pos().x() < self.zone_location[key]['right']:
+ self.setTimeZoneSelection(f"{key}")
+ self.update()
+
+ super().mouseReleaseEvent(event)
+
+
+# Driver code
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ # creating a clock object
+ win = TimeZoneWorldMap()
+
+ # show
+ win.show()
+
+ exit(app.exec_())
diff --git a/Under Construction/Date and Time.app/Resources/worker_ip_api.py b/Under Construction/Date and Time.app/Resources/worker_ip_api.py
new file mode 100644
index 000000000..ae854ed7b
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/worker_ip_api.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+from PyQt5.QtCore import pyqtSignal, QObject, QUrl
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
+
+
+class IpApiWorker(QObject):
+ finished = pyqtSignal()
+ error = pyqtSignal(object)
+ updated_timezone = pyqtSignal(object)
+
+ def __init__(self):
+ super().__init__()
+ self.host = "http://ip-api.com"
+ self.path = "line?fields=timezone"
+ self.nam = None
+
+ def refresh(self):
+ def handleResponse(reply):
+ er = reply.error()
+
+ if er == QNetworkReply.NoError:
+ try:
+ self.updated_timezone.emit(
+ bytes(reply.readAll()).decode("utf-8").strip("\n")
+ )
+ except RuntimeError as er:
+ print("RuntimeError: wrapped C/C++ object of type IpApiWorker has been deleted")
+
+ else:
+ self.error.emit(f""
+ f""
+ f""
+ f""
+ f""
+ f"{er} - {reply.errorString()}"
+ f""
+ f"
"
+ f""
+ f""
+ )
+
+ req = QNetworkRequest(QUrl(f"{self.host}/{self.path}"))
+
+ self.nam = QNetworkAccessManager()
+ self.nam.finished.connect(handleResponse)
+ self.nam.get(req)
+
+ self.finished.emit()
diff --git a/Under Construction/Date and Time.app/Resources/worker_ntp_client.py b/Under Construction/Date and Time.app/Resources/worker_ntp_client.py
new file mode 100644
index 000000000..49bbe2e5d
--- /dev/null
+++ b/Under Construction/Date and Time.app/Resources/worker_ntp_client.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+
+from datetime import datetime
+from socket import gaierror
+from PyQt5.QtCore import pyqtSignal, QObject, QDate, QDateTime
+from ntplib import NTPClient
+from ntplib import NTPException, NTPRolloverException
+
+
+class NtpClientWorker(QObject):
+ finished = pyqtSignal()
+ error = pyqtSignal(object)
+ updated_datetime = pyqtSignal(object)
+ updated_date = pyqtSignal(object)
+
+ def __init__(self, host, version=2, port=123, timeout=5):
+ super().__init__()
+ self.host = host
+ self.version = version
+ self.port = port
+ self.timeout = timeout
+
+ self.ntp_client = NTPClient()
+
+ def refresh(self):
+ try:
+ response = self.ntp_client.request(
+ host=self.host,
+ version=self.version,
+ port=self.port,
+ timeout=self.timeout
+ )
+
+ imported_timestamp = datetime.fromtimestamp(response.tx_time)
+
+ self.updated_date.emit(
+ QDate(
+ imported_timestamp.year,
+ imported_timestamp.month,
+ imported_timestamp.day,
+ )
+ )
+ self.updated_datetime.emit(
+ QDateTime(
+ imported_timestamp.year,
+ imported_timestamp.month,
+ imported_timestamp.day,
+ imported_timestamp.hour,
+ imported_timestamp.minute,
+ imported_timestamp.second,
+ )
+ )
+ # self.error.emit("")
+ except (gaierror, NTPException, NTPRolloverException) as error:
+ self.error.emit(f""
+ f""
+ f""
+ f""
+ f"{str(error)}"
+ f""
+ f"
"
+ f""
+ f""
+ )
+
+ self.finished.emit()