最近的实验需要做一个桌面小程序来收集实验信息,决定选用跨平台的Python
来写。
在Python图形界面编程
中常用的工具包有:
这次使用的是PyQt
(相关文档和资料比较多)。
PyQt
使用Qt开发程序可以从Qt Widgets
或/和Qt Quick
开始(Qt Widgets、QML、Qt Quick的区别)。我们对实验工具的界面要求不高,可以直接使用Qt Widgets
开发。它有几个重要的概念:
C1. UI 界面实现
QApplication
QWidget
C2. 组件通信
Signal & Slot (信号槽机制)
使用 PyInstaller 打包可执行程序
注意在打包后系统路径会发生变化
平时几种常用的获取当前运行脚本路径的方法:
1 2 3 4 5 6
| print(__file__) print(os.path.realpath(__file__)) print('using sys.executable:', repr(os.path.dirname(os.path.realpath(sys.executable)))) print('using sys.argv[0]:', repr(os.path.dirname(os.path.realpath(sys.argv[0])))) print(os.path.split(sys.argv[0])) print(sys.path[0])
|
在工程中的运行结果:
1 2 3 4 5 6
| /Users/huizhang/Desktop/testpath/path.py /Users/huizhang/Desktop/testpath/path.py using sys.executable: '/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/bin' using sys.argv[0]: '/Users/huizhang/Desktop/testpath' ('/Users/huizhang/Desktop/testpath', 'path.py') /Users/huizhang/Desktop/testpath
|
打包后的运行结果:
1 2 3 4 5 6
| path.py /Users/huizhang/path.py using sys.executable: '/Users/huizhang/Desktop/testpath/dist/path.app/Contents/MacOS' using sys.argv[0]: '/Users/huizhang/Desktop/testpath/dist/path.app/Contents/MacOS' ('/Users/huizhang/Desktop/testpath/dist/path.app/Contents/MacOS', 'path') /Users/huizhang/Desktop/testpath/dist/path.app/Contents/MacOS/base_library.zip
|
打包数据文件
pyinstaller
命令不能直接将工程中的数据文件一起打包,要实现这一步必须修改.spec
文件。legendtkl的博客中有PyInstaller
的简介中文教程,也可以直接查看官方文档。
添加数据文件只需要在a.datas
里面添加二元组即可,二元组第一个参数'/mygame/data'
是要添加的数据文件的本地索引,第二个参数'data'
是在打包后的工程中的位置。
1 2 3 4 5 6 7 8 9 10
| added_files = [ ( '/mygame/data', 'data' ), ( '/mygame/sfx/*.mp3', 'sfx' ), ( 'src/README.txt', '.' ) ] a = Analysis(... datas = added_files, ... )
|
PyInstaller
会把打包的位置存在sys._MEIPASS
,可以测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| img = os.path.join("res","img","test.jpg")
base_path0 = os.path.abspath(".") base_path1 = os.path.dirname(os.path.realpath(sys.argv[0])) try: base_path2 = sys._MEIPASS except Exception: base_path2 = os.path.abspath(".")
print("img0: ",os.path.join(base_path0,img)) print("img1: ",os.path.join(base_path1,img)) print("img2: ",os.path.join(base_path2,img))
|
工程中的运行结果都一样:
1 2 3 4
| img0: /Users/huizhang/Desktop/testinstaller/res/img/test.jpg img1: /Users/huizhang/Desktop/testinstaller/res/img/test.jpg img2: /Users/huizhang/Desktop/testinstaller/res/img/test.jpg
|
打包后:
1 2 3 4
| img0: /Users/huizhang/res/img/test.jpg img1: /Users/huizhang/Desktop/testinstaller/dist/test.app/Contents/MacOS/res/img/test.jpg img2: /Users/huizhang/Desktop/testinstaller/dist/test.app/Contents/MacOS/res/img/test.jpg
|
开始打包
1 2 3 4 5 6
| cd my_project_dir
pyi-makespec -w -n MyAppName -i appicon.icns MyMainScript.py
pyinstaller MyAppName.spec
|
附:使用PyQt过程中遇到的问题
QSound不能播放音乐
1
| QSoundEffect(qaudio): Error decoding source
|
QSound只能播放.wav
QUrl.fromLocalFile只能使用绝对路径
1
| QMediaPlayer.duration() == 0
|
QMediaPlayer.setMedia()
是异步执行的,如果在这个方法后马上调用QMediaPlayer.duration()
得到的将是错误的值,因为QMediaPlayer.setMedia()
还未设置好。应该在durationchanged
信号发出后重新给其赋值。
设置只播一首歌时setPlaybackMode(CurrentItemOnce)
,每次本首歌曲播完index会被重置为-1
。