笔者工作业余时间也没什么爱好,社交圈子也小,主要娱乐就是背着自己带电瓶的卖唱音响到住地附近找个人多的位置唱唱KtV。
硬件上点歌就用笔记本电脑,歌曲都是网上下载的mkv格式的含有两个音轨的视频。因此点歌软件成了笔者的需求。
点歌软件需求极简单:
- 读磁盘上的目录取全部music,双击则调用播放器播放music。
- 自己常唱的歌曲可以选到自选歌曲列表。
- 支持按简拼搜索music
之前已经用多种开发工具写过,这次逢学习python的机会用它再写一个python版。
软件界面如下:
双击启动播放器。
就代码量上和用笔者用C#写的相同功能的软件对比下:
C#版软件提供的功能与本例子是完全相同。工作逻辑代码约230行,设计器代码(界面代码,系统自动生成)约210行,还不算汉字转拼音类的代码。
python版界面加工作逻辑代码一起约150行。也因此,笔者在编写python版的music时,明显感觉到python的代码精练浓缩(何况笔者只是初学python几天)。
c#版的相同功能的软件
完整的代码如下(python3.6.1环境下开发):
1 from tkinter import *
2 import tkinter.messagebox as messagebox
3 from xpinyin import Pinyin
4 import os
5 import win32api
6 import win32con
7
8 class music():
9 __musicPlayPath='D:\\Program Files (x86)\\SPlayer\\splayer.exe'
10 __userMusicDiskFilePath='D:\\usermusic.txt'
11 __musicDefaultList=[]
12 musicCurList=[]
13 __userSelMusicList=[]
14 def __init__(self,musicPath):
15 self.__musicPath=musicPath
16
17 def delUserMusicListItem(self,item):
18 if item in music.__userSelMusicList:
19 music.__userSelMusicList.remove(item)
20 messagebox.showinfo('提示','删除成功!')
21
22 def addUserMusicListItem(self,item):
23 if not(item in music.__userSelMusicList):
24 music.__userSelMusicList.append(item)
25
26 def readUserMuslicList(self):
27 return music.__userSelMusicList
28
29 def getSerachList(self):
30 return music.musicCurList
31
32 def setDefaultMuslic(self):
33 music.__musicDefaultList = self.readMusicFromDisk()
34
35 def readDefaultMuslic(self):
36 return music.__musicDefaultList
37
38 def getMusicListByUserKey(self,searchTxt):
39 print(searchTxt)
40 if str(searchTxt).__len__()<1:
41 music.musicCurList=music.__musicDefaultList[:]
42 return
43 music.musicCurList.clear()
44 p = Pinyin()
45 for m in music.__musicDefaultList:
46 py = p.get_initials(m, '')
47 if (py.upper()).find(searchTxt.upper())>=0:
48 music.musicCurList.append(m)
49
50 def saveUserMusicListToDisk(self):
51 f=open(music.__userMusicDiskFilePath,'w')
52 tmpAry=music.__userSelMusicList[:]
53 def addReturn(x):
54 return x+'\n'
55 f.writelines(map(addReturn, tmpAry))
56 f.close()
57
58 def loadUserMusicListFromDisk(self):
59 self.ifNotFileExistCreateEmptyFile()
60 f=open(music.__userMusicDiskFilePath,'r')
61 music.__userSelMusicList.clear()
62 tmpAry=f.readlines()
63 def delReturn(x):
64 return x[0:len(x)-1]
65 music.__userSelMusicList=list(map(delReturn, tmpAry))
66 f.close()
67
68 def ifNotFileExistCreateEmptyFile(self):
69 if not(os.path.exists(music.__userMusicDiskFilePath)):
70 f = open(music.__userMusicDiskFilePath, 'a')
71 f.close()
72
73 def readMusicFromDisk(self):
74 music.__musicDefaultList= [d for d in os.listdir(self.__musicPath) if d.upper().find(".MKV")>=0]
75 return music.__musicDefaultList
76
77 def playMusic(self,event):
78 w = event.widget
79 index = int(w.curselection()[0])
80 value = w.get(index)
81 win32api.ShellExecute(0, 'open', music.__musicPlayPath,
82 '\"'+ self.__musicPath+value+'\"', '', 1)
83
84 class gui(music):
85
86 def __init__(self,winName):
87 self.winName=winName
88 self._music__musicPath='D:\\KuGou\\'
89 self.lbx = StringVar()
90
91 def addMusicToUserList(self):
92 index = int(self.listbox1.curselection()[0])
93 value = self.listbox1.get(index)
94 print(value)
95 self.addUserMusicListItem(value)
96 self.saveUserMusicListToDisk()
97 messagebox.showinfo('成功','已经添加歌曲 \"'+value+'\"!')
98
99 def UpdateUserMusicList(self):
100 self.lbx.set(self.readUserMuslicList())
101
102 def searchMusic(self,event):
103 w = event.widget
104 txt = w.get()
105 self.getMusicListByUserKey(txt)
106 self.lbx.set(self.getSerachList())
107
108 def delUserMusic(self):
109 index = int(self.listbox1.curselection()[0])
110 value = self.listbox1.get(index)
111 self.delUserMusicListItem(value)
112 self.lbx.set(self.readUserMuslicList())
113 self.saveUserMusicListToDisk()
114
115 def allmusic(self):
116 self.lbx.set(self.readDefaultMuslic())
117
118 def initForm(self):
119 self.winName.title("music v1.0 by 刘小勇")
120 self.winName.geometry('600x480+10+10')
121 self.winName['bg']="pink"
122 self.winName.resizable(width=False,height=False)
123
124 self.btn1=Button(self.winName,text='全部歌曲',bg='lightblue',command=self.allmusic)
125 self.btn1.grid(row=1,column=1)
126 self.btn2=Button(self.winName,text='选中的歌曲',bg='lightblue',command=self.UpdateUserMusicList)
127 self.btn2.grid(row=1,column=2)
128 self.btn3=Button(self.winName,text='添加到选中的歌曲',bg='lightyellow',command=self.addMusicToUserList)
129 self.btn3.grid(row=1,column=3)
130 self.btn4=Button(self.winName,text='删除歌曲',bg='lightyellow',command=self.delUserMusic)
131 self.btn4.grid(row=1,column=4)
132
133 self.lab1=Label(self.winName,text='搜索:')
134 self.lab1.grid(row=1,column=5)
135 self.txt1=Entry(self.winName, width=20)
136 self.txt1.bind('<Return>',self.searchMusic)
137 self.txt1.grid(row=1,column=6)
138
139 self.scrollbar = Scrollbar()
140 self.scrollbar.grid(row=2,column=13,rowspan=13,sticky='NS')
141 self.listbox1=Listbox(self.winName,listvariable=self.lbx,width = 82,height=25,yscrollcommand=self.scrollbar.set)
142 self.setDefaultMuslic();
143 self.lbx.set(self.readDefaultMuslic())
144 self.listbox1.bind('<Double-Button-1>',self.playMusic)
145 self.listbox1.grid(row=2,column=1,columnspan=12)
146 self.scrollbar.config(command=self.listbox1.yview)
147 self.loadUserMusicListFromDisk()
148
149
150 tk=Tk()
151 form=gui(tk)
152 form.initForm()
153 tk.mainloop()
在编码过程中研究过的一些知识点汇集如下:
1. 如何在搜索框触发搜索后更新listbox, 这个是利用tkinter的 StringVar()
参考笔者贴子:Python tkinter 控件更新信息
2. 控件listbox的滚动条
参考笔者贴子:python tkinter Listbox用法
3. 控件的事件绑定
参考笔者贴子:python tkinter教程-事件绑定
4. 控件的布局
参考笔者贴子:python tkinter学习——布局
5. 高阶函数
参考笔者贴子:python 函数式编程:高阶函数,map/reduce
6. 汉字转拼音
参考笔者贴子:Python汉字转换成拼音
7. win32模块
参考笔者贴子:Python中四种运行其他程序的方式
笔者写python程序时的开发ide集成环境是pycharm,在写tkinter代码时,关于控件的属性没有什么有意义的提示。因为去记住控件属性的完整拼写是不容易也是没有意义的事。
这也是写界面代码比较麻烦的地方。
长期依赖visual studio c#的IDE强大的语法提示、拼写补全功能, 已经让笔者手工编码的能力高度退化,对于没有属性提示的编程环境已经无法想像。