本项目实现的功能(简易的音乐电子相册):
- 每 5s 自动播放下一张图片
- 可以手动点击,播放下一张图片
- 循环播放 MP3 音乐文件
使用说明
需将文件夹 photo 和 music 以及最终执行的 album.exe 文件放到同一目录下,双击运行 album.exe 即可。
- 注意至少在 photo 文件夹下包含一张图片才能正常运行 album.exe
- music 文件夹存放所要循环播放的 MP3 文件,如果不需要播放音乐,也可直接删除 music 文件夹
- 这里没有实现获取 photo 目录下的子目录的图片。主要考虑到照片太多的话,每次自动播放都从第一张图片开始,后面的图片可能需要很久才能播放,这可能不太友好
项目目录结构
(eln35) C:\Users\eln\PycharmProjects\Album>tree
文件夹 PATH 列表
卷序列号为 209A-F77C
C:.
│ album.exe # 最终生成的 exe 文件,从 dist 文件夹中复制出来
│ album.py # 源代码
│ README.html # 从 README.md 生成的网页
│ README.md # 说明文档
├─.idea # PyCharm 中新建项目时自动生成的文件夹,用于存放项目的配置信息。其中包括版本控制信息、历史记录等等
│ └─inspectionProfiles
├─build # 生成 exe 文件时自动生成的
│ └─album
├─dist # 生成 exe 文件时自动生成的,最终的生成 exe 文件保存在这里
├─music # 测试用的音乐文件(没有该路径也不影响运行)
│ Kalimba.mp3
│ Maid with the Flaxen Hair.mp3
│ Sleep Away.mp3
│
├─photo # 测试用的图片(必须包含一张图片)
│ Chrysanthemum.jpg
│ Desert.jpg
│ Hydrangeas.jpg
│ Jellyfish.jpg
│ Koala.jpg
│ Lighthouse.jpg
│ Penguins.jpg
│ Tulips.jpg
│
└─__pycache__ # python 运行的缓存文件夹,自动生成
生成 exe 文件
安装 python 的 pyinstaller 库:
(eln35) C:\Users\eln\PycharmProjects\Album>pip install pyinstaller
生成 exe 文件:
(eln35) C:\Users\eln\PycharmProjects\Album>rmdir /s /q dist
(eln35) C:\Users\eln\PycharmProjects\Album>rmdir /s /q build
(eln35) C:\Users\eln\PycharmProjects\Album>pyinstaller -F -w album.py
-w
直接发布的 exe 应用带命令行调试窗口,在指令内加入-w
命令可以屏蔽-F
注意指令区分大小写。这里是大写。使用-F
指令可以把应用打包成一个独立的 exe 文件,否则是一个带各种 dll 和依赖文件的文件夹-p
这个指令后面可以增加 pyinstaller 搜索模块的路径。因为应用打包涉及的模块很多。这里可以自己添加路径。不过经过笔者测试, site-packages 目录下都是可以被识别的,不需要再手动添加
源代码
# -*- coding: utf-8 -*-
"""
Created on 2019/8/20
@author: eln
@requirements: PyCharm 2017.2; Python 3.5.6 |Anaconda 4.1.1 (64-bit)
@decription: 用 Python 制作一个电子相册
"""
# pip install pillow pygame mutagen
import os
import sys
import threading
import tkinter as tk
import time
from PIL import ImageTk, Image
import pygame
from mutagen.mp3 import MP3
def playmusic():
"""播放音乐。"""
Path = r'music\\'
try:
list1 = os.listdir(Path) # 获取指定路径下所有的 mp3 文件
for x in list1:
if not (x.endswith('.mp3')):
list1.remove(x)
list2 = []
for i in list1:
s = os.path.join(Path, i) # 对路径与文件进行拼接
list2.append(s)
while True:
for n in list2:
# 获取每一首歌的时长
path = n
audio = MP3(n)
pygame.mixer.init() # 初始化所有引入的模块
pygame.mixer.music.load(path) # 载入音乐,音乐可以是 ogg、mp3 等格式
pygame.mixer.music.play() # 播放载入的音乐
time.sleep(int(audio.info.length)) # 获取每一首歌曲的时长,使程序存活的时长等于歌曲时长
except Exception as e:
print("Exception: %s" % e)
resolution = (1366, 768) # 分辨率
Path = r'photo\\' # 相册路径
Interval = 5 # 播放间隔.单位:s
Index = 0 # 当前照片计数
title = "电子相册" # 窗口标题
def getfiles():
"""获取图片文件名。"""
files = os.listdir(Path)
for x in files:
if not (x.endswith('.jpg') or x.endswith('.JPG') or x.endswith('.png')):
files.remove(x)
return files
files = getfiles()
print(files)
scaler = Image.ANTIALIAS # 设定 ANTIALIAS ,即抗锯齿
root = tk.Tk() # 创建窗口
root.title(title) # 设置窗口标题
img_in = Image.open(Path + files[0]) # 加载第一张图片
# img_in = Image.open("load.jpg") # 加载第一张图片
w, h = img_in.size # 获取图片大小
size_new = (int(w * resolution[1] / h), resolution[1])
img_out = img_in.resize(size_new, scaler) # 重新设置大小
img = ImageTk.PhotoImage(img_out) # 用 PhotoImage 打开图片
panel = tk.Label(root, image=img) # Label 自适应图片大小
panel.pack(side="bottom", fill="both", expand="yes")
def callback(e):
"""手动切换图片。"""
try:
global Index
for i, x in enumerate(files):
# 判断文件是否存在
if not os.path.isfile(Path + '%s' % x):
break
if i != Index: # 跳过已播放的图片
continue
print('手动处理图片', x, Index) # python 3.5
# print(unicode('手动处理图片 %s %d' % (x, Index), "utf8", errors="ignore")) # python 2.7.15
img_in = Image.open(Path + '%s' % x)
print(img_in)
w, h = img_in.size
size_new = (int(w * resolution[1] / h), resolution[1])
img_out = img_in.resize(size_new, scaler)
img2 = ImageTk.PhotoImage(img_out)
panel.configure(image=img2)
panel.image = img2
Index += 1
if Index >= len(files):
Index = 0
break
except Exception as e:
print("Exception: %s " % e)
sys.exit(1)
# root.bind("<Return>", callback)
root.bind("<Button-1>", callback) # 点击窗口切换下一张图片
def image_change():
"""自动切换图片。"""
try:
global Index
time.sleep(3)
while True:
for i, x in enumerate(files):
# 判断文件是否存在
if not os.path.isfile(Path + '%s' % x):
break
if i != Index: # 跳过已播放的图片
continue
print('自动处理图片', x, Index) # python 3.5
# print(unicode('自动处理图片 %s %d' % (x, Index), "utf8", errors="ignore")) # python 2.7.15
img_in = Image.open(Path + '%s' % x)
w, h = img_in.size
size_new = (int(w * resolution[1] / h), resolution[1])
img_out = img_in.resize(size_new, scaler)
img2 = ImageTk.PhotoImage(img_out)
panel.configure(image=img2)
panel.image = img2
Index += 1
if Index >= len(files):
Index = 0
time.sleep(Interval)
except Exception as e:
print("Exception: %s " % e)
sys.exit(1)
m = threading.Thread(target=playmusic) # 创建音乐播放线程
t = threading.Thread(target=image_change) # 创建图片切换线程
# python 可以通过 threading module 来创建新的线程,然而在创建线程的线程(父线程)关闭之后,相应的子线程可能却没有关闭
# 需要把 setDaemon 函数放在 start 函数前面解决此问题
m.setDaemon(True)
m.start() # 启动线程
t.start() # 启动线程
root.mainloop() # 窗口循环