天天看点

【Python网络编程】-Linux及Windows系统时间同步软件

  

摘要:使用 wxFormBuilder 搭建软件框架,Python 3.6 编程实现了Linux及Windows系统时间同步软件设计,可用于系统内上位机、板卡及计算机的系统时间同步。

  

  

1 引言

    时间同步是计算机应用系统一个最基本的要求,集中式系统时间无二义性,而分布式系统本身没有标准的时间统一,所以必须建立分布式系统的时间统一系统。时间同步分为系统级时间同步和装置级时间同步,装置级时间同步较为简单,只要把原子钟产生的同步脉冲信号1PPS输入到相应的设备即可,系统级时间同步主要是针对系统内的上位机及计算机系统时间的同步。

2 软件设计原理

                         

【Python网络编程】-Linux及Windows系统时间同步软件

图1 系统级时间同步设计原理

    系统级时间同步设计原理如图1所示。基于NTP/SNTP协议的网络时间服务器,可以从GPS卫星上获取标准时钟信号,并将这些信息在网络中传输。NTP/SNTP协议除了可以估算数据包在网络上的往返延迟外,还可独立地估算本地计算机时钟偏差,从而实现在网络上的高精度计算机时间校时。NTP/SNTP协议是一种TCP/IP协议,其本身的传输基于UDP,保留端口号123。同步精度在ms量级。

    设计原理如下,首先创建网络时间客户端,客户端调用socket创建UDP套接字,用于连接网络时间服务器。然后,客户端向网络时间服务器发送请求,服务器返回包含时间信息的数据包,客户端对收到的数据包进行解析处理,计算得到数据包在网络上的往返传播延时和本地时钟补偿延时等参数,从而得到精确的本地时间,最后调用相关命令对计算机系统(Windows和Linux)的日期和时间进行修改,以实现时间同步。对于Linux系统的时间同步,需要通过SSH连接登陆到系统,然后执行同步命令。

    系统时间同步软件采用Python 3.6语言设计完成,使用wxPython GUI图形库搭建软件框架, socket库完成UDP通信,paramiko库完成SSH连接Linux系统。运行框图如图2所示。

            

【Python网络编程】-Linux及Windows系统时间同步软件

图2 软件运行界面

    软件操作使用说明如下:

        1) 首先输入网络时间服务器地址、协议及端口号;

        2) Windows系统时间同步,点击开始同步按钮,可完成计算机与网络时间服务器的单次时间同步。若需要计算机按一定时间间隔同步网络时间服务器,可勾选定时同步功能,在定时文本框中输入时间间隔,注意是毫秒,然后点击开始定时同步按钮可进行定时同步,点击暂停同步按钮可以暂停定时同步。

        3) Linux系统时间同步,输入Linux系统的IP地址、用户名、密码。点击开始同步按钮,自动登录Linux系统并完成Linux系统与网络时间服务器的单次时间同步。若需要定时同步功能,操作步骤与步骤2一样。

        4) 单次同步和定时同步的同步状态信息在下方同步结果显示文本框中可以查看,同步状态信息包括数据包在网络上的往返传播延时、本地时钟补偿延时、当前精确的系统时间以及是否成功修改本地计算机系统时间。

3 Python编码

    使用 wxFormBuilder生成wxPython GUI图形界面。下图是wxFormBuilder软件运行界面。

【Python网络编程】-Linux及Windows系统时间同步软件

生成的图形界面代码如下:

文件:system_time_sync_frame.py

# -*- coding: utf-8 -*- 

###########################################################################
## Python code generated with wxFormBuilder (version Jun 17 2015)
## http://www.wxformbuilder.org/
##
## PLEASE DO "NOT" EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc

###########################################################################
## Class system_time_sync_frame
###########################################################################

class system_time_sync_frame ( wx.Frame ):
	
	def __init__( self, parent ):
		wx.Frame.__init__ ( self, parent = None, id = wx.ID_ANY, title = u"系统时间同步软件v1.0", pos = wx.DefaultPosition, size = wx.Size( 594,477 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
        
#        # 设置窗口标题的图标
#        self.icon = wx.Icon('time.ico', wx.BITMAP_TYPE_ICO)
#        self.SetIcon(self.icon) 
        
		self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
		self.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) )
		self.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTIONTEXT ) )
		self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_MENU ) )
        
		
		bSizer1 = wx.BoxSizer( wx.VERTICAL )
		
		sbSizer1 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"【网络时间服务器配置】" ), wx.HORIZONTAL )
		
		sbSizer1.SetMinSize( wx.Size( 100,10 ) ) 
		bSizer15 = wx.BoxSizer( wx.HORIZONTAL )
		
		bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText3 = wx.StaticText( sbSizer1.GetStaticBox(), wx.ID_ANY, u"时间服务器IP地址:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText3.Wrap( -1 )
		self.m_staticText3.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer5.Add( self.m_staticText3, 0, wx.ALL, 5 )
		
		self.m_textCtrl_timeserver = wx.TextCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, u"182.92.12.11", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer5.Add( self.m_textCtrl_timeserver, 0, wx.ALL, 5 )
		
		
		bSizer15.Add( bSizer5, 1, 0, 5 )
		
		bSizer6 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText4 = wx.StaticText( sbSizer1.GetStaticBox(), wx.ID_ANY, u"端口号:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText4.Wrap( -1 )
		self.m_staticText4.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer6.Add( self.m_staticText4, 0, wx.ALL, 5 )
		
		self.m_textCtrl_port = wx.TextCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, u"123", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer6.Add( self.m_textCtrl_port, 0, wx.ALL, 5 )
		
		
		bSizer15.Add( bSizer6, 1, 0, 5 )
		
		bSizer7 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText5 = wx.StaticText( sbSizer1.GetStaticBox(), wx.ID_ANY, u"协议:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText5.Wrap( -1 )
		self.m_staticText5.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer7.Add( self.m_staticText5, 0, wx.ALL, 5 )
		
		self.m_textCtrl_protocol = wx.TextCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, u"SNTP", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer7.Add( self.m_textCtrl_protocol, 0, wx.ALL, 5 )
		
		
		bSizer15.Add( bSizer7, 1, 0, 5 )
		
		
		sbSizer1.Add( bSizer15, 0, wx.EXPAND, 5 )
		
		
		bSizer1.Add( sbSizer1, 1, wx.EXPAND|wx.TOP|wx.RIGHT|wx.LEFT, 5 )
		
		sbSizer2 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"【Windows系统时间同步】" ), wx.HORIZONTAL )
		
		bSizer10 = wx.BoxSizer( wx.VERTICAL )
		
		bSizer51 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_checkBox_win = wx.CheckBox( sbSizer2.GetStaticBox(), wx.ID_ANY, u"定时同步(ms):", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer51.Add( self.m_checkBox_win, 0, wx.ALL, 5 )
		
		self.m_textCtrl_timer_1 = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, u"1000", wx.DefaultPosition, wx.Size( -1,-1 ), 0 )
		bSizer51.Add( self.m_textCtrl_timer_1, 0, wx.ALL, 5 )
		
		
		bSizer10.Add( bSizer51, 0, 0, 5 )
		
		
		sbSizer2.Add( bSizer10, 1, wx.EXPAND|wx.ALIGN_RIGHT, 5 )
		
		bSizer12 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_button_win_sync_start = wx.Button( sbSizer2.GetStaticBox(), wx.ID_ANY, u"开始同步(B)", wx.Point( -1,-1 ), wx.DefaultSize, 0 )
		self.m_button_win_sync_start.SetFont( wx.Font( 11, 72, 90, 92, False, "Times New Roman" ) )
		
		bSizer12.Add( self.m_button_win_sync_start, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 )
		
		self.m_button_win_sync_stop = wx.Button( sbSizer2.GetStaticBox(), wx.ID_ANY, u"暂停同步(B)", wx.Point( -1,-1 ), wx.DefaultSize, 0 )
		self.m_button_win_sync_stop.SetFont( wx.Font( 11, 72, 90, 92, False, "Times New Roman" ) )
		
		bSizer12.Add( self.m_button_win_sync_stop, 0, wx.ALL, 5 )
		
		
		sbSizer2.Add( bSizer12, 1, wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT|wx.EXPAND, 5 )
		
		
		bSizer1.Add( sbSizer2, 1, wx.EXPAND|wx.BOTTOM, 5 )
		
		sbSizer3 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"【Linux系统时间同步】" ), wx.VERTICAL )
		
		bSizer151 = wx.BoxSizer( wx.HORIZONTAL )
		
		bSizer52 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText31 = wx.StaticText( sbSizer3.GetStaticBox(), wx.ID_ANY, u"IP地址:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText31.Wrap( -1 )
		self.m_staticText31.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer52.Add( self.m_staticText31, 0, wx.ALL, 5 )
		
		self.m_textCtrl_linuxserver = wx.TextCtrl( sbSizer3.GetStaticBox(), wx.ID_ANY, u"192.168.1.205", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer52.Add( self.m_textCtrl_linuxserver, 0, wx.ALL, 5 )
		
		
		bSizer151.Add( bSizer52, 1, wx.EXPAND, 5 )
		
		bSizer61 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText41 = wx.StaticText( sbSizer3.GetStaticBox(), wx.ID_ANY, u"用户名:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText41.Wrap( -1 )
		self.m_staticText41.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer61.Add( self.m_staticText41, 0, wx.ALL, 5 )
		
		self.m_textCtrl_username = wx.TextCtrl( sbSizer3.GetStaticBox(), wx.ID_ANY, u"root", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer61.Add( self.m_textCtrl_username, 0, wx.ALL, 5 )
		
		
		bSizer151.Add( bSizer61, 1, wx.EXPAND, 5 )
		
		bSizer71 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_staticText51 = wx.StaticText( sbSizer3.GetStaticBox(), wx.ID_ANY, u"密码:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText51.Wrap( -1 )
		self.m_staticText51.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 90, False, wx.EmptyString ) )
		
		bSizer71.Add( self.m_staticText51, 0, wx.ALL, 5 )
		
		self.m_textCtrl_passwd = wx.TextCtrl( sbSizer3.GetStaticBox(), wx.ID_ANY, u"fatri123", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer71.Add( self.m_textCtrl_passwd, 0, wx.ALL, 5 )
		
		
		bSizer151.Add( bSizer71, 1, wx.EXPAND, 5 )
		
		
		sbSizer3.Add( bSizer151, 1, 0, 5 )
		
		bSizer14 = wx.BoxSizer( wx.HORIZONTAL )
		
		bSizer511 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_checkBox_linux = wx.CheckBox( sbSizer3.GetStaticBox(), wx.ID_ANY, u"定时同步(ms):", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer511.Add( self.m_checkBox_linux, 0, wx.ALL, 5 )
		
		self.m_textCtrl_timer_2 = wx.TextCtrl( sbSizer3.GetStaticBox(), wx.ID_ANY, u"1000", wx.DefaultPosition, wx.Size( -1,-1 ), 0 )
		bSizer511.Add( self.m_textCtrl_timer_2, 0, wx.ALL, 5 )
		
		
		bSizer14.Add( bSizer511, 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL|wx.TOP|wx.BOTTOM, 5 )
		
		bSizer121 = wx.BoxSizer( wx.HORIZONTAL )
		
		self.m_button_linux_sync = wx.Button( sbSizer3.GetStaticBox(), wx.ID_ANY, u"开始同步(B)", wx.Point( -1,-1 ), wx.DefaultSize, 0 )
		self.m_button_linux_sync.SetFont( wx.Font( 11, 72, 90, 92, False, "Times New Roman" ) )
		self.m_button_linux_sync.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_3DLIGHT ) )
		
		bSizer121.Add( self.m_button_linux_sync, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 )
		
		self.m_button_linux_sync_stop = wx.Button( sbSizer3.GetStaticBox(), wx.ID_ANY, u"暂停同步(B)", wx.Point( -1,-1 ), wx.DefaultSize, 0 )
		self.m_button_linux_sync_stop.SetFont( wx.Font( 11, 72, 90, 92, False, "Times New Roman" ) )
		
		bSizer121.Add( self.m_button_linux_sync_stop, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 )
		
		
		bSizer14.Add( bSizer121, 1, wx.EXPAND, 5 )
		
		
		sbSizer3.Add( bSizer14, 1, wx.EXPAND, 5 )
		
		
		bSizer1.Add( sbSizer3, 2, wx.EXPAND|wx.BOTTOM, 5 )
		
		sbSizer4 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"同步结果显示:" ), wx.HORIZONTAL )
		
		self.m_textCtrl_sync_result = wx.TextCtrl( sbSizer4.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 500,200 ), wx.HSCROLL|wx.TE_MULTILINE )
		sbSizer4.Add( self.m_textCtrl_sync_result, 0, wx.ALL, 5 )
		
		
		bSizer1.Add( sbSizer4, 4, wx.EXPAND, 5 )
		
		
		self.SetSizer( bSizer1 )
		self.Layout()
		
		self.Centre( wx.BOTH )
		
		# Connect Events
		self.m_checkBox_win.Bind( wx.EVT_CHECKBOX, self.m_checkBox_win_OnCheck )
		self.m_button_win_sync_start.Bind( wx.EVT_BUTTON, self.m_button_win_sync_OnButtonClick )
		self.m_button_win_sync_stop.Bind( wx.EVT_BUTTON, self.m_button_win_sync_stop_OnButtonClick )
		self.m_checkBox_linux.Bind( wx.EVT_CHECKBOX, self.m_checkBox_linux_OnCheck )
		self.m_button_linux_sync.Bind( wx.EVT_BUTTON, self.m_button_linux_sync_OnButtonClick )
		self.m_button_linux_sync_stop.Bind( wx.EVT_BUTTON, self.m_button_linux_sync_stop_OnButtonClick )
    
    
	
	def __del__( self ):
		pass
	
	
	# Virtual event handlers, overide them in your derived class
	def m_checkBox_win_OnCheck( self, event ):
		event.Skip()
	
	def m_button_win_sync_OnButtonClick( self, event ):
		event.Skip()
	
	def m_button_win_sync_stop_OnButtonClick( self, event ):
		event.Skip()
	
	def m_checkBox_linux_OnCheck( self, event ):
		event.Skip()
	
	def m_button_linux_sync_OnButtonClick( self, event ):
		event.Skip()
	
	def m_button_linux_sync_stop_OnButtonClick( self, event ):
		event.Skip()
	
           

主程序:time_sync_main.py

# -*- coding: utf-8 -*-
"""
Date: Wed Dec 12 13:28:23 2018

Author: heat.huang
"""

import paramiko
import system_time_sync_frame

import wx
import time
import socket  
import struct  
import win32api

###########  MainWindow  ###################
class MainWindow(system_time_sync_frame.system_time_sync_frame):
    def __init__(self, parent):
        super().__init__(self)
        
        # 设置窗口标题的图标
        self.icon = wx.Icon('time.ico', wx.BITMAP_TYPE_ICO)
        self.SetIcon(self.icon)   
        
        #初始化复选框的定时间隔,用于判断复选框的勾选
        self.timer_val_win = 0
        self.timer_val_linux = 0       
        
        # 创建 win_sync 定时器
        self.timer_win = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.win_time_sync, self.timer_win)
        # 创建 linux_sync 定时器
        self.timer_linux = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.linux_time_sync, self.timer_linux)
        
    ######### 从网络时间服务器获取时间 ###############    
    def get_time_from_ntpserver(self):
        # 网络时间服务器地址 和端口号
        ntp_server = self.m_textCtrl_timeserver.GetValue()
        ntp_port = int(self.m_textCtrl_port.GetValue())
	    
        # 使用socket创建一个IPv4的UDP连接
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        # 1900年1月1日00:00:00~1970年1月1日00:00:00的总秒数,2208988800s
        #  0x83aa7e80 是1900 到 1970年的总秒数,以十六进制表示
        time1990_1970 = 0x83aa7e80
        # 起始源时间戳--请求信息离开客户端的时间
		# time.time()返回当前时间的时间戳(1970纪元后经过的浮点秒数)
        t1 = time.time()
        # 使用struct.pack()打包数据,B表示8bit,即一个字节宽度,
		# I表示32bit,Q表示64bit
        # <<是左移,它后面跟的数字表示移动的位数
        # | 表示按位或运算符:只要对应的两个二进位有一个为1时,结果位就为1        
        ntppack = struct.pack("!BBBBIIIQQQQ",3<<6 | 3<<3 | 3,1,10,1,0,0,0,0,0,0,0)
        # 客户端向网络时间服务器发送请求
        sock.sendto(ntppack, (ntp_server,ntp_port))
        # 客户端接收到时间服务器返回的包
        resp, addr = sock.recvfrom(1024)
        # 目的地时间戳--信息包到达客户端的时间
        t4 = time.time()
        # 客户端使用struct.unpack()解包;
		# 这里LI VN Mode Stratum Poll Precision放到一个64bit里面
        # struct.unpack(fmt,string)返回一个由解包数据(string)得到的一个元组(tuple)
        # ‘!’表示network(=big-endian 大端)        
        (vi, root, refeTime, oriTime, receTime, tranTime) = struct.unpack("!QQQQQQ", resp)
        # print(struct.unpack("!QQQQQQ",resp))
        # Mode=4 表示服务器返回标志,判断是否是时间服务器返回
        # 十进制4用二进制表示 0100,在64bit中,0100左移56位,
		# 随后与64bit的vi进行或运算,以此判断...
        # ...Mode的值是否为4    
        if vi == vi | 4<<56:
            # 对解包得到的64bit总秒数取高32位,即获得整秒数,
			# 并减去1900-1970年之间的秒数   
			# 接收时间戳--服务器收到查询请求时间        
            receTime = (receTime/(2**32) - time1990_1970)  
			# 传输时间戳--服务器回复时间信息包时间                                        
            tranTime = (tranTime/(2**32) - time1990_1970)                                          
            # 计算本地时钟补偿compensation time                                                    
            comTime = ((receTime - t1) + (tranTime - t4)) /2                                      
            # 计算往返传播延迟 propagation delay                                                   
            propaTime = (t4 - t1) + (tranTime - receTime)                                          
            # 将往返传播延时(ms)和本地时钟补偿(ms)信息添加到文本框                                                                              
            self.m_textCtrl_sync_result.AppendText('\n\n=====同步结果=====\n往返传播延时(ms):{0:3f} \n本地时钟补偿(ms):{1:3f}'.format(propaTime*1000,comTime*1000))       
            # 目的地时间戳加本地时钟补偿得到准确的本地时间precise local time                                 
            preTime = t4 + comTime                                                                                                                                               
            # time.strftime():
			# Convert a time tuple to a string according to a format specification 
			# 将精确的本地时间信息添加到文本框
            self.m_textCtrl_sync_result.AppendText('\n' + '' + time.strftime("Local Time: %Y-%m-%d %H:%M:%S", time.localtime(preTime))) 
        sock.close()  
        
        return preTime
    
    ######### Windows 系统时间同步 ###############  
    def win_time_sync(self, event):
        
        preTime = self.get_time_from_ntpserver()
        # 修改系统时间
        # gmtime([seconds]):
		# Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.GMT)
        (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)= time.gmtime(preTime) 
        
        # 调用win32api.SetSystemTime()修改系统时间
        win32api.SetSystemTime(tm_year, tm_mon, tm_wday, tm_mday, tm_hour, tm_min, tm_sec, 0)
        # 将成功修改系统时间信息添加到文本框
        self.m_textCtrl_sync_result.AppendText('\n' + 5*'*' + ' \nWindows系统时间修改成功 ' + '\n') 
    
    ######### Linux 系统时间同步 ###############  
    def linux_time_sync(self, event):
        try:
            ######## 创建SSH对象
            ssh = paramiko.SSHClient()
            ######## 允许连接不在know_hosts文件中的主机
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            
            host_name = self.m_textCtrl_linuxserver.GetValue()
            user_name =  self.m_textCtrl_username.GetValue()
            pass_word = self.m_textCtrl_passwd.GetValue()
            
    #        print("host_name: ", host_name)
    #        print("user_name: ", user_name)
    #        print("pass_word: ", pass_word)
            
            ####### 连接主控板
            ssh.connect(host_name, 22, user_name, pass_word)
                
            ######## 执行命令
            
            # 得到精确的本地时间
            preTime = self.get_time_from_ntpserver()
            preDate = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(preTime))
            cmd = "date -s " + "\"" + preDate + "\""
            #print("cmd: ", cmd)
            
            stdin, stdout, stderr = ssh.exec_command(cmd)
            result = stdout.read()
            result = str(result, encoding = "utf-8")
            result = '\n' + 5*'*' + '\nLinux系统时间修改为:' + result + '\n' 
            self.m_textCtrl_sync_result.AppendText(result)
    #       print(result)
            
            ####### 关闭连接
            ssh.close()   
        except:
            Show_Message_Box("Linux系统时间同步失败!")
            
    
    ###############  定时同步设置  ################### 
    
    ### 定义函数--开始定时同步
    def OnStart_Win(self, event):
        # 时间间隔为毫秒量级,启动定时器
        self.timer_win.Start(int(self.m_textCtrl_timer_1.GetValue()))
    def OnStart_Linux(self, event):
        # 时间间隔为毫秒量级,启动定时器
        self.timer_linux.Start(int(self.m_textCtrl_timer_2.GetValue()))
    
    #### 定义函数--暂停定时同步
    def OnStop_Win(self, event):
        self.timer_win.Stop()  
        self.timer_val_win = 0
    def OnStop_Linux(self, event):
        self.timer_linux.Stop()
        self.timer_val_linux = 0        

    ############# 按键对应的响应函数 ####################
    ###### win  ######
    def m_checkBox_win_OnCheck( self, event ):   #复选框1
        self.timer_val_win = int(self.m_textCtrl_timer_1.GetValue())
    
    def m_button_win_sync_OnButtonClick( self, event ): # win-button-start
        #如果复选框被选中,调用定时启动函数,实现定时同步
        if self.timer_val_win > 0:
            self.OnStart_Win(event)
        else:
            self.win_time_sync(event)
            
    def m_button_win_sync_stop_OnButtonClick( self, event ):# win-button-stop
        if self.timer_val_win > 0:
            self.OnStop_Win(event)
        else:
            pass
    ###### linux  ######        
    def m_checkBox_linux_OnCheck( self, event ): #复选框2
        self.timer_val_linux = int(self.m_textCtrl_timer_2.GetValue()) 
        
    def m_button_linux_sync_OnButtonClick( self, event ):# linux-button-start
        #如果复选框被选中,调用定时启动函数,实现定时同步
        
        if self.timer_val_linux > 0:
            self.OnStart_Linux(event)
        else:
            self.linux_time_sync(event)        
	
    def m_button_linux_sync_stop_OnButtonClick( self, event ): # linux-button-stop 
        if self.timer_val_linux > 0:
            self.OnStop_Linux(event)
        else:
            pass

## show message    
def Show_Message_Box(message):
    dlg = wx.MessageDialog(None, message, "提示信息", wx.CLOSE | wx.ICON_QUESTION)
    if dlg.ShowModal() == wx.ID_CLOSE:
        dlg.Close(True)
    dlg.Destroy        
        
######### 执行主函数   ###############     
if __name__ == '__main__':
    app = wx.App()
    main_win = MainWindow(None)  
    main_win.Show()
    app.MainLoop()          
    
           

注意:exe可执行文件的打包使用 pyinstaller 工具,具体的打包方法参见论坛的文章:https://blog.csdn.net/zt_xcyk/article/details/73786659

  

 下面是我打包生成的exe文件。

【Python网络编程】-Linux及Windows系统时间同步软件