很多健身软件都有体重档案的功能,用来记录自己过去的体重变化。今天我们也来做一个简单的小程序实现这个功能。

首先我上网找了一个tkinter做的万年历程序,然后在它的基础上,增加了绘制体重折线图、记录当日体重,以及显示身材相册的功能。

效果是这样的:

选择年月后,点击“显示”,就会显示当月日历,并加载当月的体重记录(txt);“记录体重”则是把输入的体重数据记录到txt中的当日位置;身材相册则是在子界面显示图片文件夹里的图片,点击“下一张”可以逐一阅览。

首先是主界面的代码,放在main.py里:

'''main.py'''
from tkinter import *
from tkinter.ttk import *
from show_pic import Apps
import time
import os
import numpy as np
import tkinter.messagebox
from tkinter import filedialog
import matplotlib.pyplot as plt
from PIL import Image, ImageTk#将界面定位在屏幕中央
def center_window(top,w, h):# 获取屏幕 宽、高ws = top.winfo_screenwidth()hs = top.winfo_screenheight()# 计算 x, y 位置x = (ws/2) - (w/2)y = (hs/2) - (h/2)top.geometry('%dx%d+%d+%d' % (w, h, x, y))class App:def __init__(self):self.windos = Tk()self.windos.title("体重档案")center_window(self.windos,450,400)self.lis1 = ["周一", "周二", "周三", "周四", "周五", "周六", "周天"]self.images=""#self.creat_image_lis()self.creat_res()self.windos.mainloop()#显示万年历    def func1(self):self.get_total_days(self.a, self.b)self.print_days(self.a, self.b)#打开子界面def open_image(self):a1=Apps() if self.windos.quit():#如果主程序关闭Apps.windows.quit() #子程序关闭#更新折线图def view_image(self):self.ima=PhotoImage(file=self.images)self.L3.config(image=self.ima)#显示万年历和折线图def go(self,*args):self.T1.delete(0.0,END)try:self.a = int(self.C1.get())self.b = int(self.C2.get())self.func1()if self.b<10:m="0"+self.C2.get()else:m=self.C2.get()path="res/text/"+self.C1.get()+m+".txt"  #体重记录txt的路径,每月一个文件,如202004.txtname=self.C1.get()+m  #年+月,用于文件名if not os.path.exists(path):tkinter.messagebox.showerror("错误", "数据不存在!")return#绘制折线图并保存values = [] square = []data=np.loadtxt(path,delimiter=' ')for i in range(len(data)):try:values.append(data[i][0])square.append(data[i][1])except:tkinter.messagebox.showerror("错误", "只有一天数据!")returnplt.rcParams['font.sans-serif']=['SimHei']plt.plot(values, square, linewidth=5, color='b')  #将列表传递给plot,并设置线宽,设置颜色,默认为蓝色plt.title("%s体重变化曲线"%name, fontsize=24, color='r')  plt.xlabel("日期", fontsize=14, color='g')  plt.ylabel("体重", fontsize=14, color='g')plt.tick_params(axis='both', labelsize=14)  #设置刻度标记的大小plt.savefig('res/plt/%s.png'%name,dpi=47, bbox_inches='tight')   plt.close('all')  #一定是all,否则连tkinter也会关闭;不关闭会接着上次的图继续绘制 #更新图片       self.images='res/plt/%s.png'%nameself.view_image()except Exception:tkinter.messagebox.showerror("错误", "请选择年份和月份!")#记录体重,以日期+空格+体重的形式逐行写入txtdef wdown(self):if self.E1.get()=='':tkinter.messagebox.showerror("错误", "请输入体重!")returnd=time.strftime("%d")m=time.strftime("%m")y=time.strftime("%Y")   date=y+"/"+m+"/"+d     path="res/text/"+y+m+".txt"if os.path.exists(path):data=np.loadtxt(path,delimiter=' ')#文档中已有当日记录,则覆盖for i in range(len(data)):                 try:if int(d) == int(data[i][0]):data[i][1]=int(self.E1.get()) doc=open(path,"w",encoding='utf8') for j in range(len(data)):print("%s %s"%(int(data[j][0]),data[j][1]),file=doc)tkinter.messagebox.showinfo(title = '成功!',message='%s数据已记录!'%date)return     except:if int(d) == int(data[0]):  #如果txt中只有一行数据,data会变成一维列表data[1]=int(self.E1.get()) doc=open(path,"w",encoding='utf8') print("%s %s"%(int(data[0]),data[1]),file=doc)tkinter.messagebox.showinfo(title = '成功!',message='%s数据已记录!'%date)return #不存在当日记录则追加txt = d + " "+self.E1.get()+".0"with open(path,"a") as doc:      doc.write("n"+txt)doc.close()        tkinter.messagebox.showinfo(title = '成功!',message='%s数据已记录!'%date)#初始化tkinter组件def creat_res(self):self.L1=Label(self.windos,text="年份:")self.L2=Label(self.windos,text="月份:")self.L3=Label(self.windos)self.T1=Text(self.windos)self.T1.place(x=10, y=10, width=270, height=150)self.B1 = Button(self.windos, text="显示", command=self.go)self.B1.place(x=300, y=80)self.B2 = Button(self.windos, text="身材相册", command=self.open_image)self.B2.place(x=300, y=210)self.E1=Entry(self.windos,textvariable="",font="宋体 14")self.E1.place(x=300, y=130,width=90,height=20)self.B3=Button(self.windos,text="记录体重",command=self.wdown)self.B3.place(x=300, y=160)self.temp1 = StringVar()self.temp2 = StringVar()self.C1=Combobox(self.windos,values=[x for x in range(2020,2100)])  #年份的起始在这设置self.C2=Combobox(self.windos,values=[x for x in range(1,13)])self.C1.place(x=300, y=30, width=60, height=30)self.C2.place(x=375, y=30, width=50, height=30)self.L1.place(x=300, y=0, width=70, height=30)self.L2.place(x=370, y=0, width=50, height=30)self.L3.place(x=10, y=170, width=280, height=230)#从这开始是万年历的绘制与显示def leap_year(self,a):if a % 4 == 0 and a % 100 != 0 or a % 400 == 0:return Trueelse:return Falsedef year_days(self,a,b):if b in (1,3,5,8,10,12):return 31elif b in (4,6,9,11):return 30else:if self.leap_year(self.a)==True:return 29else:return 28def get_total_days(self,a,b):total_days=0for m in range(2020,self.a):  #如果改变了起始年份,这里也要改if self.leap_year(m)==True:total_days+=366else:total_days+=365for i in range(1,self.b):total_days+=self.year_days(self.a,i)return total_daysdef get_week(self,a,b):return self.get_total_days(self.a,self.b)%7+1def print_days(self,a,b):self.T1.insert(END,"%s年 %s月"%(self.a,self.b)+"n")self.T1.insert(END," 周一 周二 周三 周四 周五 周六 周天"+"n")self.i=self.get_week(self.a,self.b)s=0lis1=[x for x in range(1,self.year_days(self.a,self.b)+1)]for m in range(1,self.i):lis1.insert(0,"  ")for i in lis1:self.T1.insert(END,"%03s"%i+"  ")s+=1if s%7==0:self.T1.insert(END,"n")if __name__ == '__main__':ap=App()

身材相册的实现,则调用了show_pic文件中的Apps类:

'''show_pic.py'''
from  tkinter import *
import random
from PIL import Image, ImageTk
import glob
from PIL import Image
import tkinter.messagebox
import cv2
import numpy as npclass Apps:def __init__(self):self.windows = Toplevel() #子界面必须是Toplevel,否则无法显示图片self.windows.title("身材相册")self.windows.geometry('+%d+%d' % (0, 0))  #只设置了子界面的位置,没有设置大小,会随照片而变化self.count=0self.creat_res()self.windows.mainloop()#初始化子界面组件并显示第一张图片  def creat_res(self):self.temp=StringVar()picp = glob.glob("res/pic/*.*")  #照片路径pic=picp[self.count]load = Image.open(pic)#为了获取照片的长和宽,用opencv重新读取了一下,Image.open打开的图片貌似没有这两个属性img=cv2.imdecode(np.fromfile(pic,dtype=np.uint8),-1)  #如果有中文名称,不能直接用cv2.imread#根据图片大小进行放大和缩小,以实现较好的视觉效果if 200<img.shape[1]<500 and 200<img.shape[0]<500:passelif img.shape[1]<200 and img.shape[0]<200:load = load.resize((int(img.shape[1]*3), int(img.shape[0]*3)),Image.ANTIALIAS)else:load = load.resize((int(img.shape[1]/2), int(img.shape[0]/2)),Image.ANTIALIAS)render = ImageTk.PhotoImage(load)#初始化label的图片为相册第一张self.L2=Label(self.windows,image=render)self.L2.grid(sticky='nw')#绑定鼠标事件进行翻页self.L2.bind('<Button-3>', self.next)  #右键下一张self.L2.bind('<Button-1>', self.pre)  #左键上一张self.windows.mainloop()#下一张def next(self,aa):self.count+=1  #用来给照片计数,到达最后一张照片后清零,从头播放try:picp = glob.glob("res/pic/*.*")pic=picp[self.count]   load = Image.open(pic)img=cv2.imdecode(np.fromfile(pic,dtype=np.uint8),-1)      if 200<img.shape[1]<500 and 200<img.shape[0]<500:passelif img.shape[1]<200 and img.shape[0]<200:load = load.resize((int(img.shape[1]*3), int(img.shape[0]*3)),Image.ANTIALIAS)else:load = load.resize((int(img.shape[1]/2), int(img.shape[0]/2)),Image.ANTIALIAS)render = ImageTk.PhotoImage(load)  self.L2.config(image=render)self.windows.mainloop()except:self.count=0picp = glob.glob("res/pic/*.*")pic=picp[self.count]     load = Image.open(pic)img=cv2.imdecode(np.fromfile(pic,dtype=np.uint8),-1)       if 200<img.shape[1]<500 and 200<img.shape[0]<500:passelif img.shape[1]<200 and img.shape[0]<200:load = load.resize((int(img.shape[1]*3), int(img.shape[0]*3)),Image.ANTIALIAS)else:load = load.resize((int(img.shape[1]/2), int(img.shape[0]/2)),Image.ANTIALIAS)render = ImageTk.PhotoImage(load) self.L2.config(image=render)self.windows.mainloop()    #上一张def pre(self,aa):self.count-=1  try:picp = glob.glob("res/pic/*.*")pic=picp[self.count]    load = Image.open(pic)img=cv2.imdecode(np.fromfile(pic,dtype=np.uint8),-1)      if 200<img.shape[1]<500 and 200<img.shape[0]<500:passelif img.shape[1]<200 and img.shape[0]<200:load = load.resize((int(img.shape[1]*3), int(img.shape[0]*3)),Image.ANTIALIAS)else:load = load.resize((int(img.shape[1]/2), int(img.shape[0]/2)),Image.ANTIALIAS)render = ImageTk.PhotoImage(load)  self.L2.config(image=render)self.windows.mainloop()except:picp = glob.glob("res/pic/*.*")self.count=len(picp)-1pic=picp[self.count]   load = Image.open(pic)img=cv2.imdecode(np.fromfile(pic,dtype=np.uint8),-1)       if 200<img.shape[1]<500 and 200<img.shape[0]<500:passelif img.shape[1]<200 and img.shape[0]<200:load = load.resize((int(img.shape[1]*3), int(img.shape[0]*3)),Image.ANTIALIAS)else:load = load.resize((int(img.shape[1]/2), int(img.shape[0]/2)),Image.ANTIALIAS)render = ImageTk.PhotoImage(load) self.L2.config(image=render)self.windows.mainloop()               

通过这两个文件,就能实现简单的体重档案程序了。不过运行之前要先建好对应的文件夹,并且放在指定路径下(txt则会在记录时自动创建)。

目前就写了这些功能,日后还会继续增加。比如加入对身材相册的管理功能,现在暂时只能查看照片,存储照片的话就要自己拷贝到对应的文件夹下面了。

注意事项:

Apps类中的两个照片翻页的函数,多了一个无用的参数aa,这个参数必须要有,否则在绑定鼠标时间的时候会报错(绑定鼠标事件时,被绑定的函数会自动传回一个参数,再加上原本的self,因此必须在定义时具有有两个形参)。

tkinter中text插入_tkinter做一个体重档案相关推荐

  1. tkinter中text属性_tkinter属性(总结)

    一.主要控件 1.Button 按钮.类似标签,但提供额外的功能,例如鼠标掠过.按下.释放以及键盘操作事件 2.Canvas 画布.提供绘图功能(直线.椭圆.多边形.矩形) 可以包含图形或位图 3.C ...

  2. 怎么用线程刷新 tkinter 进度条_tkinter做一个简易提词板(2)

    书接上文 花果山美男子:tkinter做一个简易提词板​zhuanlan.zhihu.com 上回说到,文字的动态效果还可以用after方法和StringVar来实现,今天我们就用它们来重新制作提词板 ...

  3. html中使用静态图片做一个csdn网站的首页

    简单使用csdn网站的截图做一个网站的DEMO, 简单把CSDN网站分为四个图片(自己截图),分别为上,左,中,右,使用IDEA创建一个静态Web项目. 代码演示:(不添加样式,简单演示效果) < ...

  4. python在tk界面播放本地视频_tkinter做一个本地视频播放器(2)——弹幕

    前文我们已经完成了一个集暂停.倍速.显示进度条功能为一体的视频播放器,今天我们再来增加一个新的功能--发送弹幕. tkinter播放视频的原理,就是读取每一帧的图片,然后刷新画布.所以如果想实现弹幕功 ...

  5. php如何对 mysql 中text类型拆分存入一个数组_PHP递归实现无限级分类,可选返回字符串和数组...

    正 文: 在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类. 递归,简单的说就是 ...

  6. php如何对 mysql 中text类型拆分存入一个数组_PHP、Mysql笔试题

    PHP&MySQL 笔试题 一.选择题 1. php代表什么意思?() A.Hypertext Preprocessor(超文本预处理器) B.Hyperlink Preprocessor(超 ...

  7. tkinter中text属性_python tkinter基本属性详解

    1.外形尺寸 尺寸单位:只用默认的像素或者其他字符类的值!,不要用英寸毫米之类的内容. btn = tkinter.Button(root,text = "按钮")# 设置按钮尺寸 ...

  8. 用HTML中的列表标签做一个导航栏吧

    我们在网上浏览的好多网页都有导航栏,它提供信息导航的功能,想知道它是怎么做出来的吗? 首先要知道的:HTML中的列表标签都有那些呢? ul-li无序列表 ( 网页中显示的默认样式一般为:每项 li 前 ...

  9. Vue项目中链接websocket,做一个全局通知信息

    首先在index.html中加上下面一段代码 <script>var websocket = nullvar localHref = window.location.host === 'l ...

最新文章

  1. JS事件流和事件委托
  2. 微信公众号点击菜单即可打开并登录微站的实现方法
  3. 解决递归中的重复计算问题
  4. KFold、StratifiedKFold、GroupKFold的区别
  5. 新疆大学ACM新生赛(公开赛)
  6. Java中的事务——JDBC事务和JTA事务
  7. 141.Linked List Cycle
  8. 从Docker在Linux和Windows下的区别简单理解Docker的层次结构
  9. busybox源码剖析(1)---whoami.c
  10. CentOS 7安装 MySQL 8 数据库
  11. html相册魔方代码,魔方相册制作方法现成的魔方相册代码:
  12. 数据预处理——以GSE3494为例
  13. Kotlin知识积累——let,with,run,apply,alse的用法
  14. 【win10】设置电脑固定IP,解除固定IP
  15. 计算机组成原理rs rd,计算机组成原理五章.ppt
  16. Vue3.0的新特性(8)Suspense
  17. linux 文件系统损坏修复方式
  18. winform程序内存不足或假死的问题
  19. 腾讯区块链的三年与它的打法
  20. iOS最新吊炸天的资源

热门文章

  1. mysql sql能力_MySQL SQL优化
  2. iOS15.4来袭:新增“男妈妈”表情及口罩面容解锁、AirTags反跟踪等新功能
  3. 开源影响发量?这届开源中的巾帼力量
  4. 英伟达代码签名证书遭窃取?三星也未能幸免,泄露多达190GB文件
  5. 7 个不容错过的 VS Code 扩展
  6. 测网速还能拿奖励?测速 App 的新玩法
  7. 边缘计算崛起!施耐德联手华胜天成打造胶囊数据中心,真正端到端交付
  8. 招人了!MySQL 面试必须掌握的 8 个知识点!
  9. 声网 Agora Share:从天到秒级效率提升,一切都为服务好 15 万+应用
  10. 遇到这 4 个迹象,赶紧下来、让人工智能上!