基于PyQt6開發(fā)Windows時間校時同步工具
一、概述:時間同步的重要性與工具價值
在現(xiàn)代計算機(jī)應(yīng)用中,準(zhǔn)確的時間同步至關(guān)重要。無論是金融交易、日志記錄還是分布式系統(tǒng)協(xié)同,毫秒級的時間誤差都可能導(dǎo)致嚴(yán)重問題。Windows系統(tǒng)雖然內(nèi)置了時間同步功能,但存在以下痛點(diǎn):
- 同步頻率不可控(默認(rèn)每周一次)
- 服務(wù)器選擇有限
- 缺乏可視化操作界面
- 無法實(shí)時查看同步狀態(tài)
本文介紹的Windows智能校時工具通過PyQt6實(shí)現(xiàn)了以下突破:
- 多NTP服務(wù)器智能選擇
- 可配置的自動同步周期
- 直觀的圖形化界面
- 實(shí)時狀態(tài)監(jiān)控
- 服務(wù)器連通性測試
二、功能全景圖
2.1 核心功能矩陣
| 功能模塊 | 實(shí)現(xiàn)方式 | 技術(shù)亮點(diǎn) |
|---|---|---|
| 時間顯示 | QTimer實(shí)時刷新 | 多線程避免UI阻塞 |
| NTP同步 | ntplib+win32api | 雙保險機(jī)制(NTP+HTTP備用) |
| 自動同步 | QTimer定時觸發(fā) | 智能間隔配置(1-1440分鐘) |
| 服務(wù)器測試 | 多線程并行測試 | 延遲毫秒級統(tǒng)計 |
2.2 特色功能詳解
- 智能降級策略:當(dāng)NTP協(xié)議同步失敗時,自動切換HTTP API備用方案
- 跨線程通信:通過PyQt信號槽機(jī)制實(shí)現(xiàn)線程安全的狀態(tài)更新
- 系統(tǒng)級集成:調(diào)用win32api直接修改系統(tǒng)時間(需管理員權(quán)限)
三、效果展示
3.1 主界面布局

- 頂部:大字號時間日期顯示
- 中部:NTP服務(wù)器配置區(qū)
- 底部:操作按鈕與狀態(tài)欄
3.2 服務(wù)器測試對話框

- 可視化服務(wù)器響應(yīng)延遲
- 成功/失敗狀態(tài)直觀標(biāo)識
四、實(shí)現(xiàn)步驟詳解
4.1 環(huán)境準(zhǔn)備
pip install pyqt6 ntplib requests pywin32
4.2 核心類結(jié)構(gòu)

4.3 關(guān)鍵實(shí)現(xiàn)步驟
- 時間獲取雙保險機(jī)制
def do_sync(self):
try:
# 首選NTP協(xié)議
response = self.ntp_client.request(ntp_server, version=3, timeout=5)
...
except ntplib.NTPException:
# 備用HTTP方案
ntp_time = self.get_time_from_http()
- 線程安全的UI更新
class TimeSyncSignals(QObject):
update_status = pyqtSignal(str) # 狀態(tài)更新信號
sync_complete = pyqtSignal(bool, str, str) # 完成信號
# 子線程通過信號觸發(fā)主線程UI更新
self.signals.update_status.emit("正在同步...")
五、代碼深度解析
5.1 NTP時間轉(zhuǎn)換關(guān)鍵代碼
# 獲取NTP響應(yīng)并轉(zhuǎn)換時區(qū)
response = self.ntp_client.request(server, version=3, timeout=5)
ntp_time = datetime.datetime.fromtimestamp(response.tx_time) # 本地時間
utc_time = datetime.datetime.utcfromtimestamp(response.tx_time) # UTC時間
# 設(shè)置系統(tǒng)時間(必須使用UTC)
win32api.SetSystemTime(
utc_time.year, utc_time.month, utc_time.isoweekday() % 7,
utc_time.day, utc_time.hour, utc_time.minute,
utc_time.second, int(utc_time.microsecond / 1000)
)
5.2 自定義事件處理
class ServerTestEvent(QEvent):
def __init__(self, text):
super().__init__(QEvent.Type.User)
self.text = text
# 在子線程中安全更新UI
def add_result(self, text):
QApplication.instance().postEvent(self, ServerTestEvent(text))
def customEvent(self, event):
if isinstance(event, ServerTestEvent):
self.result_list.addItem(event.text)
5.3 樣式美化技巧
/* 按鈕漸變效果 */
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #4CAF50, stop:1 #45a049);
border-radius: 4px;
color: white;
}
/* 列表項懸停效果 */
QListWidget::item:hover {
background: #e0e0e0;
}
六、源碼下載與使用說明
6.1 完整源碼
import sys
import datetime
import threading
import requests
import ntplib
import win32api
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QComboBox, QPushButton, QSpinBox, QCheckBox,
QListWidget, QMessageBox, QGroupBox, QScrollArea, QDialog)
from PyQt6.QtCore import Qt, QTimer, QDateTime, QObject, pyqtSignal, QEvent
from PyQt6.QtGui import QFont, QIcon
class TimeSyncSignals(QObject):
update_status = pyqtSignal(str)
sync_complete = pyqtSignal(bool, str, str)
class TimeSyncApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("?? Windows 自動校時工具")
self.setGeometry(100, 100, 650, 500)
# 初始化變量
self.sync_in_progress = False
self.auto_sync_timer = QTimer()
self.auto_sync_timer.timeout.connect(self.sync_time)
self.signals = TimeSyncSignals()
# 連接信號槽
self.signals.update_status.connect(self.update_status_bar)
self.signals.sync_complete.connect(self.handle_sync_result)
# 創(chuàng)建主界面
self.init_ui()
# 啟動時間更新定時器
self.time_update_timer = QTimer()
self.time_update_timer.timeout.connect(self.update_current_time)
self.time_update_timer.start(1000)
# 初始化NTP客戶端
self.ntp_client = ntplib.NTPClient()
def init_ui(self):
# 主窗口布局
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(15)
main_layout.setContentsMargins(20, 20, 20, 20)
# 標(biāo)題
title_label = QLabel("?? Windows 自動校時工具")
title_font = QFont("Segoe UI", 18, QFont.Weight.Bold)
title_label.setFont(title_font)
title_label.setStyleSheet("color: #4CAF50;")
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_layout.addWidget(title_label)
# 當(dāng)前時間顯示
time_group = QGroupBox("??? 當(dāng)前系統(tǒng)時間")
time_group.setStyleSheet("QGroupBox { font-weight: bold; }")
time_layout = QVBoxLayout(time_group)
self.current_time_label = QLabel()
time_font = QFont("Segoe UI", 24)
self.current_time_label.setFont(time_font)
self.current_time_label.setStyleSheet("color: #2196F3;")
self.current_time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
time_layout.addWidget(self.current_time_label)
self.current_date_label = QLabel()
date_font = QFont("Segoe UI", 12)
self.current_date_label.setFont(date_font)
self.current_date_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
time_layout.addWidget(self.current_date_label)
main_layout.addWidget(time_group)
# 同步選項
sync_group = QGroupBox("?? 同步選項")
sync_group.setStyleSheet("QGroupBox { font-weight: bold; }")
sync_layout = QVBoxLayout(sync_group)
# NTP服務(wù)器選擇
ntp_layout = QHBoxLayout()
ntp_label = QLabel("NTP 服務(wù)器:")
ntp_label.setFont(QFont("Segoe UI", 10))
self.ntp_combo = QComboBox()
self.ntp_combo.addItems([
"time.windows.com",
"time.nist.gov",
"pool.ntp.org",
"time.google.com",
"time.apple.com",
"ntp.aliyun.com",
"ntp.tencent.com"
])
self.ntp_combo.setCurrentIndex(0)
self.ntp_combo.setFont(QFont("Segoe UI", 10))
ntp_layout.addWidget(ntp_label)
ntp_layout.addWidget(self.ntp_combo)
sync_layout.addLayout(ntp_layout)
# 同步間隔
interval_layout = QHBoxLayout()
interval_label = QLabel("自動同步間隔 (分鐘):")
interval_label.setFont(QFont("Segoe UI", 10))
self.interval_spin = QSpinBox()
self.interval_spin.setRange(1, 1440)
self.interval_spin.setValue(60)
self.interval_spin.setFont(QFont("Segoe UI", 10))
interval_layout.addWidget(interval_label)
interval_layout.addWidget(self.interval_spin)
sync_layout.addLayout(interval_layout)
# 自動同步開關(guān)
self.auto_sync_check = QCheckBox("啟用自動同步 ??")
self.auto_sync_check.setFont(QFont("Segoe UI", 10))
self.auto_sync_check.stateChanged.connect(self.toggle_auto_sync)
sync_layout.addWidget(self.auto_sync_check)
main_layout.addWidget(sync_group)
# 按鈕區(qū)域
button_layout = QHBoxLayout()
self.sync_button = QPushButton("立即同步時間 ?")
self.sync_button.setFont(QFont("Segoe UI", 10, QFont.Weight.Bold))
self.sync_button.setStyleSheet("background-color: #4CAF50; color: white;")
self.sync_button.clicked.connect(self.sync_time)
button_layout.addWidget(self.sync_button)
self.server_test_button = QPushButton("測試服務(wù)器 ???")
self.server_test_button.setFont(QFont("Segoe UI", 10))
self.server_test_button.clicked.connect(self.show_server_test_dialog)
button_layout.addWidget(self.server_test_button)
main_layout.addLayout(button_layout)
# 狀態(tài)欄
self.status_bar = self.statusBar()
self.status_bar.setFont(QFont("Segoe UI", 9))
self.status_bar.showMessage("?? 就緒")
# 初始化當(dāng)前時間顯示
self.update_current_time()
def update_current_time(self):
now = QDateTime.currentDateTime()
self.current_time_label.setText(now.toString("HH:mm:ss"))
self.current_date_label.setText(now.toString("yyyy-MM-dd dddd"))
def toggle_auto_sync(self, state):
if state == Qt.CheckState.Checked.value:
minutes = self.interval_spin.value()
self.auto_sync_timer.start(minutes * 60000) # 轉(zhuǎn)換為毫秒
self.status_bar.showMessage(f"?? 自動同步已啟用,每 {minutes} 分鐘同步一次")
else:
self.auto_sync_timer.stop()
self.status_bar.showMessage("?? 自動同步已禁用")
def sync_time(self):
if self.sync_in_progress:
return
self.sync_in_progress = True
self.sync_button.setEnabled(False)
self.signals.update_status.emit("?? 正在同步時間...")
# 在新線程中執(zhí)行同步操作
sync_thread = threading.Thread(target=self.do_sync, daemon=True)
sync_thread.start()
def do_sync(self):
ntp_server = self.ntp_combo.currentText()
try:
# 使用NTP協(xié)議獲取時間
response = self.ntp_client.request(ntp_server, version=3, timeout=5)
# 將NTP時間轉(zhuǎn)換為本地時間
ntp_time = datetime.datetime.fromtimestamp(response.tx_time)
utc_time = datetime.datetime.utcfromtimestamp(response.tx_time)
print(f"從NTP服務(wù)器獲取的時間 (UTC): {utc_time}")
print(f"轉(zhuǎn)換為本地時間: {ntp_time}")
# 設(shè)置系統(tǒng)時間 (需要UTC時間)
win32api.SetSystemTime(
utc_time.year, utc_time.month, utc_time.isoweekday() % 7,
utc_time.day, utc_time.hour, utc_time.minute,
utc_time.second, int(utc_time.microsecond / 1000)
)
# 獲取設(shè)置后的系統(tǒng)時間
new_time = datetime.datetime.now()
print(f"設(shè)置后的系統(tǒng)時間: {new_time}")
# 通過信號發(fā)送結(jié)果
self.signals.sync_complete.emit(
True,
f"? 時間同步成功! 服務(wù)器: {ntp_server}",
f"時間已成功同步到 {ntp_server}\nNTP時間 (UTC): {utc_time}\n本地時間: {ntp_time}\n設(shè)置后系統(tǒng)時間: {new_time}"
)
except ntplib.NTPException as e:
# NTP失敗,嘗試備用方法
try:
self.signals.update_status.emit("?? NTP同步失敗,嘗試備用方法...")
ntp_time = self.get_time_from_http()
if ntp_time:
utc_time = ntp_time.astimezone(datetime.timezone.utc).replace(tzinfo=None)
print(f"從HTTP獲取的時間 (本地): {ntp_time}")
print(f"轉(zhuǎn)換為UTC時間: {utc_time}")
win32api.SetSystemTime(
utc_time.year, utc_time.month, utc_time.isoweekday() % 7,
utc_time.day, utc_time.hour, utc_time.minute,
utc_time.second, int(utc_time.microsecond / 1000)
)
new_time = datetime.datetime.now()
print(f"設(shè)置后的系統(tǒng)時間: {new_time}")
self.signals.sync_complete.emit(
True,
"? 時間同步成功! (備用方法)",
f"時間已通過備用方法同步\nHTTP時間: {ntp_time}\nUTC時間: {utc_time}\n設(shè)置后系統(tǒng)時間: {new_time}"
)
else:
raise Exception("所有同步方法均失敗")
except Exception as e:
error_msg = f"? 同步失敗: {str(e)}"
print(error_msg)
self.signals.sync_complete.emit(
False,
error_msg,
f"時間同步失敗:\n{str(e)}"
)
finally:
self.sync_in_progress = False
self.sync_button.setEnabled(True)
def get_time_from_http(self):
try:
# 嘗試使用世界時間API
response = requests.get("http://worldtimeapi.org/api/timezone/Etc/UTC", timeout=5)
data = response.json()
utc_time = datetime.datetime.fromisoformat(data["datetime"])
return utc_time.astimezone() # 轉(zhuǎn)換為本地時區(qū)
except:
try:
# 嘗試使用阿里云API
response = requests.get("http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp", timeout=5)
data = response.json()
timestamp = int(data["data"]["t"]) / 1000
return datetime.datetime.fromtimestamp(timestamp)
except:
return None
def update_status_bar(self, message):
self.status_bar.showMessage(message)
def handle_sync_result(self, success, status_msg, detail_msg):
self.status_bar.showMessage(status_msg)
if success:
QMessageBox.information(self, "成功", detail_msg)
else:
QMessageBox.critical(self, "錯誤", detail_msg)
def show_server_test_dialog(self):
self.test_dialog = ServerTestDialog(self)
self.test_dialog.show()
class ServerTestDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("??? NTP服務(wù)器測試工具")
self.setWindowModality(Qt.WindowModality.NonModal)
self.setMinimumSize(600, 400)
self.ntp_client = ntplib.NTPClient()
self.test_in_progress = False
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
layout.setSpacing(10)
# 標(biāo)題
title_label = QLabel("NTP服務(wù)器連通性測試")
title_label.setFont(QFont("Segoe UI", 14, QFont.Weight.Bold))
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(title_label)
# 說明
info_label = QLabel("測試各NTP服務(wù)器的響應(yīng)時間和可用性,結(jié)果將顯示在下方列表中:")
info_label.setFont(QFont("Segoe UI", 10))
info_label.setWordWrap(True)
layout.addWidget(info_label)
# 結(jié)果列表
self.result_list = QListWidget()
self.result_list.setFont(QFont("Consolas", 9))
self.result_list.setStyleSheet("""
QListWidget {
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
}
""")
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setWidget(self.result_list)
layout.addWidget(scroll_area, 1)
# 進(jìn)度標(biāo)簽
self.progress_label = QLabel("準(zhǔn)備開始測試...")
self.progress_label.setFont(QFont("Segoe UI", 9))
self.progress_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.progress_label)
# 按鈕區(qū)域
button_layout = QHBoxLayout()
button_layout.setSpacing(15)
self.test_button = QPushButton("開始測試")
self.test_button.setFont(QFont("Segoe UI", 10))
self.test_button.setStyleSheet("""
QPushButton {
background-color: #4CAF50;
color: white;
padding: 5px 15px;
border: none;
border-radius: 4px;
}
QPushButton:disabled {
background-color: #cccccc;
}
""")
self.test_button.clicked.connect(self.start_test)
close_button = QPushButton("關(guān)閉")
close_button.setFont(QFont("Segoe UI", 10))
close_button.setStyleSheet("""
QPushButton {
background-color: #f44336;
color: white;
padding: 5px 15px;
border: none;
border-radius: 4px;
}
""")
close_button.clicked.connect(self.close)
button_layout.addStretch(1)
button_layout.addWidget(self.test_button)
button_layout.addWidget(close_button)
button_layout.addStretch(1)
layout.addLayout(button_layout)
def start_test(self):
if self.test_in_progress:
return
self.test_in_progress = True
self.test_button.setEnabled(False)
self.result_list.clear()
self.progress_label.setText("測試進(jìn)行中...")
servers = [
"time.windows.com",
"time.nist.gov",
"pool.ntp.org",
"time.google.com",
"time.apple.com",
"ntp.aliyun.com",
"ntp.tencent.com"
]
print("\n" + "="*50)
print("Starting NTP server connectivity test...")
print("="*50)
test_thread = threading.Thread(target=self.run_server_tests, args=(servers,), daemon=True)
test_thread.start()
def run_server_tests(self, servers):
for server in servers:
self.add_result(f"正在測試 {server}...")
print(f"\nTesting server: {server}")
try:
start_time = datetime.datetime.now()
response = self.ntp_client.request(server, version=3, timeout=5)
end_time = datetime.datetime.now()
latency = (end_time - start_time).total_seconds() * 1000 # 毫秒
ntp_time = datetime.datetime.fromtimestamp(response.tx_time)
result_text = (f"? {server} - 延遲: {latency:.2f}ms - "
f"時間: {ntp_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Server {server} test successful")
print(f" Latency: {latency:.2f}ms")
print(f" NTP Time: {ntp_time}")
self.add_result(result_text)
except ntplib.NTPException as e:
error_text = f"? {server} - NTP錯誤: {str(e)}"
print(f"Server {server} test failed (NTP error): {str(e)}")
self.add_result(error_text)
except Exception as e:
error_text = f"? {server} - 錯誤: {str(e)}"
print(f"Server {server} test failed (General error): {str(e)}")
self.add_result(error_text)
print("\n" + "="*50)
print("NTP server testing completed")
print("="*50 + "\n")
self.test_complete()
def add_result(self, text):
# 使用QMetaObject.invokeMethod確保線程安全
QApplication.instance().postEvent(self, ServerTestEvent(text))
def test_complete(self):
QApplication.instance().postEvent(self, ServerTestCompleteEvent())
def customEvent(self, event):
if isinstance(event, ServerTestEvent):
self.result_list.addItem(event.text)
self.result_list.scrollToBottom()
elif isinstance(event, ServerTestCompleteEvent):
self.test_in_progress = False
self.test_button.setEnabled(True)
self.progress_label.setText("測試完成")
print("All server tests completed")
class ServerTestEvent(QEvent):
def __init__(self, text):
super().__init__(QEvent.Type.User)
self.text = text
class ServerTestCompleteEvent(QEvent):
def __init__(self):
super().__init__(QEvent.Type.User + 1)
if __name__ == "__main__":
app = QApplication(sys.argv)
# 設(shè)置應(yīng)用程序樣式
app.setStyle("Fusion")
# 設(shè)置全局字體
font = QFont("Segoe UI", 10)
app.setFont(font)
window = TimeSyncApp()
window.show()
sys.exit(app.exec())
6.2 運(yùn)行要求
- Windows 7及以上系統(tǒng)
- Python 3.8+
- 管理員權(quán)限(修改系統(tǒng)時間需要)
6.3 編譯為EXE
pyinstaller --onefile --windowed --icon=time.ico timesync.py
七、總結(jié)與拓展方向
7.1 項目亮點(diǎn)
- 采用PyQt6現(xiàn)代GUI框架,界面美觀
- 雙時間源冗余設(shè)計,可靠性高
- 完整的異常處理機(jī)制
- 符合Windows系統(tǒng)規(guī)范的時間設(shè)置
7.2 優(yōu)化建議
- 增加本地時間服務(wù)器配置
- 實(shí)現(xiàn)時間偏差歷史記錄
- 添加UTC/GMT時區(qū)切換
- 支持Linux/MacOS跨平臺
時間同步看似簡單,實(shí)則涉及操作系統(tǒng)底層、網(wǎng)絡(luò)協(xié)議、多線程編程等多個技術(shù)領(lǐng)域。本項目的價值不僅在于實(shí)用工具開發(fā),更是PyQt6高級應(yīng)用的典型案例。建議讀者在此基礎(chǔ)上深入探索Windows系統(tǒng)API和NTP協(xié)議的高級用法。
以上就是基于PyQt6開發(fā)Windows時間校時同步工具的詳細(xì)內(nèi)容,更多關(guān)于PyQt6 Windows時間校時的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于python中remove的一些坑小結(jié)
這篇文章主要給大家介紹了關(guān)于python中remove的一些坑,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
如何利用python實(shí)現(xiàn)windows的批處理及文件夾操作
最近工作中需要幾個腳本運(yùn)行其他程序,幾乎像一個Windows批處理文件,這篇文章主要給大家介紹了關(guān)于如何利用python實(shí)現(xiàn)windows的批處理及文件夾操作的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01
基于Python實(shí)現(xiàn)一個專業(yè)的密碼強(qiáng)度檢測器
這篇文章主要為大家詳細(xì)介紹了基于Python實(shí)現(xiàn)一個專業(yè)的密碼強(qiáng)度檢測器,可以用于評估密碼的安全性和強(qiáng)度,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-12-12
python搭建服務(wù)器實(shí)現(xiàn)兩個Android客戶端間收發(fā)消息
這篇文章主要為大家詳細(xì)介紹了python搭建服務(wù)器實(shí)現(xiàn)兩個Android客戶端間收發(fā)消息,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04
Python與xlwings黃金組合處理Excel各種數(shù)據(jù)和自動化任務(wù)
這篇文章主要為大家介紹了Python與xlwings黃金組合處理Excel各種數(shù)據(jù)和自動化任務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-12-12

