使用python之tkinter 创建简单记事本,采用多种实现代码。
Python的文件操作可参考https://blog.csdn.net/cnds123/article/details/108308817
特别提示,用下面语句打开由记事本创建的.txt文件:
open(filename, 'r')
时,可能报错“UnicodeDecodeError: 'gbk' codec can't decode byte……illegal multibyte sequence”
原因是,Python 的 open 方法默认编码取决于平台,如果是 Windows 中文平台,默认编码是 gbk,如果文件是 utf-8 编码,就会报这个错误。可以 encoding='utf-8'更改
改为:
open(filename, 'r', encoding='utf-8')
也就是添加参数encoding='utf-8'
代码参考或借鉴自网络。
第一种实现代码
源码如下:
- import tkinter as tk #导入tkinter库,并重命名为tk
- from tkinter import filedialog #从tkinter库中导入filedialog模块
- from tkinter import messagebox #从tkinter库中导入messagebox模块
- import os #导入os标准库
-
- mywindow = tk.Tk() #创建一个窗体
- mywindow.title("简单记事本1") #设置窗体的标题
- mywindow.geometry("400x360") #设置窗体的大小
- filename="" #文件名,全局变量
- def about():
- messagebox.showinfo('提示','这是一个消息框!很简单不需要帮助吧')
- def myopen():
- global filename
- # 下面两句用其一即可,在此用第二句:你可以试试,留意区别
- #filename=filedialog.askopenfilename(defaultextension='.txt')
- filename=filedialog.askopenfilename(filetypes=[('txt格式', 'txt')])
- if filename=="":
- filename=None
- else:
- mywindow.title("记事本"+os.path.basename(filename))
- mytext.delete(1.0,tk.END)
- f=open(filename,'r',encoding='utf-8') # encoding='utf-8'
- mytext.insert(tk.INSERT,f.read())
- f.close()
-
- def mysave():
- global filename
- # 下面两句用其一即可,在此用第二句:你可以试试,留意区别
- #f=filedialog.asksaveasfilename(initialfile="未命名.txt",defaultextension='.txt' )
- f=filedialog.asksaveasfilename(initialfile="未命名.txt",filetypes=[('txt格式', 'txt')])
- filename=f
- if filename:
- fh=open(f,'w',encoding='utf-8') # encoding='utf-8'
- msg=mytext.get(1.0,tk.END)
- fh.write(msg)
- fh.close()
- mywindow.title("记事本"+os.path.basename(f))
-
-
- #多行文本框及布局
- mytext = tk.Text(mywindow,height = 24,width =60)
- mytext.pack(side='bottom')
- #按钮控件及布局
- myb1 = tk.Button(mywindow,text="打开",command = myopen)
- myb1.pack(side = 'left')
- myb2 = tk.Button(mywindow,text="保存", command = mysave )
- myb2.pack(side = 'left')
- myb3 = tk.Button(mywindow,text="帮助", command = about)
- myb3.pack(side = 'left')
-
- mywindow.mainloop()
运行效果:
第二种实现代码(使用菜单)
源码如下:
- from tkinter import *
- from tkinter import messagebox
- from tkinter import filedialog
- import os
-
- filename='' #文件名,全局变量
- def about():
- messagebox.showinfo('提示','这是一个消息框!很简单不需要帮助吧')
- def openFile():
- global filename #使用全局变量
- # 将下句 filetypes=[('txt格式', 'txt')] 改为 defaultextension='.txt' 试试
- filename=filedialog.askopenfilename( filetypes=[('txt格式', 'txt')])
- if filename=='': #如果没有选择
- filename=None
- else:
- root.title('FileName:'+os.path.basename(filename)) #将文件名显示在标题栏中
- textPad.delete(1.0,END) #删除文本框中原有内容
- f=open(filename,'r',encoding='UTF-8') #获取文件中内容
- textPad.insert(1.0,f.read()) #显示到文本框
- f.close()
- def saveOtherFile():
- global filename
- # 将下句 filetypes=[('txt格式', 'txt')] 改为 defaultextension='.txt' 试试
- f=filedialog.asksaveasfilename(initialfile='未命名.txt', filetypes=[('txt格式', 'txt')])
- filename=f
- root.title('FileName:' + os.path.basename(filename))
- fh=open(f,'w',encoding='UTF-8') #只写方式打开文件
- msg=textPad.get(1.0,END) #从文本框中获取内容
- fh.write(msg)
- fh.close()
-
- root=Tk()
- root.title('简单记事本1b')
- root.geometry('500x500+100+100') #widthxheight+x+y
- menubar=Menu(root)#设置菜单栏
- root.config(menu=menubar)
- filemenu=Menu(menubar,tearoff=0) #第一个菜单栏
- filemenu.add_command(label='打开',accelerator='Ctrl+N',command=openFile)
- filemenu.add_command(label='保存',accelerator='Ctrl+T',command=saveOtherFile)
- menubar.add_cascade(label='文件',menu=filemenu)
-
- aboutmenu=Menu(menubar,tearoff=0)
- aboutmenu.add_command(label='关于',accelerator='Ctrl+Y',command=about)
- menubar.add_cascade(label='帮助',menu=aboutmenu)
-
- status=Label(root,text='',bd=1,relief=SUNKEN,anchor=W) #底部边界栏
- status.pack(side=BOTTOM,fill=X)
-
- lnlabel=Label(root,width=2,bg='antique white') #左边边界栏
- lnlabel.pack(side=LEFT,fill=Y)
-
- textPad=Text(root,undo=True) #设置文本框
- textPad.pack(expand=YES,fill=BOTH)
-
- scroll=Scrollbar(textPad) #右边进度条栏
- textPad.config(yscrollcommand=scroll.set)
- scroll.config(command=textPad.yview)
- scroll.pack(side=RIGHT,fill=Y)
-
- root.mainloop()
运行效果:
第三种实现代码(使用类)
源码如下:
- import tkinter
- import os
- from tkinter import *
- from tkinter.messagebox import *
- from tkinter.filedialog import *
-
-
- class Notepad:
-
- root = Tk()
-
- Width = 300
- Height = 300
- TextArea = Text(root)
- MenuBar = Menu(root)
- FileMenu = Menu(MenuBar, tearoff=0)
- EditMenu = Menu(MenuBar, tearoff=0)
- HelpMenu = Menu(MenuBar, tearoff=0)
- ScrollBar = Scrollbar(TextArea)
- file = None
-
- def __init__(self, **kwargs):
-
- # 设置文本框的大小
-
- try:
- self.Width = kwargs['width']
- except KeyError:
- pass
-
- try:
- self.Height = kwargs['height']
- except KeyError:
- pass
-
- # 设置窗口标题
- self.root.title("Python记事本")
-
- # 将窗口居中显示
- screenWidth = self.root.winfo_screenwidth()
- screenHeight = self.root.winfo_screenheight()
- left = (screenWidth / 2) - (self.Width / 2)
- top = (screenHeight / 2) - (self.Height / 2)
- self.root.geometry('%dx%d+%d+%d' %
- (self.Width, self.Height, left, top))
-
- # 文本区 域大小调整
- self.root.grid_rowconfigure(0, weight=1)
- self.root.grid_columnconfigure(0, weight=1)
-
- # Add controls (widget)
- self.TextArea.grid(sticky=N + E + S + W)
-
- # 增加新建配置
- self.FileMenu.add_command(label="新建", command=self.__newFile)
-
- # 增加打开配置
- self.FileMenu.add_command(label="打开", command=self.__openFile)
-
- # 增加保存配置
- self.FileMenu.add_command(label="保存", command=self.__saveFile)
-
- # 增加退出配置
- self.FileMenu.add_separator()
- self.FileMenu.add_command(label="退出", command=self.__quitApplication)
- # 菜单中设置文件按钮
- self.MenuBar.add_cascade(label="文件", menu=self.FileMenu)
-
- # 增加剪切功能
- self.EditMenu.add_command(label="剪切", command=self.__cut)
-
- # 增加复制功能
- self.EditMenu.add_command(label="复制", command=self.__copy)
-
- # 增加粘贴功能
- self.EditMenu.add_command(label="粘贴", command=self.__paste)
-
- # 菜单中设置编辑按钮
- self.MenuBar.add_cascade(label="编辑", menu=self.EditMenu)
-
- # 增加关于记事本选项
- self.HelpMenu.add_command(label="关于记事本", command=self.__showAbout)
- # 菜单中射者帮助按钮
- self.MenuBar.add_cascade(label="帮助", menu=self.HelpMenu)
-
- self.root.config(menu=self.MenuBar)
-
- self.ScrollBar.pack(side=RIGHT, fill=Y)
-
- # 滚动条根据内容进行调整
- self.ScrollBar.config(command=self.TextArea.yview)
- self.TextArea.config(yscrollcommand=self.ScrollBar.set)
-
- def __quitApplication(self):
- '''
- 用于退出程序(关了就消失)
- '''
- self.root.destroy()
-
- def __showAbout(self):
- '''
- 添加帮助菜单中的信息
- '''
-
- showinfo("关于记事本", "很简单不需要帮助吧")
-
- def __openFile(self):
- '''
- 打开文件
- '''
- self.file = askopenfilename(defaultextension=".txt",
- filetypes=[("All Files", "*.*"),
- ("Text Documents", "*.txt")])
-
- if self.file == "":
- self.file = None
- else:
-
- self.root.title(os.path.basename(self.file))
- self.TextArea.delete(1.0, END)
- file = open(self.file, "r",encoding='utf-8')
- self.TextArea.insert(1.0, file.read())
- file.close()
-
- def __newFile(self):
- '''
- 新文件:默认是一个未命名文件
- '''
-
- self.root.title("未命名文件")
- self.file = None
- self.TextArea.delete(1.0, END)
-
- def __saveFile(self):
- '''
- 用于保存文件,不存在的文件进行新建,存在的文件在原文件基础上覆盖保存
- '''
- if self.file == None:
- self.file = asksaveasfilename(initialfile='Untitled.txt',
- defaultextension=".txt",
- filetypes=[("All Files", "*.*"),
- ("Text Documents",
- "*.txt")])
-
- if self.file == "":
- self.file = None
- else:
-
- file = open(self.file, "w",encoding='utf-8')
- file.write(self.TextArea.get(1.0, END))
- file.close()
-
- # 更改title名字为文件名
- self.root.title(os.path.basename(self.file))
-
- else:
- file = open(self.file, "w",encoding='utf-8')
- file.write(self.TextArea.get(1.0, END))
- file.close()
-
- # 添加功能项
- def __cut(self):
- self.TextArea.event_generate("<
>" ) -
- def __copy(self):
- self.TextArea.event_generate("<
>" ) -
- def __paste(self):
- self.TextArea.event_generate("<
>" ) -
- def run(self):
- # 使用mainloop()使得窗口一直存在
- self.root.mainloop()
-
-
- notepad = Notepad(width=600, height=400)
- notepad.run()
运行效果:
第一种实现代码
- #简单点名系统
- from tkinter import *
- import random
- data = ['杨晓军', '赵 杰', '张大海', '张飞龙', '田志刚', '程 峰',
- '段雅倩', '崔彬彬', '袁飞龙', '王志强', '王翔宇', '夏 文',
- '苏欣悦', '苏欣悦', '顾文博' ]
- going = True
- is_run = False
- def lottery_roll(var1, var2):
- global going
- show_member = random.choice(data)
- var1.set(show_member)
- if going:
- window.after(50, lottery_roll, var1, var2)
- else:
- var2.set('恭喜 {} !!!'.format(show_member))
- going = True
- return
-
- def lottery_start(var1, var2):
- global is_run
- if is_run:
- return
- is_run = True
- var2.set('幸运儿是你吗。。。')
- lottery_roll(var1, var2)
-
- def lottery_end():
- global going, is_run
- if is_run:
- going = False
- is_run = False
-
- if __name__ == '__main__':
- window = Tk()
- window.geometry('405x320+250+150')
- window.title('点名')
- bg_label = Label(window, width=70, height=24, bg='#ECf5FF')
- bg_label.place(anchor=NW, x=0, y=0)
- var1 = StringVar(value='即 将 开 始')
- show_label1 = Label(window, textvariable=var1, justify='left', anchor=CENTER, width=17, height=3, bg='#BFEFFF',
- font='楷体 -40 bold', foreground='black')
- show_label1.place(anchor=NW, x=21, y=20)
- var2 = StringVar(value='幸运儿是你吗。。。')
- show_label2 = Label(window, textvariable=var2, justify='left', anchor=CENTER, width=38, height=3, bg='#ECf5FF',
- font='楷体 -18 bold', foreground='red')
- show_label2.place(anchor=NW, x=21, y=240)
- button1 = Button(window, text='开始', command=lambda: lottery_start(var1, var2), width=14, height=2, bg='#A8A8A8',
- font='宋体 -18 bold')
- button1.place(anchor=NW, x=20, y=175)
- button2 = Button(window, text='结束', command=lambda: lottery_end(), width=14, height=2, bg='#A8A8A8',
- font='宋体 -18 bold')
- button2.place(anchor=NW, x=232, y=175)
-
- window.mainloop()
运行效果:
第二种实现代码
- #点名程序
- import random
- import re
- import time
- import threading
- from tkinter import *
- from tkinter import messagebox
- from tkinter.filedialog import askopenfilename
-
-
- class APP:
- def __init__(self):
- self.root = Tk()
- self.running_flag=False #开始标志
- self.time_span=0.05 #名字显示间隔
- self.root.title('Point_name-V1.0')
- width = 680
- height = 350
- left = (self.root.winfo_screenwidth() - width) / 2
- top = (self.root.winfo_screenheight() - height) / 2
- self.root.geometry("%dx%d+%d+%d" % (width, height, left, top))
- self.root.resizable(0,0)
- self.create_widget()
- self.set_widget()
- self.place_widget()
- self.root.mainloop()
-
-
- def create_widget(self):
- self.label_show_name_var=StringVar()
- self.label_show_name=Label(self.root,textvariable=self.label_show_name_var,font=('Arial', 100,"bold"),foreground = '#1E90FF')
- self.btn_start=Button(self.root,text="开始",)
- self.btn_load_names=Button(self.root,text="加载名单",)
- self.lf1=LabelFrame(self.root,text="方式")
- self.radioBtn_var=IntVar()
- self.radioBtn_var.set(1)
- self.radioBtn_sequence=Radiobutton(self.lf1,text="顺序",variable=self.radioBtn_var, value=1)
- self.radioBtn_random=Radiobutton(self.lf1,text="随机",variable=self.radioBtn_var, value=2)
- self.label_show_name_num=Label(self.root,font=('Arial', 20),foreground = '#FF7F50')
- paned = PanedWindow(self.root)
-
-
- def set_widget(self):
- default_name_="是?"
- self.label_show_name_var.set(default_name_)
- self.label_show_name_adjust(default_name_)
- self.btn_start.config(command=lambda :self.thread_it(self.start_point_name))
- self.btn_load_names.config(command=self.load_names)
- init_names=self.load_names_txt("./names.txt")
- self.root.protocol('WM_DELETE_WINDOW',self.quit_window)
- self.root.bind('
' ,self.quit_window) - if init_names:
- self.default_names=init_names #1.文件存在但是无内容。2.文件不存在
- self.label_show_name_num.config(text=f"一共加载了{len(self.default_names)}个")
- else:
- self.btn_start.config(state=DISABLED)
- self.label_show_name_num.config(text=f"请选定名单!")
-
-
- def place_widget(self):
- self.lf1.place(x=300,y=160,width=250,height=50)
- self.radioBtn_sequence.place(x=20,y=0)
- self.radioBtn_random.place(x=150,y=0)
- self.btn_start.place(x=300,y=220,width=100,height=30)
- self.btn_load_names.place(x=450,y=220,width=100,height=30)
- #self._img.place(x=90, y=165, height=120, width=180)
- self.label_show_name_num.place(x=300,y=260)
-
-
- def label_show_name_adjust(self,the_name):
- if len (the_name)==1:
- self.label_show_name.place(x=280, y=10)
- elif len(the_name) == 2:
- self.label_show_name.place(x=180, y=10)
- elif len(the_name) == 3:
- self.label_show_name.place(x=120, y=10)
- elif len(the_name) == 4:
- self.label_show_name.place(x=80, y=10)
- else:
- self.label_show_name.place(x=0, y=10)
-
-
- def start_point_name(self):
- """
- 启动之前进行判断,获取点名模式
- :return:
- """
- if len(self.default_names)==1:
- messagebox.showinfo("提示",'名单就一个,不用选了!')
- self.label_show_name_var.set(self.default_names[0])
- self.label_show_name_adjust(self.default_names[0])
- return
- if self.btn_start["text"]=="开始":
- self.btn_load_names.config(state=DISABLED)
- self.running_flag=True
- if isinstance(self.default_names,list):
- self.btn_start.config(text="就你了")
- if self.radioBtn_var.get()==1:
- mode="sequence"
- elif self.radioBtn_var.get()==2:
- mode="random"
- else:
- pass
- self.thread_it(self.point_name_begin(mode))
-
-
- else:
- messagebox.showwarning("警告","请先选定名单!")
- else:
- self.running_flag=False
- self.btn_load_names.config(state=NORMAL)
- self.btn_start.config(text="开始")
-
-
- def point_name_begin(self,mode):
- """
- 开始点名主函数
- :param mode:
- :return:
- """
- if mode == "sequence":
- if self.running_flag:
- self.always_ergodic()
- elif mode=="random":
- while True:
- if self.running_flag:
- random_choice_name=random.choice(self.default_names)
- self.label_show_name_var.set(random_choice_name)
- self.label_show_name_adjust(random_choice_name)
- time.sleep(self.time_span)
- else:
- break
-
-
- def always_ergodic(self):
- """
- 一直遍历此列表,使用死循环会造成线程阻塞
- :return:
- """
- for i in self.default_names:
- if self.running_flag:
- self.label_show_name_var.set(i)
- self.label_show_name_adjust(i)
- time.sleep(self.time_span)
- if i==self.default_names[-1]:
- self.always_ergodic()
- else:
- break
-
- def load_names(self):
- """
- 手动加载txt格式人名单
- :return:
- """
- filename = askopenfilename(
- filetypes = [('文本文件', '.TXT'), ],
- title = "选择一个文本文件",
- initialdir="./"
- )
- if filename:
- names=self.load_names_txt(filename)
- if names:
- self.default_names=names
- no_Chinese_name_num=len([n for n in names if not self.load_name_check(n)])
- if no_Chinese_name_num==0:
- pass
- else:
- messagebox.showwarning("请注意",f'导入名单有{no_Chinese_name_num}个不是中文名字')
- self.label_show_name_num.config(text=f"一共加载了{len(self.default_names)}个")
- self.btn_start.config(state=NORMAL)
- else:
- messagebox.showwarning("警告","导入失败,请检查!")
-
-
- def load_names_txt(self,txt_file):
- """
- 读取txt格式的人名单
- :param txt_file:
- :return:
- """
- try:
- with open(txt_file,'r',encoding="utf-8")as f:
- names=[name.strip() for name in f.readlines()]
- if len(names)==0:
- return False
- else:
- return names
- except:
- return False
-
-
- def load_name_check(self,name):
- """
- 对txt文本中的人名进行校验
- 中文汉字->True
- 非中文汉字->False
- :param name:
- :return:
- """
- regex = r'[\u4e00-\u9fa5]+'
- if re.match(regex,name):
- return True
- else:
- return False
-
-
- def thread_it(self,func,*args):
- t=threading.Thread(target=func,args=args)
- #t.setDaemon(True)
- t.start()
-
-
- def quit_window(self,*args):
- """
- 程序退出触发此函数
- :param args:
- :return:
- """
- ret=messagebox.askyesno('退出','确定要退出?')
- if ret:
- self.root.destroy()
-
-
- if __name__ == '__main__':
- a=APP()
人名文本文件如
运行效果:
关于Canvas(画布)可参见https://blog.csdn.net/cnds123/article/details/127344534
画五角星源码如下:
- #使用Tkinter Canvas画五角星
- import tkinter as tk
- import math
-
- root = tk.Tk()
-
- w = tk.Canvas(root, width = 200, height = 100, background = "red")
- w.pack()
-
- center_x = 100
- center_y = 50
- r = 50
-
- points = [
- #左上点(A)
- center_x - int(r * math.sin(2 * math.pi / 5)),
- center_y - int(r * math.cos(2 * math.pi / 5)),
- #右上点(C)
- center_x + int(r * math.sin(2 * math.pi / 5)),
- center_y - int(r * math.cos(2 * math.pi / 5)),
- #左下点(E)
- center_x - int(r * math.sin(math.pi / 5)),
- center_y + int(r * math.cos(math.pi / 5)),
- #顶点(B)
- center_x,
- center_y - r,
- #右下点(D)
- center_x + int(r * math.sin(math.pi / 5)),
- center_y + int(r * math.cos(math.pi / 5))
- ]
-
- w.create_polygon(points, outline = "green", fill = "yellow") #fill 默认是 black
-
- root.mainloop()
运行效果:
说明:
"2 * PI / 5" 是用弧度的方式表示的。
1度的对应的弧长是等于圆周长的360分之一,而1弧度的是等于半径。参见下图:
它们的关系可用下式表示和计算:
角(弧度)=弧长/半径
圆的周长是半径的 2π倍,所以一个周角(360度)是 2π弧度。
一个平角是 π 弧度。
即 180度=π弧度
由此可知:
1度=π/180 弧度 ( ≈0.017453弧度 )
1弧度=180°/π (≈57.3°)
画树源码如下:
- # 使用Tkinter Canvas画树,每次画的不一样
- import tkinter
- import sys, random, math
-
- class Point(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
- def __str__(self):
- return "
: (%f, %f)" % (self.x, self.y) -
- class Branch(object):
- def __init__(self, bottom, top, branches, level = 0):
- self.bottom = bottom
- self.top = top
- self.level = level
- self.branches = branches
- self.children = []
-
- def __str__(self):
- s = "Top: %s, Bottom: %s, Children Count: %d" % \
- (self.top, self.bottom, len(self.children))
- return s
-
- def nextGen(self, n = -1, rnd = 1):
- if n <= 0: n = self.branches
- if rnd == 1:
- n = random.randint(n / 2, n * 2)
- if n <= 0: n = 1
- dx = self.top.x - self.bottom.x
- dy = self.top.y - self.bottom.y
- r = 0.20 + random.random() * 0.2
- if self.top.x == self.bottom.x:
- # 如果是一条竖线
- x = self.top.x
- y = dy * r + self.bottom.y
- elif self.top.y == self.bottom.y:
- # 如果是一条横线
- x = dx * r + self.bottom.x
- y = self.top.y
- else:
- x = dx * r
- y = x * dy / dx
- x += self.bottom.x
- y += self.bottom.y
- oldTop = self.top
- self.top = Point(x, y)
- a = math.pi / (2 * n)
- for i in range(n):
- a2 = -a * (n - 1) / 2 + a * i - math.pi
- a2 *= 0.9 + random.random() * 0.2
- self.children.append(self.mkNewBranch(self.top, oldTop, a2))
-
- def mkNewBranch(self, bottom, top, a):
- dx1 = top.x - bottom.x
- dy1 = top.y - bottom.y
- r = 0.9 + random.random() * 0.2
- c = math.sqrt(dx1 ** 2 + dy1 ** 2) * r
- if dx1 == 0:
- a2 = math.pi / 2
- else:
- a2 = math.atan(dy1 / dx1)
- if (a2 < 0 and bottom.y > top.y) \
- or (a2 > 0 and bottom.y < top.y) \
- :
- a2 += math.pi
- b = a2 - a
- dx2 = c * math.cos(b)
- dy2 = c * math.sin(b)
- newTop = Point(dx2 + bottom.x, dy2 + bottom.y)
- return Branch(bottom, newTop, self.branches, self.level + 1)
-
- class Tree(object):
- def __init__(self, root, canvas, bottom, top, branches = 3, depth = 3):
- self.root = root
- self.canvas = canvas
- self.bottom = bottom
- self.top = top
- self.branches = branches
- self.depth = depth
- self.new()
-
- def gen(self, n = 1):
- for i in range(n):
- self.getLeaves()
- for node in self.leaves:
- node.nextGen()
- self.show()
-
- def new(self):
- self.leavesCount = 0
- self.branch = Branch(self.bottom, self.top, self.branches)
- self.gen(self.depth)
- print("leaves count: %d" % self.leavesCount)
-
- def chgDepth(self, d):
- self.depth += d
- if self.depth < 0: self.depth = 0
- if self.depth > 10: self.depth = 10
- self.new()
-
- def chgBranch(self, d):
- self.branches += d
- if self.branches < 1: self.branches = 1
- if self.branches > 10: self.branches = 10
- self.new()
-
- def getLeaves(self):
- self.leaves = []
- self.map(self.findLeaf)
-
- def findLeaf(self, node):
- if len(node.children) == 0:
- self.leaves.append(node)
-
- def show(self):
- for i in self.canvas.find_all():
- self.canvas.delete(i)
- self.map(self.drawNode)
- self.canvas.tag_raise("leaf")
-
- def exit(self, evt):
- sys.exit(0)
-
- def map(self, func = lambda node: node):
- # 遍历树
- children = [self.branch]
- while len(children) != 0:
- newChildren = []
- for node in children:
- func(node)
- newChildren.extend(node.children)
- children = newChildren
-
- def drawNode(self, node):
- self.line2(
- # self.canvas.create_line(
- node.bottom.x,
- node.bottom.y,
- node.top.x,
- node.top.y,
- fill = "#100",
- width = 1.5 ** (self.depth - node.level),
- tags = "branch level_%d" % node.level,
- )
-
- if len(node.children) == 0:
- # 画叶子
- self.leavesCount += 1
- self.canvas.create_oval(
- node.top.x - 3,
- node.top.y - 3,
- node.top.x + 3,
- node.top.y + 3,
- fill = "#090",
- tag = "leaf",
- )
-
- self.canvas.update()
-
- def line2(self, x0, y0, x1, y1, width = 1, fill = "#000", minDist = 10, tags = ""):
- dots = midDots(x0, y0, x1, y1, minDist)
- dots2 = []
- for i in range(len(dots) - 1):
- dots2.extend([dots[i].x,
- dots[i].y,
- dots[i + 1].x,
- dots[i + 1].y])
- self.canvas.create_line(
- dots2,
- fill = fill,
- width = width,
- smooth = True,
- tags = tags,
- )
-
- def midDots(x0, y0, x1, y1, d):
- dots = []
- dx, dy, r = x1 - x0, y1 - y0, 0
- if dx != 0:
- r = float(dy) / dx
- c = math.sqrt(dx ** 2 + dy ** 2)
- n = int(c / d) + 1
- for i in range(n):
- if dx != 0:
- x = dx * i / n
- y = x * r
- else:
- x = dx
- y = dy * i / n
- if i > 0:
- x += d * (0.5 - random.random()) * 0.25
- y += d * (0.5 - random.random()) * 0.25
- x += x0
- y += y0
- dots.append(Point(x, y))
- dots.append(Point(x1, y1))
- return dots
-
- if __name__ == "__main__":
- root = tkinter.Tk()
- root.title("Tree")
- gw, gh = 800, 600
- canvas = tkinter.Canvas(root,
- width = gw,
- height = gh,
- )
- canvas.pack()
- tree = Tree(root, canvas, Point(gw / 2, gh - 20), Point(gw / 2, gh * 0.2), \
- branches = 2, depth = 8)
- root.bind("n", lambda evt: tree.new())
- root.bind("=", lambda evt: tree.chgDepth(1))
- root.bind("+", lambda evt: tree.chgDepth(1))
- root.bind("-", lambda evt: tree.chgDepth(-1))
- root.bind("b", lambda evt: tree.chgBranch(1))
- root.bind("c", lambda evt: tree.chgBranch(-1))
- root.bind("q", tree.exit)
- root.mainloop()
-
运行效果:
OK!