赞
踩
最近一个项目需要用到python图形界面的快速开发,还要求在我们可以看到的代码上看不到类和对象
的使用痕迹,这里直接先把pyqt
排除了,pyqt臃肿且复杂,比较适合大型界面开发,小型界面开发再使用这个东西,学习成本极大(因为无法使用designer工具,这个designer生成的代码是有类
的)。然后这里考虑使用tkinter或者gooey,这里我先尝试了tkinter,感觉不是特别美观,那就直接再次尝试Gooey这个框架的使用。
开发的是一个python图形界面的网易云音乐下载播放器
,播放为直接运行(使用本地默认音乐播放器)。网易云的API集成则是在我的个人服务器上面。
这个框架是基于另一个框架wxpython的,属于阉割版本,相比于wxpython也许功能会少一些,但是我这里用的正好合适,勉强可以凑活着用。但是虽然组件有点少,但是正好使用其他框架的组件来代替也不是不可以。而且这个Gooey好像没有页面跳转,稍显生硬,目前没发现怎么将执行完程序后的界面进行改动,如果能改动也会更好一些,总之对于这个项目能用就行了。
开发周期太短,所以做出来的内容也很简单,只预算使用不到3个小时开发出来。
首先进行一个Gooey库的安装
pip install Gooey
搜索结果直接通过程序执行后出现的界面输出了,省了一点麻烦,但是用户使用的时候又多了一点麻烦,但是没办法,没找到list空间来放搜索结果。
左侧为command选择,相当于菜单项;右侧为具体的选项。
界面获得的数据会被存放在一个namespace空间内部,这个人namespace的数据使用方式为args.变量名
,直接就能把数据给点出来了。
因为这个框架似乎没有实现按钮功能,所以怎么与用户交互呢?因为程序执行时的控制台输出会被转到这个框架内显示出来,所以呈现给用户这个我们的响应还是很简单的,直接print
出数据即可。
如果程序运行出错了的话,程序就会直接结束运行,而不会给用户有提示,这里可以考虑使用python的异常处理
和其他框架的弹出交互框
我的代码如下:
# WindowUi.py import time from gooey import Gooey, GooeyParser import CloudMusicDownloaderAndPlayer @Gooey( richtext_controls=True, # 打开终端对颜色支持 program_name="网易云下载播放器", # 程序名称 encoding="utf-8", # 设置编码格式,打包的时候遇到问题 progress_regex=r"^progress: (\d+)%$" # 正则,用于模式化运行时进度信息 ) def main(): settings_msg = '本程序提供对网易云音乐的歌单、单曲的搜索;单曲的下载与播放。\n使用网易云官方公开的API,API服务部署在我的个人服务器上(将于2022年3月份过期)' parser = GooeyParser(description=settings_msg) # 添加上方的应用信息 subs = parser.add_subparsers(help='commands', dest='command') """ 搜索界面 """ Searchparser = subs.add_parser('搜索') Searchparser.add_argument("SearchKeyWords", metavar='搜索关键词', default='牵丝戏') verbosity = Searchparser.add_mutually_exclusive_group("搜索类型选择") verbosity.add_argument('-t', '--verbozze', dest='单曲', action="store_true", help="搜索类型:单曲") verbosity.add_argument('-q', '--quiet', dest='歌单', action="store_true", help="搜索类型:歌单") """ 获取歌单内单曲界面 """ Leftparser = subs.add_parser('歌单详情') Leftparser.add_argument("GotPlayListId", metavar='歌单id', help="请输入上面搜索后获得的歌单id", default='30352891') """ 下载播放界面 """ Leftparser = subs.add_parser('下载播放') Leftparser.add_argument("GotMusicId", metavar='歌曲id', help="请输入你想下载播放的歌曲id", default='30352891') """ 播放界面 """ Leftparser = subs.add_parser('播放') """ 程序功能实现 """ args = parser.parse_args() """界面获得的数据进行处理""" global GlobalMusicId, GlobalMusicName if args.command == "搜索": inputSearchKeywords = args.SearchKeyWords if args.单曲 == True: searchType, searchKeywords = CloudMusicDownloaderAndPlayer.search("单曲:" + inputSearchKeywords) songIds, songNames, singers = CloudMusicDownloaderAndPlayer.getSingleMusicSearchResult(searchType, searchKeywords) else: searchType, searchKeywords = CloudMusicDownloaderAndPlayer.search("歌单:" + inputSearchKeywords) CloudMusicDownloaderAndPlayer.getPlayListSearchResult(searchType, searchKeywords) elif args.command == "歌单详情": songIds, songNames, singers = CloudMusicDownloaderAndPlayer.getMusicsDetail(args.GotPlayListId) for i in range(len(songIds)): print(songIds[i], songNames[i], singers[i]) elif args.command == "下载播放": try: tmp = args.GotMusicId if tmp != "": GlobalMusicId = int(tmp) songUrl, songType = CloudMusicDownloaderAndPlayer.getMusicUrl(GlobalMusicId) GlobalMusicName = CloudMusicDownloaderAndPlayer.getMusicName(GlobalMusicId) songFileName = CloudMusicDownloaderAndPlayer.downloadMusic(GlobalMusicName, songUrl, songType) print("歌曲下载成功,地址为:", songFileName) except: print("歌曲下载错误,请检查您的输入是否正确!") pass elif args.command == "播放": print("请选择你想要播放的歌曲") time.sleep(1) CloudMusicDownloaderAndPlayer.playMusic() else: print("非常感谢您的使用,期待您的下次使用。再见!") time.sleep(1) return # print(args, flush=True) # 坑点:flush=True在打包的时候会用到 if __name__ == '__main__': main()
另一个文件内容为:
""" 打包:pyinstaller -F --icon=icon.ico CloudMusicDownloaderAndPlayer.py 传递参数的时候带着格式,参数格式如下: name: 前两个字为格式,用来判断搜索的是歌曲还是歌单 """ import os import requests import tkinter from tkinter import filedialog import time baseUrl = "http://m4xlmum.top:3000" help = """《小工具使用指南》 单曲搜索: 1. 搜索单曲(歌单)->搜索单曲->输入你想搜索的歌曲关键词->根据搜索到的歌曲id的输出输入你想下载播放的歌曲->下载(后播放)歌曲->输入你想下载的歌曲的id。 歌单搜索: 2. 搜索单曲(歌单)->搜索歌单->输入你想搜索的歌单关键词->根据搜索到的歌单id的输出输入你想具体查看的歌单的详细信息(包括歌单内所包含的歌曲额详细信息)>之后的流程就跟单曲的操作相同了。 """ mainMenu = """******************* 1. 搜索单曲(歌单) 2. 下载(后播放)歌曲 3. 播放歌曲 0. 退出 ******************* [+]请输入你的选择:""" searchMenu = """******************* 1. 搜索单曲 2. 搜索歌单 3. 返回上级选择 ******************* [+]请输入你的选择:""" # 存放用户经过搜索处理之后选择的歌曲id GlobalMusicId = 30352891 # 牵丝戏 ['银临', 'Aki阿杰'] GlobalMusicName = "牵丝戏" # P1搜索处理,返回信息为{搜索类型、搜索关键词} def search(name): """ type: 搜索类型;默认为 1 即单曲 , 取值意义 : 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频, 1018:综合 """ searchType = name[:2] searchKeywords = name[3:] typeDict = {"单曲": 1, "歌单": 1000} return typeDict[searchType], searchKeywords # 有songs格式的数据获得`songIds, songNames, singers`这几个数据 def getSongsInfo(songs): songIds, songNames, singers = [], [], [] for i in range(len(songs)): song = songs[i] songId = song["id"] songName = song["name"] # 设置歌手名的格式 singer = "歌手:" artists = "artists" if "artists" in song.keys() else "ar" for artist in song[artists]: singer = singer + artist["name"] + "、" print(songId, songName, singer) # 获取的数据,加入list songIds.append(songId) songNames.append(songName) singers.append(singer) return songIds, songNames, singers # P2单曲处理,返回信息包括{歌曲id、歌曲名、歌手} def getSingleMusicSearchResult(searchType, searchKeywords): searchUrl = baseUrl + "/search" """ type: 搜索类型 limit: 限制返回个数 keywords:搜索关键词 """ params = { "type": searchType, "limit": 10, "keywords": searchKeywords } resp = requests.get(url=searchUrl, params=params).json() songs = resp["result"]["songs"] songIds, songNames, singers = getSongsInfo(songs) return songIds, songNames, singers # P2歌单处理,返回值为{歌单包含歌曲id} 注:歌单是为了区别歌曲类型,所以这里搜索歌单只返回一个歌单值,歌单内包含很多歌曲 def getPlayListSearchResult(searchType, searchKeywords): searchUrl = baseUrl + "/search" params = { "type": searchType, "limit": 10, "keywords": searchKeywords } resp = requests.get(url=searchUrl, params=params).json() playLists = resp["result"]["playlists"] playListIds, playListNames, creators, descriptions = [], [], [], [] for i in range(len(playLists)): playList = playLists[i] id, name, creator = playList["id"], playList["name"], playList["creator"]["nickname"] if playList[ "creator"] is not None else "未找到Creator" print("[-] id:{0}\tname:{1}\tcreator:{2}".format(id, name, creator)) playListId = playLists[0]["id"] # 获取搜索到的第一个歌单的id return playListId def getPlayListDetail(playListId): playListDetailUrl = baseUrl + "/playlist/detail" params = { "id": playListId } playListDetail = requests.get(url=playListDetailUrl, params=params).json() # 获取歌单详情 print("该歌单的描述为: " + playListDetail["playlist"]["description"] if playListDetail["playlist"][ "description"] is not None else "该歌单无描述" + "\n") playListMusics = playListDetail["playlist"]["trackIds"] # 获取歌单内的所有音乐id,此时获得的还不是id,只是一个包含id的待处理数据集 playListMusicIds = [] # 获取歌单内所有音乐的id # print(len(playListMusics)) for i in range(len(playListMusics)): playListMusicIds.append(playListMusics[i]["id"]) # print(playListMusicIds) return playListMusicIds # P2扩展,获取歌单内的歌曲信息 def getMusicsDetail(playListId): playListMusicIds = getPlayListDetail(playListId) playListDetailUrl = baseUrl + "/song/detail" params = { "ids": str(playListMusicIds)[1:-1] } resp = requests.get(url=playListDetailUrl, params=params).json() songs = resp["songs"] songIds, songNames, singers = getSongsInfo(songs) return songIds, songNames, singers def getMusicUrl(songId): musicUrl = baseUrl + "/song/url" params = { "id": songId } resp = requests.get(url=musicUrl, params=params).json() songUrl = resp["data"][0]["url"] songType = resp["data"][0]["type"] return songUrl, songType # 歌曲的命名格式应为"牵丝戏 歌手:银临、Aki阿杰" def getMusicName(songId): musicUrl = baseUrl + "/song/detail" params = { "ids": songId } resp = requests.get(url=musicUrl, params=params).json() song = resp["songs"][0] songName = song["name"] # 加上歌手名 songName += " 歌手:" for i in range(len(song["ar"])): songName = songName + song["ar"][i]["name"] + "、" songName = songName[:-3] return songName def downloadMusic(songName, songUrl, songType="mp3"): # 设置文件的保存路径 root = tkinter.Tk() root.withdraw() folder = filedialog.askdirectory() songFileName = "{0}.{1}".format(songName, songType) Filepath = r"{0}/{1}".format(folder, songFileName) with open(Filepath, "wb") as file: musicContent = requests.get(url=songUrl).content file.write(musicContent) os.system('"' + Filepath + '"') return Filepath def playMusic(): root = tkinter.Tk() root.withdraw() # 设置文件的保存路径 Filepath = filedialog.askopenfilename() # 获得选择好的文件 # 使用本机默认播放器播放音频 os.system('"' + Filepath + '"') def playerMenu(): global GlobalMusicId, GlobalMusicName while True: inputChoice = input(mainMenu) if inputChoice == "1": inputSearchChoice = input(searchMenu) if inputSearchChoice == "1": inputSearchKeywords = input("[+] 请输入您搜索的关键词:") searchType, searchKeywords = search("单曲:" + inputSearchKeywords) songIds, songNames, singers = getSingleMusicSearchResult(searchType, searchKeywords) tmp = input("请输入你想要进行后续播放下载的歌曲id(默认歌曲id为30352891):") try: GlobalMusicId = int(tmp) GlobalMusicName = songNames[songIds.index(GlobalMusicId)] + " " + singers[ songIds.index(GlobalMusicId)] except: pass elif inputSearchChoice == "2": inputSearchKeywords = input("[+] 请输入您搜索的关键词:") searchType, searchKeywords = search("歌单:" + inputSearchKeywords) songIds, songNames, singers = getMusicsDetail(searchType, searchKeywords) for i in range(len(songIds)): print(songIds[i], songNames[i], singers[i]) else: pass elif inputChoice == "2": try: tmp = input("请输入要下载的歌曲的id(此处不输入,则默认使用搜索处输入的id):") if tmp != "": GlobalMusicId = int(tmp) songUrl, songType = getMusicUrl(GlobalMusicId) GlobalMusicName = getMusicName(GlobalMusicId) songFileName = downloadMusic(GlobalMusicName, songUrl, songType) print("歌曲下载成功,地址为:", songFileName) except: print("歌曲下载错误,请检查您的输入是否正确!") pass elif inputChoice == "3": playMusic() else: print("非常感谢您的使用,期待您的下次使用。再见!") time.sleep(1) return """ if __name__ == '__main__': print(help) playerMenu() """
这个软件工程的大作业到这里应该就告一段落了,写的代码还是太少了,但是也确实是工期太短了,而且我没多少时间用在这个东西上面,毕竟还有其他作业,其他项目,其他实验。
总之这300多行已经差不多是我的极限了,也确实是黔驴技穷了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。