继续练习Tk,本次使用Tk制作一款壁纸下载工具,支持分类查看以及定向搜索查看壁纸,分类高达15个,让我们开始吧~
一.准备工作
python Tkinter
二.预览
1. 主界面
主界面打开后会展示十几个分类,点击分类就能进入分类
2. 壁纸检索
点击检索之后,会展示检索结果
3. 壁纸查看、下载
点击图片后会弹出新窗口,此时可以下载图片到本地
三.基本思路
四.源代码(Tk)
#-*-coding:utf-8-*-
import io
import re
import os
import threading
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
from tkinter import messagebox
from wallPaper_Spider import WallPaper_Spider
class APP:
def __init__(self):
self.root=Tk()
self.root.title('WallPpaer_downloader-V1.0')
width=720
height=560
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.click_num=0
self.search_page=0
self.search_click_num=0
self.recommand_click_num=0
self.all_cates = WallPaper_Spider().get_cates()
self.create_widget()
self.set_widget()
self.create_home_cate_page()
self.place_widget()
self.root.mainloop()
def create_widget(self):
self.note = ttk.Notebook()
self.f1 = Frame()
self.note.add(self.f1, text='分类')
self.f2 = Frame()
self.note.add(self.f2, text='检索')
self.f2_l0 = ttk.Label(self.f2, text="关键字:",justify="center")
self.f2_e1_var = StringVar()
self.f2_e1 = ttk.Entry(self.f2, textvariable=self.f2_e1_var,width=45,justify="center")
self.f2_btn1 = ttk.Button(self.f2, text='搜索', command=lambda :self.thread_it(self.do_search),width=10)
self.m=Menu(self.root)
self.root['menu']=self.m
self.s1=Menu(self.m,tearoff=False)
self.s2=Menu(self.m,tearoff=False)
def create_home_cate_page(self):
"""
点击分类后触发
:return:
"""
self.f1_btn1=ttk.Button(self.f1,text="返回分类",command=lambda :self.thread_it(self.open_home_cate_page))
self.f1_btn2=ttk.Button(self.f1,text="上一页",command=lambda :self.thread_it(self.do_turn_up_page))
self.f1_btn3=ttk.Button(self.f1,text="下一页",command=lambda :self.thread_it(self.do_turn_down_page))
for i in range(15):
exec("self.f1_l{}=ttk.Label(self.f1,background='lightblue')".format(i+1)) #exec()确实比eval强大!
exec("self.f1_paned{}=PanedWindow(self.f1)".format(i+1))
self.load_cates()
for i in range(15):
exec("self.f1_l{}.bind('<Button-1>',self.open_the_cate_window)".format(i+1))
for i in range(5):
exec("self.f1_l{}.place(x={},y=0)".format(i+1,i*120))
for i in range(5):
exec("self.f1_l{}.place(x={},y=170)".format(i+6,i*120))
for i in range(5):
eval("self.f1_l{}.place(x={},y=340)".format(i+11,i*120))
def set_widget(self):
self.note.enable_traversal()
self.m.add_cascade(menu=self.s1,label="开始")
self.m.add_cascade(menu=self.s2,label="关于")
self.s1.add_command(label="打开壁纸所在文件夹",command=self.open_wallpaper_dir)
self.s1.add_command(label="退出")
self.s2.add_command(label="关于作者",command=lambda :messagebox.showinfo("关于作者",'作者:懷淰メ'))
self.f2_e1.bind('<Return>',self.do_search)
self.note.bind('<<NotebookTabChanged>>', self.get_note_curr_index)
self.root.protocol('WM_DELETE_WINDOW',self.quit)
self.root.bind("<Escape>",self.quit)
def place_widget(self):
self.note.place(x=0,y=0,width=720,height=560)
self.f2_l0.place(x=10,y=10)
self.f2_e1.place(x=80,y=10)
self.f2_btn1.place(x=445,y=10)
def open_wallpaper_dir(self):
if self.tab_index==0:
os.startfile(os.path.abspath('./MyWallPaper/cates'))
elif self.tab_index==1:
os.startfile(os.path.abspath('./MyWallPaper/search'))
else:
pass
def get_note_curr_index(self,event):
#当前切换的组件
tab_id = self.note.select()
#当前notebok的所在索引
self.tab_index = self.note.index(tab_id)
#当前tab的text
# tab_name = self.note.tab(tab_index, "text")
def open_home_cate_page(self):
"""
返回分类 按钮触发事件
:return:
"""
self.f1_btn1.place_forget()
self.f1_btn2.place_forget()
self.f1_btn3.place_forget()
self.click_num+=1#记录被单击次数
self.create_home_cate_page()
def load_cates(self):
"""
启动后加载分类
:return:
"""
for index,cate in enumerate(self.all_cates):
image_bytes=WallPaper_Spider().get_pic_bytes(cate['cate_cover'])
data_stream = io.BytesIO(image_bytes)
pil_image2 = Image.open(data_stream)
photo = pil_image2.resize((120, 170))
exec("self.f1_paned{}.image=ImageTk.PhotoImage(photo)".format(index+1))
exec("self.f1_l{}.config(image=self.f1_paned{}.image)".format(index+1,index+1))
exec("self.f1_l{}.config( compound='bottom',text='{}')".format(index+1,cate['cate_name']))#compound设置标签所在图片位置
def open_the_cate_window(self,event):
"""
打开分类窗口
:param event:
:return:
"""
self.page_num=0
self.f1_btn1.place(x=620,y=100)
self.f1_btn2.place(x=620,y=180)
self.f1_btn3.place(x=620,y=260)
for i in range(15):#取消单击绑定事件
exec("self.f1_l{}.unbind('<Button-1>')".format(i+1))
widget=re.sub(r'.!frame.!label','',str(event.widget))
index=self.get_index_by_widget(widget)
self.cate_id=self.all_cates[index].get('cate_id')
self.cate_infos=WallPaper_Spider().get_pics_by_cate(self.cate_id,page=1)
for index_,cate in enumerate(self.cate_infos):
image_bytes=WallPaper_Spider().get_pic_bytes(cate['thumb'])
data_stream = io.BytesIO(image_bytes)
pil_image2 = Image.open(data_stream)
photo = pil_image2.resize((120, 170))
exec("self.f1_paned{}.image=ImageTk.PhotoImage(photo)".format(index_+1))
exec("self.f1_l{}.config(image=self.f1_paned{}.image,text='',compound='none')".format(index_+1,index_+1))
if index_>13:
break
for i in range(15):
exec("self.f1_l{}.bind('<Button-1>',self.do_magnify_photo)".format(i + 1))
def change_infos(self):
"""
加载分类图片信息更新
:return:
"""
for i in range(15):#取消单击绑定事件
exec("self.f1_l{}.config(image=None)".format(i+1))
exec("self.f1_l{}.unbind('<Button-1>')".format(i+1))
for index,cate in enumerate(self.cate_infos):
image_bytes=WallPaper_Spider().get_pic_bytes(cate['thumb'])
data_stream = io.BytesIO(image_bytes)
pil_image2 = Image.open(data_stream)
photo = pil_image2.resize((120, 170))
exec("self.f1_paned{}.image=ImageTk.PhotoImage(photo)".format(index+1))
exec("self.f1_l{}.config(image=self.f1_paned{}.image,text='',compound='none')".format(index+1,index+1))
if index>13:
break
for i in range(15):
exec("self.f1_l{}.bind('<Button-1>',self.do_magnify_photo)".format(i + 1))
def do_turn_down_page(self):
"""
下一页
:return:
"""
self.page_num+=1
if self.page_num==1:
self.page_num+=2
self.cate_infos=WallPaper_Spider().get_pics_by_cate(self.cate_id,self.page_num)
self.change_infos()
def do_turn_up_page(self):
"""
上一页
:return:
"""
self.page_num-=1
if self.page_num<0:
messagebox.showwarning("警告",'当前已经在第一页!')
else:
self.cate_infos=WallPaper_Spider().get_pics_by_cate(self.cate_id,self.page_num)
self.change_infos()
def do_magnify_photo(self,event):
widget=re.sub(r'.!frame.!label','',str(event.widget))
self.cate_curr_index=self.get_index_by_widget(widget)
self.show_magnify_photo(self.cate_curr_index,self.cate_infos,type='cates')
def show_magnify_photo(self,index,data,type):
"""
点击放大查看窗口
:param event:
:return:
"""
maginfy_photo_link=data[index]['thumb']
self.magnify_window=Toplevel()
image_bytes = WallPaper_Spider().get_pic_bytes(maginfy_photo_link)
data_stream = io.BytesIO(image_bytes)
pil_image = Image.open(data_stream)
w, h = pil_image.size
screen_width=self.magnify_window.winfo_screenwidth()
screen_height=self.magnify_window.winfo_screenheight()
left=(screen_width-w)/2
top=(screen_height-h)/2
self.magnify_window.geometry('%dx%d+%d+%d'%(w+110,h,left,top))
fname = maginfy_photo_link.split('/')[-1]
sf = "{} ({}x{})".format(fname, w, h)
self.magnify_window.title(sf)
tk_image = ImageTk.PhotoImage(pil_image)
label = Label(self.magnify_window, image=tk_image,)
label.pack(padx=5, pady=5,side=LEFT)
maginfy_photo_link=data[index]['img']
dir=f'./MyWallPaper/{
type}/'
try:
os.makedirs(dir)
except:
pass
filename=dir+'{}.jpg'.format(maginfy_photo_link.split('/')[3].split('?')[0])
download_btn=ttk.Button(self.magnify_window,text="下载",command=lambda :self.thread_it(self.do_download_pic,filename,maginfy_photo_link))
download_btn.pack(side=LEFT)
self.magnify_window.mainloop()
def get_index_by_widget(self,widget)->int:
#计算所选索引
if self.click_num==0:
if widget=='':
index=0
else:
index=int(int(widget) - (self.click_num*15) - 1)
else:
index = int(int(widget) - (self.click_num*15) - 1)
return index
def get_index_by_search_widget(self,widget)->int:
#计算所选索引
if self.search_click_num==1:
if widget=="2":
index=0
else:
index=int(int(widget) - (self.search_click_num*30) - 2)
else:
index = int(int(widget) - (self.search_click_num*30) - 2)
return index
def do_download_pic(self,filename,link):
"""
下载壁纸图片
:param filename: 完整路径+文件名
:param link:下载地址
:return:
"""
if WallPaper_Spider().download_pic(filename, link):
messagebox.showinfo("提示",'{}下载成功!'.format(filename.split('/')[-1]))
else:
messagebox.showerror("错误",'图片下载失败!')
self.magnify_window.destroy()
def show_search_result(self):
self.search_result = WallPaper_Spider().search_wall_paper(self.keyword,self.search_page)
self.search_total = self.search_result[0].get('total')
self.max_search_page=int(self.search_total/30)
if self.search_total != 0:
for i in range(30):
exec("self.f2_l{}=ttk.Label(self.f2,background='lightblue',text='正在加载...')".format(i + 1))
exec("self.f2_paned{}=PanedWindow(self.f2)".format(i + 1))
exec("self.f2_l{}.config(image=None)".format(i + 1))
exec("self.f2_l{}.unbind('<Button-1>')".format(i + 1))
for index, data in enumerate(self.search_result[1]):
image_bytes = WallPaper_Spider().get_pic_bytes(data['thumb'])
data_stream = io.BytesIO(image_bytes)
pil_image2 = Image.open(data_stream)
photo = pil_image2.resize((100, 120))
exec("self.f2_paned{}.image=ImageTk.PhotoImage(photo)".format(index + 1))
exec("self.f2_l{}.config(image=self.f2_paned{}.image,text='',compound='none')".format(index + 1,index + 1))
exec("self.f2_l{}.bind('<Button-1>',self.do_magnify_search_photo)".format(index + 1))
if self.search_total < 30:
self.f2_btn2.config(state=DISABLED)
self.f2_btn3.config(state=DISABLED)
for i in range(self.search_total, 30):
exec("self.f2_l{}.config(text='无预览',image=None)".format(i + 1, i + 1))
else:
self.f2_btn2.config(state=NORMAL)
self.f2_btn3.config(state=NORMAL)
for i in range(6):
exec("self.f2_l{}.place(x={},y=40)".format(i + 1, i * 100))
for i in range(6):
exec("self.f2_l{}.place(x={},y=167)".format(i + 7, i * 100))
for i in range(6):
eval("self.f2_l{}.place(x={},y=294)".format(i + 13, i * 100))
for i in range(6):
eval("self.f2_l{}.place(x={},y=421)".format(i + 19, i * 100))
for i in range(6):
eval("self.f2_l{}.place(x={},y=548)".format(i + 25, i * 100))
self.f2_btn2.place(x=620, y=180)
self.f2_btn3.place(x=620, y=260)
else:
messagebox.showinfo("提示",f'很抱歉,没有检索到关于[{
self.keyword}]的壁纸图片!')
def do_magnify_search_photo(self,event):
widget=re.sub(r'.!frame2.!label','',str(event.widget))
self.search_curr_index=self.get_index_by_search_widget(widget)
self.show_magnify_photo(self.search_curr_index,self.search_result[1],type='search')
def do_search(self,*args):
self.f2_btn2 = ttk.Button(self.f2, text='上一页', command=lambda :self.thread_it(self.do_turn_up_search_page))
self.f2_btn3 = ttk.Button(self.f2, text='下一页', command=lambda :self.thread_it(self.do_turn_down_search_page))
self.keyword=self.f2_e1.get()
if self.keyword!="":
self.search_click_num += 1
self.thread_it(self.show_search_result())
else:
messagebox.showwarning("警告",'请输入关键字!')
self.f2_e1.focus()
def do_turn_down_search_page(self):
"""
下一页
:return:
"""
if self.max_search_page<=1:
messagebox.showwarning("警告",'当前已经在第一页!')
else:
self.search_page+=1
if self.search_page==1:
self.search_page += 1
self.search_click_num += 1
self.thread_it(self.show_search_result())
def do_turn_up_search_page(self):
"""
上一页
:return:
"""
if self.max_search_page<=1:
messagebox.showwarning("警告", '当前已经在第一页!')
else:
self.search_page-=1
if self.search_page<0:
self.f2_btn2.config(state=DISABLED)
else:
self.search_click_num+=1
self.thread_it(self.show_search_result())
def quit(self,*args):
ret=messagebox.askyesno('退出','确认要退出?')
if ret :
self.root.destroy()
else:
pass
def thread_it(self,func,*args):
t=threading.Thread(target=func,args=args)
t.setDaemon(True)
t.start()
if __name__ == '__main__':
a=APP()
五.总结
本次使用Tk制作一款壁纸下载工具,在写代码过程中也遇到了一点问题,比如如何声明动态变量,如何将一个功能封装好,以供多次调用。
动态变量声明我参考了
程序打包好放在了蓝奏云,思路、代码方面有什么不足欢迎各位大佬指正、批评!