用Python实现序列帧播放器
注意
以下所有代码不可直接使用,若要使用请到百度网盘上下载源码!
链接:https://pan.baidu.com/s/1P0x8ddbnn5veFnFQJLQ0tw
提取码:6666
程序思路
- 用PyQt5创建一个窗口
- 使用tkinter.messagebox和filedialog进行用户交互
- 使用Pygame播放序列帧动画
- 使用Opencv2无损合成序列帧
窗口设计
用Qt designer设计窗口,并用PyUIC转换,有需要的小伙伴请点击这里(提取码6666)
主界面
设计完成后主窗口大概是这个样子
这里是代码
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 194)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(80, 30, 281, 31))
font = QtGui.QFont()
font.setFamily("Adobe Caslon Pro Bold")
font.setPointSize(13)
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setObjectName("label")
self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(50, 70, 311, 41))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_2 = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
self.lineEdit = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
self.lineEdit.setReadOnly(True)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.pushButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.pushButton_2 = QtWidgets.QPushButton(Form)
self.pushButton_2.setGeometry(QtCore.QRect(50, 130, 151, 41))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton_3 = QtWidgets.QPushButton(Form)
self.pushButton_3.setGeometry(QtCore.QRect(214, 130, 151, 41))
self.pushButton_3.setObjectName("pushButton_3")
Form.setFixedSize(Form.width(), Form.height())
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "序列帧播放器 v3.2.4"))
self.label.setText(_translate("Form", "欢迎使用序列帧播放器 v3.2.4"))
self.label_2.setText(_translate("Form", "文件路径:"))
self.pushButton.setText(_translate("Form", "浏览..."))
self.pushButton_2.setText(_translate("Form", "预览动画"))
self.pushButton_3.setText(_translate("Form", "合成动画"))
self.pushButton_2.setDisabled(True)
self.pushButton_3.setDisabled(True)
def show_mainwindow():
app=QtWidgets.QApplication(sys.argv)
mainwindow_ui = Ui_Form()
qMainWindow = QtWidgets.QMainWindow()
mainwindow_ui.setupUi(qMainWindow)
qMainWindow.show()
sys.exit(app.exec_())
show_mainwindow()
其他界面不作详细解释,需要源码请点击这里(提取码6666)
窗口交互
使用PyQt5的槽,绑定按钮事件
self.控件名.事件.connect(执行函数)
代码如下
def choose_file(self):
self.pushButton.setDisabled(True)
path=askdirectory()
self.pushButton.setDisabled(False)
if path!="":
self.pushButton_2.setDisabled(False)
self.pushButton_3.setDisabled(False)
self.lineEdit.setText(path)
else:
self.pushButton_2.setDisabled(True)
self.pushButton_3.setDisabled(True)
def b2(self):
f = open("setting.ini" , "w")
f.write("path=" + self.lineEdit.text())
f.close()
import compose
class Login(QtWidgets.QWidget , compose.Ui_Form):
def __init__(self):
super(Login , self).__init__()
self.setupUi(self)
self.Form.setWindowModality(QtCore.Qt.ApplicationModal)
lg = Login()
lg.show()
def brot(self):
self.pushButton_2.setDisabled(True)
self.pushButton_3.setDisabled(True)
f = open("setting.ini", "w")
f.write("path=" + self.lineEdit.text())
f.close()
import browse
class Browse(QtWidgets.QWidget, browse.Ui_Form):
def __init__(self):
super(Browse, self).__init__()
self.setupUi(self)
self.Form.setWindowModality(QtCore.Qt.ApplicationModal)
rg = Browse()
rg.show()
self.pushButton_2.setDisabled(False)
self.pushButton_3.setDisabled(False)
def bro(self):
#here
f = open("setting.ini", "w")
f.write("path=" + self.lineEdit.text())
f.close()
import browse
class Browse(QtWidgets.QWidget, browse.Ui_Form):
def __init__(self):
super(Browse, self).__init__()
self.setupUi(self)
self.Form.setWindowModality(QtCore.Qt.ApplicationModal)
rg = Browse()
rg.show()
self.pushButton_2.setDisabled(False)
self.pushButton_3.setDisabled(False)
绑定槽函数
self.pushButton.clicked.connect(self.choose_file)
self.pushButton_2.clicked.connect(self.brot)
self.pushButton_3.clicked.connect(self.b2)
预览模块
预览使用Pygame进行图片加载并循环播放。
代码如下
import glob
import os
import tkinter
from tkinter import messagebox
from PyQt5.QtWidgets import QMessageBox
root=tkinter.Tk()
root.withdraw()
from PyQt5 import QtCore, QtGui, QtWidgets
import time
import sys
import pygame
import threading
import random
from PIL import Image
from pygame.locals import *
pic=[]
def main():
global pic
pygame.init()
f = open("setting.ini", "r").read()
f = f.split("\n")[0]
f = f.split("=")[1]
fn = os.listdir(f)
imgf = fn[0].split(".")[1]
g = glob.glob(f + "/*." + imgf)
img = Image.open(g[0])
imgSize = img.size # 大小/尺寸
w = img.width # 图片的宽
o = img.height # 图片的高
h=open("setting.ini","r").read()
if bool(h.split("\n")[1].split("=")[1] == "True"):
try:
screen = pygame.display.set_mode((w, o), flags=FULLSCREEN | HWSURFACE)
except:
screen = pygame.display.set_mode((w, o))
else:
screen = pygame.display.set_mode((w, o))
text = pygame.font.SysFont("SimHei", 30)
pygame.display.set_caption("动图预览")
index = 0
fcclock = pygame.time.Clock() # 创建一个时间对象
fcclock.tick(int(h.split("\n")[2].split("=")[1])) # 调用Clock()类创建的对象中的tick()函数
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
screen.fill((100, 100, 100))
screen.blit(pic[index], (0, 0))
text = pygame.font.SysFont("SimHei", 10)
screen.blit(text.render("Animation file displayed " + f, True, (255, 255, 255)), (0, o - 10))
screen.blit(text.render("Software maker Xiao Shen", True, (255, 255, 255)), (w - 120, o - 10))
screen.blit(text.render("Current frame: " + str(index) + "/" + str(len(pic)), True, (255, 255, 255)), (0, 0))
screen.blit(text.render("Press ESC to exit", True, (255, 255, 255)), (w - 85, 0))
# Press ESC to exit
index += 1
if index >= len(pic):
index = 0
pygame.display.update()
fcclock.tick(int(h.split("\n")[2].split("=")[1]))
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 97)
font = QtGui.QFont()
font.setPointSize(11)
Form.setFont(font)
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(20, 20, 121, 16))
font = QtGui.QFont()
font.setPointSize(10)
self.label.setFont(font)
self.label.setObjectName("label")
self.progressBar = QtWidgets.QProgressBar(Form)
self.progressBar.setGeometry(QtCore.QRect(20, 50, 401, 23))
self.progressBar.setMaximum(0)
self.progressBar.setProperty("value", 0)
self.progressBar.setFormat("")
self.progressBar.setObjectName("progressBar")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(310, 20, 71, 20))
font = QtGui.QFont()
font.setPointSize(10)
self.label_2.setFont(font)
self.Form=Form
self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_2.setObjectName("label_2")
self.retranslateUi(Form)
Form.setFixedSize(Form.width(), Form.height())
t=threading.Thread(target=self.time_go)
t.start()
QtCore.QMetaObject.connectSlotsByName(Form)
def time_go(self):
for i in range(11):
self.label_2.setText("("+str(i)+"/10)")
if i==6:
time.sleep(3)
f = open("setting.ini", "r").read()
f = f.split("\n")[0]
f = f.split("=")[1]
fn = os.listdir(f)
try:
imgf = fn[0].split(".")[1]
g = glob.glob(f + "/*." + imgf)
if imgf == 'jpg':
pass
elif imgf == 'png':
pass
elif imgf == 'jpeg':
pass
elif imgf == 'bmp':
pass
else:
print(自动)
if len(g) == 0:
print(自动)
img = Image.open(g[0])
except:
QtWidgets.QMessageBox.critical(QtWidgets.QWidget(),"错误", "选择的文件夹无法被识别。\n请检查以下原因:\n1.文件夹内没有图片\n2.文件夹内有非图片文件\n请重新选择文件!")
self.Form.close()
time.sleep(random.uniform(0.0001000,0.2000000))
self.Form.hide()
time.sleep(0.3)
self.Form.setWindowTitle("Loading resource")
self.label.setText("正在加载资源...")
f = open("setting.ini", "r").read()
f = f.split("\n")[0]
f = f.split("=")[1]
fn = os.listdir(f)
imgf = fn[0].split(".")[1]
g = glob.glob(f + "/*." + imgf)
self.label_2.setText("(0/"+str(len(g))+")")
self.Form.show()
count=0
for i in g:
global pic
pic.append(pygame.image.load(i))
count+=1
self.label_2.setText("("+str(count)+"/" + str(len(g)) + ")")
self.Form.close()
main()
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Loading component"))
self.label.setText(_translate("Form", "正在加载组件..."))
# 这里是为了耗时间,不需要的朋友可以去掉
self.label_2.setText(_translate("Form", "(0/10)"))
def show_mainwindow():
app=QtWidgets.QApplication(sys.argv)
mainwindow_ui = Ui_Form()
qMainWindow = QtWidgets.QMainWindow()
mainwindow_ui.setupUi(qMainWindow)
qMainWindow.show()
sys.exit(app.exec_())
show_mainwindow()
渲染模块
使用OpenCV库进行图片的合成,这里有一个小细节
——计时模块
大概就是一个进位原理
代码如下
def time_go(self):
while True:
self.label_15.setText("已用时间:" + self.nt)
h=int(self.nt.split(":")[0])
m = int(self.nt.split(":")[1])
s = int(self.nt.split(":")[2])
#self.progressBar.setValue(int(self.value))
time.sleep(1)
s+=1
if s>=60:
s=0
m+=1
if m>=60:
s=0
m=0
h+=1
ss="00"
sm="00"
sh="00"
if s<10:
ss="0"+str(s)
else:
ss=str(s)
if m<10:
sm="0"+str(m)
else:
sm=str(m)
if h<10:
sh="0"+str(h)
else:
sh=str(h)
self.nt=sh+":"+sm+":"+ss
self.label_15.setText("已用时间:"+self.nt)
if self.mt==False:
return
我们决定使用多线程进行一个加快的处理
用法:
thread=threading.Thread(target=函数名)
thread.start # 启动线程
图片的合成过程有些复杂,不多说,直接放代码
def main_thread(self):
self.label_13.setText("当前状态:正在准备")
time.sleep(1.3)
self.label_13.setText("当前状态:正在合成")
img_array = []
fn = os.listdir(open("setting.ini","r").read().split("\n")[0].split("=")[1])
imgf = fn[0].split(".")[1]
g = glob.glob(open("setting.ini","r").read().split("\n")[0].split("=")[1] + "/*." + imgf)
add = 100 / len(g)
for filename in g:
self.label_14.setText("当前合成项:" + filename)
if self.value <= 100:
self.value += add
img = cv2.imdecode(ny.fromfile(filename, dtype=ny.uint8), -1)
img_array.append(img)
fps=self.spinBox.value()
_width=int(open("config.tmp","r").read().split(" ")[0])
_height =int(open("config.tmp", "r").read().split(" ")[1])
self.label_13.setText("当前状态:正在写入")
out = cv2.VideoWriter(self.lineEdit.text(), cv2.VideoWriter_fourcc(*'DIVX'), fps, (_width, _height))
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
self.mt=False
self.label_13.setText("当前状态:合成完毕")
self.progressBar.setMaximum(1)
self.label_16.setText("100%")
QtWidgets.QMessageBox.information(QtWidgets.QWidget(), "完成","合成已完成\n耗时 "+self.nt+" 秒!")
self.pushButton.setDisabled(False)
self.pushButton_2.setDisabled(False)
self.lineEdit.setDisabled(False)
self.spinBox.setDisabled(False)
self.comboBox_3.setDisabled(False)