您现在的位置是:首页 > 文章详情

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

日期:2021-05-31点击:394

         基于运维人员对windows/linux/网络设备系统管理比较分散,并且每个服务都要开启不同的监控和应用程序,浪费时间,浪费时间就是浪费金钱的道理没有人不会不懂这个道理,我查找了很多资源发现都是一些零散的监控工具,没有正真意思的统一监控管理(博主时间有限。目前只分享windows本地监控,后面继续更新远程监控),并且运维人员查看网络或者虚拟机信息都要远程登录,而本工具可以在本地直接写命令,获取远程配置信息,极大的提高运维人员的工作效率和节约成本时间。

1.登陆首页展示和代码分享

# 登陆页面 def log_in(self):     """     登录界面     :return:     """     try:         logging.info('***Stsrt IMS***')         var1 = StringVar()  # id框中的文字         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')         # ================背景图==================         # 组件.grid(row = x,column = y) 将组件放入self.root中(x,y)位置         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)  # frame3放入root的绝对位置上         # =======================================         logging.info('End loading avatar')         logging.info('Start loading user accounts')         # ===============用户账号==================  Entry1直接放入self.root的绝对位置上,也可以尝试放入frame2中         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')         # ===============密码框==================== Entry2 直接放入self.root的绝对位置上         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')         # 自动登录         # checkbutton 放入绝对位置         rem_pwd = Checkbutton(self.root, foreground='#1E90FF').place(relx=0.34, rely=0.72)  # 选择框         # 设置颜色,字体,宽,长  compound=LEFT, foreground='#808080', bg='#DCDCDC'         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')  # 设置颜色,字体,宽,长  compound=LEFT, foreground='#808080', bg='#DCDCDC'         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')  # 按钮,字体颜色,按钮背景色:bg='#1E90FF'         login_btn.place(relx=0.36, rely=0.8)  # 设置登录坐标位置X,Y         login_btn.bind("<Button-1>", 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_usr_btn.bind("<Button-1>", self.regiser)         # =======================================         # ===============找回密码=================          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)         # fgt_pwd_btn.bind("<Button-1>", self.fg_pwd)  #模拟enter按钮         # fgt_pwd_btn.flash()         # =======================================     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()         print('加密前为 :' + pwd)         print('加密后为 :' + 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)  # 宽不可变, 高可变,默认为True     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)  # 设置其在界面中出现的位置 column代表列 row 代表行     self.ifm_zc_sr.current(0)     # =============注册确定按钮====================     # button3放入绝对位置     login_btn = Button(self.ycsy, text='  确定  ', bg='#1E90FF', command=self.regiser_file)     login_btn.place(relx=0.38, rely=0.8)     # login_btn.bind("<Button-1>", '执行函数')     # =======================================     # =============注册取消按钮==================== button3放入绝对位置     login_btn = Button(self.ycsy, text='  关闭  ', bg='#1E90FF', command=self.regiser_cancel)     login_btn.place(relx=0.49, rely=0.8)     # login_btn.bind("<Button-1>", self.regiser_cancel)     # ======================================= # 账号注册确定 def regiser_file(self, *args):     """     注册确定事件     :return:     """     self.id_file_p = self.name_id_sr.get()  # 用户ID     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 代码执行效果图

image.png

image.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  Handler(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')  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)  # 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 客户端         """         print('ccc')     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)")  # 一级

2.1代码结果图示列

image.png


原文链接:https://blog.51cto.com/xuaijun/2835501
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章