python thinker treeview实现歌曲下载界面

界面图展示(包含菜单,滚动条,界面)

python thinker treeview实现歌曲下载界面_第1张图片

主要功能

1.本文只实现了界面展示功能(python.thinker),未添加具体下载歌曲的逻辑实现。
2.通过treeview画歌曲表格
3.选择需要下载歌曲后,点击下载按钮,通过self.list_val显示下载成功结果self.list_val显示如图所示
4.解决bug:点击按钮,后台运行耗时事件导致界面卡死问题

python thinker treeview实现歌曲下载界面_第2张图片
python thinker treeview实现歌曲下载界面_第3张图片

实现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方法
python thinker treeview实现歌曲下载界面_第4张图片

info类调用music.dll:
python thinker treeview实现歌曲下载界面_第5张图片

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("",
                             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('<>', 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

你可能感兴趣的:(python)