疯狂python讲义学习笔记——中十章完结

#第十一章 thinker
import tkinter as tk
print(help(tk.Button.__init__))#以按扭为例查看有什么属性
class myApplication(tk.Frame):
    def __init__(self,master=None):
        #一、frame
        tk.Frame.__init__(self,master)
        self.pack()
        #二、button
        onButton=tk.Button(self)#创建一个按钮下面是属性设置默认值
        onButton['activebackground']='gray25'  #激活状态背景色
        onButton['activeforeground']='gray25'  #激活状态前景色
        onButton['anchor']='center'            #位于窗口位置,另有NW,SE等八选项
        onButton['background']='gray25'        #正常状态背景色gray25
        #onButton['bitmap']=                   #位图暂时没有
        onButton['borderwidth']=30             #边框宽度,单位是像素
        onButton['cursor']='gumby'             #光标形式gumby
        #onButton['command']=                  #鼠标离开时触发调用
        onButton['disabledforeground']='gray25'#禁用状态前景色gray25
        onButton['font']='Helvetica'           #字体
        onButton['foreground']='gray25'        #正常状态前景色
        onButton['highlightbackground']='gray' #高亮状态背景色
        onButton['highlightcolor']='gray'      #高亮状态前景色
        onButton['highlightthickness']=2       #高亮状态边框宽度
        onButton['height']=20                  #以font字体字符高度为单位
        #onButton['image']=                    #按钮图像暂时未有     
        onButton['justify']='right'            #组件内部内容对齐方式
        onButton['padx']=30                    #组件水平方向留白
        onButton['pady']=30                    #组件竖直方向留白
        onButton['relief']='raised'            #组件3D效果
        #onButton['selectbackground']='gray'   #选中时背景色
        #onButton['selectborderwidth']=2       #选中时3D边框宽度
        #onButton['selectforeground']='gray'   #选中时前景色
        onButton['state']='normal'             #组件当前状态NORMAL或DISABLE1
        onButton['takefocus']=1                #键盘接收焦点SHIFT+TAB,0则不接收
        onButton['text']='ok'                  #文本
        #onButton['textvariable']=bnText       #变量名,获取字符串
        onButton['underline']=2                #文本第几字符加下划线
        onButton['width']=20                   #以font字体字符高度为单位
        onButton['wraplength']=20              #对支持换行的组件每行最大字符数
        #onButton['xscrollcommand']=scroll.set
        #onButton['yscrollcommand']=scroll.set
        onButton.pack()
        #三、Label
        w=tk.Label(self)
        w.x=tk.PhotoImage(file='./image/background.png')
        w['image']=w.x 
        w.pack()
app=myApplication()
app.master.title('CJ')
app.mainloop()

#pack
from tkinter import *  
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        fm1=Frame(self.master)# 创建第一个容器
        fm1.pack(side=LEFT,fill=BOTH,expand=YES)#该容器放在左边排列
        Button(fm1,text='第一个').pack(side=TOP)#在frm1中设按钮且不填充
        Button(fm1,text='第二个').pack(side=TOP,fill=X,expand=YES)#在frm1中设按钮且在X方向填充(无效果)
        fm2=Frame(self.master)# 创建第二个容器
        fm2.pack(side=LEFT,padx=10,fill=BOTH,expand=YES)# 该容器放在左边排列,就会挨着fm1
        Button(fm2, text='第一个').pack(side=RIGHT)## 设置按钮从右边开始排列,不填充
        Button(fm2, text='第二个').pack(side=RIGHT, fill=X, expand=YES)#X方向填充 
        Button(fm2, text='第三个').pack(side=RIGHT, fill=Y, expand=YES)#Y方向填充 
        fm3 = Frame(self.master)# 创建第三个容器
        fm3.pack(side=RIGHT, padx=10, fill=BOTH, expand=YES)# 该容器放在右边排列
        Button(fm3, text='第一个').pack(side=BOTTOM, fill=Y, expand=YES)
        Button(fm3, text='第二个').pack(side=BOTTOM, fill=Y, expand=YES)
        Button(fm3, text='第三个').pack(side=BOTTOM, fill=Y, expand=YES) 
root = Tk()
root.title("Pack布局")
display = App(root)
root.mainloop()

#grid
from tkinter import *
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        Entry(relief=SUNKEN,font=('Courier New',24),width=25).pack(side=TOP, pady=10)#创建输入组件放顶部
        p=Frame(self.master)#创建frame
        p.pack(side=TOP)#放顶部
        names=("0","1","2","3","4","5","6","7","8","9","+","-","*","/",".","=")
        for i in range(len(names)):#遍历字符串元组
            b=Button(p,text=names[i],font=('Verdana',20),width=6)#创建p的子对象Button
            b.grid(row=i//4,column=i%4)
root = Tk()
root.title("Grid布局")
App(root)
root.mainloop()

#place
from tkinter import *
import random
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        books=('疯狂Python讲义','疯狂Swift讲义','疯狂Kotlin讲义','疯狂Java讲义','疯狂Ruby讲义')
        for i in range(len(books)):
            ct = [random.randrange(256) for x in range(3)] # 生成3个随机数
            bg_color="#%02x%02x%02x" % tuple(ct)#得到背景色
            grayness=int(round(0.299*ct[0]+0.587*ct[1]+0.114*ct[2]))#得到灰度,下面用来设前景色
            lb=Label(root,text=books[i],fg='White' if grayness<120 else 'Black',bg=bg_color)
            lb.place(x=20,y=36+i*36,width=180,height=30) # 使用place()设置该Label的大小和位置
root = Tk()
root.title("Place布局")
root.geometry("250x250+30+30")#设置窗口的大小和位置width x height + x_offset + y_offset
App(root)
root.mainloop()

#command
from tkinter import *
import random
class App:
    def __init__(self):
        self.master = Tk()
        self.initWidgets()
        self.master.title("简单事件处理")
        self.master.mainloop()
    def initWidgets(self):
        self.label = Label(self.master, width=30)
        self.label['font'] = ('Courier', 20)  #字体
        self.label['bg'] = 'white'            #背景色
        self.label.pack()
        self.bn = Button(self.master, text='单击我', command=self.change)
        self.bn.pack()
    def change(self):
        self.label['text'] = '欢迎学习Python'
        ct = [random.randrange(256) for x in range(3)]# 生成3个随机数
        grayness = int(round(0.299*ct[0] + 0.587*ct[1] + 0.114*ct[2]))
        bg_color = "#%02x%02x%02x" % tuple(ct)#把三个随机数转成颜色格式
        self.label['bg'] = bg_color
        self.label['fg'] = 'black' if grayness > 125 else 'white'
my_app=App()

#bind
from tkinter import *
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.show = Label(self.master, width=30, bg='white', font=('times', 20))
        self.show.pack()
        self.bn = Button(self.master, text='CJ的按钮')
        self.bn.pack(fill=BOTH, expand=YES)
        self.bn.bind('<Button-1>',self.one)
        self.bn.bind('<B2-Motion>',self.b2_motion)
        self.bn.bind('<Double-3>',self.double)
        self.bn.bind('<Motion>',self.motion)
    def one(self, event):
        self.show['text']="左键单击:%s" % event.widget['text']
    def b2_motion(self,event):
        self.show['text']="你正在按着中键移动"
    def double(self, event):
        self.show.config(text="右键双击:%s" % event.widget['text'])
    def motion(self,event):
        self.show['text']="鼠标坐标更新为(%d %d)"%(event.x,event.y)   
root = Tk()
root.title('简单绑定')
App(root)
root.mainloop()

#demo_calculator
from tkinter import *
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
        self.flag = 0
    def initWidgets(self):
        self.show=Label(relief=SUNKEN,font=('Courier New',24),width=25,bg='white',anchor=E)
        self.show.pack(side=TOP, pady=10)
        self.p = Frame(self.master)
        self.p.pack(side=TOP)
        names=("0","1","2","3","4","5","6","7","8","9","+","-","*","/",".","=")
        for i in range(len(names)):
            b=Button(self.p,text=names[i],font=('Verdana',20),width=6)
            b.grid(row=i // 4, column=i % 4)
            b.bind('<Button-1>', self.click)
    def click(self, event):
        if(event.widget['text'] != '='):
            self.show['text'] = self.show['text'] + event.widget['text']
            self.flag = 0
        elif(event.widget['text']=='='):
            if(self.flag==1):
                self.show['text']=''
            else:
                self.flag=1#先设flag是为了下一行出错(如输入01+2)方法直接返回时也能使用双击=消值功能
                self.show['text']=str(eval(self.show['text']))#使用eval函数计算表达式的值并显示
root = Tk()
root.title("计算器")
App(root)
root.mainloop()

#ttk(就是改了下装饰)
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        cb=ttk.Combobox(self.master, font=24)#下拉列表
        cb['values'] = ('Python', 'Swift', 'Kotlin')
        cb.pack(side=LEFT, fill=X, expand=YES)
        lab = ttk.Label(self.master, text='我的标签', font=24)
        lab.pack(side=TOP, fill=BOTH, expand=YES)
        bn = ttk.Button(self.master, text='我的按钮')
        bn.pack()
root = Tk()
root.title("简单事件处理")
App(root)
root.mainloop()

#StringVar(IntVar/DoubleVar/BooleanVar)
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import random
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.st = StringVar()#这个变量的获取与调用就是用get,set,注意下面的输入框的内从与这个变量绑定
        ttk.Entry(self.master,textvariable=self.st,width=24,font=('StSong',20,'bold'),foreground='red').pack(fill=BOTH, expand=YES)
        f = Frame(self.master)
        f.pack()
        ttk.Button(f,text='改变',command=self.change).pack(side=LEFT)
        ttk.Button(f,text='获取',command=self.get).pack(side=LEFT)
    def change(self):
        books = ('疯狂Python讲义', '疯狂Kotlin讲义', '疯狂Swift讲义')
        self.st.set(books[random.randint(0, 2)])
    def get(self):
        messagebox.showinfo(title='输入内容', message=self.st.get() )
root = Tk()
root.title("variable测试")
App(root)
root.mainloop()

#PhotoImage
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        # 创建一个位图
        bm =PhotoImage(file='images/serial.png')#此处会报错,先改成None跑一次,再转成图像
        self.label = ttk.Label(self.master, text='疯狂体\n系图书',image=bm , font=('StSong', 20, 'bold'), foreground='red' )
        self.label.bm = bm
        self.label['compound'] = None
        self.label.pack()
        f=ttk.Frame(self.master)# 创建Frame容器,用于装多个Radiobutton
        f.pack(fill=BOTH, expand=YES)
        self.var = StringVar()# 定义一个StringVar变量,用作绑定Radiobutton的变量
        self.var.set('None')
        compounds = ('None', "LEFT", "RIGHT", "TOP", "BOTTOM", "CENTER")
        for val in compounds:# 使用循环创建多个Radionbutton组件,variable=self.var就是说每个单选按钮都绑定这个变量,变量值就是按钮value
            Radiobutton(f,text=val,padx=20,variable=self.var,command=self.change_compound,value=val).pack(side=LEFT,anchor=CENTER)
    def change_compound(self):
        self.label['compound'] = self.var.get().lower()
root = Tk()
root.title("compound测试")
App(root)
root.mainloop()

#entry/text/messagebox
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.entry=ttk.Entry(self.master,width=44,font=('StSong',14),foreground='green')
        self.entry.pack(fill=BOTH,expand=YES)
        self.text=Text(self.master,width=44,height=4,font=('StSong',14),foreground='gray')
        self.text.pack(fill=BOTH,expand=YES)
        f=Frame(self.master)
        f.pack()
        ttk.Button(f, text='开始处插入', command=self.insert_start).pack(side=LEFT)
        ttk.Button(f, text='光标处插入', command=self.insert_edit).pack(side=LEFT)
        ttk.Button(f, text='结尾处插入', command=self.insert_end).pack(side=LEFT)
        ttk.Button(f, text='删除文本', command=self.del_text).pack(side=LEFT)
        ttk.Button(f, text='获取文本', command=self.get_text).pack(side=LEFT)
    def insert_start(self):
        self.entry.insert(0, 'Kotlin')
        self.text.insert(0.0, 'Kotlin')#第1行第0个字符处插入
    def insert_edit(self):
        self.entry.insert(INSERT, 'Python')
        self.text.insert(INSERT, 'Python')
    def insert_end(self):
        self.entry.insert(END, 'Swift')
        self.text.insert(END, 'Swift')
    def del_text(self):
        self.text.delete(1.0,END)#删全部
        self.entry.delete(0)#删一个字符
    def get_text(self):
        messagebox.showinfo(title='输入内容', message=self.entry.get())
        messagebox.showinfo(title='输入内容', message=self.text.get(1.0, END))
root = Tk()
root.title("Entry测试")
App(root)
root.mainloop()

#webbrowser
import webbrowser
webbrowser.open('https://item.jd.com/12261787.html')

#intVar/Radiobutton单选按钮
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        ttk.Label(self.master, text='选择您喜欢的单词:').pack(fill=BOTH, expand=YES)
        self.intVar = IntVar()
        self.intVar.set(2)
        books = ('CJ', 'loves','GJL',  'forever')
        i = 1
        for book in books:
            ttk.Radiobutton(self.master,text=book,variable=self.intVar,command=self.change,value=i).pack(anchor=W)
            i += 1
    def change(self):
        print(self.intVar.get())
root = Tk()
root.title("Radiobutton测试")
App(root)
root.mainloop()

#intVar/Checkbutton多选按钮
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.chars = []
        for ch in ('孙悟空', '猪八戒','唐僧', '牛魔王'):
            intVar=IntVar()
            self.chars.append(intVar)
            ttk.Checkbutton(self.master,text=ch,variable=intVar,command=self.change).pack(anchor=W)#不加w会居中
    def change(self):
        print(','.join([str(e.get()) for e in self.chars]))
root = Tk()
root.title("Checkbutton测试")
root.iconbitmap('images/fklogo.ico')
App(root)
root.mainloop()

#Listbox
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #第一个frame
        topF = Frame(self.master)
        topF.pack(fill=Y, expand=YES)
        #列表框
        self.lb = Listbox(topF)
        self.lb.pack(side=LEFT, fill=Y, expand=YES)
        for item in ['Python', 'Kotlin', 'Swift', 'Ruby']:self.lb.insert(END, item)
        #self.lb.insert(ANCHOR, 'Python', 'Kotlin', 'Swift', 'Ruby')#与上一行等价
        self.lb.bind("<Double-1>", self.click)#列表框中的双击事件
        #滚动条
        scroll=Scrollbar(topF, command=self.lb.yview)# 创建Scrollbar组件,设置该组件与self.lb的纵向滚动关联
        scroll.pack(side=RIGHT, fill=Y)
        self.lb.configure(yscrollcommand=scroll.set)# 设置self.lb的纵向滚动影响scroll滚动条
        #第二个frame
        f = Frame(self.master)
        f.pack()
        #标签
        Label(f, text = '选择模式:').pack(side=LEFT)
        #多选按钮
        self.strVar = StringVar()
        self.strVar.set('browse')
        for m in ('multiple', 'browse', 'single', 'extended'):#单选可拖动,多选,单选必单击,多选必ctrl/shift
            ttk.Radiobutton(f,text=m,value=m,variable=self.strVar,command=self.choose_mode).pack(side=LEFT)
    def choose_mode(self):
        print(self.strVar.get())
        self.lb['selectmode'] = self.strVar.get()
    def click(self, event):
        print(str(self.lb.curselection()))
root = Tk()
root.title("Listbox测试")
root.iconbitmap('images/fklogo.ico')
App(root)
root.mainloop()
        
#Combobox
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #下拉列表绑定字符串变量
        self.strVar = StringVar()
        self.cb = ttk.Combobox(self.master,textvariable=self.strVar,postcommand=self.choose)
        self.cb.pack(side=TOP)
        self.cb['values'] = ['Python', 'Ruby', 'Kotlin', 'Swift']
        #frame
        f = Frame(self.master)
        f.pack()
        #多选按钮绑定整型变量
        self.isreadonly = IntVar()
        Checkbutton(f, text = '是否只读:',variable=self.isreadonly,command=self.change).pack(side=LEFT)
        Button(f, text = '绑定变量设置',command=self.setvalue).pack(side=LEFT)
    def choose(self):
        print(str(self.cb.get()))
    def change(self):
        self.cb['state'] = 'readonly' if self.isreadonly.get() else 'enable'#改cb的state属性
    def setvalue(self):
        self.strVar.set('我爱Python')#改字符串变量
root = Tk()
root.title("Combobox测试")
root.iconbitmap('images/fklogo.ico')
App(root)
root.mainloop()

#scale
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.doubleVar = DoubleVar()
        self.scale = Scale(self.master,
            from_ = -100,  # 设置最大值
            to = 100,  # 设置最小值
            resolution = 5, # 设置步长
            label = '示范Sacle', # 设置标签内容
            length = 400, # 设置轨道的长度
            width = 30, # 设置轨道的宽度
            troughcolor='lightblue', # 设置轨道的背景色
            sliderlength=20, # 设置滑块的长度
            sliderrelief=SUNKEN, # 设置滑块的立体样式
            showvalue=YES, # 设置显示当前值…………………………………这个就是本例中要控制的变量
            orient = HORIZONTAL,  #设置水平方向………………………这个就是本例中要控制的变量
            digits = 10, # 设置十位有效数字
            command = self.change, # 绑定事件处理函数(value会自动传过去)
            variable = self.doubleVar # 绑定变量(如果只是获取的话不用绑定变量也可,直接self.scale.get())
        )
        self.scale.set(20)
        self.scale.pack()
        #第一个frame
        f = Frame(self.master)
        f.pack(fill=X, expand=YES, padx=10)
        Label(f, text='是否显示值:').pack(side=LEFT)
        self.showVar=IntVar()
        self.showVar.set(1)
        Radiobutton(f, text='不显示', value=0,variable=self.showVar,command=self.switch_show).pack(side=LEFT)
        Radiobutton(f, text='显示', value=1,variable=self.showVar,command=self.switch_show).pack(side=LEFT)
        #第一个frame
        f = Frame(self.master)
        f.pack(fill=X, expand=YES, padx=10)
        Label(f, text='方向:').pack(side=LEFT)
        self.orientVar = IntVar()
        self.orientVar.set(0)
        Radiobutton(f, text='水平', value=0,variable=self.orientVar,command=self.switch_orient).pack(side=LEFT)
        Radiobutton(f, text='垂直', value=1,variable=self.orientVar,command=self.switch_orient).pack(side=LEFT)
    def switch_show(self):
        self.scale['showvalue'] = self.showVar.get()
    def switch_orient(self):
        self.scale['orient'] = VERTICAL if self.orientVar.get() else HORIZONTAL
    def change(self, value):
        print(value, self.scale.get(), self.doubleVar.get())
root = Tk()
root.title("Scale测试")
App(root)
root.mainloop()

#LabeledScale(简化版scale)
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        self.scale=ttk.LabeledScale(self.master,from_=-100,to=100,compound=BOTTOM)#设置显示数值在下方
        self.scale.value = -20
        self.scale.pack(fill=X, expand=YES)
root = Tk()
root.title("LabeledScale测试")
App(root)
root.mainloop()

#Labelframe(frame上有标签)
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        lf = ttk.Labelframe(self.master, text='请选择',padding=20)
        lf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
        i = 0
        self.intVar = IntVar()
        for book in ['CJ', 'LOVES', 'GJL', '1314']:
            Radiobutton(lf, text=book,value=i,variable=self.intVar,command=self.rb).pack(side=LEFT)
            i += 1     
    def rb(self):
        print(self.intVar.get())
        self.intVar.set((self.intVar.get()+1)%4)
root = Tk()
root.title("Labelframe测试")
# 改变窗口图标
root.iconbitmap('images/fklogo.ico')
App(root)
root.mainloop()

#Panedwindow(用户可拉动分界线)
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #创建Panedwindow组件,通过style属性配置分隔线
        style = ttk.Style()
        style.configure("fkit.TPanedwindow", background='darkgray', relief=RAISED)
        pwindow = ttk.Panedwindow(self.master,orient=VERTICAL, style="fkit.TPanedwindow") 
        pwindow.pack(fill=BOTH, expand=1)
        #插入一般部件,add,remove,insert
        pwindow.add(ttk.Label(pwindow, text="this is a label"))
        okBn=ttk.Button(pwindow,text="this is a button",command=lambda:pwindow.remove(okBn))
        pwindow.add(okBn)# 因为上面remove要引用okBn所以不能直接把创建代码扔进add里
        pwindow.add(ttk.Entry(pwindow, width=30))
        pwindow.insert(1, Label(pwindow, text="this is a label too"))
        #插入pwindow部件,创建pwindow子对象是水平方向
        rightwindow = PanedWindow(pwindow, orient=HORIZONTAL)
        pwindow.add(rightwindow)
        rightwindow.add(Label(rightwindow, text="左标签", background='lightgreen'))  
        rightwindow.add(Label(rightwindow, text="右标签", background='lightblue'))  
root = Tk()
root.title("Panedwindow测试")
App(root)
root.mainloop()

#OptionMenu
from tkinter import *
from tkinter import ttk
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #先建个可选菜单
        self.sv = StringVar()
        self.om = ttk.OptionMenu(root,self.sv,'CJ','CJ','loves','GJL',command=self.print_option)
        self.om.pack()
        #然后建个有标签的frame
        lf = ttk.Labelframe(self.master, padding=20, text='choose:')
        lf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
        #层里建单选按钮
        self.directions = ['below', 'above', 'left', 'right', 'flush']
        i = 0
        self.intVar = IntVar()
        for direct in self.directions:
            Radiobutton(lf,text=direct,value=i,command=self.change,variable=self.intVar).pack(side=LEFT)
            i += 1
    def print_option(self, val):
        print(self.sv.get(), val)#其实就是绑定变量然后在函数里get就行了,同理set回去也可以
    def change(self):
        self.om['direction'] = self.directions[self.intVar.get()]#修改属性
root = Tk()
root.title("OptionMenu测试")
root.iconbitmap('images/fklogo.ico')
App(root)
root.mainloop()

#simpledialog,dialog提示对话框
from tkinter import *
from tkinter import ttk
from tkinter import simpledialog
from tkinter import dialog
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        ttk.Button(self.master,text='SimpleDialog',command=self.open_simpledialog).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master,text='Dialog',command=self.open_dialog).pack(side=LEFT, ipadx=5, ipady=5, padx = 10)
    def open_simpledialog(self):
        d = simpledialog.SimpleDialog(self.master, # 设置该对话框所属的窗口
            title='SimpleDialog测试',              # 标题
            text='CJ loves GJL.',                 # 内容
            cancel=3,                             # 用户点击x关闭对话框时返回值
            default=0,                            # 设置默认是哪个按钮得到焦点
            buttons=["是", "否", "取消"]        )
        print(d.go())  #①
    def open_dialog(self):
        d = dialog.Dialog(self.master  # 设置该对话框所属的窗口
            , {'title': 'Dialog测试',  # 标题
            'text':'CJ loves GJL.',    # 内容
            'bitmap': 'question',      # 图标
            'default': 0,              # 设置默认选中项
            'strings': ('确定','取消','退出')})
        print(d.num)  #②
root = Tk()
root.title("对话框测试")
App(root)
root.mainloop()

##askinteger输入框
from tkinter import *
from tkinter import ttk
from tkinter import simpledialog
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        ttk.Button(self.master,text='int',command=self.open_integer).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master,text='float',command=self.open_float).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='str',command=self.open_string).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master,text='colour',command=self.choose_color).pack(side=LEFT,ipadx=5,ipady=5,padx= 10)
    def open_integer(self):
        print(simpledialog.askinteger("猜岁数","我老婆多少岁:",initialvalue=30,minvalue=10,maxvalue=50))
    def open_float(self):
        print(simpledialog.askfloat("猜体重","我老婆多少千克:",initialvalue=30,minvalue=10,maxvalue=50))
    def open_string(self):
        print(simpledialog.askstring("猜名字","我老婆叫什么名字:",initialvalue='GJL'))
    def choose_color(self):
        print(colorchooser.askcolor(parent=self.master, title='选择画笔颜色'))
root = Tk()
root.title("输入对话框测试")
App(root)
root.mainloop()

#message(共四部分:标题,文字,图标,按钮)
from tkinter import *
from tkinter import ttk
from tkinter import messagebox as msgbox
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #先弄个大frame
        topF = Frame(self.master)
        topF.pack(fill=BOTH)
        #大frame下设图标选择
        lf1 = ttk.Labelframe(topF, text='请选择图标类型')
        lf1.pack(side=LEFT, fill=BOTH, expand=YES, padx=10, pady=5)
        i = 0
        self.iconVar = IntVar()
        self.icons = [None, "error", "info", "question", "warning"]
        for icon in self.icons:
            Radiobutton(lf1, text = icon if icon is not None else '默认',value=i,variable=self.iconVar).pack(side=TOP, anchor=W)
            i += 1
        self.iconVar.set(0)
        #大frame下设按钮选择
        lf2 = ttk.Labelframe(topF, text='请选择按钮类型')
        lf2.pack(side=LEFT,fill=BOTH, expand=YES, padx=10, pady=5)
        i = 0
        self.typeVar = IntVar()
        self.types = [None, "abortretryignore", "ok", "okcancel","retrycancel", "yesno", "yesnocancel"]
        for tp in self.types:
            Radiobutton(lf2, text= tp if tp is not None else '默认',value=i,variable=self.typeVar).pack(side=TOP, anchor=W)
            i += 1
        self.typeVar.set(0)
        #最下面是按钮事件
        bottomF = Frame(self.master)
        bottomF.pack(fill=BOTH)
        btn1 = ttk.Button(bottomF, text="showinfo",command=self.showinfo_clicked)
        btn1.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn2 = ttk.Button(bottomF, text="showwarning",command=self.showwarning_clicked)
        btn2.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn3 = ttk.Button(bottomF, text="showerror",command=self.showerror_clicked)
        btn3.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn4 = ttk.Button(bottomF, text="askquestion",command=self.askquestion_clicked)
        btn4.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn5 = ttk.Button(bottomF, text="askokcancel",command=self.askokcancel_clicked)
        btn5.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn6 = ttk.Button(bottomF, text="askyesno",command=self.askyesno_clicked)
        btn6.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn7 = ttk.Button(bottomF, text="askyesnocancel",command=self.askyesnocancel_clicked)
        btn7.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
        btn8 = ttk.Button(bottomF, text="askretrycancel",command=self.askretrycancel_clicked)
        btn8.pack(side=LEFT, fill=X, ipadx=5, ipady=5,pady=5, padx=5)
    def showinfo_clicked(self):
        print(msgbox.showinfo("Info", "showinfo测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def showwarning_clicked(self):
        print(msgbox.showwarning("Warning", "showwarning测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def showerror_clicked(self):
        print(msgbox.showerror("Error", "showerror测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def askquestion_clicked(self):
        print(msgbox.askquestion("Question", "askquestion测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def askokcancel_clicked(self):
        print(msgbox.askokcancel("OkCancel", "askokcancel测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def askyesno_clicked(self):
        print(msgbox.askyesno("YesNo", "askyesno测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def askyesnocancel_clicked(self):
        print(msgbox.askyesnocancel("YesNoCancel", "askyesnocancel测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
    def askretrycancel_clicked(self):
        print(msgbox.askretrycancel("RetryCancel", "askretrycancel测试.",icon=self.icons[self.iconVar.get()],type=self.types[self.typeVar.get()]))
root = Tk()
root.title("消息框测试")
App(root)
root.mainloop()

#filedialog(注意返回值是文件还是文件名/路径)
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        ttk.Button(self.master, text='打开单个文件',command=self.open_file).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='打开多个文件',command=self.open_files).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='获取单个打开文件的文件名',command=self.open_filename).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='获取多个打开文件的文件名',command=self.open_filenames).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='获取保存文件',command=self.save_file).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='获取保存文件的文件名',command=self.save_filename).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
        ttk.Button(self.master, text='打开路径',command=self.open_dir).pack(side=LEFT, ipadx=5, ipady=5, padx= 10)
    def open_file(self):# 调用askopenfile方法获取单个打开的文件
        print(filedialog.askopenfile(title='打开单个文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/'))
    def open_files(self):# 调用askopenfile方法获取多个打开的文件
        print(filedialog.askopenfiles(title='打开多个文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/')) 
    def open_filename(self):# 调用askopenfilename方法获取单个文件的文件名
        print(filedialog.askopenfilename(title='打开单个文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/')) 
    def open_filenames(self):# 调用askopenfilenames方法获取多个文件的文件名
        print(filedialog.askopenfilenames(title='打开多个文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/')) 
    def save_file(self):# 调用asksaveasfile方法保存文件
        print(filedialog.asksaveasfile(title='保存文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/'))
    def save_filename(self):# 调用asksaveasfilename方法获取保存文件的文件名
        print(filedialog.asksaveasfilename(title='保存文件',filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')],initialdir='g:/'))
    def open_dir(self):#  标题,初始路径,比上面的方法少了个文件类型(因为现在打开的是路径)
        print(filedialog.askdirectory(title='打开目录',initialdir='g:/')) # 初始目录
root = Tk()
root.title("文件对话框测试")
App(root)
root.mainloop()

#Menu(command/checkbutton/radiobutton/separator,label标签/command方法/image贴图/compound方位)
from tkinter import *
from tkinter import ttk
from tkinter import messagebox as msgbox
class App:
    def __init__(self, master):
        self.master = master
        self.init_menu()
    def init_menu(self):
        #一、总菜单对象
        menubar = Menu(self.master)
        self.master['menu'] = menubar
        #二、主菜单对象
        file_menu = Menu(menubar, tearoff=0)# 创建file_menu菜单,它被放入menubar中
        lang_menu = Menu(menubar, tearoff=0)# 创建lang_menu菜单,它被放入menubar中
        menubar.add_cascade(label='文件', menu=file_menu)# 使用add_cascade方法添加file_menu菜单
        menubar.add_cascade(label='语言', menu=lang_menu)# 使用add_cascade方法添加lang_menu菜单
        #三、为file_menu添加下拉菜单(注意加add_command是叶子项,加add_cascade是子级菜单)
        self.master.filenew_icon = PhotoImage(file='images/filenew.png')
        self.master.fileopen_icon = PhotoImage(file='images/fileopen.png')
        file_menu.add_command(label="新建", command = None,image=self.master.filenew_icon, compound=LEFT)
        file_menu.add_command(label="打开", command = None,image=self.master.fileopen_icon, compound=LEFT)
        file_menu.add_separator()#分隔条
        sub_menu = Menu(file_menu, tearoff=0)# 创建sub_menu菜单,它被放入file_menu中
        file_menu.add_cascade(label='性别', menu=sub_menu) #使用add_cascade方法添加sub_menu子菜单
        #四、为sub_menu添加子菜单(注意加了add_radiobutton单选按钮)
        self.genderVar = IntVar()
        for i, im in enumerate(['男', '女', '保密']):# 使用enumerate循环为sub_menu子菜单添加菜单项(特别注意这里的写法)
            sub_menu.add_radiobutton(label=im,command=self.choose_gender,variable=self.genderVar, value=i)#枚举标签,同方法,同变量,枚举值
        #五、为lang_menu添加下拉菜单(注意加了add_checkbutton多选按钮)
        self.langVars = [StringVar(), StringVar(), StringVar(), StringVar()]
        for i, im in enumerate(('Python', 'Kotlin','Swift', 'Java')):
            lang_menu.add_checkbutton(label=im, command=self.choose_lang,onvalue=im, variable=self.langVars[i])
    def choose_gender(self):
        msgbox.showinfo(message=('选择的性别为: %s' % self.genderVar.get()))
    def choose_lang(self):
        rt_list = [e.get() for e in self.langVars]
        msgbox.showinfo(message=('选择的语言为: %s' % ','.join(rt_list)))
root = Tk()
root.title("菜单测试")
root.geometry('400x200')  
root.resizable(width=False, height=False)#禁止改变窗口大小
App(root)
root.mainloop()

#右键菜单(函数传参写法)
from tkinter import *
from tkinter import ttk
from collections import OrderedDict
class App:
    def __init__(self, master):
        self.master = master
        self.initWidgets()
    def initWidgets(self):
        #主界面就是一个text
        self.text=Text(self.master,height=12,width=60,foreground='darkgray',font=('微软雅黑',12),spacing2=8,spacing3=12)
        self.text.insert(END,'CJ loves GJL.')
        self.text.pack()
        self.text.bind('<Button-3>',self.popup)
        self.text.bind('<Up>', self.keyup)
        #右键一级菜单是m1与m2
        self.popup_menu = Menu(self.master,tearoff = 0)
        m1 = Menu(self.popup_menu, tearoff = 0)
        m2 = Menu(self.popup_menu, tearoff = 0)
        self.popup_menu.add_cascade(label='fc',menu = m1)
        self.popup_menu.add_cascade(label='bc',menu = m2)
        #m1下的菜单是rgb三个,通过用variable绑定value来换前景色
        self.fcVar=StringVar()
        m1.add_radiobutton(label='r',command=self.choose1,variable=self.fcVar,value='red')
        m1.add_radiobutton(label='b',command=self.choose1,variable=self.fcVar,value='blue')
        m1.add_radiobutton(label='g',command=self.choose1,variable=self.fcVar,value='green')
        #m2下的菜单也是rgb三个,但是用换了一种函数写法来换背景色
        self.my_items=OrderedDict([('红色','red'), ('绿色','green'), ('蓝色', 'blue')])
        for i in self.my_items:
            m2.add_command(label=i,command=self.handlerAdaptor(self.choose2,val=i))
    def popup(self, event):
        self.popup_menu.post(event.x_root,event.y_root)
    def choose1(self):
        self.text['foreground'] =  self.fcVar.get()
    def choose2(self,val):
        self.text['background'] = self.my_items[val]
    def handlerAdaptor(self, fun,**kwds):
        return lambda fun=fun, kwds=kwds: fun(**kwds)
    def keyup(self, event):
        print('up',event)#up <KeyPress event state=Mod1|0x40000 keysym=Up keycode=38 x=149 y=261>
root = Tk()
root.title("右键菜单测试")
App(root)
root.mainloop()

#tearoff=1时的情况:能独立出来
import tkinter
root = tkinter.Tk()
root.title('menu')
menu = tkinter.Menu(root)
submenu = tkinter.Menu(menu, tearoff = 1)
submenu.add_command(label = '打开')
menu.add_cascade(label = '文件', menu = submenu)
root.config(menu = menu)
root.mainloop()

#Canvas
from tkinter import *
root = Tk()
cv = Canvas(root, background='white')
cv.pack(fill=BOTH, expand=YES)
cv.create_rectangle(30, 30, 200, 200,outline='red',stipple = 'question',fill="blue",width=5)
cv.create_oval(240, 30, 330, 200,outline='yellow',fill='pink',width=4)
Canvas.create_window(cv, 0, 0, window=Button(cv,text = '单击我', padx=10, pady=5,command = lambda :print('按钮单击')),anchor=NW)
root.mainloop()

#tag_bind与focus_set/bind
from tkinter import *
def first(event):
    print('第一次的函数')
def second(event):
    print('第二次的函数')
def move_left(event):
    print('left')
root = Tk()
cv = Canvas(root,bg = 'white')# 创建一个Canvas,设置其背景色为白色
cv.pack()
cv.create_rectangle(30, 30, 220, 150,width = 8,tags = ('r1','r2','r3'))
cv.tag_bind('r1','<Button-1>', first)# 为指定图形项的左键单击事件绑定处理函数
cv.tag_bind('r1','<Button-1>', second, add=True)# add为True是添加,否则是替代
cv.focus_set()#让画布得到焦点,才可以响应按键事件
cv.bind('<Left>',move_left)
root.mainloop()
#第十二章:文件IO
#PurePath自动转PureWindowsPath
from pathlib import *
print(type(PurePath('setup.py')))  # <class 'pathlib.PureWindowsPath'>,自动转为win格式
print(PurePath('crazyit', 'some/path', 'info')) # 'crazyit\some\path\info'
print(PurePath(Path('crazyit'), Path('info'))) # 'crazyit\info'
print(PurePosixPath('crazyit','some/path' 'info')) # crazyit/some/path/info,Unix风格的路径
print(PurePath()) # . 不传入参数,默认使用当前路径
print(PurePosixPath('/etc', '/usr','lib64'))# /usr/lib64,传入参数包含多个根路径
print(PureWindowsPath('c:/Windows','d:info'))# d:info,仅最后一个根路径及后面子路径生效
print(PureWindowsPath('c:/Windows','/Program Files'))#c:\Program Files,在Win中盘符才算根路径
print(PurePath('crazyit//info')) # crazyit\info,路径字符串中多出来的斜杠和点号都会被忽略
print(PurePath('crazyit/./info')) # crazyit\info
print(PurePath('crazyit/../info')) # crazyit\..\info,相当于找和crazyit同一级的info路径
print(PurePosixPath('info') == PurePosixPath('INFO'))# False比较Unix风格路径区分大小写
print(PureWindowsPath('info') == PureWindowsPath('INFO')) # True比较WIN风格路径不区分大小写
print(PureWindowsPath('crazyit') == PurePosixPath('crazyit')) # False不同风格路径总不等
print(PureWindowsPath('abc') / 'xyz' / 'wawa') # abc\xyz\wawa(Win风格的路径)
print(PurePosixPath('abc') / 'xyz' / 'wawa') # abc/xyz/wawa(Unix风格的路径)
print(str(PureWindowsPath('abc','xyz','wawa'))) # abc\xyz\wawa
print(str(PurePosixPath('abc', 'xyz', 'wawa'))) # abc/xyz/wawa

#PurePath的属性
from pathlib import *
# 访问drive属性:驱动器盘符 
print(PureWindowsPath('c:/Program Files/').drive) # c:
print(PureWindowsPath('/Program Files/').drive) # ''
print(PurePosixPath('/etc').drive) # ''
# 访问root属性:根路径
print(PureWindowsPath('c:/Program Files/').root) # \
print(PureWindowsPath('c:Program Files/').root) # ''
print(PurePosixPath('/etc').root) # /
# 访问anchor属性:盘符与根路径
print(PureWindowsPath('c:/Program Files/').anchor) # c:\
print(PureWindowsPath('c:Program Files/').anchor) # c:
print(PurePosixPath('/etc').anchor) # /
# 访问parents属性:全部父路径
pp = PurePath('abc/xyz/wawa/haha')
print(pp.parents[0]) # abc\xyz\wawa
print(pp.parents[1]) # abc\xyz
print(pp.parents[2]) # abc
print(pp.parents[3]) # .
# 访问parent属性:上一级路径,相当于parents[0]
print(pp.parent) # abc\xyz\wawa
# 访问name属性:当前路径文件名
print(PurePath('abc/wawa/bb.txt').name) # bb.txt
# 访问suffixes属性:所有后缀名
pp = PurePath('abc/wawa/bb.txt.tar.zip')
print(pp.suffixes[0]) # .txt
print(pp.suffixes[1]) # .tar
print(pp.suffixes[2]) # .zip
# 访问suffix属性:suffixes最后一个值
print(pp.suffix) # .zip
# 访问stem属性:当前路径主文件名
print(pp.stem) # bb.txt.tar
# 类型转换方法
pp = PurePath('d:/', 'Python', 'Python3.6')
print(pp.as_posix()) # d:/Python/Python3.6,转成Unix风格的路径
print(pp.as_uri()) # file:///d:/Python/Python3.6,绝对路径转换成Uri
# 判断当前路径是否匹配指定模式
print(PurePath('a/b.py').match('*.py')) # True
print(PurePath('/a/b/c.py').match('b/*.py')) # True
print(PurePath('/a/b/c.py').match('a/*.py')) # False
# 测试relative_to方法:去除基准路径的路径
pp = PurePosixPath('c:/abc/xyz/wawa')
print(pp.relative_to('c:/'))  # abc\xyz\wawa
print(pp.relative_to('c:/abc'))  # xyz\wawa
print(pp.relative_to('c:/abc/xyz'))  # wawa
# 测试with_name方法:把当前路径文件名替换掉(当前路径无文件名会报错)
p = PureWindowsPath('e:/Downloads/pathlib.tar.gz')
print(p.with_name('fkit.py')) # e:\Downloads\fkit.py
# 测试with_suffix方法:把当前路径文件名后缀替换掉,若没有后缀则加上
print(PureWindowsPath('e:/pathlib.tar.gz').with_suffix('.zip'))  # e:\pathlib.tar.zip
print(PureWindowsPath('README').with_suffix('.txt'))  # README.txt

#Path的属性
from pathlib import *
for x in Path('.').iterdir():print(x)#当前目录下所有文件与子目录
for x in Path('../').glob('**/*.py'):print(x)#上级目录及其所有子目录下的的py文件
p = Path('a_test.txt')
print(p.write_text('''I LOVE GJL''',  encoding='GBK'))# 返回输出的字符数
print(p.read_text(encoding='GBK'))# 输出读取的文本内容
print(p.read_bytes())# 读取字节内容

#os.path的属性
import os
import time
print(os.path.abspath("abc.txt")) # G:\publish\codes\12\12.2\abc.txt 获取绝对路径
print(os.path.commonprefix(['/usr/lib', '/usr/local/lib'])) # /usr/l 获取共同前缀
print(os.path.commonpath(['/usr/lib', '/usr/local/lib'])) # \usr 获取共同路径
print(os.path.dirname('abc/xyz/README.txt')) #abc/xyz 获取目录
print(os.path.exists('abc/xyz/README.txt')) # False 判断指定目录是否存在 
print(time.ctime(os.path.getatime('a_test.txt')))# 获取最近一次访问时间
print(time.ctime(os.path.getmtime('a_test.txt')))# 获取最后一次修改时间
print(time.ctime(os.path.getctime('a_test.txt')))# 获取创建时间
print(os.path.getsize('a_test.txt'))# 获取文件大小
print(os.path.isfile('a_test.txt')) # True 判断是否为文件
print(os.path.isdir('a_test.txt')) # False 判断是否为目录
print(os.path.samefile('a_test.txt', './a_test.txt')) # True 判断是否为同一个文件

#fnmatch:file name match
from pathlib import *
import fnmatch
#fnmatch:对文件名进行匹配
for file in Path('.').iterdir():#遍历当前目录下所有文件和子目录
    if fnmatch.fnmatch(file,'*.PY'):print(file)# 访问所有以_test.py结尾的文件
#filter:对列表中的串进行匹配
names = ['a.py', 'b.py', 'c.py', 'd.py']
print(fnmatch.filter(names, '[ac].py')) # ['a.py', 'c.py']
#translate:把UNIX的shell风格转换成pattern风格
print(fnmatch.translate('?.py')) # (?s:.\.py)\Z
print(fnmatch.translate('[ac].py')) # (?s:[ac]\.py)\Z
print(fnmatch.translate('[a-c].py')) # (?s:[a-c]\.py)\Z

#写文件
#一、默认生成的txt文件使用的编码是ANSI
import os
f = open('ANSI_test.txt', 'w+')#清空写入
f.write('我爱龚嘉露13' + os.linesep)# os.linesep代表当前操作系统上的换行符
f.writelines(('CJ'+os.linesep,'LOVES'+os.linesep,'GJL'+os.linesep))
f.close()
f = open('ANSI_test.txt', 'a+')#追加写入
f.write('我爱龚嘉露14' + os.linesep)# os.linesep代表当前操作系统上的换行符
f.writelines(('CJ'+os.linesep,'LOVES'+os.linesep,'GJL'+os.linesep,'1314'+ os.linesep))
f.close()
#二、生成以utf-8编码的txt文件要用二进制打开再显式设置utf-8
f = open('utf-8_test.txt', 'wb+')
f.write(('陈俊爱龚嘉露' + os.linesep).encode('utf-8'))
f.writelines((('I'+os.linesep).encode('utf-8'),('love'+os.linesep).encode('utf-8'),('GJL'+os.linesep).encode('utf-8')))
f.close()
#三、ANSI编码方式的读入
f=open("ANSI_test.txt", 'r+', True)
print(f.read())
f.close()
#四、utf-8编方式的读入
#(1)使用codecs读入时直接以编码形式读
import codecs
f=codecs.open('utf-8_test.txt', 'r+', 'utf-8', buffering=True)
print(f.read())
f.close()
#(2)先以二进制形式读入再转换编码
f=open('utf-8_test.txt','rb+',True) #指定使用二进制方式读取文件内容,得到的是bytes类型
print(f.read().decode('utf-8'))     #用bytes的decode可将字节内容恢复成字符串
f.close()
#(3)补充:若上面输出不decode的话会得到
f=open('utf-8_test.txt','rb+',True) #指定使用二进制方式读取文件内容,得到的是bytes类型
print(f.read())#b'\xe9\x99\x88\xe4\xbf\x8a\xe7\x88\xb1\xe9\xbe\x9a\xe5\x98\x89\xe9\x9c\xb2\r\nI\r\nlove\r\nGJL\r\n'
f.close()
'''字符串前缀
一、字符串前加 r
r" " 的作用是去除转义字符
str1= 'input\n'
str= r'input\n'
print(str1)#input
print(str)#input\n
二、字符串前加 b
b" "前缀表示后面字符串是bytes类型
网络编程中,服务器和浏览器只认bytes类型数据
在 Python3 中,bytes 和 str 的互相转换方式是
str.encode('utf-8')
bytes.decode('utf-8')
三、字符串前加 u
例:u"我是含有中文字符组成的字符串。"
后面字符串以 Unicode 格式编码,防止因源码储存格式问题,导致再次使用时乱码
'''

#读文件
#零、热身
f = open('a_test.txt') # 默认打开方式
print(f.encoding)  # cp936  访问文件的编码方式此处即utf-8
print(f.mode)      # r      访问文件的访问模式
print(f.closed)    # False  访问文件是否已经关闭
print(f.name)      # a_test.txt  访问文件对象打开的文件名
'''(用三个+即可,二进制读入再加b)
r:只读(不清空,指针在开头)
w:只写(先清空)
a:只写(不清空,指针在结尾)
r+:读写(不清空,指针在开头)
w+:读写(先清空)
a+:读写(不清空,指针在结尾)
b:以二进制形式读写文件,用于非文本文件
'''
#一、逐字符读(第三个参数True表示使用缓冲)
f = open("a_test.txt", 'r', True)
while True:
    ch = f.read(1)    # 每次读取一个字符
    if not ch: break  # 如果没有读到数据,跳出循环
    print(ch, end='') # 输出ch
f.close()
#二、一次读完
f = open("a_test.txt", 'r', True)
print(f.read())# 直接读取全部文件
f.close()
#三、逐行读出
import codecs
f = codecs.open("a_test.txt", 'r', 'utf-8', buffering=True)
while True:
    line = f.readline() # 每次读取一行(其实就是遇\r\n或EOF为止),指针自动下移
    if not line: break  # 如果没有读到数据,跳出循环
    print(line, end='') # 输出line
f.close()
import codecs
f = codecs.open("a_test.txt", 'r', 'utf-8', buffering=True)
for l in f.readlines():# 使用readlines()读取所有行,返回所有行组成的列表
    print(l, end='')
f.close()
#四、读多文件
import fileinput
for line in fileinput.input(files=('a_test.txt', 'a_test.txt')):
    print(fileinput.filename(), fileinput.filelineno(), line, end='')#名,行号,行
fileinput.close()# 关闭文件流
#五、with:该语句会负责关闭文件
import codecs
with codecs.open("a_test.txt", 'r', 'utf-8', buffering=True) as f:
    for line in f:
        print(line, end='')
import fileinput
with fileinput.input(files=('a_test.txt', 'a_test.txt')) as f:
    for line in f:
        print(line, end='')
#六、with关键字原理:__enter__与__exit__
class FkResource:
    def __init__(self, tag):
        self.tag = tag
        print('类构造器: %s' % tag)
    def __enter__(self):# 定义__enter__方法,with体之前的执行的方法
        print('[__enter__ %s]: ' % self.tag)
        return '我是'+self.tag  # 该返回值将作为as子句中变量的值
    def __exit__(self, exc_type, exc_value, exc_traceback):
        print('[__exit__ %s]: ' % self.tag)
        if exc_traceback is None:# exc_traceback为None,代表没有异常
            print('无异常,关闭资源')
        else:
            print('有异常,关闭资源')
            return False   # 可以省略,默认返回None也被看做是False
with FkResource('孙悟空') as dr:
    print('[with代码块] 开始')
    print(dr)#正常执行
    print('[with代码块] 结束')
print('------------------------------') 
with FkResource('白骨精'):
    print('[with代码块] 开始')
    #raise Exception#出现异常
    print('[with代码块] 结束')
#七、读指定行
import linecache
print(linecache.getline('a_test.txt', 2))# 读取普通文件的第2行
#八、用seek操作指针位置,tell访问指针下标,read读取字节
f=open('Python_test2.py', 'rb')
print(f.tell())   # 0,当前文件指针的位置
f.seek(3)         # 将文件指针移动到3处
print(f.tell())   # 3,当前文件指针的位置
print(f.read(1))  # b'p',读取一个字节,文件指针自动后移1个数据
print(f.tell())   # 4,当前文件指针的位置
f.seek(5)         # 将文件指针移动到5处,与f.seek(5,0)等价,以开头为基准
print(f.tell())   # 5
f.seek(5, 1)      # 将文件指针向后移动5个数据,以指针当前位置为基准
print(f.tell())   # 10
f.seek(-10, 2)    # 将文件指针移动到倒数第10处,以指针结尾位置为基准
print(f.tell())   # 574
print(f.read(1))  # b')'
'''
问:在哪里查看一个txt文件格式的编码?
答:打开文档,点另存为,最下面编码栏默认显示的即是当前文件的编码格式。
问:关于编码方式之间的关系:
一、ANSI,最原始的ASCII码,只有127位(但现在说的ANSI是含有GBK的ANSI)
二、ASCII+扩展字符,变成255位
三、GB2312,中国人取消掉127位以后的符号,在原ASCII码基础上加入6000多个汉字
······此时,原ACSII码若用两字节表示叫全角字符,用一字节表示叫半角字符(如,和,)
······区分全/半角:一个小于127的字符意义不变,但两个大于127的字符连在一起就表示一个汉字
四、GBK,在GB2312基础上加入20000个新的汉字(包括繁体字)和符号
······不要求低字节一定是127后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始
······现在所说的ANSI就是指原ASCII加上GBK扩展包的,我的手提笔记本默认是ANSI编码格式
五、GB18030,在GBK基础上加入几千个新的少数民族的字
六、UNICODE,世界各国都搞编码系统太乱,ISO出台的统一编码系统,全球通用
······用两字节表示一字符,总共可以组合出65535个字符,可覆盖世界上所有文化的符号
······半角英文符号只需要用到低8位,所以其高8位永远是0,虽然浪费但是硬盘空间已不是问题
······笔记本另存为中除了ANSI,Unicode,UTF-8外还有一项Unicode big endian是大端模式
七、UTF(UCS Transfer Format),用于传输,UTF8是每次8个位传输数据,UTF16是每次16个位
'''

#目录
import os
print(os.getcwd())  # 获取当前目录F:\ProjectPython\hello_test2
os.chdir('F:')      # ch=change改变当前目录
os.mkdir('my_dir',0o755)#在当前目录创建目录,755是所有者/组用户/其他用户的读/写/执行权限
os.makedirs("abc/xyz/wawa", 0o755) # 递归创建目录
os.rename('my_dir', 'your_dir') # 直接重命名当前目录下的子目录
os.renames("abc/xyz/wawa", 'foo/bar/haha') # 递归重命名子目录
os.rmdir('your_dir')           # 直接删除当前目录下的子目录
os.removedirs('foo/bar/haha')  # 递归删除子目录
#权限
print(os.access('.', os.F_OK|os.R_OK|os.W_OK|os.X_OK))#当前目录权限F存在R读W写X执行
print(os.access('Python_test2.py', os.F_OK|os.R_OK|os.W_OK|os.X_OK))#文件权限
#链接
#os.symlink('Python_test4.py', 'soft_link.py')#创建快捷方式(WIN下要管理员权限)
#os.link('Python_test4.py', 'hard_link.py')#创建硬连接(Windows上就是复制文件)
#临时
import tempfile
fp = tempfile.TemporaryFile()# 创建临时文件
print(fp.name)
fp.write('两情若是久长时,'.encode('utf-8'))
fp.write('又岂在朝朝暮暮。'.encode('utf-8'))
fp.seek(0)# 将文件指针移到开始处,准备读取文件
print(fp.read().decode('utf-8')) # 输出刚才写入的内容
fp.close()#此时关闭就会自动删除
with tempfile.TemporaryFile() as fp:#通过with语句创建临时文件,块结束时自动关闭并删除临时文
    fp.write(b'I Love Python!')# 写入内容
    fp.seek(0)# 将文件指针移到开始处,准备读取文件
    print(fp.read()) # b'I Love Python!'# 读取文件内容
with tempfile.TemporaryDirectory() as tmpdirname:# 通过with语句创建临时目录
    print('创建临时目录', tmpdirname)
#第十三章:数据库
#sqlite3
import sqlite3
conn = sqlite3.connect('first.db')# ①、打开或创建数据库
c = conn.cursor()# ②、获取游标

#增
c.execute('''create table user_tb(_id integer primary key autoincrement,name text,pass text,gender text)''')#执行DDL语句创建数据表
#插
c.execute('insert into user_tb values(null, ?, ?, ?)',('孙悟空', '123456', 'male'))#调用执行insert语句插入数据
#改
c.executemany('update user_tb set name=? where _id=?',(('小孙',2),('小白',3),('小猪',4)))#调用executemany()方法同时修改多个语句
print('修改的记录条数:',c.rowcount)# 通过rowcount获取被修改的记录条数
#查
c.execute('select * from user_tb where _id > ?', (2,))#调用执行select语句查询数据
print('查询返回的记录数:', c.rowcount)
for col in (c.description):print(col[0], end='\t')#通过游标的description属性获取列信息
print('\n--------------------------------')
while True:
    row = c.fetchone()# 获取一行记录,每行数据都是一个元组
    if not row :break# 如果抓取的row为None,退出循环
    print(row)
#SQL脚本
c.executescript('''insert into user_tb values(null, '武松', '3444', 'male')''')# 执行一段SQL脚本
#自定义函数
def reverse_ext(st):# 先定义一个普通函数,准备注册为SQL中的自定义函数
    return '[' + st[::-1] + ']'# 对字符串反转,前后加方括号
conn.create_function('enc', 1, reverse_ext)## 调用create_function注册自定义函数:enc,这句要放在c游标赋值之前
c.execute('insert into user_tb values(null, ?, enc(?), ?)', ('贾宝玉', '123456', 'male'))# 在SQL语句中使用enc自定义函数
#聚集函数
class MinLen:# 先定义一个普通类,准备注册为SQL中的自定义聚集函数
    def __init__(self):
        self.min_len = None
    def step(self, value):
        if self.min_len is None : # 如果self.min_len还未赋值,直接将当前value赋值给self.min_lin
            self.min_len = value
            return
        if len(self.min_len) > len(value):# 找到一个长度更短的value,用value代替self.min_len
            self.min_len = value
    def finalize(self):
        return self.min_len
conn.create_aggregate('min_len', 1, MinLen)# 调用聚集函数名字,所需参数数目,函数实现类,这句要放在c游标赋值之前
c.execute('select min_len(pass) from user_tb')# 在SQL语句中使用min_len自定义聚集函数
print(c.fetchone()[0])#输出user_tb表中长度最短的密码
#比较函数
def my_collate(st1, st2):# 去掉字符串第一个、最后一个字符后比较大小
    if st1[1: -1] == st2[1: -1]:return 0
    elif st1[1: -1] > st2[1: -1]:return 1
    else:return -1
conn.create_collation('sub_cmp', my_collate)# 调用create_collation注册自定义比较函数:sub_cmp,这句要放在c游标赋值之前
c.execute('select * from user_tb order by pass collate sub_cmp')# ③、在SQL语句中使用sub_cmp自定义的比较函数
for row in c:print(row)# 采用for循环遍历游标,不需要fetchone()

conn.commit()
c.close()# ④、关闭游标
conn.close()# ⑤、关闭连接

'''
DDL(data definition language): DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用,不需要commit

DML(data manipulation language): 它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言,需要commit

DCL(Data Control Language): 是数据库控制功能。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL

TCL - Transaction Control Language:事务控制语言,COMMIT - 保存已完成的工作,SAVEPOINT - 在事务中设置保存点,可以回滚到此处,ROLLBACK - 回滚,SET TRANSACTION - 改变事务选项

'''
#第十四章:并发编程
'''
新建态start()变就绪态
就绪态得到CPU变运行态,运行态失去CPU变就绪态(CPU控制不关程序员事)
运行态sleep()或IO阻塞或等待锁或等待通知变阻塞态
阻塞态sleep()完或IO方法返回或获得锁或收到通知变就绪态
运行态run()或target完成变死亡态(若中途Error或Exception也变死亡态)
'''

#创建线程
#第一种方式:直接使用Thread类(推荐)
import threading
def action(maxs):
    for j in range(maxs):
        print(threading.current_thread().getName() +  " " + str(j))
for i in range(10):
    print(threading.current_thread().getName() +  " " + str(i))
    if i == 3:
        t1 =threading.Thread(target=action,name='t1',args=(20,))
        t1.start()#必须调用start()才能把run方法变成线程执行体,若调用run就是普通调用函数
        t2 =threading.Thread(target=action,name='t2',args=(20,))
        t2.start()#只有新建状态的线程才能调用start方法转换为就绪态
print('主线程执行完成!')
#threading.Thread的参数:
#group=None:线程所属线程组
#target=None:线程要调度的目标方法
#name=None:线程名字
#args=():传入参数
#kwargs=None:指定字典传入参数
#daemon=None:指定所构建的线程是否为后台线程
#第二种方式:继承Threado在(不推荐)
import threading
class myThread(threading.Thread):
    def __init__(self,x):  
        threading.Thread.__init__(self,target=self.go,name=x)#相当于上一例的传参
        self.j = 0
    def go(self):
        print('go')
'''
# 如果重写run()方法作为线程执行体,则会覆盖上面的go函数
    def run(self):  
        while self.j < 20:
            print(threading.current_thread().getName() +  " " + str(self.j))
            self.j += 1
'''
for i in range(10):
    print(threading.current_thread().getName() +  " " + str(i))
    if i == 9:
        ft1 = myThread('t1')
        ft1.start()
        ft2 = myThread('t2')
        ft2.start()
print('主线程执行完成!')

#join
import time
import threading
def action():
    time.sleep(2)
    for i in range(5):
        print(threading.current_thread().name+str(i)+'\n')
jt=threading.Thread(target=action, name="Join线程")
jt.start()
jt.join()#有参数timeout=None表示等待被join的最大时长
time.sleep(1)
print(threading.current_thread().name+'\n')

#daemon thread后台线程(全部前台线程结束就会结束)
import threading
def action(max):# 定义后台线程的线程执行体与普通线程没有任何区别
    time.sleep(1)
    for i in range(max):
        print(threading.current_thread().name + "  " + str(i))
t = threading.Thread(target=action,args=(20,),daemon = True,name='后台线程')
t.start()
time.sleep(1)
for i in range(10):
    print(threading.current_thread().name + "  " + str(i))
# -----程序执行到此处,前台线程(主线程)结束,后台线程也应该随之结束------

#time
import time
for i in range(10):
    print("当前时间: %s" % time.ctime())
    time.sleep(1)
    
#同步锁
import threading
import time
class Account:
    def __init__(self, account_no, balance):
        self.account_no = account_no #账户编号
        self._balance = balance      #账户余额
        self.lock = threading.RLock()
    def draw(self, draw_amount):# 提供一个线程安全的draw()方法来完成取钱操作
        time.sleep(1)
        self.lock.acquire()#blocking=True,timeout=-1指定加锁时间
        try:
            if self._balance>=draw_amount:#账户余额大于取钱数目
                self._balance-=draw_amount#修改余额
                print(threading.current_thread().name+"取钱成功,余额为:"+str(self._balance)+'\n',end='')
            else:
                print(threading.current_thread().name+"取钱失败,余额为:"+str(self._balance)+'\n',end='')
        finally:
            self.lock.release()# 修改完成,释放锁
def draw(account, draw_amount):
    account.draw(draw_amount)# 直接调用account对象的draw()方法来执行取钱操作
acct = Account("1234567" , 1000)
threading.Thread(name='甲', target=draw , args=(acct , 500)).start()
threading.Thread(name='乙', target=draw , args=(acct , 600)).start()
#LOCK与RLOCK
#Lock:基本锁对象,每次锁一次,其余锁请求需等待锁释放后才能获取
#RLock:可重入锁,N次acquire就有N次release(推荐)

#死锁
import threading
import time
class A:
    def __init__(self):
        self.lock = threading.RLock()
    def foo(self, b):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name+ " 进入了A实例的foo()方法" )     # ①
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name+ " 企图调用B实例的last()方法")   # ③
            b.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
        finally:
            self.lock.release()
class B:
    def __init__(self):
        self.lock = threading.RLock()
    def bar(self, a):
        try:
            self.lock.acquire()
            print("当前线程名: " + threading.current_thread().name+ " 进入了B实例的bar()方法" )   # ②
            time.sleep(0.2)
            print("当前线程名: " + threading.current_thread().name+ " 企图调用A实例的last()方法")  # ④
            a.last()
        finally:
            self.lock.release()
    def last(self):
        try:
            self.lock.acquire()
        finally:
            self.lock.release()
a = A()
b = B()
def init():
    threading.current_thread().name = "主线程"
    a.foo(b)
def action():
    threading.current_thread().name = "副线程"
    b.bar(a)
threading.Thread(target=action).start()
init()

#condition
#acquire/release:就是关联lock的acquire/release
#wait:当前进程进入condition等待池等待通知并释放锁
#notify:唤醒在condition等待池的单个线程(任意)
#notify_all:唤醒在condition等待池的全部线程
import threading
import time
class Account:
    def __init__(self, account_no, balance):
        self.account_no = account_no
        self._balance = balance
        self.cond = threading.Condition()
        self._flag = False# 定义代表是否已经存钱的旗标
    def draw(self, draw_amount):
        time.sleep(0.5)
        self.cond.acquire()# 加锁,相当于调用Condition绑定的Lock的acquire()
        try:
            if not self._flag:# 如果self._flag为假,表明账户中还没有人存钱进去,取钱方法阻塞
                self.cond.wait()
            else:
                self._balance -= draw_amount
                print(threading.current_thread().name+"取钱"+str(draw_amount)+" 账户余额"+str(self._balance)+'\n',end='')
                self._flag = False# 将标识账户是否已有存款的旗标设为False
                self.cond.notify_all()# 唤醒其他线程
        finally:# 使用finally块来释放锁
            self.cond.release()
    def deposit(self, deposit_amount):
        time.sleep(1)
        self.cond.acquire()# 加锁,相当于调用Condition绑定的Lock的acquire()
        try:
            if self._flag:# 如果self._flag为真,表明账户中已有人存钱进去,存钱方法阻塞
                self.cond.wait()
            else:
                self._balance += deposit_amount
                print(threading.current_thread().name+"存款"+str(deposit_amount)+" 账户余额"+str(self._balance)+'\n',end='')
                self._flag = True# 将表示账户是否已有存款的旗标设为True
                self.cond.notify_all()# 唤醒其他线程
        finally:# 使用finally块来释放锁
            self.cond.release()
def draw_many(account, draw_amount, maxs):#模拟重复max次执行取钱操作
    for i in range(maxs):
        account.draw(draw_amount)
def deposit_many(account, deposit_amount, maxs):#模拟重复max次执行存款操作
    for i in range(maxs):
        account.deposit(deposit_amount)
acct = Account("1234567" , 0)# 创建一个账户
threading.Thread(name="取钱者", target=draw_many,args=(acct, 800, 10)).start()
threading.Thread(name="存款者甲", target=deposit_many,args=(acct , 800, 10)).start()
threading.Thread(name="存款者乙", target=deposit_many,args=(acct , 800, 10)).start()
threading.Thread(name="存款者丙", target=deposit_many,args=(acct , 800, 10)).start()

#queue
'''
queue.Queue(maxsize=0):FIFO
queue.LifoQueue(maxsize=0):LIFO
PriorityQueue(maxsize=0):优先队列
Queue.qsize():队列元素个数
Queue.empty():队列是否为空
Queue.full():队列是否为满
Queue.put(item,block=True,timeout=None):入队
Queue.put_nowait(item):入队且不阻塞(入不了就丢弃)
Queue.get(item,block=True,timeout=None):出队
Queue.get_nowait(item):出队且不阻塞(没元素就是空)
'''
import threading
import time
import queue
ans=[]
def product(bq):
    for i in range(3):
        t=threading.current_thread().name+"'s no."+str(i)
        bq.put(t)# 尝试放入元素,如果队列已满,则线程被阻塞
        ans.append(threading.current_thread().name+"第%d次生产%s"%(i,t))
def consume(bq):
    for i in range(9):
        t = bq.get()# 尝试取出元素,如果队列已空,则线程被阻塞
        ans.append(threading.current_thread().name+"第%d次消费%s"%(i,t))
bq = queue.Queue(maxsize=1)# 创建一个容量为1的Queue
threading.Thread(target=product,name='a',args=(bq, )).start()# 启动3个生产者线程
threading.Thread(target=product,name='b',args=(bq, )).start()
threading.Thread(target=product,name='c',args=(bq, )).start()
threading.Thread(target=consume,name='x',args=(bq, )).start()# 启动一个消费者线程
time.sleep(1)#等待上面的线程跑完
print(ans)#我发现不用上面的ans而是直接print会十分混乱!

#event
#is_set():返回内部旗标是否为true
#set():设内部旗标为true
#clear():设内部旗标为false,后面常接wait
#wait(timeout=None):等待内部旗标为false
import threading
import time
event = threading.Event()
def cal(name):
    print(name+'正式等待'+'\n',end='')
    event.wait()
    print(name+'正式计算'+'\n',end='')
threading.Thread(target=cal, args=('甲', )).start()
threading.Thread(target=cal, args=("乙", )).start()
time.sleep(0.1) 
print('主线程发出事件')
time.sleep(0.1) 
event.set()

#线程池:需要创建大量生存期很短暂的线程时使用
#result得到结果
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def action(max):#定义线程任务是累加和
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i)+'\n',end='')
        my_sum += i
    return my_sum
pool = ThreadPoolExecutor(max_workers=2)# 创建一个包含2条线程的线程池
future1 = pool.submit(action, 10)# 向线程池提交一个task, 10会作为action()函数的参数
future2 = pool.submit(action, 20)# 向线程池提交一个task, 20会作为action()函数的参数
print(str(future1.done())+'\n',end='')# 判断future1代表的任务是否结束
time.sleep(0.1)
print(future2.done())# 判断future2代表的任务是否结束
print(future1.result())# 查看future1代表的任务返回的结果(如未返回会阻塞主线程)
print(future2.result())# 查看future2代表的任务返回的结果
pool.shutdown()# 关闭线程池
print('--------------')
#注意:最后四行一定是True,45,190,-------因为存在阻塞关系!
#add_done_callback:用法是调用回调函数,再调用result就可不阻塞主线程且何时执行完何时输出
from concurrent.futures import ThreadPoolExecutor
import threading
def action(max):# 定义一个准备作为线程任务的函数
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i)+'\n',end='')
        my_sum += i
    return my_sum
with ThreadPoolExecutor(max_workers=2) as pool:# 用with写就不用手动关了
    future1 = pool.submit(action, 10)# 向线程池提交一个task, 50会作为action()函数的参数
    future2 = pool.submit(action, 20)# 向线程池再提交一个task, 100会作为action()函数的参数
    def get_result(future):
        print(str(future.result())+'\n',end='')
    future1.add_done_callback(get_result)# 为future1添加线程完成的回调函数
    future2.add_done_callback(get_result)# 为future2添加线程完成的回调函数
    print('--------------'+'\n',end='')
#map
from concurrent.futures import ThreadPoolExecutor
import threading
def action(max):# 定义一个准备作为线程任务的函数
    my_sum = 0
    for i in range(max):
        print(threading.current_thread().name + '  ' + str(i)+'\n',end='')
        my_sum += i
    return my_sum
with ThreadPoolExecutor(max_workers=4) as pool:# 创建一个包含4条线程的线程池
    results = pool.map(action, (10, 20, 30))#后面元组3元素故程序启动3条线程来执行action函数
    print('--------------'+'\n',end='')
    for r in results:
        print(str(r)+'\n',end='')

#local(其实在线程执行函数里面定义局部变量即可)
import threading
from concurrent.futures import ThreadPoolExecutor
mydata = threading.local()# 定义线程局部变量:即每个线程都复制一个
def action (max):
    for i in range(max):
        try:
            mydata.x += i
        except:
            mydata.x = i
        print('%s mydata.x的值为: %d' %(threading.current_thread().name, mydata.x)+'\n',end='')
with ThreadPoolExecutor(max_workers=2) as pool:
    pool.submit(action , 5)
    pool.submit(action , 10)

#timer
    from threading import Timer
def hello():
    print("hello, world")
t=Timer(10.0, hello)# 指定10秒后执行hello函数
t.start()#定时器开始
from threading import Timer
import time
count = 0# 定义总共输出几次的计数器
def print_time():
    print("当前时间:%s" % time.ctime())
    global t, count
    count += 1
    if count < 10:# 如果count小于10,开始下一次调度
        t=Timer(1, print_time)#定时器要重新装载
        t.start()
t = Timer(1, print_time)# 指定1秒后执行print_time函数
t.start()

#任务调度
import sched, time
s = sched.scheduler()# 定义线程调度器
def print_time(name='default'):# 定义被调度的函数
    print("%s 的时间: %s" % (name, time.ctime()))
print('主线程:', time.ctime())
s.enter(10, 1, print_time)# 指定10秒之后执行print_time函数
s.enter(5, 2, print_time, argument=('位置参数',))# 指定5秒之后执行print_time函数,优先级为2
s.enter(5, 1, print_time, kwargs={'name': '关键字参数'})# 指定5秒之后执行print_time函数,优先级为1
s.run()# 执行调度的任务,会阻塞主线程
print('主线程:', time.ctime())

#多进程
#fork
import os#在windows系统上无效
print('父进程(%s)开始执行' % os.getpid())
pid = os.fork()# 开始fork一个子进程,下面代码都会被两个进程执行
print('进程进入:%s' % os.getpid())
if pid == 0:# 如果pid为0,表明是子进程
    print('子进程,其ID为 (%s), 父进程ID为 (%s)' % (os.getpid(), os.getppid()))
else:
    print('我 (%s) 创建的子进程ID为 (%s).' % (os.getpid(), pid))
print('进程结束:%s' % os.getpid())

#Process(我的编译器好像有问题,开不了子进程)
#run重新进程执行体
#start启动进程
#join当前进程等被join进程执行完才能往下执行,name设置访问进程的名字
#is_alive()进程是否活着
#daemon进程是否是后台状态
#pid进程ID
#authkery进程授权key
#terminate()中断进程
import multiprocessing
import os
def action(maxs):
    for i in range(maxs):
        print("(%s)子进程(父进程:(%s)):%d" %(os.getpid(), os.getppid(), i))
if __name__ == '__main__':# 下面是主程序(也就是主进程)
    for i in range(10):
        print("(%s)主进程: %d" % (os.getpid(), i))
        if i == 2:
            mp=multiprocessing.Process(target=action,args=(10,))
            mp.start()
            mp.join()
    print('主进程执行完成!')
import multiprocessing
import os
class MyProcess(multiprocessing.Process):
    def __init__(self, max):
        self.max = max
        super().__init__()
    def run(self):# 重写run()方法作为进程执行体
        for i in range(self.max):
            print("(%s)子进程(父进程:(%s)):%d"%(os.getpid(),os.getppid(),i))
if __name__ == '__main__':
    for i in range(10):# 下面是主程序(也就是主进程)
        print("(%s)主进程: %d" % (os.getpid(), i))
        if i == 2:
            mp=MyProcess(10)
            mp.start()
            mp.join()
    print('主进程执行完成!')

#Context和启动进程的方式
import multiprocessing#fork在UNIX
def foo(q):
    q.put('Python')
if __name__ == '__main__':
    multiprocessing.set_start_method('fork')             # 设置使用fork方式启动进程
    q = multiprocessing.Queue()
    mp = multiprocessing.Process(target=foo, args=(q, )) # 创建进程
    mp.start()        # 启动进程
    print(q.get())    # 获取队列中的消息 
import multiprocessing#spawn在WIN
def foo(q):
    q.put('Python!')
if __name__ == '__main__':
    ctx = multiprocessing.get_context('spawn') # 使用spawn方式启动进程并获取Context对象
    q = ctx.Queue()# 接下来就可用Context对象来代替mutliprocessing模块了
    mp = ctx.Process(target=foo, args=(q, ))   # 创建进程
    mp.start()                                 # 启动进程
    print(q.get()+'abc')                       # 获取队列中的消息 

#进程池(唯一BUG:子进程不能输出到控制台)
import multiprocessing
import time
def action(name='default'):
    print(name)
    time.sleep(1)
if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)# 创建包含4条进程的进程池
    pool.apply_async(action)# 将action分3次提交给进程池
    pool.apply_async(action, args=('位置参数', ))
    pool.apply_async(action, kwds={'name': '关键字参数'})
    pool.close()
    pool.join()
    print('主进程结束')
import multiprocessing
def action(max):
    my_sum = 0
    for i in range(max):
        print(i)#老样子,子进程不能输出到控制台
        my_sum += i
    return my_sum
if __name__ == '__main__':
    with multiprocessing.Pool(processes=4) as pool:# 创建一个包含4条进程的进程池
        results = pool.map(action, (50, 100, 150))
        for r in results:print(r)
        
#队列通信
import multiprocessing
def f(q):
    print('(%s) 进程开始放入数据...' % multiprocessing.current_process().pid)
    q.put('Python')
if __name__ == '__main__':
    q = multiprocessing.Queue()# 创建进程通信的Queue
    p = multiprocessing.Process(target=f, args=(q,))# 创建子进程
    p.start()# 启动子进程
    p.join()
    print('(%s) 进程开始取出数据...' % multiprocessing.current_process().pid)
    print(q.get())  # 取出数据 Python
    
#管道通信
import multiprocessing
def f(conn):
    print('(%s) 进程开始发送数据...' % multiprocessing.current_process().pid)
    conn.send('Python')# 使用conn发送数据
if __name__ == '__main__':
    parent_conn, child_conn = multiprocessing.Pipe()# 创建Pipe,该函数返回两个PipeConnection对象
    p = multiprocessing.Process(target=f, args=(child_conn, ))# 创建子进程
    p.start()# 启动子进程
    print('(%s) 进程开始接收数据...' % multiprocessing.current_process().pid)
    print(parent_conn.recv())  # 通过conn读取数据 Python
    p.join()
#第十五章:网络编程
'''基本模块
传输层
socket------------重点(TCP/UDP服务器与客户端通讯)
asyncore
asynchat
应用层
email
mailbox
mailcap
ftplib
httplib
imaplib
nntplib
smtplib------------重点(发邮件)
poplib-------------重点(收邮件)
telnetlib
urllib-------------重点(分析网址内容,分析网页内容,cookie)
其他
xmlrpc,xmlrpc.server,xmlrpc.client
cgi
'''

#urllib
#urllib.request:open and read URL
#urllib.error:catch error
#urllib.parse:解析URL
#urllib.robotparser解析robots.txt文件
from urllib.parse import *#分析网址内容
result = urlparse('http://www.crazyit.org:80/index.php;yeeku?name=fkit#frag')# 解析URL字符串
print(result)# 下面通过属性名和索引来获取URL的各部分
print('scheme体系:', result.scheme, result[0])
print('主机和端口:', result.netloc, result[1])
print('主机:', result.hostname)
print('端口:', result.port)
print('资源路径:', result.path, result[2])
print('参数:', result.params, result[3])
print('查询字符串:', result.query, result[4])
print('fragment碎片:', result.fragment, result[5])
print(result.geturl())
print('---a--------------')
result = urlunparse(('http','www.crazyit.org:80','index.php','yeeku','name=fkit','frag'))
print('URL为:', result)
print('---b--------------')
result = urlparse('//www.crazyit.org:80/index.php')# 解析以//开头的URL
print('scheme:', result.scheme, result[0])
print('主机和端口:', result.netloc, result[1])
print('资源路径:', result.path, result[2])
print('---c--------------')
result = urlparse('www.crazyit.org/index.php')
print('scheme:', result.scheme, result[0])# 解析没有scheme,也没有以双斜线(//)开头的URL
print('主机和端口:', result.netloc, result[1])
print('资源路径:', result.path, result[2])# 从开头部分开始就会被当成资源路径
print('---d--------------')
result = parse_qs('name=fkit&name=%E7%96%AF%E7%8B%82java&age=12')# 解析查询字符串,返回dict
print(result)
result = parse_qsl('name=fkit&name=%E7%96%AF%E7%8B%82java&age=12')# 解析查询字符串,返回list
print(result)
print(urlencode(result))# 将列表格式的请求参数恢复成请求参数字符串
print('---e--------------')
# 被拼接URL以多少个/开头就会中踢掉后面多少个/段
result = urljoin('http://www.crazyit.org/users/login.html', 'help.html')
print(result) # http://www.crazyit.org/users/help.html
result = urljoin('http://www.crazyit.org/users/login.html', 'book/list.html')
print(result) # http://www.crazyit.org/users/book/list.html
result = urljoin('http://www.crazyit.org/users/login.html', '/help.html')# 被拼接URL以斜线(代表根路径path)开头
print(result) # http://www.crazyit.org/help.html
result = urljoin('http://www.crazyit.org/users/login.html', '//help.html')# 被拼接URL以双斜线(代表绝对URL)开头
print(result) # http://help.html

#urlopen(需要先在本地部署web应用,不然就改网址)
from urllib.parse import *#下面三例是不提交data,提交data-str及dict
with urlopen(url='https://www.baidu.com/') as f:
    print(f.read().decode('utf-8'))# 读取服务器全部响应
with urlopen(url='https://www.baidu.com/',data='测试数据'.encode('utf-8')) as f:
    print(f.read().decode('utf-8'))# 读取服务器全部响应
params = urllib.parse.urlencode({'name': '疯狂软件', 'password': '123888'}).encode('utf-8')
with urlopen("https://www.baidu.com/", data=params) as f:# 使用data指定请求参数
    print(f.read().decode('utf-8'))

import requests                #需求(分析网页内容,常结合re匹配要找的串)
from bs4 import BeautifulSoup  #BeautifulSoup库解析代码
url='http://www.wsbookshow.com'#定义网址
html=requests.get(url)         #引用需求库的获取网址HTML文件
html.encoding="GBK"            #编码是GBK
soup=BeautifulSoup(html.text,'html.parser')#分析文件标签划分
print(soup)
#htmllist=html.text.splitlines()       #把文本分隔
#for row in htmllist:print(row)
links=soup.find_all(["a","img"])#得到a,img两个标签的内容
for link in links:#逐个连接扫一次
    href=link.get("href")#获取此连接中href关键字内容
    if href!=None and href.startswith("http://"):#HREF以http://开头
        print(href)#输出

#爬虫网站中的图片
import requests,os
from bs4 import BeautifulSoup
from urllib.request import urlopen
url='http://www.tooopen.com/img/87.aspx'   #定义网址
html=requests.get(url)  #获取HTML文件
html.encoding="utf-8"   #定义编码方式
sp=BeautifulSoup(html.text,'html.parser') #解释
images_dir="images/"                #定义路径
if not os.path.exists(images_dir):  #如果路径不存在
    os.mkdir(images_dir)            #创建路径
all_links=sp.find_all(['a','img'])  #寻获取所有A与IMG标签中的内容
for link in all_links:#遍历每一个标签内容
    src=link.get('src')#获得标签内容中含SRC的串
    href=link.get('href')#获得标签内容中含HREF的串
    attrs=[href,src]#定义一个列表,两个元素分别是两个串
    for attr in attrs:#遍历一次,这里要注意一个标签里可以有多个串含SRC或HREF
        if attr!=None and ('.jpg' in attr or '.png' in attr):#如果非空且含JPG与PNG后缀
            full_path=attr#读回其路径
            filename=full_path.split('/')[-1]#文件名是按/划分的子串的倒数第一个
            ext=filename.split('.')[-1]#后缀名是按.划分的子串中的倒数第一个
            filename=filename.split('.')[-2]#文件名是按.划分的子串中的到数第二个
            if 'jpg' in ext: filename=filename+'.jpg'#文件名加上后缀
            else: filename=filename+'.png'#文件名加上后缀
            print(filename)#输出文件名
            try:#尝试
                image=urlopen(full_path)#通过完整路径获取图片
                f=open(os.path.join(images_dir,filename),'wb')#wb:以二进制写模式打开本地文件(由路径知其实是图片)
                f.write(image.read())#写入文件(就是复制图片)
                f.close()#关闭
            except:#失败
                print("error")  #报错

#爬虫PM2.5实例
from bs4 import BeautifulSoup
import requests
url1='http://www.PM25X.com/'
html=requests.get(url1)
sp1=BeautifulSoup(html.text,'html.parser')
city=sp1.find("a",{"title":"北京PM2.5"})#find返回匹配结果的第一个元素
print(city)#输出
citylink=city.get("href")#获取href关键字的内容
print(citylink)#输出
url2=url1+citylink#由此可以得到子网页的网址
print(url2)#get the suburl!
html2=requests.get(url2)#获HTML
sp2=BeautifulSoup(html2.text,'html.parser')#分析
data1=sp2.select(".aqivalue")#选择
pm25=data1[0].text#得第0个元素的值
print("now Beijing's PM2.5 is: "+pm25)
'''
find_all(name,attrs,recursive,text,**kwargs)根据标签名/属性/内容查找文档,返回一个列表
text结果返回的是查到的所有的text='***'的文本
find(name,attrs,recursive,text,**kwargs)返回的匹配结果的第一个元素
get_text()可以获取文本内容
attrs可以传入字典的方式来查找标签,这里有个特殊的就是class,因为class在python中是特殊的字段,所以如果想要查找class相关的可以更改attrs={'class_':'element'}或者soup.find_all('',{"class":"element}),特殊的标签属性可以不写attrs,例如id
'''

#cookie保存(没服务器运行不了)
from urllib.request import *
import http.cookiejar, urllib.parse
cookie_jar = http.cookiejar.MozillaCookieJar('a.txt')# 以指定文件创建CookieJar对象,对象将可以把cookie保存在文件中
cookie_processor = HTTPCookieProcessor(cookie_jar)# 创建HTTPCookieProcessor对象
opener = build_opener(cookie_processor)# 创建OpenerDirector对象
user_agent = r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36' \
    r' (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'# 定义模拟Chrome浏览器的user_agent
headers = {'User-Agent':user_agent, 'Connection':'keep-alive'}# 定义请求头
#-------------下面代码发送登录的POST请求----------------
params = {'name':'crazyit.org', 'pass':'leegang'}# 定义登录系统的请求参数
postdata = urllib.parse.urlencode(params).encode()
request = Request('http://localhost:8888/test/login.jsp',data = postdata, headers = headers)# 创建向登录页面发送POST请求的Request
response = opener.open(request)# 使用OpenerDirector发送POST请求
print(response.read().decode('utf-8'))
cookie_jar.save(ignore_discard=True, ignore_expires=True)  # 将cookie信息写入磁盘文件
#-------------下面代码发送访问被保护资源的GET请求----------------
request = Request('http://localhost:8888/test/secret.jsp',headers=headers)# 创建向"受保护页面"发送GET请求的Request
response = opener.open(request)
print(response.read().decode())

#cookie加载(没服务器运行不了)
from urllib.request import *
import http.cookiejar, urllib.parse
cookie_jar = http.cookiejar.MozillaCookieJar('a.txt')# 以指定文件创建CookieJar对象,对象将可以把cookie保存在文件中
cookie_jar.load('a.txt',ignore_discard=True,ignore_expires=True)# 直接加载a.txt中的Cookie信息
for item in cookie_jar:# 遍历a.txt中保存的cookie信息
    print('Name ='+ item.name)
    print('Value ='+ item.value)
cookie_processor = HTTPCookieProcessor(cookie_jar)# 创建HTTPCookieProcessor对象
opener = build_opener(cookie_processor)# 创建OpenerDirector对象
user_agent = r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36' \
    r' (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'# 定义模拟Chrome浏览器的user_agent
headers = {'User-Agent':user_agent, 'Connection':'keep-alive'}# 定义请求头
request = Request('http://localhost:8888/test/secret.jsp',headers=headers)# 创建向"受保护页面"发送GET请求的Request
response = opener.open(request)
print(response.read().decode())

'''
下面是本章重点,涉及编码问题
首先重申一下上一章的内容
二、字符串前加 b
b" "前缀表示后面字符串是bytes类型
网络编程中,服务器和浏览器只认bytes类型数据
在 Python3 中,bytes 和 str 的互相转换方式是
str.encode('utf-8')
bytes.decode('utf-8')
'''
with open('utf-8_test.txt','rb+',True) as f:
	print(f.read().decode('utf-8'))#得到字符串注意编译器默认就是uft-8编码
with open('utf-8_test.txt','rb+',True) as f:
	print(f.read())#得到字节类对象注意汉字是用24位二进制表示,如‘我’是\xe9\x99\x88

#UDP服务器端
import socket
s = socket.socket(type=socket.SOCK_DGRAM)#UDP
s.bind(('127.0.0.1',30000))#将该socket绑定到本机的指定IP和端口(注意这是本机IP与端口)
for i in range(3):#采用循环接收数据,此处设置接收三次就要结束连接
    data,addr=s.recvfrom(4096)#读取s中的数据(最大4KB)的数据及发送地址(即源机IP与端口)
    if data.decode('utf-8')=='exit':break#如果是退出就断开连接
    else: print(data.decode('utf-8'))#否则将接收到的内容转换成字符串后输出
    s.sendto('I love you.'.encode('utf-8'),addr)#将数据报发送给addr地址
s.close()
#UDP客户端
import socket
s=socket.socket(type=socket.SOCK_DGRAM)#创建基于UDP协议的socket
while True:
    line=input('')#不断地读取键盘输入
    s.sendto(line.encode('utf-8'),("127.0.0.1",30000))#往目的IP与PORT发送数据报
    if line=='exit':break
    print(s.recv(4096).decode('utf-8'))#读取socket中的数据,最大4KB
s.close()
#UDP多点广播(主客端一样的代码,SENDERPORT与TARGETPORT改同一个端口即可多点互通,下面用两个端口是因为只有一台机不能同一个端口绑定两次)
import socket, threading
SENDERIP = '127.0.0.1'# 定义本机IP地址
SENDERPORT = 30030# 定义本地发送端口,也是本机接收信息的端口
TARGETPORT = 30031# 定义目的接收端口,也是目的发送信息的端口
MYGROUP = '230.0.0.1'# 定义本程序的多点广播IP地址
s = socket.socket(type=socket.SOCK_DGRAM)# 通过type属性指定创建基于UDP协议的socket
s.bind(('0.0.0.0', SENDERPORT))#将该socket绑定到0.0.0.0的虚拟IP,在多点广播组内只要端口对就能接收
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 64)# 设置广播消息的TTL(Time-To-Live)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 设置允许多点广播使用相同的端口
status=s.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MYGROUP)+socket.inet_aton(SENDERIP))#将socket进入广播组
def read_socket(sock):
    while True:#多线程循环执行体,从socket读取数据并输出
        data = sock.recv(2048)#读回来的data是字节类对象
        if data.decode('utf-8')=='exit':break#转成str才可以比较
        print("\n收到的信息:", data.decode('utf-8'))
t=threading.Thread(target=read_socket, args=(s, ))
t.start()# 以read_socket作为target启动多线程
while True:#主线程循环执行体,采用循环不断读取键盘输入,并输出到socket中
    line=input("发送的信息: ")#输入的line就是字符串类型
    s.sendto(line.encode('utf-8'),(MYGROUP, TARGETPORT))#多点广播输出到广播IP中
    if line=='exit':break#可以直接比较
t.join()
import time
time.sleep(0.1)
s.close()

#TCP例一:基本通讯
#server
import socket
import threading
ss = socket.socket()#创建socket对象
ss.bind(('192.168.101.9', 30003))#将socket绑定到本机IP和端口
ss.listen()#服务端开始监听来自客户端的连接
def server_target(s):     #采用循环不断地从socket中读取客户端发送过来的数据
    for i in range(3):    #改成while True:可以一直死循环
        content = s.recv(2048).decode('utf-8')#获得信息内容
        print(content)
        s.send(content.encode('utf-8'))
for i in range(1):        #改成while True:可以一直死循环
    s,addr=ss.accept()    #此行代码会阻塞,将一直等待别人的连接
    threading.Thread(target=server_target, args=(s,)).start()#每当客户端连接后启动一个线程为该客户端服务
#client
import socket
import threading
s = socket.socket()                 #创建socket对象
s.connect(('192.168.101.9', 30003)) #连接远程主机
def read_from_server(s):            #客户端启动线程不断地读取来自服务器的数据
    for i in range(3):              #改成while True:可以一直死循环
        print(s.recv(2048).decode('utf-8'))
threading.Thread(target=read_from_server, args=(s, )).start()
for i in range(3):                  #改成while True:可以一直死循环
    line=input('')
    if line=='exit':break
    s.send(line.encode('utf-8'))#将用户的键盘输入内容写入socket
#补充一:要实现保存每个socket对应的数据,如用户名及密码,可用dict或创建新的类来维护
#补充二:要实现帐号密码登陆的,只需要在多线程执行体的循环前加上两次帐号密码通迅即可
#补充三、要实现TCP的多人聊天广播,每个群用一个列表(元素类型socket)来维护即可,发送到群就改为列表里逐个socket发
    
#TCP例二:半关闭的socket
#server
import socket
s = socket.socket()              # 创建socket对象
s.bind(('192.168.101.9', 30000)) # 将socket绑定到本机IP和端口
s.listen()                       # 服务端开始监听来自客户端的连接
skt, addr = s.accept() #每当接收到客户端socket的请求时,该方法返回对应的socket和远程地址
skt.send("服务器的第一行数据".encode('utf-8'))
skt.send("服务器的第二行数据".encode('utf-8'))
skt.shutdown(socket.SHUT_WR)     #关闭socket的输出,但仍可以接受数据,相当于发送空串
while True:
    line = skt.recv(2048).decode('utf-8')# 从socket读取数据
    if line is None:break
    print(line)
skt.close()
s.close()
#client
import socket
s = socket.socket()# 创建socket对象
s.connect(('192.168.101.9', 30000))# 连接远程主机
while True:
    line = s.recv(2048).decode('utf-8')  #从socket读取数据
    if line is None:break  #读到空就break
    print(line)
s.send("客户端的第一行数据".encode('utf-8'))
s.send("客户端的第二行数据".encode('utf-8'))
s.close()#相当于发送空串

#TCP例三:非阻塞读selectors
#好处:设置了监听事件后,不用死循环!
    #server
import selectors,socket,time
sel = selectors.DefaultSelector()# 创建默认的selectors对象
def read(skt, mask):# 负责监听“有数据可读”事件的函数
    try:
        data = skt.recv(1024)# 读取数据
        if data:
            for s in socket_list:# 将读取的数据采用循环向每个socket发送一次
                s.send(data)  # Hope it won't block
        else:# 如果该socket已被对方关闭,关闭该socket,并从socket_list列表中删除
            print('关闭', skt)
            sel.unregister(skt)
            skt.close()
            socket_list.remove(skt)
    except:# 如果捕捉到异常, 将该socket关闭,并从socket_list列表中删除
        print('关闭', skt)
        sel.unregister(skt)
        skt.close()
        socket_list.remove(skt)
socket_list = []
def accept(sock, mask):# 负责监听“客户端连接进来”事件的函数
    conn, addr = sock.accept()
    socket_list.append(conn)# 使用socket_list保存代表客户端的socket
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)#使用sel为conn的EVENT_READ事件注册read监听函数
sock = socket.socket()
sock.bind(('192.168.101.9', 30000))
sock.listen()
sock.setblocking(False)             #设置该socket是非阻塞的
sel.register(sock, selectors.EVENT_READ, accept)#使用sel为sock的EVENT_READ事件注册accept监听函数
while True:                         #采用死循环不断提取sel的事件
    time.sleep(1)                   #这一秒可以去做其他的事情
    events = sel.select()           #回顾过去一秒监听到的事件
    for key, mask in events:        #逐个事件调取对应的监听函数
        callback = key.data         #key的data属性获取为该事件注册的监听函数
        callback(key.fileobj, mask) #调用监听函数, key的fileobj属性获取被监听的socket对象
#client
import selectors, socket, threading
sel = selectors.DefaultSelector()  # 创建默认的selectors对象
def read(conn, mask):              # 负责监听“有数据可读”事件的函数
    data = conn.recv(1024)         # Should be ready
    if data:
        print(data.decode('utf-8'))
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()
s = socket.socket()                # 创建socket对象
s.connect(('192.168.101.9', 30000))# 连接远程主机
s.setblocking(False)               # 设置该socket是非阻塞的
sel.register(s, selectors.EVENT_READ, read)#使用sel为s的EVENT_READ事件注册read监听函数
def keyboard_input(s):             # 定义不断读取用户键盘输入的函数
    while True:
        line = input('')
        if line == 'exit':break
        s.send(line.encode('utf-8'))# 将用户的键盘输入内容写入socket
threading.Thread(target=keyboard_input, args=(s, )).start()# 采用线程不断读取用户的键盘输入
while True:
    events = sel.select()# 获取事件
    for key, mask in events:
        callback = key.data# key的data属性获取为该事件注册的监听函数
        callback(key.fileobj, mask)# 调用监听函数, key的fileobj属性获取被监听的socket对象

#SMTP发送
import smtplib
import email
from email.message import EmailMessage
#一、设置变量
smtp_server='smtp.qq.com'#定义SMTP服务器地址:
from_addr='1064789374@qq.com'#定义发件人地址
password='lllchhbtzygrbfbb'#定义登录邮箱的密码,但这里是授权码
to_addr='15521108978@163.com'#定义收件人地址
#二、连接登陆
#conn=smtplib.SMTP(smtp_server, 25)#创建SMTP连接普通版
conn=smtplib.SMTP_SSL(smtp_server,465)#创建SMTP连接SSL版
conn.set_debuglevel(1)#显示调试过程
conn.login(from_addr, password)#登陆帐号
#三、创建邮件
msg = EmailMessage()#创建邮件对象
pic_id=email.utils.make_msgid()
with open('F:\ProjectPython\hello_test2\images\z.png','rb') as f:#正文中图片
    msg.add_attachment(f.read(),maintype='image',subtype='png',filename='z.png', cid=pic_id)
with open('F:\ProjectPython\hello_test2\images\z.png','rb') as f:#附件
    msg.add_attachment(f.read(),maintype='application',subtype='png',filename='z.png',)
msg.set_content('老婆我爱你!'+'<img src="cid:'+pic_id[1:-1]+'">','html','utf-8')#设置邮件内容,指定邮件内容为HTML
msg['subject']='情书'
msg['from']='老公<%s>'%from_addr
msg['to']='老婆<%s>'%to_addr
#四、发送邮件
conn.sendmail(from_addr, [to_addr], msg.as_string())#发送邮件
conn.quit()#退出连接

#POP3接收
import poplib, os.path, mimetypes
from email.policy import default
#一、输入邮件地址, 口令和POP3服务器地址:
email = '1064789374@qq.com'
password = 'lllchhbtzygrbfbb'
pop3_server = 'pop.qq.com'
#二、连接到POP 3服务器:
#conn = poplib.POP3(pop3_server, 110)
conn = poplib.POP3_SSL(pop3_server, 995)
conn.set_debuglevel(0)# 可以打开或关闭调试信息
#print(conn.getwelcome().decode('utf-8'))# 可选:打印POP 3服务器的欢迎文字:
conn.user(email)#发送POP 3的user命令输入用户名、密码信息
conn.pass_(password)#发送POP 3的pass命令获取邮件统计信息,相当于发送POP 3的stat命令
message_num, total_size = conn.stat()
print('邮件数: %s. 总大小: %s' % (message_num, total_size))
resp, mails, octets = conn.list()# 发送POP 3的list命令获取服务器上的邮件列表
#print(resp, mails)# resp保存服务器的响应码,mails列表保存每封邮件的编号、大小
resp,data,octets=conn.retr(len(mails))#发送POP3的retr命令获取指定邮件内容(传入总长度是获取最后一封邮件),resp响应码data邮件内容
# data保存该邮件的内容
msg_data = b'\r\n'.join(data)# 将data的所有数据(原本是一个字节列表)拼接在一起
msg=BytesParser(policy=default).parsebytes(msg_data)# 将字符串内容解析成邮件类对象,此处一定要指定policy=default
print('发件人:' + msg['from'])
print('收件人:' + msg['to'])
print('主题:' + msg['subject'])
print('第一个收件人名字:' + msg['to'].addresses[0].username)
print('第一个发件人名字:' + msg['from'].addresses[0].username)
for part in msg.walk():#分开正文与附件,其中正文
    counter = 1
    if part.get_content_maintype()=='multipart':continue#multipart是容器(用于包含正文附件)
    elif part.get_content_maintype()=='text':print(part.get_content())#text是邮件正文部分
    else :                              # 处理附件
        filename = part.get_filename()  # 获取附件的文件名
        if not filename:                # 如果没有文件名,程序要负责为附件生成文件名
            ext = mimetypes.guess_extension(part.get_content_type())# 根据附件的contnet_type来推测它的后缀名
            if not ext:ext = '.bin'                         # 如果推测不出后缀名,使用.bin作为后缀名
            filename = 'part-%03d%s' % (counter, ext)       # 程序为附件来生成文件名
        counter += 1
        with open(os.path.join('.', filename), 'wb') as fp: # 将附件写入的本地文件
            fp.write(part.get_payload(decode=True))
conn.quit()                                                 # 退出服务器,相当于发送POP 3的quit命令

 

 

 

 

 

 

 

 

 下面是聊天室程序

  

服务端:

```python

import asyncore,asynchat                    # 导入两包

class ChatServer(asyncore.dispatcher):      # 服务器类

    def __init__(self,port):                # 构造函数

        asyncore.dispatcher.__init__(self)  # 父类初始化(不写会默认调用,但重写了就要显式用父类才调用)

        self.create_socket()                # 创建socket

        self.set_reuse_addr()               # 地址重用

        self.bind(('127.0.0.1',port))       # 绑定

        self.listen(5)                      # 侦听,最大有五个连接等待被接入

        self.users={}                       # 创建用户列表

        self.main_room=ChatRoom(self)       # 主房间是聊天房间(为何放在server类这?因仅1服务器也仅1房间)

    def handle_accepted(self,sock,addr):    # 处理用户的接入

        print('socket:',sock,'addr:',addr)  # 打印socket与addr

        ChatSession(self,sock)              # 创建一个聊天会话类,把self(下面的server)与socket传过去

class ChatSession(asynchat.async_chat):         # 聊天会话类,负责与客户端通讯

    def __init__(self,server,sock):             # 构造函数

        asynchat.async_chat.__init__(self,sock) # 父类的构造函数就是显式调用

        self.server=server                      # 复合类(把上面的类传下来了)

        self.set_terminator(b'\n')              # 数据结束标志

        self.data=[]                            # 接受客户端的接口数据

        self.name=None                          # 保存用户名字

        self.enter(LoginRoom(self.server))      # 变登陆状态

    def enter(self,room):                       # 从当前房间移除自身,然后添加到指定房间

        try:                                    # 尝试

            cur=self.room                       # 取出当前状态

        except AttributeError:                  # 没有的话

            pass                                # 跳过

        else:                                   # 否则,就是有当前状态room

            cur.remove(self)                    # 就移除

        self.room=room                          # 把新状态更新为传入的状态(上面刚创建LoginRoom就存到这)

        room.add(self)                          # 新状态增加我

    def collect_incoming_data(self, data):      # 收集接口出现数据

        self.data.append(data.decode())         # 数据添加

    def found_terminator(self):                 # 找到了结束符

        line=''.join(self.data)                 # 将消息拼接成字符串

        self.data=[]                            # 把接口数据清空

        self.room.handle(self,line.encode())    # 处理接收的指令

    def handle_closed(self):                    # 关闭话柄

        asynchat.async_chat.handle_closed(self) # 父类结束方法

class Room:                                     # 状态类

    def handle(self,session,line):              # 处理

        line=line.decode()                      # 解码

        if not line.strip():                    # 空命令

            return                              # 返回

        parts=line.split(' ',1)                 # 切2份如login abc, say hello, 或无参的logout, look

        cmd=parts[0]                            # 读出下标为0的字段

        try:                                    # 尝试

            line=parts[1].strip()               # 获取下标为1的字段

        except IndexError:                      # 下标错误

            line=''                             # 指令为空

        method=getattr(self,'do_'+cmd,None)     # 获取'do_'+cmd的方法

        method(session,line)                    # 调用这个方法

    def __init__(self,server):                      # 构造函数

        self.server=server                          # 服务器成员属性

        self.sessions=[]                            # 会话集合

    def add(self,session):                          # 添加会话(可看到基类方法的作用是子类重写后可调用,避免重复写)

        self.sessions.append(session)               # 往会话群里加会话

    def remove(self,session):                       # 移除会话(五)

        self.sessions.remove(session)               # 从会话群里减会话(六)

    def broadcast(self,line):                       # 广播

        for session in self.sessions:               # 逐个会话读出来

            session.push(line)                      # 发送内容

    def do_logout(self,session,line):               # 登出(一)

        self.remove(session)                        # 从当前状态移出会话(二)

        del self.server.users[session.name]         # 服务器的用户列表移除用户名字(八)

class ChatRoom(Room):                               # 正在聊天的客户(成员有服务器与会话集合)

    def add(self,session):                          # 当前状态添加会话

        session.push(b'login success')              # 发送login success

        self.broadcast((session.name+' has entered the room. \n').encode())  # 广播

        self.server.users[session.name]=session     # 用户字典添加用户名-会话键值对

        Room.add(self,session)                      # 状态添加会话

    def remove(self,session):                       # 移除会话(三)

        Room.remove(self,session)                   # 状态移除(四)

        self.broadcast((session.name+' has left the room.\n').encode())  # 广播 (七)

    def do_say(self,session,line):                  # 发送信息

        self.broadcast((session.name+':'+line+'\n').encode()) # 广播

    def do_look(self,session,line):                 # 查在线用户

        session.push(b'Online Users:\n')            # 发送Online Users:\n

        for user in self.sessions:                  # 遍历会话列表

            session.push((user.name+'\n').encode()) # 发送会话中的用户名

class LoginRoom(Room):                              # 正在登陆的用户

    def add(self,session):                          # 增加

        Room.add(self,session)                      # 调用父类的添加(下面do_login末句enter会把self.room指向的对象改掉,则会话级别的LoginRoom由于不再有引用(或者说指针)指向他而被释放,服务器级别的ChatRoom是永远只有一个且不被释放,这就是为什么要在ChatServer中初始化ChatRoom为main_room然后在LoginRoom中传入self.server.main_room了)

        session.push(b'Connect Success')            # 发送信息

    def do_login(self,session,line):                # 登陆功能

        name=line.strip()                           # 取出指令

        if not name:                                # 如果用户名为空

            session.push(b'Username empty')         # 发送Username empty

        elif name in self.server.users:             # 如果名字已在服务器用户名列表

            session.push(b'Username exists')        # 发送Username exists

        else:                                       # 否则

            session.name=name                       # 赋值

            session.enter(self.server.main_room)    # 进入新的状态(就是聊天状态)

if __name__=='__main__':                            # 主函数

    print('server will run on 127.0.0.1:6666')      # 提示

    ChatServer(6666)                                # 聊天服务器

    asyncore.loop()                                 # 循环检查



```

客户端:

```python

import PySimpleGUI as sg

import telnetlib

def login(conn):

    is_login=False

    layout=[

        [sg.Text('登陆Python聊天室')],

        [sg.Text('请输入您的用户名:'),sg.InputText(key='-username-'),sg.Button('进入聊天室')]

    ]

    window=sg.Window('Python聊天室',layout)

    while True:

        event,values=window.read()  # 读出窗口里面的事件

        if event=='进入聊天室':      # 登入事件

            username=values['-username-'].strip()   # 取出'-username-'控件的值

            if username:            # 用户名非空

                try:                # 尝试打开连接

                    conn.open('127.0.0.1',6666,timeout=10)  # 打开

                except:             # 否则

                    sg.popup('连接失败,请检查服务器设置')  # 提示

                    break                       # 跳出

                resp=conn.read_some()           # 读回响应

                if resp!=b'Connect Success':    # 如果不是连接成功

                    sg.popup('连接失败,请检查服务器设置')  # 弹窗提示 

                    break                       # 跳出

                conn.write(('login '+username+'\n').encode())   # 发送登陆指令

                resp=conn.read_some()           # 读回响应

                if resp==b'Username exists':   # 如果收到Username exists

                    sg.popup('用户名已存在')    # 弹窗提示失败

                else:                          # 否则

                    sg.popup('登陆成功')        # 弹窗提示成功

                    is_login=True              # 改变标记

                    break                      # 跳出

            else:                              # 否则(用户名为空)

                sg.popup('请输入用户名')        # 弹窗提示失败

        if event in (None,'Cancel'):           # 退出事件

            break                              # 跳出循环

    window.close()  # 关窗

    del window      # 删窗

    if is_login:    # 判登陆标记

        chat(conn,username) # 是就弹下一个窗

def chat(conn,username):

    layout=[

        [sg.Text('聊天窗口('+username+')')],

        [sg.Multiline(disabled=True,size=(80,20),key='-chathistory-')],

        [sg.InputText(size=(70,5),key='-message-'),sg.Button('发送信息')],

        [sg.Button('查询在线用户'),sg.Button('退出聊天室')]

    ]

    window=sg.Window('Python聊天室',layout)             # 建窗

    while True:                                        # 循环

        event,values=window.read(timeout=10)           # 读窗口信息(含接收到的信息)

        if values:                                     # 值非空

            history_msg=values['-chathistory-']        # 读出对话聊天记录框字段值

            result=conn.read_very_eager().decode()     # 读服务器发过来的所有消息

            if result.strip():                         # 去掉result两边窗白非空(那就是服务器有信息传入)

                if 'Online' in result:                 # 出现Online

                    sg.popup('当前在线用户',result)     # 弹出显示

                else:                                  # 否则

                    window['-chathistory-'].update(history_msg+'\n'+result.strip())  # 更新聊天对话窗口内容

            if event in (None,'退出聊天室'):            # 点击x或按退出 

                break                                  # 跳出循环

            elif event.strip()=='发送信息':             # 点击发送信息

                send(conn,'say',values['-message-'])   # 发送

                window['-message-'].update('')         # 清空编辑框

            elif event=='查询在线用户':                 # 点击查询在线用户

                send(conn,type='look')                 # 发送 

    conn.write(b'logout\n') # 发送指令

    conn.close()    # 关闭连接

    window.close()  # 关窗

    del window      # 删窗

def send(conn,type=None,msg=None):              # 发

    if type=='say':                             # 说

        conn.write(('say '+msg +'\n').encode()) # 发

    elif type=='look':                          # 查

        conn.write(b'look\n')                   # 发

if __name__=='__main__':    # 主函数

    conn=telnetlib.Telnet() # 创建连接

    login(conn)             # 调用函数



```

```markdown

python原生socket编程存在哪些问题

一、在整个通信过程中,有很多不同的事件,这些事件需要我们自己处理,如果没有处理则极容易产生异常

二、必须使用多线程/多进程方式处理多个socket客户端的连接,而多线程在高并发场景下极容易发生C10K问题(C10指单机连接数过万)

·解决方案:IO多路复用。使用一个线程,通过记录IO流的状态来同时管理多个IO流,当其中任何一个IO流的状态发生改变,比如客户端发送了消息过来变成为可读状态,则再来通知该线程进行处理。通过这种可以大大提高服务器的吞吐能力,这种方式特别适合像socket网络编程这种IO密集型应用(如果是计算密集型则的确需要多个线程)。


网络编程中几个常见的易错概念:

并发:一个时间段内有几个程序在同一个CPU上运行,但任意时刻只有一个程序在CPU上运行,这种情景叫并发

并行:任意时刻点上,有多个程序同时运行在多个CPU上,所以最大并行数跟CPU的核心数相同

同步与异步关注的是消息通信机制,关心被调用方的行为

同步:代码在调用IO操作时,必须等待IO操作完成才返回的调用方式

异步:代码在调用IO操作时,不必等待IO操作完成就返回的调用方式,计算完了再返回一次结果

阻塞和非阻塞关注的是程序在等待调用结果时的状态,也就是调用方的状态

阻塞:调用方当前线程被挂起,等结果回到了再激活

非阻塞:调用方线程不会被挂起,而是立即返回做其他事情,过几分钟偶尔check一下有没有返回结果

总结:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞


asyncore框架概念

asyncore模块是Python自带的一个原生模块,提供简单的API以实现异步socket通信,并且为我们提供了异步socket服务器端和客户端的基础架构

在使用asyncore框架时,我们需要注意两个东西:

一、全局函数loop:创建asyncore事件循环,在事件循环中调用底层的select方法来检测特定网络信道,如果信道对应的socket对象状态发生改变,则自动产生一个高层次的事件信息,然后针对该信息调用相应的回调方法进行处理。

二、基类dispatcher:一个底层的socket类的封装对象,编程中继承于dispatchero在或其子类,dispatchero在里面已经定义好了socket通信中的各种事件,我们只需要重写特定的事件即可在该事件发生时实现自动回调处理。


为什么作为socket server的时候,服务器端的连接会自动断开?

因为socket连接是用完即断,所以为了保持通信服务端必须在socket连接建立好后立即创建一个新的dispatcher实例来保存这个socket连接对象,否则socket连接会被服务器端自动断开


补充

handle_accept是服务端接收信息

b' ' 表示这是一个 bytes 对象


asyncore聊天室实战

主要知识点:

asyncore作为服务器端的主要用法

async_chat模块的使用

pySimpleGUI界面框架

telnetlib作为客户端socket模块


如何设计一个聊天室的应用?

必要条件:服务器端,多客户端

必要约束:数据传输协议——以换行符为消息分隔符

原理:服务器监听消息来源,客户端连接服务器并发送消息到服务器


async_chat模块介绍:是dispatcher这个类的抽象子类,定义了一些专门用于数据传输方面的方法,非常有利于实现数据传输

主要方法:

collect_incoming_data:接收数据

found_terminator:当输入数据流符合由set_terminator设置的终止条件时被调用

set_terminator:设置终止条件

push:向数据传输队列压入要传输的数据



···

#第十六章:文档测试
#doctest:自动执行注释里的样例并生成错误报告
def square (x):
    '''
    一个用于计算平方的函数
    例如
    >>> square(2)
    4
    >>> square(3)
    9
    >>> square(-3)
    9
    >>> square(0)
    0
    '''
    return x * 2  # ①、故意写错的
    return x ** 2
class User:
    '''
    定义一个代表用户的类,该类包含如下两个属性:
    name - 代表用户的名字
    age - 代表用户的年龄
    例如
    >>> u = User('fkjava', 9)
    >>> u.name
    'fkjava'
    >>> u.age
    9
    >>> u.say('i love python')
    'fkjava说: i love python'
    '''
    def __init__(self, name, age):
        self.name = name
        self.name = 'fkit' # ②、故意写错的
        self.age = age
    def say(self, content):
        return self.name + '说: ' + content
if __name__=='__main__':
    import doctest
    doctest.testmod()
    
#单元测试逻辑覆盖
def test (a, b, m):
    if a > 10 and b == 0:
        m = a + b
    if a == 20 or m > 15:
        m += 1
'''
一、语句覆盖:每条语句都至少执行一次
a=20,b=0,m=3  两真
二、判定(边)覆盖/分支覆盖:每个判断分支取真取假各至少执行一次(a>10与b==0是同一分支)
a=20,b=0,m=3  两真
a=10,b=0,m=3  两假
三、条件覆盖:每个判断中每个条件的可能取值都至少满足一次(a>10与b==0是两个条件)
a=20,b=1,m=10 先假再真
a=9,b=0,m=20  先假再真
四、判定-条件覆盖:每个条件所有可能的组合至少出现一次
a=20,b=0,m=3  两真
a=20,b=1,m=10 先假再真
a=10,b=0,m=20 两假
a=1,b=2,m=3   两假
五、路径覆盖:覆盖所有路径
a=20,b=0,m=3  两真
a=5,b=0,m=2   两假
a=20,b=1,m=10 先假再真 
a=20,b=0,m=7  先真再假
'''

#测试用类例
#fk_math.py
def one_equation(a , b):
    '''
    求一元一次方程a * x + b = 0的解
    参数a - 方程中变量的系数
    参数b - 方程中的常量
    返回 方程的解
    '''
    # 如果a = 0,则方程无法求解
    if a == 0:
        raise ValueError("参数错误")
    # 返回方程的解
    else:
#        return -b / a  # ①
        return b / a
def two_equation(a , b , c):
    '''
    求一元二次方程a * x * x + b * x + c = 0的解
    参数a - 方程中变量二次幂的系数
    参数b - 方程中变量的系数
    参数c - 方程中的常量
    返回 方程的根
    '''
    if a == 0:raise ValueError("参数错误")# 如果a == 0,变成一元一次方程
    elif b * b - 4 * a * c < 0:raise ValueError("方程在有理数范围内无解")
    elif b * b - 4 * a * c == 0:return -b / (2 * a)# 方程有唯一的解
    else:
        r1 = (-b + (b * b - 4 * a * c) ** 0.5) / 2 / a
        r2 = (-b - (b * b - 4 * a * c) ** 0.5) / 2 / a
        return r1, r2# 方程的两个解
#test_fk_math.py
import unittest
from fk_math import *
class TestFkMath(unittest.TestCase):
    def test_one_equation(self):# 测试一元一次方程的求解
        self.assertEqual(one_equation(5 , 9) , -1.8)# 断言该方程求解应该为-1.8
        self.assertTrue(one_equation(4 , 10) == -2.5 , .00001)# 断言该方程求解应该为-2.5
        self.assertTrue(one_equation(4 , -27) == 27 / 4)# 断言该方程求解应该为27/4
        with self.assertRaises(ValueError):one_equation(0 , 9)# 断言当a == 0时的情况,断言引发ValueError
    def test_two_equation(self):# 测试一元二次方程的求解
        self.assertCountEqual(two_equation(1 , -3 , 2), (1.0, 2.0), '求解出错')
        self.assertCountEqual(two_equation(2 , -7 , 6), (1.5, 2.0), '求解出错')
        self.assertEqual(two_equation(1 , -4 , 4), 2.0, '求解出错')# 断言只有一个解的情形
        with self.assertRaises(ValueError):two_equation(0, 9, 3)# 断言当a == 0时的情况,断言引发ValueError
        with self.assertRaises(ValueError):two_equation(4, 2, 3)# 断言引发ValueError
if __name__ == '__main__':
    unittest.main()
    
#测试包与运行器(就是把前面的测试用例组合起来)
#hello.py
def say_hello():
    return "Hello World."
def add(nA, nB):
    return nA + nB
#test_hello.py
import unittest
from hello import *
class TestHello(unittest.TestCase):
    def test_say_hello(self):
        self.assertEqual(say_hello() , "Hello World.")
    def test_add(self):
        self.assertEqual(add(3, 4) , 7)
        self.assertEqual(add(0, 4) , 4)
        self.assertEqual(add(-3, 0) , -3)
#suite_test.py
import unittest
from test_fk_math import TestFkMath
from test_hello import TestHello
test_cases = (TestHello, TestFkMath)
def whole_suite():
    loader = unittest.TestLoader()# 创建测试加载器
    suite = unittest.TestSuite()# 创建测试包
    for test_class in test_cases:# 遍历所有测试类
        tests = loader.loadTestsFromTestCase(test_class)# 从测试类中加载测试用例
        suite.addTests(tests)# 将测试用例添加到测试包中
    return suite    
if __name__ == '__main__':
    with open('fk_test_report.txt', 'a') as f:
        runner = unittest.TextTestRunner(verbosity=2, stream=f)#创建测试运行器(TestRunner),测试报告输出到文件中
        runner.run(whole_suite())#测试
#第十七章:pyinstaller
#Python_test2.py文件
def say_hello(name):
    return name + ",您好!"

#Python_test3.py文件
from Python_test2 import *
def main():
    print('程序开始执行')
    print(say_hello('孙悟空'))
# 增加调用main()函数
if __name__ == '__main__':
    main()

#命令行的代码
#pyinstaller -F F:\ProjectPython\hello_test2\Python_test3.py
#-F表示生成一个大EXE文件,-D表示生成一个目录(动态重定位),加-w取消命令行
#-n NAME或-name NAME可以指定项目名字,如果省略则第一个脚本的主文件名将作为spec名字(其实就是main.py,你在开发的时候把这个主程序入口的py文件命名好就行了,例如tank_game.py则最后就会生成tank_game.exe)
#现在还未知道如何改图标

#第十八章:pygame
import pygame
def main():
    pygame.init()                               #初始化
    screen=pygame.display.set_mode((1024,640))  #开窗口
    pygame.display.set_caption("tank game")     #定标题
    background=pygame.Surface(screen.get_size())#获大小
    background=background.convert()             #屏幕转像素
    background.fill((100,100,100))              #白色填充
    clock=pygame.time.Clock()                   #开定时器
    running=True                                #运行标志
    while running:                       #主程序大循环
        clock.tick(30)                   #最大每秒30帧
        for event in pygame.event.get(): #检测退出信息
            if event.type==pygame.QUIT:  #如果有的话
                running=False            #更改标志位
        screen.blit(background,(0,0))    #位块传送把背景回到左上角
        #鼠标事件
        buttons=pygame.mouse.get_pressed()          #获鼠标按下事件
        if buttons[0]:print(pygame.mouse.get_pos()) #按左键输出坐标
        #键盘事件
        keys=pygame.key.get_pressed()               #获键盘按下事件
        if keys[pygame.K_SPACE]:print('space')      #按下空格输出space
        #绘图:画矩形(在screen上画,[r,g,b],[左上角x,y,长,宽],线宽),线宽取0是填充
        pygame.draw.rect(screen,[255,0,0],[600,600,10,10],0)
        #贴图
        maps = pygame.image.load('GJL.png')         #获取图片
        screen.blit(maps,(0,0))                     #贴到screen的左上角
        pygame.display.update()          #更新显示
    pygame.quit()                        #退出游戏
main()

'''
KeyASCII	ASCII	描述
K_BACKSPACE	\b	退格键(Backspace)
K_TAB	\t	制表键(Tab)
K_CLEAR		清楚键(Clear)
K_RETURN	\r	回车键(Enter)
K_PAUSE		暂停键(Pause)
K_ESCAPE	^[	退出键(Escape)
K_SPACE		空格键(Space)
K_EXCLAIM	!	感叹号(exclaim)
K_QUOTEDBL	"	双引号(quotedbl)
K_HASH	#	井号(hash)
K_DOLLAR	$	美元符号(dollar)
K_AMPERSAND	&	and 符号(ampersand)
K_QUOTE	'	单引号(quote)
K_LEFTPAREN	(	左小括号(left parenthesis)
K_RIGHTPAREN	)	右小括号(right parenthesis)
K_ASTERISK	*	星号(asterisk)
K_PLUS	+	加号(plus sign)
K_COMMA	,	逗号(comma)
K_MINUS	-	减号(minus sign)
K_PERIOD	.	句号(period)
K_SLASH	/	正斜杠(forward slash)
K_0	0	0
K_1	1	1
K_2	2	2
K_3	3	3
K_4	4	4
K_5	5	5
K_6	6	6
K_7	7	7
K_8	8	8
K_9	9	9
K_COLON	:	冒号(colon)
K_SEMICOLON	;	分号(semicolon)
K_LESS	<	小于号(less-than sign)
K_EQUALS	=	等于号(equals sign)
K_GREATER	>	大于号(greater-than sign)
K_QUESTION	?	问号(question mark)
K_AT	@	at 符号(at)
K_LEFTBRACKET	[	左中括号(left bracket)
K_BACKSLASH	\	反斜杠(backslash)
K_RIGHTBRACKET	]	右中括号(right bracket)
K_CARET	^	脱字符(caret)
K_UNDERSCORE	_	下划线(underscore)
K_BACKQUOTE	`	重音符(grave)
K_a	a	a
K_b	b	b
K_c	c	c
K_d	d	d
K_e	e	e
K_f	f	f
K_g	g	g
K_h	h	h
K_i	i	i
K_j	j	j
K_k	k	k
K_l	l	l
K_m	m	m
K_n	n	n
K_o	o	o
K_p	p	p
K_q	q	q
K_r	r	r
K_s	s	s
K_t	t	t
K_u	u	u
K_v	v	v
K_w	w	w
K_x	x	x
K_y	y	y
K_z	z	z
K_DELETE		删除键(delete)
K_KP0		0(小键盘)
K_KP1		1(小键盘)
K_KP2		2(小键盘)
K_KP3		3(小键盘)
K_KP4		4(小键盘)
K_KP5		5(小键盘)
K_KP6		6(小键盘)
K_KP7		7(小键盘)
K_KP8		8(小键盘)
K_KP9		9(小键盘)
K_KP_PERIOD	.	句号(小键盘)
K_KP_DIVIDE	/	除号(小键盘)
K_KP_MULTIPLY	*	乘号(小键盘)
K_KP_MINUS	-	减号(小键盘)
K_KP_PLUS	+	加号(小键盘)
K_KP_ENTER	\r	回车键(小键盘)
K_KP_EQUALS	=	等于号(小键盘)
K_UP		向上箭头(up arrow)
K_DOWN		向下箭头(down arrow)
K_RIGHT		向右箭头(right arrow)
K_LEFT		向左箭头(left arrow)
K_INSERT		插入符(insert)
K_HOME		Home 键(home)
K_END		End 键(end)
K_PAGEUP		上一页(page up)
K_PAGEDOWN		下一页(page down)
K_F1		F1
K_F2		F2
K_F3		F3
K_F4		F4
K_F5		F5
K_F6		F6
K_F7		F7
K_F8		F8
K_F9		F9
K_F10		F10
K_F11		F11
K_F12		F12
K_F13		F13
K_F14		F14
K_F15		F15
K_NUMLOCK		数字键盘锁定键(numlock)
K_CAPSLOCK		大写字母锁定键(capslock)
K_SCROLLOCK		滚动锁定键(scrollock)
K_RSHIFT		右边的 shift 键(right shift)
K_LSHIFT		左边的 shift 键(left shift)
K_RCTRL		右边的 ctrl 键(right ctrl)
K_LCTRL		左边的 ctrl 键(left ctrl)
K_RALT		右边的 alt 键(right alt)
K_LALT		左边的 alt 键(left alt)
K_RMETA		右边的元键(right meta)
K_LMETA		左边的元键(left meta)
K_LSUPER		左边的 Window 键(left windows key)
K_RSUPER		右边的 Window 键(right windows key)
K_MODE		模式转换键(mode shift)
K_HELP		帮助键(help)
K_PRINT		打印屏幕键(print screen)
K_SYSREQ		魔术键(sysrq)
K_BREAK		中断键(break)
K_MENU		菜单键(menu)
K_POWER		电源键(power)
K_EURO		欧元符号(euro)
'''

第十九章:matplotlib
#入门
import matplotlib.pyplot as plt
import numpy as np
plt.figure()
plt.subplot(2,1,1)#将整个figure分成两行两列,并将下面图形放在第4个网格
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700]
ln1,=plt.plot(x_data,y_data,color='red',linewidth=2.0,linestyle='--',label='cj')#折线颜色、线宽和样式
ln2,=plt.plot(x_data,y_data2,color='blue',linewidth=3.0,linestyle='-.',label='gjl')#-实线--虚线:点线-.点虚线
plt.legend(loc='lower right')#调用legend函数设置图例,location默认是'best'自动选择最佳位置
plt.xlabel('year')
plt.ylabel('salary')
plt.title('gogogo')
#plt.yticks([50000,70000,100000],[r'A',r'B',r'C'])#这行出现就会代替Y坐标值
#plt.show()# 调用show()函数显示图形
ax = plt.gca()#返回的是axis坐标轴
ax.spines['bottom'].set_position(('data',70000))#设置坐标轴的bottom处的位置是data=70000处
#ax.yaxis.set_ticks_position('left')#设置坐标轴的Y轴的位置在left处,可以无视,默认就是这样
#ax.xaxis.set_ticks_position('bottom')#设置坐标轴的X轴的的位置在bottom处,可以无视,默认就是这样
#ax.spines['right'].set_color('red')#设置右边坐标轴线的颜色(设置为none表示不显示),可以无视,没啥用
#ax.spines['top'].set_color('blue')#设置顶部坐标轴线的颜色(设置为none表示不显示),可以无视,没啥用
plt.subplot(2,1,2)#将整个figure分成两行两列,第三个参数表示下面的图形放在第1个网格
x_data=np.linspace(-np.pi,np.pi,64,endpoint=True)#利用np.pi线性均分成64个点
plt.plot(x_data, np.sin(x_data))#开始绘制正弦曲线,注意此时的plt是画在212位置的
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('sin')
plt.show()

#子图
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
plt.figure()
x_data = np.linspace(-np.pi, np.pi, 64, endpoint=True)#取生成x点
gs = gridspec.GridSpec(2, 3)# 将绘图区域分成2行3列
ax1 = plt.subplot(gs[0, :])# 指定ax1占用第零行
ax2 = plt.subplot(gs[1, 0])# 指定ax1占用第一行的第零格
ax3 = plt.subplot(gs[1, 1:3])# 指定ax1占用一行的第二、三格
ax1.plot(x_data, np.sin(x_data))# 绘制正弦曲线
ax1.spines['right'].set_color('none')
ax1.spines['top'].set_color('none')
ax1.spines['bottom'].set_position(('data', 0))
ax1.spines['left'].set_position(('data', 0))
ax1.set_title('sin')
ax2.plot(x_data, np.cos(x_data))# 绘制余弦曲线
ax2.spines['right'].set_color('none')
ax2.spines['top'].set_color('none')
ax2.spines['bottom'].set_position(('data', 0))
ax2.spines['left'].set_position(('data', 0))
ax2.set_title('cos')
ax3.plot(x_data, np.tan(x_data))# 绘制正切曲线
ax3.spines['right'].set_color('none')
ax3.spines['top'].set_color('none')
ax3.spines['bottom'].set_position(('data', 0))
ax3.spines['left'].set_position(('data', 0))
ax3.set_title('tan')
plt.show()

#饼图
import matplotlib.pyplot as plt
data=[0.16881, 0.14966, 0.07471, 0.06992,0.04762, 0.03541, 0.02925, 0.02411, 0.02316, 0.01409, 0.36326]
labels=['Java','C','C++','Python','Visual Basic .NET','C#','PHP','JavaScript','SQL','Assembly langugage','other']
explode = [0, 0, 0, 0.3, 0, 0, 0, 0, 0, 0, 0]# 将第4个语言(Python)分离出来
colors=['red', 'pink', 'magenta','purple','orange'] # 使用自定义颜色
plt.axes(aspect='equal')#正圆
plt.xlim(0,8)#边框大小
plt.ylim(0,8)#边框大小
plt.pie(x = data, # 绘图数据
    labels=labels, # 添加编程语言标签
    explode=explode, # 突出显示Python
    colors=colors, # 设置饼图的自定义填充色
    autopct='%.3f%%', # 设置百分比的格式,此处保留3位小数
    pctdistance=0.8,  # 设置百分比标签与圆心的距离
    labeldistance = 1.15, # 设置标签与圆心的距离
    startangle = 180, # 设置饼图的初始角度
    center = (4, 4), # 设置饼图的圆心(相当于X轴和Y轴的范围)
    radius = 3.8, # 设置饼图的半径(相当于X轴和Y轴的范围)
    counterclock = False, # 是否逆时针,这里设置为顺时针方向
    wedgeprops = {'linewidth': 1, 'edgecolor':'green'},# 设置饼图内外边界的属性值
    textprops = {'fontsize':12, 'color':'black'}, # 设置文本标签的属性值
    frame = 1) # 是否显示饼图的圆圈,此处设为显示
plt.xticks(())
plt.yticks(())
plt.title('2018-08 languagu rank')
plt.show()

#竖柱状图
import matplotlib.pyplot as plt
import numpy as np
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700]
bar_width=0.3
#range(len(x_data)就是0、1、2控制中心坐标故np.arange(len(x_data))是左柱中心坐标,加+bar_width就是右柱中心坐标
plt.bar(x=range(len(x_data)),height=y_data,label='CJ',color='steelblue', alpha=0.8, width=bar_width)
plt.bar(x=np.arange(len(x_data))+bar_width+0.05, height=y_data2,label='GJL', color='indianred', alpha=0.8, width=bar_width)
for x,y in enumerate(y_data):plt.text(x,y,'%s'%y,ha='center',va='bottom')#x,y是坐标,'%s'%y是内容
for x,y in enumerate(y_data2):plt.text(x+bar_width,y,'%s'%y,ha='center',va='top')#ha水平对齐, va垂直对齐
plt.xticks(np.arange(len(x_data))+bar_width/2, x_data)# 为X轴设置刻度值
plt.title("CJ&GJL")#设置标题
plt.xlabel("year")#X标签
plt.ylabel("salary")#Y标答
plt.legend()#图例
plt.show()
#横柱状图
import matplotlib.pyplot as plt
import numpy as np
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700]
bar_width=0.3
plt.barh(y=range(len(x_data)), width=y_data, label='CJ',color='steelblue', alpha=0.8, height=bar_width)
plt.barh(y=np.arange(len(x_data))+bar_width, width=y_data2,label='GJL', color='indianred', alpha=0.8, height=bar_width)
for y, x in enumerate(y_data):plt.text(x+5000,y-bar_width/2,'%s'%x,ha='center',va='bottom')
for y, x in enumerate(y_data2):plt.text(x+5000,y+bar_width/2,'%s'%x,ha='center',va='bottom')
plt.yticks(np.arange(len(x_data))+bar_width/2, x_data)# 为Y轴设置刻度值
plt.title("CJ&GJL")#设置标题
plt.xlabel("year")#X标签
plt.ylabel("salary")#Y标答
plt.legend()#图例
plt.show()

#散点图
import matplotlib.pyplot as plt
import numpy as np
plt.figure()
x_data = np.linspace(-np.pi, np.pi, 64, endpoint=True)# 定义从-pi到pi之间的数据,平均取64个数据点
plt.scatter(x_data, #x值
    np.sin(x_data), #y值
    c='purple',     #设置点的颜色
    s=50,           #设置点半径
    alpha = 0.5,    #设置透明度
    marker='p',     #设置使用五边形标记
    linewidths=1,   #设置边框的线宽
    edgecolors=['green', 'yellow']) # 设置边框的颜色
plt.scatter(x_data[0], np.sin(x_data)[0], c='red',s=150, alpha = 1)
plt.scatter(x_data[63], np.sin(x_data)[63], c='black',s=150,alpha = 1)
plt.gca().spines['right'].set_color('none')
plt.gca().spines['top'].set_color('none')
plt.gca().spines['bottom'].set_position(('data', 0))
plt.gca().spines['left'].set_position(('data', 0))
plt.title('sin')
plt.show()

#等高线图
import matplotlib.pyplot as plt
import numpy as np
delta = 0.025
x=np.arange(-3.0,3.0,delta)#生成代表X轴数据的列表
y=np.arange(-2.0,2.0,delta)#生成代表Y轴数据的列表
X,Y=np.meshgrid(x, y)#对x、y数据执行网格化
Z1=np.exp(-X**2-Y**2)
Z2=np.exp(-(X-1)**2-(Y-1)**2)
Z=(Z1-Z2)*2# 计算Z轴数据(高度数据)
plt.contourf(x,y,Z,16,alpha=0.75,cmap='rainbow')#使用颜色映射来区分16种不同高度的区域
C=plt.contour(x,y,Z,16,#绘制等高线
    colors='black',#指定等高线的颜色
    linewidth=0.5)#指定等高线的线宽
plt.clabel(C, inline = True, fontsize = 10)#绘制等高线数据
plt.xticks(())#去除坐标轴
plt.yticks(())#去除坐标轴
plt.title("contour")
plt.xlabel("x")
plt.ylabel("y")
plt.show()

#3D图形
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(12, 8))
ax = Axes3D(fig)
delta = 0.125
x = np.arange(-3.0, 3.0, delta)# 生成代表X轴数据的列表
y = np.arange(-2.0, 2.0, delta)# 生成代表Y轴数据的列表
X, Y = np.meshgrid(x, y)# 对x、y数据执行网格化
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2# 计算Z轴数据(高度数据)
ax.plot_surface(X, Y, Z, # 绘制3D图形
    rstride=1,  # rstride(row)指定行的跨度
    cstride=1,  # cstride(column)指定列的跨度
    cmap=plt.get_cmap('rainbow'))  # 设置颜色映射
ax.set_zlim(-2, 2)# 设置Z轴范围
plt.title("3D")# 设置标题
plt.show()

#pygal:实在太简单了,缺点是必须用浏览器看
import pygal

#例一柱图
y_data = [58000, 60200, 63000, 71000, 84000, 90500, 107000]
y_data2 = [52000, 54200, 51500,58300, 56800, 59500, 62700]
bar = pygal.Bar()# 创建pygal.Bar对象(柱状图)
bar.add('Java', y_data)# 添加两组代表条柱的数据
bar.add('Android', y_data2)
bar.render_to_file('fk_books.svg')# 添加两组代表条柱的数据

#例二柱图加强版
x_data = ['2011', '2012', '2013', '2014', '2015', '2016', '2017']
bar.x_labels = x_data# 设置X轴的刻度值
bar.title = '销量'
bar.x_title = '年份'
bar.y_title = '销量'
bar.x_label_rotation = 45# 设置X轴的刻度值旋转45度
bar.legend_at_bottom = True# 设置将图例放在底部
bar.margin = 35# 设置数据图四周的页边距,也可通过margin_bottom、margin_left、margin_right、margin_top只设置单独一边的页边距
bar.show_y_guides=False# 隐藏Y轴上的网格线
bar.show_x_guides=True# 显示X轴上的网格线
bar.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#例三折线图
line = pygal.Line()# 创建pygal.Line对象(折线图)
line.add('Java', y_data)# 添加两组代表折线的数据
line.add('Android', y_data2)
line.x_labels = x_data# 设置X轴的刻度值
line.y_labels = [20000, 40000, 60000, 80000, 100000]# 重新设置Y轴的刻度值
line.title = '销量'
line.x_title = '年份'
line.y_title = '销量'
line.legend_at_bottom = True# 设置将图例放在底部
line.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#水平柱状图
horizontal_bar = pygal.HorizontalBar()# 创建pygal.HorizontalBar对象(水平柱状图)
horizontal_bar.add('Java', y_data)# 添加两组数据
horizontal_bar.add('Android', y_data2)
horizontal_bar.x_labels = x_data# 设置Y轴的刻度值(注意xy这里掉乱说的,有点迷糊)
horizontal_bar.y_labels = [20000, 40000, 60000, 80000, 100000]# 重新设置X轴的刻度值
horizontal_bar.title = '销量'
horizontal_bar.x_title = '销量'
horizontal_bar.y_title = '年份'
horizontal_bar.legend_at_bottom = True# 设置将图例放在底部
horizontal_bar.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#水平折线图
horizontal_line = pygal.HorizontalLine()# 创建pygal.HorizontalLine对象(水平折线图)
horizontal_line.add('Java', y_data)# 添加两组代表折线的数据
horizontal_line.add('Android', y_data2)
horizontal_line.x_labels = x_data# 设置Y轴(确实如此)的刻度值
horizontal_line.y_labels = [20000, 40000, 60000, 80000, 100000]# 重新设置X轴(确实如此)的刻度值
horizontal_line.title = '销量'
horizontal_line.x_title = '销量'
horizontal_line.y_title = '年份'
horizontal_line.legend_at_bottom = True# 设置将图例放在底部
horizontal_line.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#叠加柱状图
stacked_bar = pygal.StackedBar()
stacked_bar.add('Java', y_data)# 添加两组数据
stacked_bar.add('Android', y_data2)
stacked_bar.x_labels = x_data# 设置X轴的刻度值
stacked_bar.y_labels = [20000, 40000, 60000, 80000, 100000]# 重新设置Y轴的刻度值
stacked_bar.title = '销量'
stacked_bar.x_title = '销量'
stacked_bar.y_title = '年份'
stacked_bar.legend_at_bottom = True# 设置将图例放在底部
stacked_bar.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#叠加折线图
stacked_line = pygal.StackedLine()
stacked_line.add('Java', y_data)# 添加两组数据
stacked_line.add('Android', y_data2)
stacked_line.x_labels = x_data# 设置X轴的刻度值
stacked_line.y_labels = [20000, 40000, 60000, 80000, 100000]# 重新设置Y轴的刻度值
stacked_line.title = '销量'
stacked_line.x_title = '销量'
stacked_line.y_title = '年份'
stacked_line.legend_at_bottom = True# 设置将图例放在底部
stacked_line.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#点图
dot = pygal.Dot()
dot.dots_size = 5
dot.add('Java', y_data)# 添加两组数据
dot.add('Android', y_data2)
dot.x_labels = x_data# 设置X轴的刻度值
dot.y_labels = ['Java', 'Android']# 重新设置Y轴的刻度值
dot.y_label_rotation = 45# 设置Y轴刻度值的旋转角度
dot.title = '销量'
dot.x_title = '年份'
dot.legend_at_bottom = True# 设置将图例放在底部
dot.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#饼图
data=[0.16881,0.14966,0.07471,0.06992,0.04762,0.03541,0.02925,0.02411,0.02316,0.01409,0.36326]
labels=['Java','C','C++','Python','Visual Basic .NET','C#','PHP','JavaScript','SQL','Assembly langugage','其他']# 准备标签
pie = pygal.Pie()# 创建pygal.Pie对象(饼图)
for i, per in enumerate(data): pie.add(labels[i], per)# 采用循环为饼图添加数据
pie.title = '2018年8月编程语言'
pie.legend_at_bottom = True# 设置将图例放在底部
pie.inner_radius = 0.4# 设置内圈的半径长度
pie.half_pie = True# 创建半圆数据图
pie.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#仪表图
gauge = pygal.Gauge()# 创建pygal.Gauge对象(仪表图)
gauge.range = [0, 1]
for i, per in enumerate(data):gauge.add(labels[i], per)# 采用循环为仪表图添加数据
gauge.title = '2018年8月编程语言'
gauge.legend_at_bottom = True# 设置将图例放在底部
gauge.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#雷达图
data = [[5, 4.0, 5, 5, 5],# 准备数据
    [4.8, 2.8, 4.8, 4.8, 4.9],
    [4.5, 2.9, 4.6, 4.0, 4.9],
    [4.0, 4.8, 4.9, 4.0, 5],
    [3.0, 4.2, 2.3, 3.5, 2],
    [4.8, 4.3, 3.9, 3.0, 4.5]]
labels = ['Java', 'C', 'C++', 'Python','C#', 'PHP']# 准备标签
rader = pygal.Radar()# 创建pygal.Radar对象(雷达图)
for i, per in enumerate(labels):rader.add(labels[i], data[i])# 采用循环为雷达图添加数据
rader.x_labels = ['平台健壮性', '语法易用性', '社区活跃度','市场份额', '未来趋势']
rader.title = '编程语言对比图'
rader.dots_size = 8# 控制各数据点的大小
rader.legend_at_bottom = True# 设置将图例放在底部
rader.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#csv
import csv
import pygal
from datetime import datetime
from datetime import timedelta
with open('guangzhou-2017.csv') as f:# 打开文件
    reader = csv.reader(f)# 创建cvs文件读取器
    header_row = next(reader)# 读取第一行,这行是表头数据。
    print(header_row)
    shades, sunnys, cloudys, rainys = 0, 0, 0, 0# 准备展示的数据
    prev_day = datetime(2016, 12, 31)
    for row in reader:
        try:
            cur_day = datetime.strptime(row[0], '%Y-%m-%d')# 将第一列的值格式化为日期
            description = row[3]
        except ValueError:
            print(cur_day, '数据出现错误')
        else:
            diff = cur_day - prev_day# 计算前、后两天数据的时间差
            if diff != timedelta(days=1):# 如果前、后两天数据的时间差不是相差一天,说明数据有问题
                print('%s之前少了%d天的数据' % (cur_day, diff.days - 1))
            prev_day = cur_day   
            if '阴' in description:shades += 1
            elif '晴' in description:sunnys += 1
            elif '云' in description:cloudys += 1
            elif '雨' in description:rainys += 1
            else:print(description)
pie = pygal.Pie()# 创建pygal.Pie对象(饼图)
pie.add("阴", shades)# 为饼图添加数据
pie.add("晴", sunnys)
pie.add("多云", cloudys)
pie.add("雨", rainys)
pie.title = '2017年广州天气汇总'
pie.legend_at_bottom = True# 设置将图例放在底部
pie.render_to_file('fk_books.svg')# 指定将数据图输出到SVG文件中

#json
import json
with open('gdp_json.json') as f:
    gpd_list = json.load(f)
for gpd_dict in gpd_list:# 遍历列表的每个元素,每个元素是一个GDP数据项
    if gpd_dict['Year']==2016 and gpd_dict['Country Code']=='CHN':#中国2016年GDP
        print(gpd_dict['Country Name'], gpd_dict['Value'])
#第二十章:自动登陆及刷阅读量
from selenium import webdriver
from time import sleep
url='http://www.51cto.com'#定义网址
browser=webdriver.Chrome()#打开浏览器并获句柄
browser.maximize_window() #最大化窗口
browser.get(url)          #打开URL
browser.find_element_by_xpath('//*[@id="login_status"]/a[1]').click()#点击登陆按钮
browser.find_element_by_xpath('//*[@id="loginform-username"]').clear()#帐户框清空
browser.find_element_by_xpath('//*[@id="loginform-username"]').send_keys('13690670863')#输入帐号
browser.find_element_by_xpath('//*[@id="loginform-password"]').clear()#密码框清空
browser.find_element_by_xpath('//*[@id="loginform-password"]').send_keys('abcde123456789')#输入密码
sleep(3)#延时三秒
browser.find_element_by_xpath('//*[@id="login-form"]/div[3]/input').click()#点击确定
sleep(3)#延时三秒
browser.quit()#退出

补充一句:spyder中一键注释快捷键是ctrl+1

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值