界面图展示(包含菜单,滚动条,界面)
主要功能
1.本文只实现了界面展示功能(python.thinker),未添加具体下载歌曲的逻辑实现。
2.通过treeview画歌曲表格
3.选择需要下载歌曲后,点击下载按钮,通过self.list_val显示下载成功结果
4.解决bug:点击按钮,后台运行耗时事件导致界面卡死问题
实现python两个类之间的相互调用
music_dl类
info类
music_dl类调用info.set_list_val(result)
info类调用music_dl.init(),
music_dl.downloadSong(list_song, self.musiclist,self)//self是类info本身
music.dll调用info类的set_list_var方法
info类调用music.dll:
import tkinter as tk
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
from PIL import ImageTk, Image
import music_dl
import threading
class info():
def __init__(self, ):
self.root = Tk()
self.orm = {}
self.list_val=StringVar()
# 背景
image2 = Image.open(r'D:/音效/紫蓝色.gif')
background_image = ImageTk.PhotoImage(image2)
w = background_image.width()
h = background_image.height()
# app.geometry('%dx%d+0+0' % (w, h))
background_label = tk.Label(self.root, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
self.root.geometry('900x580')
# 调用方法会禁止根窗体改变大小
self.root.resizable(False, False)
self.set_song_id = set()
#歌曲搜索初始化
try:
music_dl.init()
except (EOFError, KeyboardInterrupt):
sys.exit(0)
except:
pass
finally:
pass
def runfunction(self,):
# 画搜索框
self.create_search()
# 画表格
self.create_heading()
self.create_tv()
self.create_download()
self.root.mainloop()
#线程,解决点击下载后:后台响应时间慢导致界面卡顿问题
def thread_it(self,func, *args):
'''将函数打包进线程'''
# 创建
t = threading.Thread(target=func, args=args)
# 守护 !!!
t.setDaemon(True)
# 启动
t.start()
# 阻塞--卡死界面!
# t.join()
def down_loadsong(self, ):
# song_result = ""
# for item in self.set_song_id:
# music_dl.downloadSong(item,self.musiclist)
# # song_result += item + " "
list_song=list(self.set_song_id)
music_dl.downloadSong(list_song, self.musiclist,self)
self.list_val.set(("歌曲正在下载。。。"))
def create_download(self):
download_frame = tk.Frame(self.root, bg="#000080")
download_frame.pack(fill=X,side=BOTTOM)
self.list_val = StringVar()
self.lb_resultsong = tk.Label(download_frame, textvariable=self.list_val,font=("楷体", 12), fg="#FF0000", bg="#000080")
self.lb_resultsong.pack()
self.download_bt=tk.Button(download_frame, text="下载", font=("楷体", 12), fg="#FFFFFF",command=self.down_loadsong, bg="#000080")
self.download_bt.pack()
def create_heading(self):
'''重新做一个treeview的头,不然滚动滚动条,看不到原先的头!!!'''
heading_frame = tk.Frame(self.root, bg="#000080")
heading_frame.pack(fill=X)
# 填充用
button_frame = tk.Label(heading_frame, width=1, bg="#000080")
button_frame.pack(side=LEFT, )
# 全选按钮
self.all_buttonvar = IntVar()
self.all_button = tk.Checkbutton(heading_frame, text='', variable=self.all_buttonvar, command=self.select_all,bg="#000080")
self.all_button.pack(side=LEFT)
self.all_buttonvar.set(0)
self.columns = ['序号', '歌名', '歌手', '大小', '时长', '专辑', '来源']
self.widths = [100, 100, 100, 100, 100, 100, 100]
# 重建tree的头
for i in range(len(self.columns)):
tk.Label(heading_frame, text=self.columns[i], width=int(self.widths[i] * 0.14), anchor='center',
# width表头宽度
relief=GROOVE, font=("楷体", 12), fg="#FFFFFF",bg="#000080").pack(side=LEFT)
def create_tv(self):
# 放置 canvas、滚动条的frame
canvas_frame = tk.Frame(self.root, width=880, height=500,bg="#000080")
canvas_frame.pack(fill=X)
# 只剩Canvas可以放置treeview和按钮,并且跟滚动条配合
self.canvas = Canvas(canvas_frame, width=680, height=400,bg="#000080", scrollregion=(0, 0, 680, 600))
self.canvas.pack(side=LEFT, fill=BOTH, expand=1)
# 滚动条
ysb = tk.Scrollbar(canvas_frame, orient=VERTICAL, command=self.canvas.yview,bg="#000080")
self.canvas.configure(yscrollcommand=ysb.set)
ysb.pack(side=RIGHT, fill=Y)
# !!!!=======重点:鼠标滚轮滚动时,改变的页面是canvas 而不是treeview
self.canvas.bind_all("<MouseWheel>",
lambda event: self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units"))
# 想要滚动条起效,得在canvas创建一个windows(frame)!!
tv_frame = tk.Frame(self.canvas,bg="#000080")
self.tv_frame = self.canvas.create_window(0, 0, window=tv_frame, anchor='nw', width=870, # width表的宽度
height=590) # anchor该窗口在左上方
# 放置button的frame
self.button_frame = tk.Frame(tv_frame,bg="#000080")
self.button_frame.pack(side=LEFT, fill=Y)
tk.Label(self.button_frame, width=3,bg="#000080").pack() # 填充用
# 创建treeview
self.tv = Treeview(tv_frame, height=10, columns=self.columns, show='headings') # height好像设定不了行数,实际由插入的行数决定
self.tv.pack(expand=1, side=LEFT, fill=BOTH)
# 设定每一列的属性
for i in range(len(self.columns)):
self.tv.column(self.columns[i], width=0, minwidth=self.widths[i], anchor='center', stretch=True)
# 设定treeview格式
# import tkinter.font as tkFont
# ft = tkFont.Font(family='Fixdsys', size=20, weight=tkFont.BOLD)
self.tv.tag_configure('oddrow', font=("楷体", 12),background='#000080',foreground="#DAA520") # 设定treeview里字体格式font=ft
self.tv.tag_configure('select', background='#00FFFF', font=("楷体", 12),foreground="#DAA520") # SkyBlue当对应的按钮被打勾,那么对于的行背景颜色改变!
self.rowheight = 25 # 很蛋疼,好像tkinter里只能用整数!
Style().configure('Treeview', rowheight=self.rowheight) # 设定每一行的高度
# 设定选中的每一行字体颜色、背景颜色 (被选中时,没有变化)
Style().map("Treeview",
foreground=[('focus', '#DAA520'), ],
background=[('active', '#DAA520')]
)
self.tv.bind('<<TreeviewSelect>>', self.select_tree) # 绑定tree选中时的回调函数
def insert_tv(self):
self.list_val.set(("歌曲搜索中。。。"))
# 清空tree、checkbutton
self.set_song_id = set()
items = self.tv.get_children()
[self.tv.delete(item) for item in items]
self.tv.update()
for child in self.button_frame.winfo_children()[1:]: # 第一个构件是label,所以忽略
child.destroy()
#调用歌曲搜索接口
self.musiclist = music_dl.searchSong(self.searchEntry.get())
# 重设tree、button对应关系
self.orm = {}
# print(self.musiclist)
for index ,music in enumerate(self.musiclist):
music.idx = index
tv_item = self.tv.insert('',"end" , value=[index,self.musiclist[index].title,
self.musiclist[index].singer, self.musiclist[index].size,
self.musiclist[index].duration, self.musiclist[index].album,self.musiclist[index].source], tags=('oddrow')) # item默认状态tags
import tkinter
ck_button = tkinter.Checkbutton(self.button_frame, variable=IntVar(),bg="#000080")
ck_button['command'] = lambda item=tv_item: self.select_button(item)
ck_button.pack()
self.orm[tv_item] = [ck_button]
# 每次点击插入tree,先设定全选按钮不打勾,接着打勾并且调用其函数
self.all_buttonvar.set(0)
# self.all_button.invoke()
# 更新canvas的高度
height = (len(self.tv.get_children()) + 1) * self.rowheight # treeview实际高度
self.canvas.itemconfigure(self.tv_frame, height=height) # 设定窗口tv_frame的高度
self.root.update()
self.canvas.config(scrollregion=self.canvas.bbox("all")) # 滚动指定的范围
self.list_val.set((""))
def select_all(self):
'''全选按钮的回调函数
作用:所有多选按钮打勾、tree所有行都改变底色(被选中)'''
for item, [button] in self.orm.items():
rst = self.tv.item(item, "values")
if self.all_buttonvar.get() == 1:
button.select()
self.tv.item(item, tags='select')
self.set_song_id.add(rst[0])
else:
button.deselect()
self.tv.item(item, tags='oddrow')
self.set_song_id.remove(rst[0])
def select_button(self, item):
'''多选按钮的回调函数
作用:1.根据按钮的状态,改变对应item的底色(被选中)
2.根据所有按钮被选的情况,修改all_button的状态'''
button = self.orm[item][0]
button_value = button.getvar(button['variable'])
rst = self.tv.item(item, "values")
if button_value == '1':
self.tv.item(item, tags='select')
self.set_song_id.add(rst[0])
else:
self.tv.item(item, tags='oddrow')
self.set_song_id.remove(rst[0])
self.all_button_select() # 根据所有按钮改变 全选按钮状态
def select_tree(self, event):
'''tree绑定的回调函数
作用:根据所点击的item改变 对应的按钮'''
select_item = self.tv.focus()
button = self.orm[select_item][0]
button.invoke() # 改变对应按钮的状态,而且调用其函数
def all_button_select(self):
'''根据所有按钮改变 全选按钮状态
循环所有按钮,当有一个按钮没有被打勾时,全选按钮取消打勾'''
for [button] in self.orm.values():
button_value = button.getvar(button['variable'])
if button_value == '0':
self.all_buttonvar.set(0)
break
else:
self.all_buttonvar.set(1)
def create_search(self):
lf = tk.LabelFrame(self.root, text="请输入要搜索的歌曲,名称和歌手一起输入可以提高匹配(如 空帆船 朴树)",
font=("楷体", 12), fg="#FFFFFF", bg="#000080")
lf.pack(fill=X, padx=15, pady=8)
top_frame = tk.Frame(lf, bg="#000080")
top_frame.pack(fill=X, expand=YES, side=TOP, padx=15, pady=8)
self.search_key = StringVar()
self.searchEntry = tk.Entry(top_frame, textvariable=self.search_key, font=("楷体", 12), width=50)
self.searchEntry.pack(fill=X, expand=YES, side=LEFT)
tt1=tk.Button(top_frame, text="搜索", font=("楷体", 12), fg="#FFFFFF",
command=self.insert_tv, bg="#000080").pack(padx=15, fill=X, expand=YES)
def set_list_val(self,result):
# op.list_val=StringVar()
self.list_val.set(result)
if __name__ == '__main__':
op = info()
op.runfunction()
颜色选择来自博文:https://blog.csdn.net/cigo_2018/article/details/81092685