评论

收藏

[python] python 开发的 windows版运维图形界面-信息管理系统

编程语言 编程语言 发布于:2021-06-24 10:23 | 阅读数:330 | 评论:0

  基于运维人员对windows/linux/网络设备系统管理比较分散,并且每个服务都要开启不同的监控和应用程序,浪费时间,浪费时间就是浪费金钱的道理没有人不会不懂这个道理,我查找了很多资源发现都是一些零散的监控工具,没有正真意思的统一监控管理(博主时间有限。目前只分享windows本地监控,后面继续更新远程监控),并且运维人员查看网络或者虚拟机信息都要远程登录,而本工具可以在本地直接写命令,获取远程配置信息,极大的提高运维人员的工作效率和节约成本时间。
1.登陆首页展示和代码分享
# 登陆页面
def log_in(self):
    """
    登录界面
    :return:
    """
    try:
        logging.info('*Stsrt IMS*')
        var1 = StringVar()  
        var2 = StringVar()  
        self.root.geometry('450x380' + self.winfo_landing_x + self.winfo_landing_y)  
        self.root.resizable(0, 0) 
        self.root.title("信息化管理系统")  
        logging.info('Start loading background image')
        f = Frame(width=523, height=150, bg='green')  # frame1
        canvas = tk.Canvas(self.root, width=525, height=150, bg='yellow')  # canvas1
        self.img = itk.PhotoImage(file="img/win.png") 
        canvas.create_image(209, 75, image=self.img)  
        canvas.grid(row=0, column=0, columnspan=2, padx=1, pady=3)  
        logging.info('End loading background image')
        logging.info('Start loading the gray box')
        f = Frame(width=520, height=220, bg='#DCDCDC')  # frame2
        f_head = Frame(width=80, height=80, bg='green')  # frame3
        logging.info('End loading the gray box')
        logging.info('Start loading avatar')
        self.canvas2 = tk.Canvas(self.root, width=80, height=80, bg='red')
        self.img2 = Img.open("img/TX.png")
        self.img2 = itk.PhotoImage(self.img2)
        self.canvas2.create_image(40, 40, image=self.img2)
        self.canvas2.place(in_=f_head, anchor=NW)  # canvas2放入frame3中,位置是偏西北
        f_head.place(relx=0.10, rely=0.56)  
        logging.info('End loading avatar')
        logging.info('Start loading user accounts')
        self.usr = Entry(self.root, textvariable=var1, bg='#F5F5F5', highlightcolor='#1E90FF')
        self.usr.place(relx=0.35, rely=0.56)
        logging.info('End loading user accounts')
        logging.info('Start loading the password box')
        self.pwd = Entry(self.root, textvariable=var2, bg='#F5F5F5', highlightcolor='#1E90FF')
        self.pwd['show'] = ''
        self.pwd.place(relx=0.35, rely=0.65)
        logging.info('End loading the password box')
        logging.info('Start loading automatic login')
        rem_pwd = Checkbutton(self.root, foreground='#1E90FF').place(relx=0.34, rely=0.72) 
        rem_pwd_lab = Label(self.root, width=6, height=1, text='自动登录',foreground='#808080')
        rem_pwd_lab.place(relx=0.39, rely=0.72)  
        logging.info('End loading automatic login')
        logging.info('Start loading the remember password')
        rem_pwd = Checkbutton(self.root, foreground='#1E90FF').place(relx=0.51, rely=0.72)  
        rem_pwd_lab = Label(self.root, width=6, height=1, text='记住密码',foreground='#808080') 
        rem_pwd_lab.place(relx=0.56, rely=0.72)  
        logging.info('End loading the remember password')
        login_btn = Button(self.root, text='             登陆           ', bg='#1E90FF') 
        login_btn.place(relx=0.36, rely=0.8)  # 设置登录坐标位置X,Y
        login_btn.bind("", self.load)  # 触发函数执行 
        fgt_usr_btn = Button(self.root, text='注册账号', foreground='#1E90FF', command=self.regiser)  # 灰色:bg='#DCDCDC'
        fgt_usr_btn.place(relx=0.7, rely=0.54)
        fgt_pwd_btn = Button(self.root, text='找回密码', foreground='#1E90FF', command=self.fg_pwd)  # 灰色:bg='#DCDCDC'
        fgt_pwd_btn.place(relx=0.7, rely=0.64)
    except Exception as loginerror:
        logging.info(str(loginerror))
        print(str(loginerror))
 登陆认证
def load(self, args):
    """
     登录事件
    :param args:
    :return:
    """
    try:
        logging.info('Start running the login event ')
        user = self.usr.get()
        pwd = self.pwd.get()
        License_p = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
        hl = hashlib.md5()
        hl.update(pwd.encode(encoding='utf8'))
        pwd_md5_jm = hl.hexdigest()
        if License_p <= self.LICENSE_TIMES:
            with open(r'db\imsap.txt', 'r', encoding='utf8') as f:
                for i in f:
                    user1 = i.strip().split(':')[2]
                    pwd2 = i.strip().split(':')[3]
                    if user == user1 and pwd2 == pwd_md5_jm:
                        # if self.username == user and self.password == pwd_md5_jm:
                        logging.info('login successful')
                        print('登录成功')
                        self.root.quit()  # 关闭登录窗口
                        self.root.destroy()  # destroy()销毁一个小部件,quit()退出mainloop。
                        print('已关闭登陆界面。。')
                        print('开始加载主页面。。')
                        logging.info('Start loading page')
                        # self.cs1()
                        t1 = threading.Thread(target=self.cs1())
                        t1.start()
                        t1.join()
                        logging.info('End  loading page')
                        print('主页面加载结束。。')
                    else:
                        print('登录失败')
                        lbtime2 = tk.Label(self.root, fg='red', anchor='w')
                        lbtime2.place(x=156, y=180, width=150)
                        lbtime2['text'] = '请输入正确的用户名或密码'
                        # messagebox.showinfo(title='登陆日志', message='用户名或密码错误,请重新登录')
        else:
            lbtime2 = tk.Label(self.root, fg='red', anchor='w')
            lbtime2.place(x=149, y=180, width=155)
            lbtime2['text'] = 'License已过期,请联系管理员授权!'
        logging.info('End  running the login event ')
    except Exception as ruuingerror:
        print(str(ruuingerror))
 账号注册页面
def regiser(self, *args):
    """
    登录账号注册
    :param event:
    :return:
    """
    self.ycsy = tk.Tk()
    self.ycsy.title('登录账号注册') 
    self.ycsy.geometry("580x265+460+300") 
    self.ycsy.resizable(width=False, height=False) 
    name = tk.StringVar()
    ttk.Label(self.ycsy, text="").grid(row=0, column=1, columnspan=1)
    tk.Label(self.ycsy, text="用户ID ", font=("黑体", 10, "bold"), width=40, height=3, wraplength=80, anchor='w').grid(
        row=1, column=1, columnspan=1)
    tk.Label(self.ycsy, text="用户名 ", font=("黑体", 10, "bold"), width=40, height=3, wraplength=80, anchor='w').grid(
        row=1, column=2, columnspan=1)
    self.name_id = tk.StringVar()
    self.name_id_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.name_id)
    self.name_id_sr.grid(row=1, column=1)
    self.name_id_sr.focus()
    self.name_zc = tk.StringVar()
    self.name_zc_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.name_zc)
    self.name_zc_sr.grid(row=1, column=2)
    self.name_zc_sr.focus()
    tk.Label(self.ycsy, text=u"账号状态", font=("黑体", 10, "bold"), width=40, height=3, wraplength=80, anchor='w').grid(
        row=2, column=1)
    tk.Label(self.ycsy, text=u"密码 *", font=("黑体", 10, "bold"), width=40, height=3, wraplength=80, anchor='w').grid(
        row=2, column=2)
    self.account_zc = tk.StringVar()
    self.account_zc_sr = ttk.Combobox(self.ycsy, width=18, textvariable=self.account_zc,
                                      state='readonly')  # 下拉框字体,内容为weather,宽度,state='editable'表示内容可编辑
    self.account_zc_sr['values'] = (' 可 用', ' 禁 用')  # 设置下拉列表的值
    self.account_zc_sr.grid(column=1, row=2)  # 设置其在界面中出现的位置 column代表列 row 代表行
    self.account_zc_sr.current(0)
    self.pwd_zc = tk.StringVar()
    self.pwd_zc_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.pwd_zc, show='')
    self.pwd_zc_sr.grid(row=2, column=2)
    self.pwd_zc_sr.focus()
    tk.Label(self.ycsy, text=u"邮箱 ",
             font=("黑体", 10, "bold"),
             width=40, height=3, wraplength=80, anchor='w').grid(row=3, column=1)
    tk.Label(self.ycsy, text=u"确认密码 *", font=("黑体", 10, "bold"),
             width=40, height=3, wraplength=80,
             anchor='w').grid(row=3, column=2)
    self.email_zc = tk.StringVar()
    self.email_zc_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.email_zc)
    self.email_zc_sr.grid(row=3, column=1)
    self.email_zc_sr.focus()
    self.pwd_qr_zc = tk.StringVar()
    self.pwd_qr_zc_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.pwd_qr_zc, show='')
    self.pwd_qr_zc_sr.grid(row=3, column=2)
    self.pwd_qr_zc_sr.focus()
    tk.Label(self.ycsy, text=u"手机号码 ",
             font=("黑体", 10, "bold"),
             width=40, height=3, wraplength=80,
             anchor='w').grid(row=4, column=1)
    tk.Label(self.ycsy, text=u"信息安全",
             font=("黑体", 10, "bold"),
             width=40, height=3, wraplength=80, anchor='w').grid(row=4, column=2)
    self.phone_zc = tk.StringVar()
    self.phone_zc_sr = ttk.Entry(self.ycsy, width=20, textvariable=self.phone_zc)
    self.phone_zc_sr.grid(row=4, column=1)
    self.phone_zc_sr.focus()
    self.ifm_zc = tk.StringVar()
    self.ifm_zc_sr = ttk.Combobox(self.ycsy, width=18, textvariable=self.ifm_zc, state='readonly')
    self.ifm_zc_sr['values'] = (' 不加密', ' 加 密')  # 设置下拉列表的值
    self.ifm_zc_sr.grid(column=2, row=4)  
    self.ifm_zc_sr.current(0)
    login_btn = Button(self.ycsy, text='  确定  ', bg='#1E90FF', command=self.regiser_file)
    login_btn.place(relx=0.38, rely=0.8)
    # login_btn.bind("", '执行函数')
    login_btn = Button(self.ycsy, text='  关闭  ', bg='#1E90FF', command=self.regiser_cancel)
    login_btn.place(relx=0.49, rely=0.8)
    # login_btn.bind("", self.regiser_cancel)
 账号注册确定
def regiser_file(self, *args):
    """
    注册确定事件
    :return:
    """
    self.id_file_p = self.name_id_sr.get()  
    self.name_file_p = self.name_zc_sr.get()  
    self.accout_file_p = self.account_zc_sr.get() 
    self.pwd_file_p = self.pwd_zc_sr.get() 
    hl = hashlib.md5()
    hl.update(self.pwd_file_p.encode(encoding='utf8'))
    self.pwd_file_p = hl.hexdigest()
    self.email_file_p = self.email_zc_sr.get() 
    self.pwd_qr_file_p = self.pwd_qr_zc_sr.get()  
    self.phone_file_p = self.phone_zc_sr.get() 
    self.ifm_file_p = self.ifm_zc_sr.get()  
    if self.name_file_p or self.pwd_file_p or self.pwd_qr_file_p != '' and self.pwd_file_p != self.pwd_qr_file_p:
        # [IMSAP]:用户ID:用户名,密码,邮箱,手机号码,账号状态,信息安全
        file_imsap = '[IMSAP]:{0}:{1}:{2}:{3}:{4}:{5}:{6}'.format(
            self.id_file_p,
            self.name_file_p,
            self.pwd_file_p,
            self.email_file_p,
            self.phone_file_p,
            self.accout_file_p,
            self.ifm_file_p,
            self.ifm_file_p
        ).replace(' ', '')
        with open(r'report\imsap.txt', 'a', encoding='utf8') as f:
            f.writelines(file_imsap + '\n')
            f.close()
            lbtime2 = tk.Label(self.ycsy, fg='red', anchor='w')
            lbtime2.place(x=205, y=2, width=160)
            lbtime2['text'] = '提示:已注册成功请登录'
    else:
        lbtime2 = tk.Label(self.ycsy, fg='red', anchor='w')
        lbtime2.place(x=210, y=2, width=160)
        lbtime2['text'] = '提示:带“”为必填项!!'
    # # 密码
    if self.pwd_file_p != self.pwd_qr_file_p:
        lbtime2 = tk.Label(self.ycsy, fg='red', anchor='w')
        lbtime2.place(x=205, y=2, width=160)
        lbtime2['text'] = '提示:密码不一致!!'
 账号注册取消
def regiser_cancel(self):
    """
    注册取消事件
    :return:
    """
    self.ycsy.destroy()
 找回密码
def fg_pwd(self, args):
    """
    找回密码事件
    :param event:
    :return:
    """
    messagebox.showinfo(title='找回密码', message='程序正在更新中......')
1.1 代码执行效果图 DSC0000.png DSC0001.png DSC0002.png 2.监控部分代码逻辑
 def fbytes(B):
    'Return the given bytes as a human friendly KB, MB, GB, or TB string'
    B = float(B)
    KB = float(1024)
    MB = float(KB ** 2)  # 1,048,576
    GB = float(KB ** 3)  # 1,073,741,824
    TB = float(KB ** 4)  # 1,099,511,627,776
    if B < KB:
        return '{0} {1}'.format(B, 'Bytes' if 0 == B > 1 else 'Byte')
    elif KB <= B < MB:
        return '{0:.2f} KB'.format(B / KB)
    elif MB <= B < GB:
        return '{0:.2f} MB'.format(B / MB)
    elif GB <= B < TB:
        return '{0:.2f} GB'.format(B / GB)
    elif TB <= B:
        return '{0:.2f} TB'.format(B / TB)
class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
    """Simple HTTP request handler with GET/HEAD/POST commands.
    This serves files from the current directory and any of its
    subdirectories.  The MIME type for files is determined by
    calling the .guess_type() method. And can reveive file uploaded
    by client.
    The GET/HEAD/POST requests are identical except that the HEAD
    request omits the actual contents of the file.
    """
    def do_GET(self):
        """Serve a GET request."""
        f = self.send_head()
        if f:
            self.copyfile(f, self.wfile)
            f.close()
    def do_HEAD(self):
        """Serve a HEAD request."""
        f = self.send_head()
        if f:
            f.close()
    def do_POST(self):
        """Serve a POST request."""
        r, info = self.deal_post_data()
        print((r, info, "by: ", self.client_address))
        f = BytesIO()
        f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write(b"<html>\n<title>Upload Result Page</title>\n")
        f.write(b'<style type="text/css">\n')
        f.write(b' {font-family: Helvetica; font-size: 16px; }\n')
        f.write(b'a { text-decoration: none; }\n')
        f.write(b'</style>\n')
        f.write(b"<body>\n<h2>Upload Result Page</h2>\n")
        f.write(b"<hr>\n")
        if r:
            f.write(b"<strong>Success!</strong>")
        else:
            f.write(b"<strong>Failed!</strong>")
        f.write(info.encode())
        f.write(("<br><br><a href="%s">" % self.headers['referer']).encode())
        f.write(b"<button>Back</button></a>\n")
        f.write(b"<hr><small>Powered By: bones7456<br>Check new version ")
        f.write(b"<a href="https://gist.github.com/UniIsland/3346170" target="_blank">")
        f.write(b"here</a>.</small></body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        if f:
            self.copyfile(f, self.wfile)
            f.close()
    def deal_post_data(self):
        uploaded_files = []
        content_type = self.headers['content-type']
        if not content_type:
            return (False, "Content-Type header doesn't contain boundary")
        boundary = content_type.split("=")[1].encode()
        remainbytes = int(self.headers['content-length'])
        line = self.rfile.readline()
        remainbytes -= len(line)
        if not boundary in line:
            return (False, "Content NOT begin with boundary")
        while remainbytes > 0:
            line = self.rfile.readline()
            remainbytes -= len(line)
            fn = re.findall(r'Content-Disposition.name="file"; filename="(.*)"', line.decode())
            if not fn:
                return (False, "Can't find out file name...")
            path = self.translate_path(self.path)
            fn = os.path.join(path, fn[0])
            line = self.rfile.readline()
            remainbytes -= len(line)
            line = self.rfile.readline()
            remainbytes -= len(line)
            try:
                out = open(fn, 'wb')
            except IOError:
                return (False, "<br><br>Can't create file to write.<br>Do you have permission to write?")
            else:
                with out:
                    preline = self.rfile.readline()
                    remainbytes -= len(preline)
                    while remainbytes > 0:
                        line = self.rfile.readline()
                        remainbytes -= len(line)
                        if boundary in line:
                            preline = preline[0:-1]
                            if preline.endswith(b'\r'):
                                preline = preline[0:-1]
                            out.write(preline)
                            uploaded_files.append(fn)
                            break
                        else:
                            out.write(preline)
                            preline = line
        return (True, "<br><br>'%s'" % "'<br>'".join(uploaded_files))
    def send_head(self):
        """Common code for GET and HEAD commands.
        This sends the response code and MIME headers.
        Return value is either a file object (which has to be copied
        to the outputfile by the caller unless the command was HEAD,
        and must be closed by the caller under all circumstances), or
        None, in which case the caller has nothing further to do.
        """
        path = self.translate_path(self.path)
        f = None
        if os.path.isdir(path):
            if not self.path.endswith('/'):
                # redirect browser - doing basically what apache does
                self.send_response(301)
                self.send_header("Location", self.path + "/")
                self.end_headers()
                return None
            for index in "index.html", "index.htm":
                index = os.path.join(path, index)
                if os.path.exists(index):
                    path = index
                    break
            else:
                return self.list_directory(path)
        ctype = self.guess_type(path)
        try:
            # Always read in binary mode. Opening files in text mode may cause
            # newline translations, making the actual size of the content
            # transmitted less than the content-length!
            f = open(path, 'rb')
        except IOError:
            self.send_error(404, "File not found")
            return None
        self.send_response(200)
        self.send_header("Content-type", ctype)
        fs = os.fstat(f.fileno())
        self.send_header("Content-Length", str(fs[6]))
        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
        self.end_headers()
        return f
    def list_directory(self, path):
        """Helper to produce a directory listing (absent index.html).
        Return value is either a file object, or None (indicating an
        error).  In either case, the headers are sent, making the
        interface the same as for send_head().
        """
        try:
            list = os.listdir(path)
        except os.error:
            self.send_error(404, "No permission to list directory")
            return None
        enc = sys.getfilesystemencoding()
        list.sort(key=lambda a: a.lower())
        f = BytesIO()
        displaypath = html.escape(urllib.parse.unquote(self.path))
        f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write(b'<html>\n')
        f.write(('<meta http-equiv="Content-Type" '
                 'content="text/html; charset=%s">' % enc).encode(enc))
        f.write(("<title>Directory listing for %s</title>\n" % displaypath).encode(enc))
        f.write(b'<style type="text/css">\n')
        f.write(b'* {font-family: Helvetica; font-size: 16px; }\n')
        f.write(b'a { text-decoration: none; }\n')
        f.write(b'a:link { text-decoration: none; font-weight: bold; color: #0000ff; }\n')
        f.write(b'a:visited { text-decoration: none; font-weight: bold; color: #0000ff; }\n')
        f.write(b'a:active { text-decoration: none; font-weight: bold; color: #0000ff; }\n')
        f.write(b'a:hover { text-decoration: none; font-weight: bold; color: #ff0000; }\n')
        f.write(b'table {\n  border-collapse: separate;\n}\n')
        f.write(b'th, td {\n  padding:0px 10px;\n}\n')
        f.write(b'</style>\n')
        f.write(("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath).encode(enc))
        f.write(b"<hr>\n")
        f.write(b"<form ENCTYPE="multipart/form-data" method="post">")
        f.write(b"<input name="file" type="file" multiple/>")
        f.write(b"<input type="submit" value="upload"/></form>\n")
        f.write(b"<hr>\n")
        f.write(b'<table>\n')
class VrvAgent():
    """主逻辑"""
    def __init__(self):
        self.app = wx.App()
        self.win = wx.Frame(None, -1,
                            title='设备配置管理系统',
                            size=(710, 620),
                            pos=(500, 100),
                            style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX)  
        self.taskBarIcon = TaskBarIcon(frame=self.win) 
        self.agent_menu() 
        self.agent_box()  
    def agent_menu(self):
        """
        总菜单栏
        :return:
        """
        self.memBar = wx.MenuBar()  
        self.agent_fileMenu_f()
        self.agent_deitMenu_e() 
        self.agent_helpMenu_h()
        self.win.SetMenuBar(self.memBar) 
    def agent_fileMenu_f(self):
        """
         菜单栏 - 文件
        :return:
        """
        # ------------------------------文件
        file_menu = wx.Menu()  # 二级
        newItem = wx.MenuItem(file_menu, id=wx.ID_NEW, text="新建(N)\tCtrl+N", kind=wx.ITEM_NORMAL)
        openItem = wx.MenuItem(file_menu, id=wx.ID_OPEN, text="打开(O)...\tCtrl+O", kind=wx.ITEM_NORMAL)
        saveItem = wx.MenuItem(file_menu, id=wx.ID_SAVE, text="保存(S)\tCtrl+S", kind=wx.ITEM_NORMAL)
        # saveItem.SetBitmap(wx.Bitmap("exit.png"))  #添加保存图标
        saveasItem = wx.MenuItem(file_menu, id=wx.ID_SAVEAS, text="另存为(A)...", kind=wx.ITEM_NORMAL)
        exitItem = wx.MenuItem(file_menu, id=20, text="退出(X)", kind=wx.ITEM_NORMAL)
        file_menu.Append(newItem) 
        file_menu.Append(openItem)
        file_menu.Append(saveItem)
        file_menu.Append(saveasItem)
        file_menu.AppendSeparator()  
        file_menu.Append(exitItem)
        self.win.Bind(wx.EVT_MENU, self.OnExit, saveasItem)  
        self.win.Bind(wx.EVT_MENU, self.OnExit, exitItem)  
        self.memBar.Append(file_menu, title="文件(F)")  
    def agent_deitMenu_e(self):
        """
        菜单栏 - 编辑
        :return:
        """
        deitMenu = wx.Menu()
        ftpclient = wx.MenuItem(id=21, text="FTP客户端\tCtrl+W", kind=wx.ITEM_NORMAL)
        ftpserver = wx.MenuItem(id=21, text="FTP服务端\tCtrl+Q", kind=wx.ITEM_NORMAL)
        # deitMenu.Append(id=21, item="FTP服务端\tCtrl+Q", kind=wx.ITEM_NORMAL)
        self.memBar.Append(deitMenu, title="工具(E)")
        deitMenu.Append(ftpclient)
        deitMenu.Append(ftpserver)
        self.win.Bind(wx.EVT_MENU, self.ftpclient, ftpclient) 
        self.win.Bind(wx.EVT_MENU, self.ftpserver, ftpserver)  
    def ftpclient(self, event):
        """
        ftp 客户端
        """
        pass
    def ftpserver(self, event):
        """
        ftp 服务端
        """
        FTP_files().ftp_files()
    def agent_helpMenu_h(self):
        """
        菜单栏 - 帮助
        :return:
        """
        helpMenu = wx.Menu()
        editMenu = wx.Menu()  
        cutItem = wx.MenuItem(editMenu, id=122, text="Cut", kind=wx.ITEM_NORMAL)
        editMenu.Append(cutItem)  
        helpMenu.Append(wx.ID_ANY, "Edit", editMenu)  
        self.memBar.Append(helpMenu, title="帮助(H)") 
   def vrvagent_thread(self, func, *args):
    t = threading.Thread(target=func)
    t.setDaemon(True)  
    t.start()
def OnStart(self):
    while True:
        time.sleep(0.06)
        self.count = self.count + 1
        self.gauge.SetValue(self.count)
        if self.count >= 100:
            print("备份完成")
            return 'success'
关注下面的标签,发现更多相似文章